diff --git a/client/brewpi.ui b/client/brewpi.ui index 3fd69f8..4787b55 100644 --- a/client/brewpi.ui +++ b/client/brewpi.ui @@ -3712,6 +3712,7 @@ &File + @@ -3765,6 +3766,14 @@ Stop + + + &New Sud + + + Replace the running Sud's schedule with an empty one + + &Save Sud... diff --git a/client/brewpi_gui.py b/client/brewpi_gui.py index e4ffa0a..dbead21 100755 --- a/client/brewpi_gui.py +++ b/client/brewpi_gui.py @@ -160,6 +160,15 @@ class SudForecastPlot(FigureCanvasQTAgg): self.figure.tight_layout() 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): self.progress_line.set_xdata([t_min, t_min]) 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.connected = 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_user_message = None # 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.actionPause.triggered.connect(self.on_action_sud_pause) 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.actionSudLoad.triggered.connect(self.on_action_sud_load) 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: # Nothing reported over a dead connection can be trusted anymore. self.sud_loaded = False + self.sud_empty = True self.sud_state = None self.sud_user_message = None self.ambient_temp = None @@ -353,11 +369,12 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow): self.update_status_env_label() def update_sud_actions(self): - running = self.sud_loaded and self.sud_state in SUD_RUNNING_STATES - paused = self.sud_loaded and self.sud_state == SUD_PAUSED_STATE + can_run = self.sud_loaded and not self.sud_empty + 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 # 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.actionStop.setEnabled(running or paused) @@ -429,6 +446,11 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow): QtWidgets.QMessageBox.information(self, "Sud", message) 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): # Pick the destination up front, on the GUI thread - the actual # 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) except (KeyError, TypeError): 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 # started; fall back to a plausible ambient guess. start_theta = self.plot_temp_ist if self.plot_temp_ist else 20.0 diff --git a/client/main_window.py b/client/main_window.py index e8634e0..6a01936 100644 --- a/client/main_window.py +++ b/client/main_window.py @@ -1255,10 +1255,13 @@ class Ui_MainWindow(object): self.actionPause.setObjectName("actionPause") self.actionStop = QtWidgets.QAction(MainWindow) self.actionStop.setObjectName("actionStop") + self.actionSudNew = QtWidgets.QAction(MainWindow) + self.actionSudNew.setObjectName("actionSudNew") self.actionSudSave = QtWidgets.QAction(MainWindow) self.actionSudSave.setObjectName("actionSudSave") self.actionSudLoad = QtWidgets.QAction(MainWindow) self.actionSudLoad.setObjectName("actionSudLoad") + self.menu_File.addAction(self.actionSudNew) self.menu_File.addAction(self.actionSudSave) self.menu_File.addAction(self.actionSudLoad) self.menubar.addAction(self.menu_File.menuAction()) @@ -1306,6 +1309,8 @@ class Ui_MainWindow(object): self.actionPause.setToolTip(_translate("MainWindow", "Pause")) self.actionStop.setText(_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.setToolTip(_translate("MainWindow", "Save the running Sud\'s schedule to a local sud.json")) self.actionSudLoad.setText(_translate("MainWindow", "&Load Sud..."))