From bc37bd9049c9fd1bae26ba9b15561b666840e357 Mon Sep 17 00:00:00 2001 From: Luca Toniolo <10792599+grandixximo@users.noreply.github.com> Date: Sat, 20 Jun 2026 10:54:57 +0800 Subject: [PATCH] qtvcp: log unhandled exceptions and skip modal dialog when headless The excepthook only showed a modal QMessageBox, so a crash left no trace in the logs and, offscreen (CI), the dialog blocked forever; during construction that hung before SIGTERM was armed and qtdragon-quit timed out. Always write the traceback to stderr and the log, and when offscreen (or no QApplication) run shutdown and exit instead of the dialog. Interactive path unchanged. --- src/emc/usr_intf/qtvcp/qtvcp.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/emc/usr_intf/qtvcp/qtvcp.py b/src/emc/usr_intf/qtvcp/qtvcp.py index 8acb3398b72..f7351df81a0 100644 --- a/src/emc/usr_intf/qtvcp/qtvcp.py +++ b/src/emc/usr_intf/qtvcp/qtvcp.py @@ -556,6 +556,25 @@ def excepthook(self, exc_type, exc_obj, exc_tb): + "information may be useful in troubleshooting:\n" + 'LinuxCNC Version : %s\n'% self.INFO.LINUXCNC_VERSION) + # Always surface unhandled exceptions to stderr/log. A crash is a + # serious problem that must be visible in CI logs, not buried in a + # dialog that the run never captures. + sys.stderr.write(''.join(lines)) + sys.stderr.flush() + LOG.critical("Qtvcp unhandled exception:\n{}".format(''.join(lines))) + + # Offscreen (e.g. CI): nobody can dismiss a modal dialog, so + # msg.exec_() below would block forever; if this happens during + # construction it hangs before the SIGTERM handler is armed and the + # process cannot be killed cleanly. Exit instead of popping a dialog. + app = QtWidgets.QApplication.instance() + if app is None or app.platformName() == 'offscreen': + try: + self.shutdown() + except Exception: + pass + os._exit(1) + msg = QtWidgets.QMessageBox() msg.setIcon(QtWidgets.QMessageBox.Critical) msg.setText(self._message)