gui: add "New Sud" menu action, disable Start/Pause/Stop for an empty sud
File > New Sud loads an empty schedule (zero steps) onto the server, same mechanism as Load but with a built-in empty document. Tracks whether the loaded schedule actually has steps (sud_empty) separately from whether a Sud is configured at all (sud_loaded) - Start/Pause/Stop now require both, and the Automatic tab's forecast shows "No sud loaded" instead of an empty plot while sud_empty.
This commit is contained in:
@@ -3712,6 +3712,7 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>&File</string>
|
<string>&File</string>
|
||||||
</property>
|
</property>
|
||||||
|
<addaction name="actionSudNew"/>
|
||||||
<addaction name="actionSudSave"/>
|
<addaction name="actionSudSave"/>
|
||||||
<addaction name="actionSudLoad"/>
|
<addaction name="actionSudLoad"/>
|
||||||
</widget>
|
</widget>
|
||||||
@@ -3765,6 +3766,14 @@
|
|||||||
<string>Stop</string>
|
<string>Stop</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionSudNew">
|
||||||
|
<property name="text">
|
||||||
|
<string>&New Sud</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Replace the running Sud's schedule with an empty one</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="actionSudSave">
|
<action name="actionSudSave">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Save Sud...</string>
|
<string>&Save Sud...</string>
|
||||||
|
|||||||
+30
-3
@@ -160,6 +160,15 @@ class SudForecastPlot(FigureCanvasQTAgg):
|
|||||||
self.figure.tight_layout()
|
self.figure.tight_layout()
|
||||||
self.draw_idle()
|
self.draw_idle()
|
||||||
|
|
||||||
|
def show_no_schedule(self):
|
||||||
|
self.line.set_data([], [])
|
||||||
|
self.clear_progress()
|
||||||
|
self.ax.relim()
|
||||||
|
self.ax.autoscale_view()
|
||||||
|
self.ax.set_title("No sud loaded", fontsize='small')
|
||||||
|
self.figure.tight_layout()
|
||||||
|
self.draw_idle()
|
||||||
|
|
||||||
def set_progress(self, t_min):
|
def set_progress(self, t_min):
|
||||||
self.progress_line.set_xdata([t_min, t_min])
|
self.progress_line.set_xdata([t_min, t_min])
|
||||||
self.progress_line.set_visible(True)
|
self.progress_line.set_visible(True)
|
||||||
@@ -189,6 +198,11 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
self.ws_client = WsClient(listener=self.msg_dispatch, loop=loop)
|
self.ws_client = WsClient(listener=self.msg_dispatch, loop=loop)
|
||||||
self.connected = False
|
self.connected = False
|
||||||
self.sud_loaded = False
|
self.sud_loaded = False
|
||||||
|
# Whether the loaded schedule has zero steps (e.g. just after "New
|
||||||
|
# Sud") - distinct from sud_loaded, which just means a Sud is
|
||||||
|
# configured server-side at all. Defaults True so Start/Pause/Stop
|
||||||
|
# stay disabled until a non-empty schedule is actually confirmed.
|
||||||
|
self.sud_empty = True
|
||||||
self.sud_state = None
|
self.sud_state = None
|
||||||
self.sud_user_message = None
|
self.sud_user_message = None
|
||||||
# Global, not Sud-specific - from the 'System' channel's one-time
|
# Global, not Sud-specific - from the 'System' channel's one-time
|
||||||
@@ -244,6 +258,7 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
self.actionStart.triggered.connect(self.on_action_sud_start)
|
self.actionStart.triggered.connect(self.on_action_sud_start)
|
||||||
self.actionPause.triggered.connect(self.on_action_sud_pause)
|
self.actionPause.triggered.connect(self.on_action_sud_pause)
|
||||||
self.actionStop.triggered.connect(self.on_action_sud_stop)
|
self.actionStop.triggered.connect(self.on_action_sud_stop)
|
||||||
|
self.actionSudNew.triggered.connect(self.on_action_sud_new)
|
||||||
self.actionSudSave.triggered.connect(self.on_action_sud_save)
|
self.actionSudSave.triggered.connect(self.on_action_sud_save)
|
||||||
self.actionSudLoad.triggered.connect(self.on_action_sud_load)
|
self.actionSudLoad.triggered.connect(self.on_action_sud_load)
|
||||||
self.Slider_pwr_soll.valueChanged.connect(self.on_slider_pwr_soll_changed)
|
self.Slider_pwr_soll.valueChanged.connect(self.on_slider_pwr_soll_changed)
|
||||||
@@ -341,6 +356,7 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
if not connected:
|
if not connected:
|
||||||
# Nothing reported over a dead connection can be trusted anymore.
|
# Nothing reported over a dead connection can be trusted anymore.
|
||||||
self.sud_loaded = False
|
self.sud_loaded = False
|
||||||
|
self.sud_empty = True
|
||||||
self.sud_state = None
|
self.sud_state = None
|
||||||
self.sud_user_message = None
|
self.sud_user_message = None
|
||||||
self.ambient_temp = None
|
self.ambient_temp = None
|
||||||
@@ -353,11 +369,12 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
self.update_status_env_label()
|
self.update_status_env_label()
|
||||||
|
|
||||||
def update_sud_actions(self):
|
def update_sud_actions(self):
|
||||||
running = self.sud_loaded and self.sud_state in SUD_RUNNING_STATES
|
can_run = self.sud_loaded and not self.sud_empty
|
||||||
paused = self.sud_loaded and self.sud_state == SUD_PAUSED_STATE
|
running = can_run and self.sud_state in SUD_RUNNING_STATES
|
||||||
|
paused = can_run and self.sud_state == SUD_PAUSED_STATE
|
||||||
# Start also doubles as Resume while paused; Stop can still abort a
|
# Start also doubles as Resume while paused; Stop can still abort a
|
||||||
# paused run, but there's nothing left to Pause once already paused.
|
# paused run, but there's nothing left to Pause once already paused.
|
||||||
self.actionStart.setEnabled(self.sud_loaded and not running)
|
self.actionStart.setEnabled(can_run and not running)
|
||||||
self.actionPause.setEnabled(running)
|
self.actionPause.setEnabled(running)
|
||||||
self.actionStop.setEnabled(running or paused)
|
self.actionStop.setEnabled(running or paused)
|
||||||
|
|
||||||
@@ -429,6 +446,11 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
QtWidgets.QMessageBox.information(self, "Sud", message)
|
QtWidgets.QMessageBox.information(self, "Sud", message)
|
||||||
self.msg_sud.send({'Confirm': True})
|
self.msg_sud.send({'Confirm': True})
|
||||||
|
|
||||||
|
def on_action_sud_new(self):
|
||||||
|
# Replaces the running Sud's schedule with an empty one, same as
|
||||||
|
# Load but with a built-in empty document instead of a file dialog.
|
||||||
|
self.msg_sud.send({'Load': {'Name': '', 'Description': '', 'pot_mass': 0, 'pot_material': None, 'steps': []}})
|
||||||
|
|
||||||
def on_action_sud_save(self):
|
def on_action_sud_save(self):
|
||||||
# Pick the destination up front, on the GUI thread - the actual
|
# Pick the destination up front, on the GUI thread - the actual
|
||||||
# write happens in on_sud_changed(), which runs off a worker thread
|
# write happens in on_sud_changed(), which runs off a worker thread
|
||||||
@@ -579,6 +601,11 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
_, _, schedule, _, _ = Sud._parse_data(doc)
|
_, _, schedule, _, _ = Sud._parse_data(doc)
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
return
|
return
|
||||||
|
self.sud_empty = len(schedule) == 0
|
||||||
|
self.update_sud_actions()
|
||||||
|
if self.sud_empty:
|
||||||
|
self.forecast_plot.show_no_schedule()
|
||||||
|
return
|
||||||
# No measured starting temperature is available before a brew has
|
# No measured starting temperature is available before a brew has
|
||||||
# started; fall back to a plausible ambient guess.
|
# started; fall back to a plausible ambient guess.
|
||||||
start_theta = self.plot_temp_ist if self.plot_temp_ist else 20.0
|
start_theta = self.plot_temp_ist if self.plot_temp_ist else 20.0
|
||||||
|
|||||||
@@ -1255,10 +1255,13 @@ class Ui_MainWindow(object):
|
|||||||
self.actionPause.setObjectName("actionPause")
|
self.actionPause.setObjectName("actionPause")
|
||||||
self.actionStop = QtWidgets.QAction(MainWindow)
|
self.actionStop = QtWidgets.QAction(MainWindow)
|
||||||
self.actionStop.setObjectName("actionStop")
|
self.actionStop.setObjectName("actionStop")
|
||||||
|
self.actionSudNew = QtWidgets.QAction(MainWindow)
|
||||||
|
self.actionSudNew.setObjectName("actionSudNew")
|
||||||
self.actionSudSave = QtWidgets.QAction(MainWindow)
|
self.actionSudSave = QtWidgets.QAction(MainWindow)
|
||||||
self.actionSudSave.setObjectName("actionSudSave")
|
self.actionSudSave.setObjectName("actionSudSave")
|
||||||
self.actionSudLoad = QtWidgets.QAction(MainWindow)
|
self.actionSudLoad = QtWidgets.QAction(MainWindow)
|
||||||
self.actionSudLoad.setObjectName("actionSudLoad")
|
self.actionSudLoad.setObjectName("actionSudLoad")
|
||||||
|
self.menu_File.addAction(self.actionSudNew)
|
||||||
self.menu_File.addAction(self.actionSudSave)
|
self.menu_File.addAction(self.actionSudSave)
|
||||||
self.menu_File.addAction(self.actionSudLoad)
|
self.menu_File.addAction(self.actionSudLoad)
|
||||||
self.menubar.addAction(self.menu_File.menuAction())
|
self.menubar.addAction(self.menu_File.menuAction())
|
||||||
@@ -1306,6 +1309,8 @@ class Ui_MainWindow(object):
|
|||||||
self.actionPause.setToolTip(_translate("MainWindow", "Pause"))
|
self.actionPause.setToolTip(_translate("MainWindow", "Pause"))
|
||||||
self.actionStop.setText(_translate("MainWindow", "Stop"))
|
self.actionStop.setText(_translate("MainWindow", "Stop"))
|
||||||
self.actionStop.setToolTip(_translate("MainWindow", "Stop"))
|
self.actionStop.setToolTip(_translate("MainWindow", "Stop"))
|
||||||
|
self.actionSudNew.setText(_translate("MainWindow", "&New Sud"))
|
||||||
|
self.actionSudNew.setToolTip(_translate("MainWindow", "Replace the running Sud\'s schedule with an empty one"))
|
||||||
self.actionSudSave.setText(_translate("MainWindow", "&Save Sud..."))
|
self.actionSudSave.setText(_translate("MainWindow", "&Save Sud..."))
|
||||||
self.actionSudSave.setToolTip(_translate("MainWindow", "Save the running Sud\'s schedule to a local sud.json"))
|
self.actionSudSave.setToolTip(_translate("MainWindow", "Save the running Sud\'s schedule to a local sud.json"))
|
||||||
self.actionSudLoad.setText(_translate("MainWindow", "&Load Sud..."))
|
self.actionSudLoad.setText(_translate("MainWindow", "&Load Sud..."))
|
||||||
|
|||||||
Reference in New Issue
Block a user