From 760aee89a934f180b122b288f6944871dba1d816 Mon Sep 17 00:00:00 2001 From: Luca Toniolo <10792599+grandixximo@users.noreply.github.com> Date: Thu, 5 Nov 2026 14:07:39 +1100 Subject: [PATCH 1/2] hal_glib: clear the estop when the state leaves ESTOP for any state GStat emitted state-estop-reset only for ESTOP => ESTOP_RESET. A client that resets the estop and turns the machine on between two polls goes from ESTOP straight to ON, and qtdragon and gmoccapy kept showing the estop set on a machine that was on. --- lib/python/common/hal_glib.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/python/common/hal_glib.py b/lib/python/common/hal_glib.py index 6227a3dae3b..3e6820a9959 100644 --- a/lib/python/common/hal_glib.py +++ b/lib/python/common/hal_glib.py @@ -615,8 +615,9 @@ def update(self): state_new = self.old['state'] if state_new != state_old: - # set machine estop/clear - if state_old == linuxcnc.STATE_ESTOP and state_new == linuxcnc.STATE_ESTOP_RESET: + # set machine estop/clear; a reset and machine on between two + # polls go from ESTOP straight to ON and still clear the estop + if state_old == linuxcnc.STATE_ESTOP: self.emit('state-estop-reset') self.emit('interp-idle') elif state_new == linuxcnc.STATE_ESTOP: From 0c0957069d60e812e634663ac3ce79bef40347a1 Mon Sep 17 00:00:00 2001 From: Luca Toniolo <10792599+grandixximo@users.noreply.github.com> Date: Thu, 5 Nov 2026 16:29:10 +1100 Subject: [PATCH 2/2] gmoccapy: show the machine state on its buttons without sending it again The status handlers set the estop and machine on buttons, whose toggled handlers then sent that state again. Once GStat reports the estop reset that comes with an ESTOP => ON change, gmoccapy sent an ESTOP_RESET to a machine already on, which aborts its motion. Block the toggled handler while showing the state; on machine on, also update the widgets as the toggled handler did. --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index cf17081c331..feacd444974 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -3100,16 +3100,24 @@ def on_hal_status_tool_in_spindle_changed(self, object, new_tool_no): self.prefs.putpref("tool_in_spindle", new_tool_no, int) self._update_toolinfo(new_tool_no) + # Show a state change on a toggle button without its toggled handler, + # which would send the state again: when this runs late, that is an old + # state, and an ESTOP_RESET to a machine already on aborts its motion. + def _show_toggle(self, widget, active): + handler = icon_theme_helper.find_handler_id_by_signal(widget, "toggled") + with widget.handler_block(handler): + widget.set_active(active) + def on_hal_status_state_estop(self, widget=None): - self.widgets.tbtn_estop.set_active(True) + self._show_toggle(self.widgets.tbtn_estop, True) self.widgets.tbtn_estop.set_image(self.widgets.img_emergency) self.widgets.tbtn_on.set_image(self.widgets.img_machine_off) self.widgets.tbtn_on.set_sensitive(False) - self.widgets.tbtn_on.set_active(False) + self._show_toggle(self.widgets.tbtn_on, False) self.command.mode(linuxcnc.MODE_MANUAL) def on_hal_status_state_estop_reset(self, widget=None): - self.widgets.tbtn_estop.set_active(False) + self._show_toggle(self.widgets.tbtn_estop, False) self.widgets.tbtn_estop.set_image(self.widgets.img_emergency_off) self.widgets.tbtn_on.set_image(self.widgets.img_machine_off) self.widgets.tbtn_on.set_sensitive(True) @@ -3132,8 +3140,7 @@ def on_hal_status_state_off(self, widget): "btn_tool_touchoff_x", "btn_tool_touchoff_z" ] self._sensitize_widgets(widgetlist, False) - if self.widgets.tbtn_on.get_active(): - self.widgets.tbtn_on.set_active(False) + self._show_toggle(self.widgets.tbtn_on, False) self.widgets.tbtn_on.set_image(self.widgets.img_machine_off) self.widgets.btn_exit.set_sensitive(True) self.widgets.ntb_main.set_current_page(0) @@ -3148,8 +3155,8 @@ def on_hal_status_state_on(self, widget): "btn_spindle_100", "spc_rapid", "spc_spindle" ] self._sensitize_widgets(widgetlist, True) - if not self.widgets.tbtn_on.get_active(): - self.widgets.tbtn_on.set_active(True) + self._show_toggle(self.widgets.tbtn_on, True) + self._update_widgets(True) self.widgets.tbtn_on.set_image(self.widgets.img_machine_on) self.widgets.btn_exit.set_sensitive(False) if self.widgets.ntb_main.get_current_page() != 0: