Show user_message as a modal dialog when the Sud waits for confirmation
Adds a thread-safe path from on_sud_changed() (websocket recv thread)
to a modal QMessageBox on the GUI thread via Qt's cross-thread
signal/slot queuing, fired when the State transition lands on
WAIT_USER - which the schedule is already blocked on server-side, so
the dialog just gives it a UI: closing it (OK) sends Confirm.
Deliberately scoped to WAIT_USER transitions, not every UserMessage -
most steps in sude/sud_0010.json inherit the schedule's default
placeholder text ("Put user message here") rather than overriding it,
so popping a dialog for every step would mostly show that placeholder.
This commit is contained in:
@@ -19,6 +19,7 @@ import asyncio
|
|||||||
# str(SudState.X) values (as sent in the 'State' message) that count as
|
# str(SudState.X) values (as sent in the 'State' message) that count as
|
||||||
# "running" for enabling/disabling the Start/Pause/Stop toolbar actions.
|
# "running" for enabling/disabling the Start/Pause/Stop toolbar actions.
|
||||||
SUD_RUNNING_STATES = {"SudState.RAMPING", "SudState.HOLDING", "SudState.WAIT_USER"}
|
SUD_RUNNING_STATES = {"SudState.RAMPING", "SudState.HOLDING", "SudState.WAIT_USER"}
|
||||||
|
SUD_WAIT_USER_STATE = "SudState.WAIT_USER"
|
||||||
SUD_PAUSED_STATE = "SudState.PAUSED"
|
SUD_PAUSED_STATE = "SudState.PAUSED"
|
||||||
|
|
||||||
|
|
||||||
@@ -169,9 +170,16 @@ class SudForecastPlot(FigureCanvasQTAgg):
|
|||||||
|
|
||||||
|
|
||||||
class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||||
|
# on_sud_changed() runs on the websocket recv thread, but a modal dialog
|
||||||
|
# must be shown from the GUI thread - a signal/slot crosses that thread
|
||||||
|
# boundary safely (Qt auto-queues the slot call onto the receiver's
|
||||||
|
# thread when sender and receiver differ).
|
||||||
|
user_message_signal = QtCore.pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QtWidgets.QMainWindow.__init__(self)
|
QtWidgets.QMainWindow.__init__(self)
|
||||||
self.setWindowIcon(QtGui.QIcon('res/beer.png'))
|
self.setWindowIcon(QtGui.QIcon('res/beer.png'))
|
||||||
|
self.user_message_signal.connect(self.on_user_message_signal)
|
||||||
|
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
@@ -181,6 +189,7 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
self.connected = False
|
self.connected = False
|
||||||
self.sud_loaded = False
|
self.sud_loaded = False
|
||||||
self.sud_state = None
|
self.sud_state = None
|
||||||
|
self.sud_user_message = None
|
||||||
# time.monotonic() of the last IDLE/DONE -> running transition, used
|
# time.monotonic() of the last IDLE/DONE -> running transition, used
|
||||||
# to position the forecast plot's progress line; None while not
|
# to position the forecast plot's progress line; None while not
|
||||||
# running. sud_paused_total accumulates time spent PAUSED so the
|
# running. sud_paused_total accumulates time spent PAUSED so the
|
||||||
@@ -304,6 +313,7 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
# 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_state = None
|
self.sud_state = None
|
||||||
|
self.sud_user_message = None
|
||||||
self.sud_start_time = None
|
self.sud_start_time = None
|
||||||
self.sud_paused_total = 0.0
|
self.sud_paused_total = 0.0
|
||||||
self.sud_pause_started_at = None
|
self.sud_pause_started_at = None
|
||||||
@@ -352,6 +362,13 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
def on_action_sud_stop(self):
|
def on_action_sud_stop(self):
|
||||||
self.msg_sud.send({'Stop': True})
|
self.msg_sud.send({'Stop': True})
|
||||||
|
|
||||||
|
def on_user_message_signal(self, message):
|
||||||
|
# Runs on the GUI thread (queued there by the cross-thread signal
|
||||||
|
# emit in on_sud_changed()). The Sud is already blocked in
|
||||||
|
# WAIT_USER server-side; closing this dialog is what unblocks it.
|
||||||
|
QtWidgets.QMessageBox.information(self, "Sud", message)
|
||||||
|
self.msg_sud.send({'Confirm': True})
|
||||||
|
|
||||||
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
|
||||||
@@ -482,6 +499,11 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
self.sud_start_time = None
|
self.sud_start_time = None
|
||||||
self.sud_paused_total = 0.0
|
self.sud_paused_total = 0.0
|
||||||
self.sud_pause_started_at = None
|
self.sud_pause_started_at = None
|
||||||
|
|
||||||
|
if self.sud_state == SUD_WAIT_USER_STATE and prev_state != SUD_WAIT_USER_STATE and self.sud_user_message:
|
||||||
|
self.user_message_signal.emit(self.sud_user_message)
|
||||||
|
elif "UserMessage" in key:
|
||||||
|
self.sud_user_message = msg['UserMessage']
|
||||||
elif "Step" in key:
|
elif "Step" in key:
|
||||||
step = msg['Step']
|
step = msg['Step']
|
||||||
descr = step.get('Descr')
|
descr = step.get('Descr')
|
||||||
|
|||||||
Reference in New Issue
Block a user