From 92bb62a1bb97e17c90c6021a134ca407efd17af4 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 26 Sep 2025 22:09:20 -0700 Subject: [PATCH 01/81] hal_glib -add a function to run the gobject mainloop once so GUIs not basd in gobject can run the message system --- lib/python/common/hal_glib.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/python/common/hal_glib.py b/lib/python/common/hal_glib.py index 864bf95b0ec..caa00c6ba75 100644 --- a/lib/python/common/hal_glib.py +++ b/lib/python/common/hal_glib.py @@ -353,6 +353,11 @@ def __init__(self, stat = None): def set_timer(self): GLib.timeout_add(CYCLE_TIME, self.update) + # used to run the Gobject mainloop once + # allows a GUI that is not GLib based to update the mainloop + def run_iteration(self): + GLib.MainContext.default().iteration (True) + # open a zmq socket for writing out data def init_write_socket(self): context = zmq.Context() @@ -363,6 +368,7 @@ def init_write_socket(self): self.write_available = True except Exception as e: LOG.debug('hal_glib write socket not available: {}'.format(e)) + LOG.debug('hal_glib write socket not available\n {}'.format(e)) self.write_available = False # convert and actually send out the message From aa7e901e72e123c0aeb2794dbe74d6775d70f5ec Mon Sep 17 00:00:00 2001 From: Cmorley Date: Wed, 8 Jul 2026 23:03:57 -0700 Subject: [PATCH 02/81] hal_glib -add function to process one ZMQ message In some cases, we need to directly process one message. eg when using 'wait to don't block' dialog option --- lib/python/common/hal_glib.py | 50 +++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/lib/python/common/hal_glib.py b/lib/python/common/hal_glib.py index caa00c6ba75..ec7e19aec02 100644 --- a/lib/python/common/hal_glib.py +++ b/lib/python/common/hal_glib.py @@ -404,30 +404,40 @@ def init_read_socket(self): GObject.IO_IN|GObject.IO_ERR|GObject.IO_HUP, self.onReadMsg, self.readSocket) - # convert message to a function name and data - # then call that function + # called directly to process any current message + def readNextMsg(self): + event = self.readSocket.poll(timeout=0) + if event & zmq.POLLIN: + self.convertMsg() + + # called when GObject notices a change def onReadMsg(self, queue, condition, sock): while self.readSocket.getsockopt(zmq.EVENTS) & zmq.POLLIN: - # get raw message - topic, data = self.readSocket.recv_multipart() - # convert from json object to python object - y = json.loads(data) - function = y.get('FUNCTION') - data = y.get('ARGS') - LOG.debug('REQUESTED:{}'.format(y)) - if data == '': - try: - self[function]() - except Exception as e: - LOG.debug('not a valid request\n {}'.format(e)) - else: - try: - self[function](data) - except Exception as e: - LOG.debug('not a valid request\n {}'.format(e)) - #self. action(y.get('MESSAGE'),y.get('ARGS')) + self.convertMsg() return True + # convert message to a function name and data + # then call that function + def convertMsg(self): + # get raw message + topic, data = self.readSocket.recv_multipart() + # convert from json object to python object + y = json.loads(data) + function = y.get('FUNCTION') + data = y.get('ARGS') + LOG.debug('REQUESTED:{}'.format(y)) + if data == '': + try: + self[function]() + except Exception as e: + LOG.debug('not a valid request\n {}'.format(e)) + else: + try: + self[function](data) + except Exception as e: + LOG.debug('not a valid request\n {}'.format(e)) + #self. action(y.get('MESSAGE'),y.get('ARGS')) + def merge(self): self.old['command-state'] = self.stat.state self.old['state'] = self.stat.task_state From b6ea859a30b0060086e9052733d5257af302f3eb Mon Sep 17 00:00:00 2001 From: Cmorley Date: Tue, 14 Jul 2026 21:34:09 -0700 Subject: [PATCH 03/81] hal_glib - add soft key messages --- lib/python/common/hal_glib.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/python/common/hal_glib.py b/lib/python/common/hal_glib.py index ec7e19aec02..7da10816f76 100644 --- a/lib/python/common/hal_glib.py +++ b/lib/python/common/hal_glib.py @@ -248,6 +248,7 @@ class _GStat(GObject.GObject): 'cycle-start-request': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_BOOLEAN,)), 'cycle-pause-request': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_BOOLEAN,)), 'macro-call-request': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)), + 'softkey-pressed': (GObject.SignalFlags.RUN_FIRST , GObject.TYPE_NONE, (GObject.TYPE_INT,)), } STATES = { linuxcnc.STATE_ESTOP: 'state-estop' @@ -421,6 +422,7 @@ def onReadMsg(self, queue, condition, sock): def convertMsg(self): # get raw message topic, data = self.readSocket.recv_multipart() + LOG.debug(f'RAW REQUESTED:{topic},{data}') # convert from json object to python object y = json.loads(data) function = y.get('FUNCTION') @@ -1497,6 +1499,9 @@ def request_ok(self, data): def request_cancel(self, data): self.emit('cancel-request', data) + def request_softkey(self, index): + self.emit('softkey-pressed', index) + ############################################# def shutdown(self): From c5d9dc09b5f4a0392a4b0c74f8657b35ab514887 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sun, 1 Feb 2026 17:45:44 -0800 Subject: [PATCH 04/81] add python3-zmq package for halui --- debian/configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/configure b/debian/configure index 096b3f4a5ed..635bf664ecb 100755 --- a/debian/configure +++ b/debian/configure @@ -121,7 +121,7 @@ PYTHON_GST=python3-gst-1.0,gstreamer1.0-plugins-base TCLTK_VERSION=8.6 PYTHON_IMAGING=python3-pil PYTHON_IMAGING_TK=python3-pil.imagetk -QTVCP_DEPENDS="python3-qtpy,\n python3-pyqt5,\n python3-pyqt5.qsci,\n python3-pyqt5.qtsvg,\n python3-pyqt5.qtopengl,\n python3-opencv,\n python3-dbus,\n python3-espeak,\n python3-dbus.mainloop.pyqt5,\n python3-pyqt5.qtwebengine,\n espeak-ng,\n pyqt5-dev-tools,\n gstreamer1.0-tools,\n espeak,\n sound-theme-freedesktop" +QTVCP_DEPENDS="python3-qtpy,\n python3-pyqt5,\n python3-pyqt5.qsci,\n python3-pyqt5.qtsvg,\n python3-pyqt5.qtopengl,\n python3-opencv,\n python3-dbus,\n python3-espeak,\n python3-dbus.mainloop.pyqt5,\n python3-pyqt5.qtwebengine,\n espeak-ng,\n pyqt5-dev-tools,\n gstreamer1.0-tools,\n espeak,\n sound-theme-freedesktop,\n python3-zmq" YAPPS_RUNTIME="python3-yapps" DEBHELPER="debhelper (>= 12)" COMPAT="12" From 9ddce74433ae048a4131d33164f28f24f78eeae0 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 7 Feb 2026 16:39:36 -0800 Subject: [PATCH 05/81] add python3-zmq to python3 depends --- debian/control.main-pkg.in | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/control.main-pkg.in b/debian/control.main-pkg.in index 56653dc0827..2af4d385f91 100644 --- a/debian/control.main-pkg.in +++ b/debian/control.main-pkg.in @@ -22,6 +22,7 @@ Depends: python3-opengl, python3-configobj, python3-xlib, + python3-zmq, libgtksourceview-4-dev, tcl@TCLTK_VERSION@, tk@TCLTK_VERSION@, From 071762a2119579dae9cc0319c22f41e89e542365 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 16 Aug 2025 11:21:10 -0700 Subject: [PATCH 06/81] halui initial testing of embedded python halui add bridgeui folder for HALUI halui -inject socket axis selection bridge -remove HAL pin code, get MDI commands working halui -code clenup, GUI based MDI command feature halui -add cycle start cycle pause pins calls these functions in a GUI halui -add gui ok/canel pins halui -fix memory leaks halui switch u32 to s32 s32 is more common the u32 (easier to connect to) also axis select uses -1 as 'unselected' halui -add mpg_select0 pin Some GUIs use the MPG for other things then jogging. Qtdragon uses MPG for scrolling and needs a selected pin. --- .../qtdragon/qtdragon_xyz/qtdragon_metric.ini | 2 +- lib/python/bridgeui/__init__.py | 3 + lib/python/bridgeui/bridge.py | 268 ++++++++++ src/emc/usr_intf/Submakefile | 3 +- src/emc/usr_intf/halui.cc | 478 ++++++++++++++++-- 5 files changed, 722 insertions(+), 32 deletions(-) create mode 100644 lib/python/bridgeui/__init__.py create mode 100644 lib/python/bridgeui/bridge.py diff --git a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini index 7e5d56e05aa..f437be0811a 100644 --- a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini +++ b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini @@ -151,7 +151,7 @@ SPINDLES = 1 [HAL] HALUI = halui -HALBRIDGE = hal_bridge +#HALBRIDGE = hal_bridge # loads the HAL machine simulation HALFILE = core_sim.hal diff --git a/lib/python/bridgeui/__init__.py b/lib/python/bridgeui/__init__.py new file mode 100644 index 00000000000..b28b04f6431 --- /dev/null +++ b/lib/python/bridgeui/__init__.py @@ -0,0 +1,3 @@ + + + diff --git a/lib/python/bridgeui/bridge.py b/lib/python/bridgeui/bridge.py new file mode 100644 index 00000000000..56577ebb664 --- /dev/null +++ b/lib/python/bridgeui/bridge.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 + +import os +import sys +import time + +import json +import signal + +import hal +from common.iniinfo import _IStat as IStatParent +from common import logger + +# LOG is for running code logging +LOG = logger.initBaseLogger('HAL bridge', log_file=None, + log_level=logger.WARNING, logToFile=False) + +# Force the log level for this module +LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL + +try: + import zmq + ZMQ = True +except: + LOG.critical('ZMQ python library problem - Is python3-zmq installed?') + ZMQ = False + +class Info(IStatParent): + _instance = None + _instanceNum = 0 + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = IStatParent.__new__(cls, *args, **kwargs) + return cls._instance + +# Instantiate the library with global reference + + +class Bridge(object): + def __init__(self, readAddress = "tcp://127.0.0.1:5690", + writeAddress = "tcp://127.0.0.1:5691"): + super(Bridge, self).__init__() + self.INFO = Info() + + self.currentSelectedAxis = 'None' + self.axesSelected = {'X':0,'Y':0,'Z':0,'A':0,'B':0,'C':0, + 'U':0,'V':0,'W':0,'MPG0':0} + self.readAddress = readAddress + self.writeAddress = writeAddress + LOG.debug('read port: {}'.format(readAddress)) + LOG.debug('write port: {}'.format(writeAddress)) + + self.readTopic = "" + self.writeTopic = "STATUSREQUEST" + + # catch control c and terminate signals + signal.signal(signal.SIGTERM, self.shutdown) + signal.signal(signal.SIGINT, self.shutdown) + + self.init() + if ZMQ: + self.init_read() + self.init_write() + + def update(self, *arg): + print(self, arg) + raw=arg[0]; row=arg[1];column=arg[2];state=arg[3] + LOG.debug('raw {}, row {}, col {}, state {}'.format(raw,row,column,state)) + print ('raw',raw,'row:',row,'column:',column,'state:',state) + self.writeMsg('set_selected_axis','Y') + self.activeJoint.set(10) + + def init(self): + self.jogRate = 0 + self.jogRateAngular = 0 + + self.jogIncrement = 0 + self.jogIncrementAngular = 0 + + self.activeJoint = 0 + + def init_write(self): + context = zmq.Context() + self.writeSocket = context.socket(zmq.PUB) + self.writeSocket.bind(self.writeAddress) + + def init_read(self): + # ZeroMQ Context + self.readContext = zmq.Context() + + # Define the socket using the "Context" + self.readSocket = self.readContext.socket(zmq.SUB) + + # Define subscription and messages with topic to accept. + self.readSocket.setsockopt_string(zmq.SUBSCRIBE, self.readTopic) + self.readSocket.connect(self.readAddress) + + # callback from ZMQ read socket + def readMsg(self): + if self.readSocket.getsockopt(zmq.EVENTS) & zmq.POLLIN: + while self.readSocket.getsockopt(zmq.EVENTS) & zmq.POLLIN: + # get raw message + topic, data = self.readSocket.recv_multipart() + # convert from json object to python object + y = json.loads(data) + self. action(y.get('MESSAGE'),y.get('ARGS')) + + # set our variables from messages from hal_glib + def action(self, msg, data): + LOG.debug('{} -> {} -> {}'.format(msg, data, data[0])) + if msg == 'jograte-changed': + self.jogRate = float(data[0]) + elif msg == 'jograte-angular-changed': + self.jogRateAngular = float(data[0]) + elif msg == 'jogincrements-changed': + self.jogIncrement = float(data[0][0]) + elif msg == 'jogincrement-angular-changed': + self.jogIncremtAngular = float(data[0][0]) + elif msg == 'joint-selection-changed': + self.activeJoint = int(data[0]) + elif msg == 'axis-selection-changed': + print ('pre axis state', self.axesSelected,self.currentSelectedAxis) + flag = 1 + if data[0] == 'MPG0': + self.currentSelectedAxis = data[0] + flag = 0 + self.axesSelected['MPG0'] = True + else: + self.axesSelected['MPG0'] = False + for i in(self.INFO.AVAILABLE_AXES): + if data[0] == i: + state = True + self.currentSelectedAxis = data[0] + flag = 0 + else: + state = False + self.axesSelected[i] = int(state) + + if flag: + self.currentSelectedAxis = 'None' + print ('axis state', self.axesSelected,self.currentSelectedAxis) + + # send msg to hal_glib + def writeMsg(self, msg, data): + print('Write Msg called') + if ZMQ: + topic = self.writeTopic + message = json.dumps({'FUNCTION':msg,'ARGS':data}) + LOG.debug('Sending ZMQ Message:{} {}'.format(topic, message)) + self.writeSocket.send_multipart( + [bytes(topic.encode('utf-8')), + bytes((message).encode('utf-8'))]) + + def shutdown(self,signum=None,stack_frame=None): + LOG.debug('shutdown') + global app + app.quit() + + def cycleStart(self): + # cycle start + self.writeMsg('request_cycle_start', True) + + def cyclePause(self): + self.writeMsg('request_cycle_pause', True) + + def ok(self): + self.writeMsg('request_ok', True) + + def cancel(self): + self.writeMsg('request_cancel', True) + + def getMdiName(self, num): + if num >len(self.INFO.MDI_COMMAND_DICT)-1: + return 'None' + temp = list(self.INFO.MDI_COMMAND_DICT.keys())[num] + LOG.debug('{} {}'.format(num,temp)) + return temp + + def getMacroNames(self): + for i in self.INFO.INI_MACROS: + name = i.split()[0] + LOG.debug('{} {}'.format(name,i)) + + def runIndexedMacro(self, num): + name = self.getMdiName(num) + LOG.debug('Macro name:{} ,index: {}'.format(name, num)) + if name != 'None': + self.writeMsg('request_macro_call', name) + + def getMdiCount(self): + print(len(self.INFO.MDI_COMMAND_DICT)) + return len(self.INFO.MDI_COMMAND_DICT) + + def getJogRate(self): + return self.jogRate + def setJogRate(self, value): + self.writeMsg('set_jograte', value) + + def getJogRateAngular(self): + return self.jogRateAngular + def setJogRateAngular(self, value): + self.writeMsg('set_jograte_angular', value) + + def getSelectedAxis(self): + name = self.currentSelectedAxis + if name == 'None': + index = -1 + elif name =='MPG0': + index = 100 + else: + index = 'XYZABCUVW'.index(name) + return index + def setSelectedAxis(self, value): + if value < 0: + letter = 'None' + elif value == 100: + letter = 'MPG0' + else: + letter ='XYZABCUVW'[value] + self.writeMsg('set_selected_axis', letter) + + def isAxisSelected(self, index): + if index == 100: + letter = 'MPG0' + else: + letter = 'XYZABCUVW'[index] + return int(self.axesSelected[letter]) + + def __getitem__(self, item): + return getattr(self, item) + def __setitem__(self, item, value): + return setattr(self, item, value) + +if __name__ == "__main__": + import sys + import getopt + from PyQt5.QtWidgets import QApplication + + letters = 'dh' # the : means an argument needs to be passed after the letter + keywords = ['readport=', 'writeport=' ] # the = means that a value is expected after + # the keyword + + opts, extraparam = getopt.getopt(sys.argv[1:],letters,keywords) + # starts at the second element of argv since the first one is the script name + # extraparms are extra arguments passed after all option/keywords are assigned + # opts is a list containing the pair "option"/"value" + + readport = "tcp://127.0.0.1:5690" + writeport = "tcp://127.0.0.1:5691" + + for o,p in opts: + if o in ['-d']: + LOG.setLevel(logger.DEBUG) + elif o in ['--readport']: + readport = p + elif o in ['--writeport']: + writeport = p + elif o in ['-h','--help']: + print('HAL bridge: GUI to HAL interface using ZMQ') + print('option "-d" = debug print mode') + print('option "--readport=" read socket address') + print('option "--writeport=" write socket address') + print('example: hal_bridge -d --readport=tcp://127.0.0.1:5692') + + app = QApplication(sys.argv) + test = Bridge(readport, writeport) + sys.exit(app.exec_()) diff --git a/src/emc/usr_intf/Submakefile b/src/emc/usr_intf/Submakefile index b914e30ffe1..4dbd9ec9afb 100644 --- a/src/emc/usr_intf/Submakefile +++ b/src/emc/usr_intf/Submakefile @@ -42,5 +42,6 @@ TARGETS += ../bin/linuxcnclcd ../bin/halui: $(call TOOBJS, $(HALUISRCS)) ../lib/liblinuxcnc.a ../lib/liblinuxcncini.so.1 ../lib/libnml.so.0 ../lib/liblinuxcnchal.so.0 ../lib/libtooldata.so.0 $(ECHO) Linking $(notdir $@) - $(Q)$(CXX) $(CXXFLAGS) -o $@ $(ULFLAGS) $^ $(LDFLAGS) + $(Q)$(CXX) $(CXXFLAGS) -o $@ $(ULFLAGS) $^ $(LDFLAGS) $(PYTHON_LIBS) $(PYTHON_EXTRA_LIBS) TARGETS += ../bin/halui + diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index 6796e4cf48d..99de81a1436 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -43,6 +43,10 @@ using namespace linuxcnc; +#define PY_SSIZE_T_CLEAN +#include +#include + /* Using halui: see the man page */ static int axis_mask = 0; @@ -89,6 +93,8 @@ typedef hal_uint_t hal_ui32_t; FIELD(bool,program_is_running) /* pin for notifying user that program is running */ \ FIELD(bool,halui_mdi_is_running) /* pin for notifying user that halui MDI commands is running */ \ FIELD(bool,program_is_paused) /* pin for notifying user that program is paused */ \ + FIELD(bool,cycle_start) /* pin for requesting the gui to start */ \ + FIELD(bool,cycle_pause) /* pin for requesting the gui to pause */ \ FIELD(bool,program_run) /* pin for running program */ \ FIELD(bool,program_pause) /* pin for pausing program */ \ FIELD(bool,program_resume) /* pin for resuming program */ \ @@ -101,7 +107,7 @@ typedef hal_uint_t hal_ui32_t; FIELD(bool,program_bd_off) /* pin for setting block delete off */ \ FIELD(bool,program_bd_is_on) /* status pin that block delete is on */ \ \ - FIELD(uint,tool_number) /* pin for current selected tool */ \ + FIELD(sint,tool_number) /* pin for current selected tool */ \ FIELD(real,tool_length_offset_x) /* current applied x tool-length-offset */ \ FIELD(real,tool_length_offset_y) /* current applied y tool-length-offset */ \ FIELD(real,tool_length_offset_z) /* current applied z tool-length-offset */ \ @@ -137,7 +143,8 @@ typedef hal_uint_t hal_ui32_t; ARRAY(bool,joint_override_limits,EMCMOT_MAX_JOINTS+1) /* status pin that the joint is on the hardware max limit */ \ ARRAY(bool,joint_has_fault,EMCMOT_MAX_JOINTS+1) /* status pin that the joint has a fault */ \ FIELD(uint,joint_selected) /* status pin for the joint selected */ \ - FIELD(uint,axis_selected) /* status pin for the axis selected */ \ + FIELD(sint,axis_selected) /* status pin for the axis selected */ \ + FIELD(bool,mpg_select0)\ \ ARRAY(bool,joint_nr_select,EMCMOT_MAX_JOINTS) /* nr. of pins to select a joint */ \ ARRAY(bool,axis_nr_select,EMCMOT_MAX_AXIS) /* nr. of pins to select a axis */ \ @@ -206,6 +213,10 @@ typedef hal_uint_t hal_ui32_t; FIELD(bool,home_all) /* pin for homing all joints in sequence */ \ FIELD(bool,abort) /* pin for aborting */ \ ARRAY(bool,mdi_commands,MDI_MAX) \ + ARRAY(bool,gui_mdi_commands,MDI_MAX) \ +\ + FIELD(bool,gui_ok) /* pin for acknowledging dialog ok */ \ + FIELD(bool,gui_cancel) /* pin for acknowledging dialog cancel */ \ \ FIELD(real,units_per_mm) \ @@ -240,11 +251,21 @@ HAL_FIELDS typedef halui_str_base halui_str; typedef halui_str_base local_halui_str; +PyObject *pModule, *pFuncRead, *pFuncWrite, *pInstance, *pClass; +PyObject *pValue; + static halui_str *halui_data; static local_halui_str old_halui_data; +static double lastjogspeed = 0; +static double internaljogspeed = 0; +static int lastaxis = -1; + static char *mdi_commands[MDI_MAX]; static int num_mdi_commands=0; + +static char *gui_mdi_commands[MDI_MAX]; +static int num_gui_mdi_commands = 0; static int have_home_all = 0; static int comp_id, done; /* component ID, main while loop */ @@ -537,6 +558,123 @@ int halui_export_pin_OUT_bit(hal_bool_t *pin, const char *name) return 0; } +static void py_call_cycleStart() { + + // check socket messages for jogspeed + pFuncWrite = PyObject_GetAttrString(pInstance, "cycleStart"); + if (pFuncRead && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallNoArgs(pFuncWrite); + if (pValue == NULL){ + fprintf(stderr, "Halui Bridge: cycleStart function failed: returned NULL\n"); + } + Py_DECREF(pValue); + } + Py_DECREF(pFuncWrite); + return ; +} + +static void py_call_cyclePause() { + + // check socket messages for jogspeed + pFuncWrite = PyObject_GetAttrString(pInstance, "cyclePause"); + if (pFuncRead && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallNoArgs(pFuncWrite); + if (pValue == NULL){ + fprintf(stderr, "Halui Bridge: cyclePause function failed: returned NULL\n"); + } + Py_DECREF(pValue); + } + Py_DECREF(pFuncWrite); + return ; +} + +static void py_call_ok() { + + // check socket messages for gui ok message + pFuncWrite = PyObject_GetAttrString(pInstance, "ok"); + if (pFuncRead && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallNoArgs(pFuncWrite); + if (pValue == NULL){ + fprintf(stderr, "Halui Bridge: ok function failed: returned NULL\n"); + } + Py_DECREF(pValue); + } + Py_DECREF(pFuncWrite); + return ; +} +static void py_call_cancel() { + + // check socket messages for gui cancel message + pFuncWrite = PyObject_GetAttrString(pInstance, "cancel"); + if (pFuncRead && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallNoArgs(pFuncWrite); + if (pValue == NULL){ + fprintf(stderr, "Halui Bridge: cancel function failed: returned NULL\n"); + } + Py_DECREF(pValue); + } + Py_DECREF(pFuncWrite); + return ; +} +static int py_call_get_mdi_count() { + int value = 0; + // check socket messages for jogspeed + pFuncRead = PyObject_GetAttrString(pInstance, "getMdiCount"); + if (pFuncRead && PyCallable_Check(pFuncRead)) { + pValue = PyObject_CallNoArgs(pFuncRead); + if (pValue == NULL){ + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "Halui Bridge: getMdiCountfunction failed: returned NULL\n"); + value = -1; + }else{ + if (PyLong_Check(pValue)) { + value = (int) PyLong_AsLong(pValue); + //fprintf(stderr, "axis value %d\n",value); + if (PyErr_Occurred()) { + value = -1; + // Handle conversion error + PyErr_Print(); + // Clear the error state if needed + PyErr_Clear(); + } + } + } + Py_DECREF(pValue); + } + Py_DECREF(pFuncRead); + return value; +} + +// turns a undex number into a macro name +static char* py_call_get_mdi_name( int num) { + pFuncWrite = PyObject_GetAttrString(pInstance, "getMdiName"); + + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallFunction(pFuncWrite, "i", num); + + if (pValue != NULL) { + if (PyUnicode_Check(pValue)) { + PyObject *pBytes = PyUnicode_AsUTF8String(pValue); + char *result_string = PyBytes_AsString(pBytes); + Py_XDECREF(pBytes); + printf("Python function returned: %s %i\n", result_string,num); + Py_DECREF(pValue); + Py_DECREF(pFuncWrite); + return result_string; + } else { + fprintf(stderr, "Return value is not a string. %i\n", num); + } + } else { + PyErr_Print(); // Print Python error if call failed + } + Py_DECREF(pValue); + }else{ + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "halui Bridge: Failed python function"); + } + Py_DECREF(pFuncWrite); + return NULL; +} /******************************************************************** * @@ -656,8 +794,8 @@ int halui_hal_init(void) CHK(hal_pin_new_real(comp_id, HAL_OUT, &(halui_data->fo_value), 0.0, "halui.feed-override.value")); CHK(hal_pin_new_real(comp_id, HAL_OUT, &(halui_data->ro_value), 0.0, "halui.rapid-override.value")); CHK(hal_pin_new_ui32(comp_id, HAL_OUT, &(halui_data->joint_selected), 0, "halui.joint.selected")); - CHK(hal_pin_new_ui32(comp_id, HAL_OUT, &(halui_data->axis_selected), 0, "halui.axis.selected")); - CHK(hal_pin_new_ui32(comp_id, HAL_OUT, &(halui_data->tool_number), 0, "halui.tool.number")); + CHK(hal_pin_new_si32(comp_id, HAL_OUT, &(halui_data->axis_selected), 0, "halui.axis.selected")); + CHK(hal_pin_new_si32(comp_id, HAL_OUT, &(halui_data->tool_number), 0, "halui.tool.number")); CHK(hal_pin_new_real(comp_id, HAL_OUT, &(halui_data->tool_length_offset_x), 0.0, "halui.tool.length_offset.x")); CHK(hal_pin_new_real(comp_id, HAL_OUT, &(halui_data->tool_length_offset_y), 0.0, "halui.tool.length_offset.y")); CHK(hal_pin_new_real(comp_id, HAL_OUT, &(halui_data->tool_length_offset_z), 0.0, "halui.tool.length_offset.z")); @@ -684,6 +822,8 @@ int halui_hal_init(void) CHK(halui_export_pin_IN_bit(&(halui_data->mist_off), "halui.mist.off")); CHK(halui_export_pin_IN_bit(&(halui_data->flood_on), "halui.flood.on")); CHK(halui_export_pin_IN_bit(&(halui_data->flood_off), "halui.flood.off")); + CHK(halui_export_pin_IN_bit(&(halui_data->cycle_start), "halui.cycle.start")); + CHK(halui_export_pin_IN_bit(&(halui_data->cycle_pause), "halui.cycle.pause")); CHK(halui_export_pin_IN_bit(&(halui_data->program_run), "halui.program.run")); CHK(halui_export_pin_IN_bit(&(halui_data->program_pause), "halui.program.pause")); CHK(halui_export_pin_IN_bit(&(halui_data->program_resume), "halui.program.resume")); @@ -749,6 +889,7 @@ int halui_hal_init(void) CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->ajog_increment_minus[axis_num]), 0, "halui.axis.%c.increment-minus", c)); } + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->mpg_select0), 0, "halui.mpg-select.0")); CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->joint_home[num_joints]), 0, "halui.joint.selected.home")); CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->joint_unhome[num_joints]), 0, "halui.joint.selected.unhome")); CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->jjog_plus[num_joints]), 0, "halui.joint.selected.plus")); @@ -772,6 +913,15 @@ int halui_hal_init(void) CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->mdi_commands[n]), 0, "halui.mdi-command-%02d", n)); } + for (int n=0; ngui_mdi_commands[n]), 0, "halui.gui.mdi-command-%s", py_call_get_mdi_name(n))); + } + + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_ok), 0, "halui.gui.ok")); + + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_cancel), 0, "halui.gui.cancel")); + hal_ready(comp_id); return 0; } @@ -1112,6 +1262,7 @@ static void sendJogStop(int ja, int jjogmode) static void sendJogCont(int ja, double speed, int jjogmode) { + EMC_JOG_CONT emc_jog_cont_msg; if (emcStatus->task.state != EMC_TASK_STATE::ON) { return; } @@ -1340,6 +1491,12 @@ static int iniLoad(const char *filename) mdi_commands[num_mdi_commands++] = strdup(mc->c_str()); } + int temp = py_call_get_mdi_count(); + for (int n=0; naxis_nr_select[axis_num], old_halui_data.axis_nr_select[axis_num] = 0); + hal_set_bool(halui_data->mpg_select0, old_halui_data.mpg_select0 = 0); hal_set_bool(halui_data->ajog_minus[axis_num], old_halui_data.ajog_minus[axis_num] = 0); hal_set_bool(halui_data->ajog_plus[axis_num], old_halui_data.ajog_plus[axis_num] = 0); hal_set_real(halui_data->ajog_analog[axis_num], old_halui_data.ajog_analog[axis_num] = 0); @@ -1396,7 +1554,7 @@ static void hal_init_pins() hal_set_real(halui_data->ajog_speed, 0); hal_set_ui32(halui_data->joint_selected, 0); // select joint 0 by default - hal_set_ui32(halui_data->axis_selected, 0); // select axis 0 by default + hal_set_si32(halui_data->axis_selected, -1); // select no axis by default hal_set_real(halui_data->fo_scale, old_halui_data.fo_scale = 0.1); //sane default hal_set_real(halui_data->ro_scale, old_halui_data.ro_scale = 0.1); //sane default @@ -1460,6 +1618,118 @@ static bool jogging_selected_axis(local_halui_str &hal) { return (hal.ajog_plus[EMCMOT_MAX_AXIS] || hal.ajog_minus[EMCMOT_MAX_AXIS]); } +static double py_call_axis_get_jogspeed() { + double jspd = 0; + // check socket messages for jogspeed + pFuncRead = PyObject_GetAttrString(pInstance, "getJogRate"); + if (pFuncRead && PyCallable_Check(pFuncRead)) { + pValue = PyObject_CallNoArgs(pFuncRead); + if (pValue == NULL){ + fprintf(stderr, "Halui Bridge: getJogRate function failed: returned NULL\n"); + jspd = 0; + }else{ + if (PyFloat_Check(pValue)) { + jspd = PyFloat_AsDouble(pValue); + if (PyErr_Occurred()) { + jspd = 0; + // Handle conversion error + PyErr_Print(); + // Clear the error state if needed + PyErr_Clear(); + } + } + } + Py_DECREF(pValue); + } + Py_DECREF(pFuncRead); + return jspd; +} + +static void py_call_axis_jogspeed(double speed) +{ + pFuncWrite = PyObject_GetAttrString(pInstance, "setJogRate"); + + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallFunction(pFuncWrite, "d", speed); + if (pValue == NULL){ + fprintf(stderr, "halui bridge: writeMsg function failed: returned NULL\n"); + if (PyErr_Occurred()) PyErr_Print(); + } + Py_DECREF(pValue); + + }else{ + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "halui Bridge: Failed python function"); + } + Py_DECREF(pFuncWrite); +} + +static int py_call_get_axis_selected() { + int value = 0; + // check socket messages for jogspeed + pFuncRead = PyObject_GetAttrString(pInstance, "getSelectedAxis"); + if (pFuncRead && PyCallable_Check(pFuncRead)) { + pValue = PyObject_CallNoArgs(pFuncRead); + if (pValue == NULL){ + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "Halui Bridge: getSelectAxis function failed: returned NULL\n"); + value = -1; + }else{ + if (PyLong_Check(pValue)) { + value = (int) PyLong_AsLong(pValue); + //fprintf(stderr, "axis value %d\n",value); + if (PyErr_Occurred()) { + value = -1; + // Handle conversion error + PyErr_Print(); + // Clear the error state if needed + PyErr_Clear(); + } + } + } + Py_DECREF(pValue); + } + Py_DECREF(pFuncRead); + return value; +} + +static void py_call_axis_changed( int axis) +{ + pFuncWrite = PyObject_GetAttrString(pInstance, "setSelectedAxis"); + + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallFunction(pFuncWrite, "i", axis); + if (pValue == NULL){ + fprintf(stderr, "halui bridge: writeMsg function failed: returned NULL\n"); + if (PyErr_Occurred()) PyErr_Print(); + } + Py_DECREF(pValue); + + }else{ + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "halui Bridge: Failed python function"); + } + Py_DECREF(pFuncWrite); +} + +static void py_call_request_MDI( int index) +{ + pFuncWrite = PyObject_GetAttrString(pInstance, "runIndexedMacro"); + + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallFunction(pFuncWrite, "i", index); + if (pValue == NULL){ + fprintf(stderr, "halui bridge: runIndexedMacro function failed: returned NULL\n"); + if (PyErr_Occurred()) PyErr_Print(); + } + Py_DECREF(pValue); + + }else{ + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "halui Bridge: Failed python function runIndexedMacro"); + } + Py_DECREF(pFuncWrite); +} // this function looks if any of the hal pins has changed // and sends appropriate messages if so @@ -1471,10 +1741,54 @@ static void check_hal_changes() rtapi_bool bit; int js; rtapi_real floatt; + double jogspeed; int jjog_speed_changed; int ajog_speed_changed; + int is_any_axis_selected, deselected; + + // get python to process socket messages + pFuncRead = PyObject_GetAttrString(pInstance, "readMsg"); + + if (pFuncRead && PyCallable_Check(pFuncRead)) { + pValue = PyObject_CallNoArgs(pFuncRead); + if (pValue == NULL){ + fprintf(stderr, "Halui Bridge: readMsg function failed: returned NULL\n"); + } + if (PyErr_Occurred()) PyErr_Print(); + }else{ + if (PyErr_Occurred()){ + PyErr_Print(); + } + fprintf(stderr, "Bridge: Failed python function"); + exit(1); + } + Py_DECREF(pFuncRead); + Py_DECREF(pValue); + + // check socket messages for current axis selection + int value = py_call_get_axis_selected(); local_halui_str new_halui_data_mutable; + + if (value != lastaxis) { + // inject socket axis selection over hal data + for (axis_num = 0; axis_num < EMCMOT_MAX_AXIS; axis_num++) { + if ( !(axis_mask & (1 << axis_num)) ) { continue; } + + if (axis_num == value) { + hal_set_bool(halui_data->axis_nr_select[axis_num], 1); + }else{ + hal_set_bool(halui_data->axis_nr_select[axis_num], 0); + } + } + if (value == 100) { + hal_set_bool(halui_data->mpg_select0, 1); + }else{ + hal_set_bool(halui_data->mpg_select0, 0); + } + lastaxis = value; + } + copy_hal_data(*halui_data, new_halui_data_mutable); const local_halui_str &new_halui_data = new_halui_data_mutable; @@ -1518,6 +1832,16 @@ static void check_hal_changes() if (check_bit_changed(new_halui_data.flood_off, old_halui_data.flood_off) != 0) sendFloodOff(); + if (check_bit_changed(new_halui_data.cycle_start, old_halui_data.cycle_start) != 0){ + fprintf(stderr, "cycle-start value = %i\n", new_halui_data.cycle_start ); + py_call_cycleStart(); + } + + if (check_bit_changed(new_halui_data.cycle_pause, old_halui_data.cycle_pause) != 0){ + fprintf(stderr, "cycle-pause value = %i\n", new_halui_data.cycle_pause ); + py_call_cyclePause(); + } + if (check_bit_changed(new_halui_data.program_run, old_halui_data.program_run) != 0) sendProgramRun(0); @@ -1692,11 +2016,23 @@ static void check_hal_changes() // re-start the jog with the new speed if (fabs(old_halui_data.ajog_speed - new_halui_data.ajog_speed) > 0.00001) { old_halui_data.ajog_speed = new_halui_data.ajog_speed; + internaljogspeed = new_halui_data.ajog_speed; ajog_speed_changed = 1; + py_call_axis_jogspeed(internaljogspeed); } else { ajog_speed_changed = 0; } + // check socket messages for jogspeed + jogspeed = py_call_axis_get_jogspeed(); + if (fabs(jogspeed - lastjogspeed) > 0.00001) { + ajog_speed_changed = 1; + lastjogspeed = jogspeed; + internaljogspeed = jogspeed; + fprintf(stderr, "JogRate value = %f\n", jogspeed ); + } + + for (joint=0; joint < num_joints; joint++) { if (check_bit_changed(new_halui_data.joint_home[joint], old_halui_data.joint_home[joint]) != 0) sendHome(joint); @@ -1776,47 +2112,58 @@ static void check_hal_changes() } } + + // check thru axes + is_any_axis_selected = 0; + deselected = 0; for (axis_num = 0; axis_num < EMCMOT_MAX_AXIS; axis_num++) { + if ( !(axis_mask & (1 << axis_num)) ) { continue; } + + // axis jog - bit = new_halui_data.ajog_minus[axis_num]; if ((bit != old_halui_data.ajog_minus[axis_num]) || (bit && ajog_speed_changed)) { if (bit != 0) - sendJogCont(axis_num,-new_halui_data.ajog_speed,JOGTELEOP); + sendJogCont(axis_num,-internaljogspeed,JOGTELEOP); else sendJogStop(axis_num,JOGTELEOP); old_halui_data.ajog_minus[axis_num] = bit; } + // axis jog + bit = new_halui_data.ajog_plus[axis_num]; if ((bit != old_halui_data.ajog_plus[axis_num]) || (bit && ajog_speed_changed)) { if (bit != 0) - sendJogCont(axis_num,new_halui_data.ajog_speed,JOGTELEOP); + sendJogCont(axis_num,internaljogspeed,JOGTELEOP); else sendJogStop(axis_num,JOGTELEOP); old_halui_data.ajog_plus[axis_num] = bit; } + // axis jog analog floatt = new_halui_data.ajog_analog[axis_num]; bit = (fabs(floatt) > new_halui_data.ajog_deadband); if ((floatt != old_halui_data.ajog_analog[axis_num]) || (bit && ajog_speed_changed)) { if (bit) - sendJogCont(axis_num,(new_halui_data.ajog_speed) * (new_halui_data.ajog_analog[axis_num]),JOGTELEOP); + sendJogCont(axis_num,(internaljogspeed) * (new_halui_data.ajog_analog[axis_num]),JOGTELEOP); else sendJogStop(axis_num,JOGTELEOP); old_halui_data.ajog_analog[axis_num] = floatt; } + // axis jog + increment bit = new_halui_data.ajog_increment_plus[axis_num]; if (bit != old_halui_data.ajog_increment_plus[axis_num]) { if (bit) - sendJogIncr(axis_num, new_halui_data.ajog_speed, new_halui_data.ajog_increment[axis_num],JOGTELEOP); + sendJogIncr(axis_num, internaljogspeed, new_halui_data.ajog_increment[axis_num],JOGTELEOP); old_halui_data.ajog_increment_plus[axis_num] = bit; } + // jog a- increment bit = new_halui_data.ajog_increment_minus[axis_num]; if (bit != old_halui_data.ajog_increment_minus[axis_num]) { if (bit) - sendJogIncr(axis_num, new_halui_data.ajog_speed, -(new_halui_data.ajog_increment[axis_num]),JOGTELEOP); + sendJogIncr(axis_num, internaljogspeed, -(new_halui_data.ajog_increment[axis_num]),JOGTELEOP); old_halui_data.ajog_increment_minus[axis_num] = bit; } @@ -1824,30 +2171,56 @@ static void check_hal_changes() bit = new_halui_data.axis_nr_select[axis_num]; if (bit != old_halui_data.axis_nr_select[axis_num]) { if (bit != 0) { - hal_set_ui32(halui_data->axis_selected, axis_num); - aselect_changed = axis_num; // flag that we changed the selected axis - } - old_halui_data.axis_nr_select[axis_num] = bit; - } + is_any_axis_selected = 1; + hal_set_si32(halui_data->axis_selected, axis_num); + py_call_axis_changed(axis_num); + aselect_changed = axis_num; // flag that we changed the selected axis + }else{ + deselected = 1; + } + old_halui_data.axis_nr_select[axis_num] = bit; + } + } + + // is MPG0 selected? + bit = new_halui_data.mpg_select0; + if (bit != old_halui_data.mpg_select0) { + if (bit != 0) { + is_any_axis_selected = 1; + py_call_axis_changed(100); + hal_set_si32(halui_data->axis_selected, 100); + }else{ + deselected = 1; + } + old_halui_data.mpg_select0 = bit; + } + + + // last axis has been deselected - no axis is selected now + if (is_any_axis_selected == 0 and deselected == 1) { + py_call_axis_changed(-1); + hal_set_si32(halui_data->axis_selected, -1); } if (aselect_changed >= 0) { - for (axis_num = 0; axis_num < EMCMOT_MAX_AXIS; axis_num++) { - if ( !(axis_mask & (1 << axis_num)) ) { continue; } - if (axis_num != aselect_changed) { - hal_set_bool(halui_data->axis_is_selected[axis_num], 0); + fprintf(stderr, "halui Bridge: axis selected %d\n",aselect_changed); + for (axis_num = 0; axis_num < EMCMOT_MAX_AXIS; axis_num++) { + if ( !(axis_mask & (1 << axis_num)) ) { continue; } + if (axis_num != aselect_changed) { + hal_set_bool(halui_data->axis_is_selected[axis_num], 0); if (jogging_selected_axis(old_halui_data) && !jogging_axis(old_halui_data, axis_num)) { - sendJogStop(axis_num,JOGTELEOP); + sendJogStop(axis_num,JOGTELEOP); } } else { - hal_set_bool(halui_data->axis_is_selected[axis_num], 1); + hal_set_bool(halui_data->axis_is_selected[axis_num], 1); if (hal_get_bool(halui_data->ajog_plus[num_axes])) { - sendJogCont(axis_num, new_halui_data.ajog_speed,JOGTELEOP); + fprintf(stderr, "halui: jog plus: %d\n",num_axes); + sendJogCont(axis_num, internaljogspeed,JOGTELEOP); } else if (hal_get_bool(halui_data->ajog_minus[num_axes])) { - sendJogCont(axis_num, -new_halui_data.ajog_speed,JOGTELEOP); + sendJogCont(axis_num, -internaljogspeed,JOGTELEOP); } - } - } + } + } } if (check_bit_changed(new_halui_data.joint_home[num_joints], old_halui_data.joint_home[num_joints]) != 0) @@ -1896,7 +2269,7 @@ static void check_hal_changes() js = new_halui_data.axis_selected; if ((bit != old_halui_data.ajog_minus[EMCMOT_MAX_AXIS]) || (bit && ajog_speed_changed)) { if (bit != 0) - sendJogCont(js, -new_halui_data.ajog_speed,JOGTELEOP); + sendJogCont(js, -internaljogspeed,JOGTELEOP); else sendJogStop(js,JOGTELEOP); old_halui_data.ajog_minus[EMCMOT_MAX_AXIS] = bit; @@ -1906,7 +2279,7 @@ static void check_hal_changes() js = new_halui_data.axis_selected; if ((bit != old_halui_data.ajog_plus[EMCMOT_MAX_AXIS]) || (bit && ajog_speed_changed)) { if (bit != 0) - sendJogCont(js,new_halui_data.ajog_speed,JOGTELEOP); + sendJogCont(js,internaljogspeed,JOGTELEOP); else sendJogStop(js,JOGTELEOP); old_halui_data.ajog_plus[EMCMOT_MAX_AXIS] = bit; @@ -1916,7 +2289,7 @@ static void check_hal_changes() js = new_halui_data.axis_selected; if (bit != old_halui_data.ajog_increment_plus[EMCMOT_MAX_AXIS]) { if (bit) - sendJogIncr(js, new_halui_data.ajog_speed, new_halui_data.ajog_increment[EMCMOT_MAX_AXIS],JOGTELEOP); + sendJogIncr(js, internaljogspeed, new_halui_data.ajog_increment[EMCMOT_MAX_AXIS],JOGTELEOP); old_halui_data.ajog_increment_plus[EMCMOT_MAX_AXIS] = bit; } @@ -1924,14 +2297,33 @@ static void check_hal_changes() js = new_halui_data.axis_selected; if (bit != old_halui_data.ajog_increment_minus[EMCMOT_MAX_AXIS]) { if (bit) - sendJogIncr(js, new_halui_data.ajog_speed, -(new_halui_data.ajog_increment[EMCMOT_MAX_AXIS]),JOGTELEOP); + sendJogIncr(js, internaljogspeed, -(new_halui_data.ajog_increment[EMCMOT_MAX_AXIS]),JOGTELEOP); old_halui_data.ajog_increment_minus[EMCMOT_MAX_AXIS] = bit; } + // run HALUI commands for(int n = 0; n < num_mdi_commands; n++) { if (check_bit_changed(new_halui_data.mdi_commands[n], old_halui_data.mdi_commands[n]) != 0) sendMdiCommand(n); } + + // request GUI ti run MDI commands + for(int n = 0; n < num_gui_mdi_commands; n++) { + if (check_bit_changed(new_halui_data.gui_mdi_commands[n], old_halui_data.gui_mdi_commands[n]) != 0){ + fprintf(stderr,"GUI MDI command called index: %i\n", n); + py_call_request_MDI(n); + } + } + + if (check_bit_changed(new_halui_data.gui_ok, old_halui_data.gui_ok) != 0) { + fprintf(stderr,"GUI OK command called\n"); + py_call_ok(); + } + + if (check_bit_changed(new_halui_data.gui_cancel, old_halui_data.gui_cancel) != 0) { + fprintf(stderr,"GUI CANCEL command called\n"); + py_call_cancel(); + } } // this function looks at the received NML status message @@ -1992,7 +2384,7 @@ static void modify_hal_pins() hal_set_bool(halui_data->mist_is_on, emcStatus->io.coolant.mist); hal_set_bool(halui_data->flood_is_on, emcStatus->io.coolant.flood); - hal_set_ui32(halui_data->tool_number, emcStatus->io.tool.toolInSpindle); + hal_set_si32(halui_data->tool_number, emcStatus->io.tool.toolInSpindle); hal_set_real(halui_data->tool_length_offset_x, emcStatus->task.toolOffset.tran.x); hal_set_real(halui_data->tool_length_offset_y, emcStatus->task.toolOffset.tran.y); hal_set_real(halui_data->tool_length_offset_z, emcStatus->task.toolOffset.tran.z); @@ -2122,6 +2514,27 @@ int main(int argc, char *argv[]) exit(1); } + /* import the python module and get references for needed function */ + + PyConfig config; + PyConfig_InitPythonConfig(&config); + char name[] = "halui"; + wchar_t *wname = Py_DecodeLocale(name, NULL); + PyConfig_SetString(&config, &config.program_name, wname); + Py_Initialize(); + + PyRun_SimpleString("print('PYTHON EMBEDDED!!')\n"); + pModule = PyImport_ImportModule("bridgeui.bridge"); + if (pModule != NULL) { + pClass = PyObject_GetAttrString(pModule, "Bridge"); + pInstance = PyObject_CallObject(pClass, NULL); + }else{ + PyErr_Print(); + fprintf(stderr, "bridge: Failed to load \"%s\"\n", "pyui"); + exit(1); + } + Py_DECREF(pClass); + // get configuration information if (0 != iniLoad(emc_inifile)) { rcs_print_error("iniLoad error\n"); @@ -2137,6 +2550,9 @@ int main(int argc, char *argv[]) //initialize safe values hal_init_pins(); + + + // init NML if (0 != tryNml()) { rcs_print_error("can't connect to emc\n"); @@ -2172,6 +2588,8 @@ int main(int argc, char *argv[]) } } check_hal_changes(); //if anything changed send NML messages + + modify_hal_pins(); //if status changed modify HAL too esleep(0.02); //sleep for a while updateStatus(); From aaffa61791033bb1681174fd80089fa9029e3050 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sun, 8 Feb 2026 16:42:51 -0800 Subject: [PATCH 07/81] makefile - add halui's bridgeui folder --- src/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Makefile b/src/Makefile index f43362f943b..c1189561729 100644 --- a/src/Makefile +++ b/src/Makefile @@ -757,6 +757,7 @@ install-kernel-indep: install-dirs install-kernel-indep: install-python install-python: install-dirs $(DIR) $(DESTDIR)$(SITEPY) $(DESTDIR)$(SITEPY)/rs274 + $(DIR) $(DESTDIR)$(SITEPY)/bridgeui $(DIR) $(DESTDIR)$(SITEPY)/common $(DIR) $(DESTDIR)$(SITEPY)/touchy $(DIR) $(DESTDIR)$(SITEPY)/gscreen @@ -780,6 +781,7 @@ install-python: install-dirs $(DIR) $(DESTDIR)$(SITEPY)/pyui $(DIR) $(DESTDIR)$(SITEPY)/plasmac $(FILE) ../lib/python/*.py ../lib/python/*.so $(DESTDIR)$(SITEPY) + $(FILE) ../lib/python/bridgeui/*.py $(DESTDIR)$(SITEPY)/bridgeui $(FILE) ../lib/python/common/*.py $(DESTDIR)$(SITEPY)/common $(FILE) ../lib/python/rs274/*.py $(DESTDIR)$(SITEPY)/rs274 $(FILE) ../lib/python/touchy/*.py $(DESTDIR)$(SITEPY)/touchy From 5d56eb5effb70387f5216b6f93ea3015f3e22ec9 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 10 Apr 2026 13:38:30 -0700 Subject: [PATCH 08/81] halui -add an angular jograte pin and send angular jograte messages halui doesn't use the angular jograte in it's jogging yet but will tell the gui the jograte has changed --- src/emc/usr_intf/halui.cc | 78 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index 99de81a1436..09ac813da1d 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -165,6 +165,7 @@ typedef hal_uint_t hal_ui32_t; ARRAY(bool,jjog_increment_minus,EMCMOT_MAX_JOINTS+1) /* Incremental jogging, negative direction */ \ \ FIELD(real,ajog_speed) /* pin for setting the jog speed (halui internal) */ \ + FIELD(real,ajog_speed_angular) /* pin for setting the angular jog speed (halui internal) */ \ ARRAY(bool,ajog_minus,EMCMOT_MAX_AXIS+1) /* pin to jog in positive direction */ \ ARRAY(bool,ajog_plus,EMCMOT_MAX_AXIS+1) /* pin to jog in negative direction */ \ ARRAY(real,ajog_analog,EMCMOT_MAX_AXIS+1) /* pin for analog jogging (-1..0..1) */ \ @@ -259,6 +260,8 @@ static local_halui_str old_halui_data; static double lastjogspeed = 0; static double internaljogspeed = 0; +static double lastangularjogspeed = 0; +static double internalangularjogspeed = 0; static int lastaxis = -1; static char *mdi_commands[MDI_MAX]; @@ -907,6 +910,7 @@ int halui_hal_init(void) CHK(halui_export_pin_IN_float(&(halui_data->jjog_deadband), "halui.joint.jog-deadband")); CHK(halui_export_pin_IN_float(&(halui_data->ajog_speed), "halui.axis.jog-speed")); + CHK(halui_export_pin_IN_float(&(halui_data->ajog_speed_angular), "halui.axis.jog-speed-angular")); CHK(halui_export_pin_IN_float(&(halui_data->ajog_deadband), "halui.axis.jog-deadband")); for (int n = 0; n < num_mdi_commands; n++) { @@ -1552,6 +1556,7 @@ static void hal_init_pins() hal_set_bool(halui_data->ajog_increment_minus[EMCMOT_MAX_AXIS], old_halui_data.ajog_increment_minus[EMCMOT_MAX_AXIS] = 0); hal_set_real(halui_data->ajog_deadband, 0.2); hal_set_real(halui_data->ajog_speed, 0); + hal_set_real(halui_data->ajog_speed_angular, 0); hal_set_ui32(halui_data->joint_selected, 0); // select joint 0 by default hal_set_si32(halui_data->axis_selected, -1); // select no axis by default @@ -1664,9 +1669,55 @@ static void py_call_axis_jogspeed(double speed) Py_DECREF(pFuncWrite); } +static double py_call_axis_get_angular_jogspeed() { + double jspd = 0; + // check socket messages for angular jogspeed + pFuncRead = PyObject_GetAttrString(pInstance, "getJogRateAngular"); + if (pFuncRead && PyCallable_Check(pFuncRead)) { + pValue = PyObject_CallNoArgs(pFuncRead); + if (pValue == NULL){ + fprintf(stderr, "Halui Bridge: getJogRateAngular function failed: returned NULL\n"); + jspd = 0; + }else{ + if (PyFloat_Check(pValue)) { + jspd = PyFloat_AsDouble(pValue); + if (PyErr_Occurred()) { + jspd = 0; + // Handle conversion error + PyErr_Print(); + // Clear the error state if needed + PyErr_Clear(); + } + } + } + Py_DECREF(pValue); + } + Py_DECREF(pFuncRead); + return jspd; +} + +static void py_call_axis_angular_jogspeed(double speed) +{ + pFuncWrite = PyObject_GetAttrString(pInstance, "setJogRateAngular"); + + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallFunction(pFuncWrite, "d", speed); + if (pValue == NULL){ + fprintf(stderr, "halui bridge: setJogRateAngular function failed: returned NULL\n"); + if (PyErr_Occurred()) PyErr_Print(); + } + Py_DECREF(pValue); + + }else{ + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "halui Bridge: Failed python function"); + } + Py_DECREF(pFuncWrite); +} + static int py_call_get_axis_selected() { int value = 0; - // check socket messages for jogspeed + // check socket messages for selected axis pFuncRead = PyObject_GetAttrString(pInstance, "getSelectedAxis"); if (pFuncRead && PyCallable_Check(pFuncRead)) { pValue = PyObject_CallNoArgs(pFuncRead); @@ -1742,8 +1793,10 @@ static void check_hal_changes() int js; rtapi_real floatt; double jogspeed; + double angularjogspeed; int jjog_speed_changed; int ajog_speed_changed; + int ajog_speed_angular_changed; int is_any_axis_selected, deselected; // get python to process socket messages @@ -2009,6 +2062,7 @@ static void check_hal_changes() } else { jjog_speed_changed = 0; } + // axis stuff (selection, homing..) aselect_changed = -1; // flag to see if the selected joint changed @@ -2032,7 +2086,27 @@ static void check_hal_changes() fprintf(stderr, "JogRate value = %f\n", jogspeed ); } - + // if the ANGULAR jog-speed changes while in a continuous jog, we want to + // re-start the jog with the new speed + if (fabs(old_halui_data.ajog_speed_angular - new_halui_data.ajog_speed_angular) > 0.00001) { + old_halui_data.ajog_speed_angular = new_halui_data.ajog_speed_angular; + internalangularjogspeed = new_halui_data.ajog_speed_angular; + ajog_speed_angular_changed = 1; + py_call_axis_angular_jogspeed(internalangularjogspeed); + } else { + ajog_speed_angular_changed = 0; + } + + // check socket messages for ANGULAR jogspeed + angularjogspeed = py_call_axis_get_angular_jogspeed(); + if (fabs(angularjogspeed - lastangularjogspeed) > 0.00001) { + ajog_speed_angular_changed = 1; + lastangularjogspeed = angularjogspeed; + internalangularjogspeed = angularjogspeed; + fprintf(stderr, "angular JogRate value = %f\n", angularjogspeed ); + } + + for (joint=0; joint < num_joints; joint++) { if (check_bit_changed(new_halui_data.joint_home[joint], old_halui_data.joint_home[joint]) != 0) sendHome(joint); From 4167fa6b374dc65a57eb9707a69f876a1768981d Mon Sep 17 00:00:00 2001 From: CMorley Date: Sun, 12 Apr 2026 10:15:33 -0700 Subject: [PATCH 09/81] halui -use angular jograte pin/messages for angular axis now checks for axis type and uses the apropriate jograte --- lib/python/bridgeui/bridge.py | 9 ++++ src/emc/usr_intf/halui.cc | 98 ++++++++++++++++++++++++++++------- 2 files changed, 89 insertions(+), 18 deletions(-) diff --git a/lib/python/bridgeui/bridge.py b/lib/python/bridgeui/bridge.py index 56577ebb664..1d0edfaeddd 100644 --- a/lib/python/bridgeui/bridge.py +++ b/lib/python/bridgeui/bridge.py @@ -227,6 +227,15 @@ def isAxisSelected(self, index): letter = 'XYZABCUVW'[index] return int(self.axesSelected[letter]) + def getAxisIndexType(self, axis): + try: + num = self.INFO.GET_JOINT_NUM_FROM_AXIS_INDEX[axis] + flag = self.INFO.JOINT_TYPE_INT[num] + return flag + except: + return 1 + + def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index 09ac813da1d..4dd76e3ee61 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -262,6 +262,7 @@ static double lastjogspeed = 0; static double internaljogspeed = 0; static double lastangularjogspeed = 0; static double internalangularjogspeed = 0; +static double tempjogspeed = 0; static int lastaxis = -1; static char *mdi_commands[MDI_MAX]; @@ -648,7 +649,7 @@ static int py_call_get_mdi_count() { return value; } -// turns a undex number into a macro name +// turns a index number into a macro name static char* py_call_get_mdi_name( int num) { pFuncWrite = PyObject_GetAttrString(pInstance, "getMdiName"); @@ -1782,6 +1783,35 @@ static void py_call_request_MDI( int index) Py_DECREF(pFuncWrite); } +static int py_call_get_axis_type( int index) { + int value = 0; + // check socket messages for selected axis + pFuncRead = PyObject_GetAttrString(pInstance, "getAxisIndexType"); + if (pFuncRead && PyCallable_Check(pFuncRead)) { + pValue = PyObject_CallFunction(pFuncRead, "i", index); + if (pValue == NULL){ + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "Halui Bridge: getSelectAxis function failed: returned NULL\n"); + value = -1; + }else{ + if (PyLong_Check(pValue)) { + value = (int) PyLong_AsLong(pValue); + //fprintf(stderr, "axis value %d\n",value); + if (PyErr_Occurred()) { + value = -1; + // Handle conversion error + PyErr_Print(); + // Clear the error state if needed + PyErr_Clear(); + } + } + } + Py_DECREF(pValue); + } + Py_DECREF(pFuncRead); + return value; +} + // this function looks if any of the hal pins has changed // and sends appropriate messages if so static void check_hal_changes() @@ -1796,7 +1826,6 @@ static void check_hal_changes() double angularjogspeed; int jjog_speed_changed; int ajog_speed_changed; - int ajog_speed_angular_changed; int is_any_axis_selected, deselected; // get python to process socket messages @@ -2091,16 +2120,16 @@ static void check_hal_changes() if (fabs(old_halui_data.ajog_speed_angular - new_halui_data.ajog_speed_angular) > 0.00001) { old_halui_data.ajog_speed_angular = new_halui_data.ajog_speed_angular; internalangularjogspeed = new_halui_data.ajog_speed_angular; - ajog_speed_angular_changed = 1; + ajog_speed_changed = 1; py_call_axis_angular_jogspeed(internalangularjogspeed); } else { - ajog_speed_angular_changed = 0; + ajog_speed_changed = 0; } // check socket messages for ANGULAR jogspeed angularjogspeed = py_call_axis_get_angular_jogspeed(); if (fabs(angularjogspeed - lastangularjogspeed) > 0.00001) { - ajog_speed_angular_changed = 1; + ajog_speed_changed = 1; lastangularjogspeed = angularjogspeed; internalangularjogspeed = angularjogspeed; fprintf(stderr, "angular JogRate value = %f\n", angularjogspeed ); @@ -2194,11 +2223,18 @@ static void check_hal_changes() if ( !(axis_mask & (1 << axis_num)) ) { continue; } + // check for axis type: linear/rotary + if (py_call_get_axis_type(axis_num) == 1){ + tempjogspeed = internaljogspeed; + }else{ + tempjogspeed = internalangularjogspeed; + } + // axis jog - bit = new_halui_data.ajog_minus[axis_num]; if ((bit != old_halui_data.ajog_minus[axis_num]) || (bit && ajog_speed_changed)) { if (bit != 0) - sendJogCont(axis_num,-internaljogspeed,JOGTELEOP); + sendJogCont(axis_num,-tempjogspeed,JOGTELEOP); else sendJogStop(axis_num,JOGTELEOP); old_halui_data.ajog_minus[axis_num] = bit; @@ -2208,7 +2244,7 @@ static void check_hal_changes() bit = new_halui_data.ajog_plus[axis_num]; if ((bit != old_halui_data.ajog_plus[axis_num]) || (bit && ajog_speed_changed)) { if (bit != 0) - sendJogCont(axis_num,internaljogspeed,JOGTELEOP); + sendJogCont(axis_num,tempjogspeed,JOGTELEOP); else sendJogStop(axis_num,JOGTELEOP); old_halui_data.ajog_plus[axis_num] = bit; @@ -2219,7 +2255,7 @@ static void check_hal_changes() bit = (fabs(floatt) > new_halui_data.ajog_deadband); if ((floatt != old_halui_data.ajog_analog[axis_num]) || (bit && ajog_speed_changed)) { if (bit) - sendJogCont(axis_num,(internaljogspeed) * (new_halui_data.ajog_analog[axis_num]),JOGTELEOP); + sendJogCont(axis_num,(tempjogspeed) * (new_halui_data.ajog_analog[axis_num]),JOGTELEOP); else sendJogStop(axis_num,JOGTELEOP); old_halui_data.ajog_analog[axis_num] = floatt; @@ -2229,7 +2265,7 @@ static void check_hal_changes() bit = new_halui_data.ajog_increment_plus[axis_num]; if (bit != old_halui_data.ajog_increment_plus[axis_num]) { if (bit) - sendJogIncr(axis_num, internaljogspeed, new_halui_data.ajog_increment[axis_num],JOGTELEOP); + sendJogIncr(axis_num, tempjogspeed, new_halui_data.ajog_increment[axis_num],JOGTELEOP); old_halui_data.ajog_increment_plus[axis_num] = bit; } @@ -2237,7 +2273,7 @@ static void check_hal_changes() bit = new_halui_data.ajog_increment_minus[axis_num]; if (bit != old_halui_data.ajog_increment_minus[axis_num]) { if (bit) - sendJogIncr(axis_num, internaljogspeed, -(new_halui_data.ajog_increment[axis_num]),JOGTELEOP); + sendJogIncr(axis_num, tempjogspeed, -(new_halui_data.ajog_increment[axis_num]),JOGTELEOP); old_halui_data.ajog_increment_minus[axis_num] = bit; } @@ -2286,12 +2322,20 @@ static void check_hal_changes() sendJogStop(axis_num,JOGTELEOP); } } else { + + // check for axis type: linear/rotary + if (py_call_get_axis_type(axis_num) == 1){ + tempjogspeed = internaljogspeed; + }else{ + tempjogspeed = internalangularjogspeed; + } + hal_set_bool(halui_data->axis_is_selected[axis_num], 1); if (hal_get_bool(halui_data->ajog_plus[num_axes])) { fprintf(stderr, "halui: jog plus: %d\n",num_axes); - sendJogCont(axis_num, internaljogspeed,JOGTELEOP); + sendJogCont(axis_num, tempjogspeed,JOGTELEOP); } else if (hal_get_bool(halui_data->ajog_minus[num_axes])) { - sendJogCont(axis_num, -internaljogspeed,JOGTELEOP); + sendJogCont(axis_num, -tempjogspeed,JOGTELEOP); } } } @@ -2342,8 +2386,17 @@ static void check_hal_changes() bit = new_halui_data.ajog_minus[EMCMOT_MAX_AXIS]; js = new_halui_data.axis_selected; if ((bit != old_halui_data.ajog_minus[EMCMOT_MAX_AXIS]) || (bit && ajog_speed_changed)) { - if (bit != 0) - sendJogCont(js, -internaljogspeed,JOGTELEOP); + if (bit != 0){ + + // check for axis type: linear/rotary + if (py_call_get_axis_type(js) == 1){ + tempjogspeed = internaljogspeed; + }else{ + tempjogspeed = internalangularjogspeed; + } + + sendJogCont(js, -tempjogspeed,JOGTELEOP); + } else sendJogStop(js,JOGTELEOP); old_halui_data.ajog_minus[EMCMOT_MAX_AXIS] = bit; @@ -2352,8 +2405,17 @@ static void check_hal_changes() bit = new_halui_data.ajog_plus[EMCMOT_MAX_AXIS]; js = new_halui_data.axis_selected; if ((bit != old_halui_data.ajog_plus[EMCMOT_MAX_AXIS]) || (bit && ajog_speed_changed)) { - if (bit != 0) - sendJogCont(js,internaljogspeed,JOGTELEOP); + if (bit != 0){ + + // check for axis type: linear/rotary + if (py_call_get_axis_type(js) == 1){ + tempjogspeed = internaljogspeed; + }else{ + tempjogspeed = internalangularjogspeed; + } + + sendJogCont(js,tempjogspeed,JOGTELEOP); + } else sendJogStop(js,JOGTELEOP); old_halui_data.ajog_plus[EMCMOT_MAX_AXIS] = bit; @@ -2363,7 +2425,7 @@ static void check_hal_changes() js = new_halui_data.axis_selected; if (bit != old_halui_data.ajog_increment_plus[EMCMOT_MAX_AXIS]) { if (bit) - sendJogIncr(js, internaljogspeed, new_halui_data.ajog_increment[EMCMOT_MAX_AXIS],JOGTELEOP); + sendJogIncr(js, tempjogspeed, new_halui_data.ajog_increment[EMCMOT_MAX_AXIS],JOGTELEOP); old_halui_data.ajog_increment_plus[EMCMOT_MAX_AXIS] = bit; } @@ -2371,7 +2433,7 @@ static void check_hal_changes() js = new_halui_data.axis_selected; if (bit != old_halui_data.ajog_increment_minus[EMCMOT_MAX_AXIS]) { if (bit) - sendJogIncr(js, internaljogspeed, -(new_halui_data.ajog_increment[EMCMOT_MAX_AXIS]),JOGTELEOP); + sendJogIncr(js, tempjogspeed, -(new_halui_data.ajog_increment[EMCMOT_MAX_AXIS]),JOGTELEOP); old_halui_data.ajog_increment_minus[EMCMOT_MAX_AXIS] = bit; } From 4a1987a1355862f718121b678f5400d4f98e0783 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Thu, 2 Jul 2026 19:42:10 -0700 Subject: [PATCH 10/81] bridge -look for macro commands too MDI and macro commands are similar enough to combine --- lib/python/bridgeui/bridge.py | 45 +++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/lib/python/bridgeui/bridge.py b/lib/python/bridgeui/bridge.py index 1d0edfaeddd..c9d8d4aac4b 100644 --- a/lib/python/bridgeui/bridge.py +++ b/lib/python/bridgeui/bridge.py @@ -63,14 +63,6 @@ def __init__(self, readAddress = "tcp://127.0.0.1:5690", self.init_read() self.init_write() - def update(self, *arg): - print(self, arg) - raw=arg[0]; row=arg[1];column=arg[2];state=arg[3] - LOG.debug('raw {}, row {}, col {}, state {}'.format(raw,row,column,state)) - print ('raw',raw,'row:',row,'column:',column,'state:',state) - self.writeMsg('set_selected_axis','Y') - self.activeJoint.set(10) - def init(self): self.jogRate = 0 self.jogRateAngular = 0 @@ -143,7 +135,7 @@ def action(self, msg, data): # send msg to hal_glib def writeMsg(self, msg, data): - print('Write Msg called') + #print('Write Msg called') if ZMQ: topic = self.writeTopic message = json.dumps({'FUNCTION':msg,'ARGS':data}) @@ -170,27 +162,43 @@ def ok(self): def cancel(self): self.writeMsg('request_cancel', True) + # if the number is bigger then MDI command list + # then look for MACRO commands def getMdiName(self, num): if num >len(self.INFO.MDI_COMMAND_DICT)-1: + offset = len(self.INFO.MDI_COMMAND_DICT) + return self.getMacroName(num-offset) + else: + temp = list(self.INFO.MDI_COMMAND_DICT.keys())[num] + LOG.debug('MDI:{} {}'.format(num,temp)) + return temp + + def getMacroName(self, num): + if num >len(self.INFO.MACRO_COMMAND_DICT)-1: return 'None' - temp = list(self.INFO.MDI_COMMAND_DICT.keys())[num] - LOG.debug('{} {}'.format(num,temp)) + temp = list(self.INFO.MACRO_COMMAND_DICT.keys())[num] + LOG.debug('MACRO:{} {}'.format(num,temp)) return temp - def getMacroNames(self): - for i in self.INFO.INI_MACROS: - name = i.split()[0] - LOG.debug('{} {}'.format(name,i)) - def runIndexedMacro(self, num): + # check for any MDI commands first: name = self.getMdiName(num) LOG.debug('Macro name:{} ,index: {}'.format(name, num)) if name != 'None': self.writeMsg('request_macro_call', name) + # else look for any MACRO commands: + else: + offset = len(self.INFO.MDI_COMMAND_DICT) + name = self.getMacroName(num-offset) + LOG.debug('Macro name:{} ,index: {}'.format(name, num)) + if name != 'None': + self.writeMsg('request_macro_call', name) + + # cound of MDI and MACRO commands def getMdiCount(self): - print(len(self.INFO.MDI_COMMAND_DICT)) - return len(self.INFO.MDI_COMMAND_DICT) + #print('->',len(self.INFO.MDI_COMMAND_DICT),len(self.INFO.MACRO_COMMAND_DICT)) + return len(self.INFO.MDI_COMMAND_DICT) + len(self.INFO.MACRO_COMMAND_DICT) def getJogRate(self): return self.jogRate @@ -202,6 +210,7 @@ def getJogRateAngular(self): def setJogRateAngular(self, value): self.writeMsg('set_jograte_angular', value) + # XYZABCUVW, None, or MPG0 def getSelectedAxis(self): name = self.currentSelectedAxis if name == 'None': From 553f296544439c613b63d6da45cfdcf5a19a582e Mon Sep 17 00:00:00 2001 From: Cmorley Date: Thu, 2 Jul 2026 19:51:13 -0700 Subject: [PATCH 11/81] hal_bridge -add a warning if the write address is already used --- src/hal/user_comps/hal_bridge.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hal/user_comps/hal_bridge.py b/src/hal/user_comps/hal_bridge.py index 1833ab7e7af..39977946508 100644 --- a/src/hal/user_comps/hal_bridge.py +++ b/src/hal/user_comps/hal_bridge.py @@ -118,7 +118,10 @@ def init_hal(self): def init_write(self): context = zmq.Context() self.writeSocket = context.socket(zmq.PUB) - self.writeSocket.bind(self.writeAddress) + try: + self.writeSocket.bind(self.writeAddress) + except zmq.error.ZMQError: + LOG.critical(f'Write address already in use? {self.writeAddress}') def init_read(self): # ZeroMQ Context From c18617ba4c7cf697e1c01ccff78cf850c2b88134 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Thu, 9 Jul 2026 14:11:26 -0700 Subject: [PATCH 12/81] halui -add reload display and shutdown pins --- lib/python/bridgeui/bridge.py | 8 +++++- src/emc/usr_intf/halui.cc | 54 ++++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/lib/python/bridgeui/bridge.py b/lib/python/bridgeui/bridge.py index c9d8d4aac4b..777f88e625f 100644 --- a/lib/python/bridgeui/bridge.py +++ b/lib/python/bridgeui/bridge.py @@ -134,7 +134,7 @@ def action(self, msg, data): print ('axis state', self.axesSelected,self.currentSelectedAxis) # send msg to hal_glib - def writeMsg(self, msg, data): + def writeMsg(self, msg, data=''): #print('Write Msg called') if ZMQ: topic = self.writeTopic @@ -162,6 +162,12 @@ def ok(self): def cancel(self): self.writeMsg('request_cancel', True) + def reloadDisplay(self): + self.writeMsg('request_reload_display', True) + + def shutdownController(self): + self.writeMsg('request_shutdown') + # if the number is bigger then MDI command list # then look for MACRO commands def getMdiName(self, num): diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index 4dd76e3ee61..de316aba602 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -218,6 +218,9 @@ typedef hal_uint_t hal_ui32_t; \ FIELD(bool,gui_ok) /* pin for acknowledging dialog ok */ \ FIELD(bool,gui_cancel) /* pin for acknowledging dialog cancel */ \ +\ + FIELD(bool,gui_reload) /* pin for acknowledging dialog ok */ \ + FIELD(bool,gui_shutdown) /* pin for acknowledging dialog cancel */ \ \ FIELD(real,units_per_mm) \ @@ -566,7 +569,7 @@ static void py_call_cycleStart() { // check socket messages for jogspeed pFuncWrite = PyObject_GetAttrString(pInstance, "cycleStart"); - if (pFuncRead && PyCallable_Check(pFuncWrite)) { + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { pValue = PyObject_CallNoArgs(pFuncWrite); if (pValue == NULL){ fprintf(stderr, "Halui Bridge: cycleStart function failed: returned NULL\n"); @@ -581,7 +584,7 @@ static void py_call_cyclePause() { // check socket messages for jogspeed pFuncWrite = PyObject_GetAttrString(pInstance, "cyclePause"); - if (pFuncRead && PyCallable_Check(pFuncWrite)) { + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { pValue = PyObject_CallNoArgs(pFuncWrite); if (pValue == NULL){ fprintf(stderr, "Halui Bridge: cyclePause function failed: returned NULL\n"); @@ -596,7 +599,7 @@ static void py_call_ok() { // check socket messages for gui ok message pFuncWrite = PyObject_GetAttrString(pInstance, "ok"); - if (pFuncRead && PyCallable_Check(pFuncWrite)) { + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { pValue = PyObject_CallNoArgs(pFuncWrite); if (pValue == NULL){ fprintf(stderr, "Halui Bridge: ok function failed: returned NULL\n"); @@ -610,7 +613,7 @@ static void py_call_cancel() { // check socket messages for gui cancel message pFuncWrite = PyObject_GetAttrString(pInstance, "cancel"); - if (pFuncRead && PyCallable_Check(pFuncWrite)) { + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { pValue = PyObject_CallNoArgs(pFuncWrite); if (pValue == NULL){ fprintf(stderr, "Halui Bridge: cancel function failed: returned NULL\n"); @@ -620,6 +623,34 @@ static void py_call_cancel() { Py_DECREF(pFuncWrite); return ; } +static void py_call_reload_display() { + + // check socket messages for gui reload display message + pFuncWrite = PyObject_GetAttrString(pInstance, "reloadDisplay"); + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallNoArgs(pFuncWrite); + if (pValue == NULL){ + fprintf(stderr, "Halui Bridge: reload display function failed: returned NULL\n"); + } + Py_DECREF(pValue); + } + Py_DECREF(pFuncWrite); + return ; +} +static void py_call_shutdown_controller() { + + // check socket messages for gui shutdown message + pFuncWrite = PyObject_GetAttrString(pInstance, "shutdownController"); + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallNoArgs(pFuncWrite); + if (pValue == NULL){ + fprintf(stderr, "Halui Bridge: shutdownController function failed: returned NULL\n"); + } + Py_DECREF(pValue); + } + Py_DECREF(pFuncWrite); + return ; +} static int py_call_get_mdi_count() { int value = 0; // check socket messages for jogspeed @@ -927,6 +958,10 @@ int halui_hal_init(void) CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_cancel), 0, "halui.gui.cancel")); + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_reload), 0, "halui.gui.reload-display")); + + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_shutdown), 0, "halui.gui.shutdown")); + hal_ready(comp_id); return 0; } @@ -2460,6 +2495,17 @@ static void check_hal_changes() fprintf(stderr,"GUI CANCEL command called\n"); py_call_cancel(); } + + if (check_bit_changed(new_halui_data.gui_reload, old_halui_data.gui_reload) != 0) { + fprintf(stderr,"GUI RELOAD DISPLAY command called\n"); + py_call_reload_display(); + } + + if (check_bit_changed(new_halui_data.gui_shutdown, old_halui_data.gui_shutdown) != 0) { + fprintf(stderr,"GUI SHUTDOWN command called\n"); + py_call_shutdown_controller(); + } + } // this function looks at the received NML status message From a123bd86e6c6b7dc9b3bb53ffc43d950a39c329a Mon Sep 17 00:00:00 2001 From: Cmorley Date: Tue, 14 Jul 2026 21:36:44 -0700 Subject: [PATCH 13/81] halui -add softkey pins and message --- lib/python/bridgeui/bridge.py | 8 ++++++-- src/emc/usr_intf/halui.cc | 36 ++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/lib/python/bridgeui/bridge.py b/lib/python/bridgeui/bridge.py index 777f88e625f..e04ccb7837d 100644 --- a/lib/python/bridgeui/bridge.py +++ b/lib/python/bridgeui/bridge.py @@ -168,8 +168,12 @@ def reloadDisplay(self): def shutdownController(self): self.writeMsg('request_shutdown') - # if the number is bigger then MDI command list - # then look for MACRO commands + def softkey(self, index): + print(f'Softkey index {index}') + self.writeMsg('request_softkey', index) + + # if the number is bigger then MDI command list + # then look for MACRO commands def getMdiName(self, num): if num >len(self.INFO.MDI_COMMAND_DICT)-1: offset = len(self.INFO.MDI_COMMAND_DICT) diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index de316aba602..69bab329614 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -54,6 +54,7 @@ static int axis_mask = 0; #define JOGTELEOP 0 #define MDI_MAX 64 +#define SOFTKEY_MAX 20 // FIXME: This will have to go again when we do proper 64-bit // The typedefs are necessary to map to hal_[gs]et_[su]i32() in the expansion @@ -218,6 +219,7 @@ typedef hal_uint_t hal_ui32_t; \ FIELD(bool,gui_ok) /* pin for acknowledging dialog ok */ \ FIELD(bool,gui_cancel) /* pin for acknowledging dialog cancel */ \ + ARRAY(bool,gui_soft_keys,SOFTKEY_MAX) \ \ FIELD(bool,gui_reload) /* pin for acknowledging dialog ok */ \ FIELD(bool,gui_shutdown) /* pin for acknowledging dialog cancel */ \ @@ -275,6 +277,8 @@ static char *gui_mdi_commands[MDI_MAX]; static int num_gui_mdi_commands = 0; static int have_home_all = 0; +static int num_gui_soft_keys = SOFTKEY_MAX; + static int comp_id, done; /* component ID, main while loop */ static int num_axes = 0; //number of axes, taken from the INI [TRAJ] section @@ -954,6 +958,10 @@ int halui_hal_init(void) CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_mdi_commands[n]), 0, "halui.gui.mdi-command-%s", py_call_get_mdi_name(n))); } + for (int n=0; ngui_soft_keys[n]), 0, "halui.gui.softkey-%d", n)); + } + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_ok), 0, "halui.gui.ok")); CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_cancel), 0, "halui.gui.cancel")); @@ -1818,6 +1826,24 @@ static void py_call_request_MDI( int index) Py_DECREF(pFuncWrite); } +static void py_call_request_softkey( int index) +{ + pFuncWrite = PyObject_GetAttrString(pInstance, "softkey"); + + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallFunction(pFuncWrite, "i", index); + if (pValue == NULL){ + fprintf(stderr, "halui bridge: softkey function failed: returned NULL\n"); + if (PyErr_Occurred()) PyErr_Print(); + } + Py_DECREF(pValue); + + }else{ + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "halui Bridge: Failed python function softkey"); + } + Py_DECREF(pFuncWrite); +} static int py_call_get_axis_type( int index) { int value = 0; // check socket messages for selected axis @@ -2478,7 +2504,7 @@ static void check_hal_changes() sendMdiCommand(n); } - // request GUI ti run MDI commands + // request GUI to run MDI commands for(int n = 0; n < num_gui_mdi_commands; n++) { if (check_bit_changed(new_halui_data.gui_mdi_commands[n], old_halui_data.gui_mdi_commands[n]) != 0){ fprintf(stderr,"GUI MDI command called index: %i\n", n); @@ -2486,6 +2512,14 @@ static void check_hal_changes() } } + // request GUI soft keys + for(int n = 0; n < num_gui_soft_keys; n++) { + if (check_bit_changed(new_halui_data.gui_soft_keys[n], old_halui_data.gui_soft_keys[n]) != 0){ + fprintf(stderr,"GUI SOFTKEY called index: %i\n", n); + py_call_request_softkey(n); + } + } + if (check_bit_changed(new_halui_data.gui_ok, old_halui_data.gui_ok) != 0) { fprintf(stderr,"GUI OK command called\n"); py_call_ok(); From 6e6b3248cb5ccfb513a52479a91d0a3a97d1e2f6 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Sat, 18 Jul 2026 21:51:46 -0700 Subject: [PATCH 14/81] halui -change pin names for cycles and softkeys numbers 00 to 19 for softkeys. add gui. to cycle.start and cycle.pause --- src/emc/usr_intf/halui.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index 69bab329614..d8886bb26fb 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -861,8 +861,8 @@ int halui_hal_init(void) CHK(halui_export_pin_IN_bit(&(halui_data->mist_off), "halui.mist.off")); CHK(halui_export_pin_IN_bit(&(halui_data->flood_on), "halui.flood.on")); CHK(halui_export_pin_IN_bit(&(halui_data->flood_off), "halui.flood.off")); - CHK(halui_export_pin_IN_bit(&(halui_data->cycle_start), "halui.cycle.start")); - CHK(halui_export_pin_IN_bit(&(halui_data->cycle_pause), "halui.cycle.pause")); + CHK(halui_export_pin_IN_bit(&(halui_data->cycle_start), "halui.gui.cycle.start")); + CHK(halui_export_pin_IN_bit(&(halui_data->cycle_pause), "halui.gui.cycle.pause")); CHK(halui_export_pin_IN_bit(&(halui_data->program_run), "halui.program.run")); CHK(halui_export_pin_IN_bit(&(halui_data->program_pause), "halui.program.pause")); CHK(halui_export_pin_IN_bit(&(halui_data->program_resume), "halui.program.resume")); @@ -959,7 +959,7 @@ int halui_hal_init(void) } for (int n=0; ngui_soft_keys[n]), 0, "halui.gui.softkey-%d", n)); + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_soft_keys[n]), 0, "halui.gui.softkey-%02d", n)); } CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_ok), 0, "halui.gui.ok")); From f8249f664f9b06a1352fdde14bd2a31fc60301ee Mon Sep 17 00:00:00 2001 From: Cmorley Date: Mon, 20 Jul 2026 19:10:15 -0700 Subject: [PATCH 15/81] halui -gui.mpg-select.0 pin name change --- src/emc/usr_intf/halui.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index d8886bb26fb..edb7a8bed29 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -928,7 +928,7 @@ int halui_hal_init(void) CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->ajog_increment_minus[axis_num]), 0, "halui.axis.%c.increment-minus", c)); } - CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->mpg_select0), 0, "halui.mpg-select.0")); + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->mpg_select0), 0, "halui.gui.mpg-select.0")); CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->joint_home[num_joints]), 0, "halui.joint.selected.home")); CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->joint_unhome[num_joints]), 0, "halui.joint.selected.unhome")); CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->jjog_plus[num_joints]), 0, "halui.joint.selected.plus")); From dffe9f9d7a38c60521d3caad28e562b1e4f678e7 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 16 Aug 2025 11:12:19 -0700 Subject: [PATCH 16/81] gladevcp -speedcontrol: put Gstat status in the loop Gstat sends out socket messages for jog rate --- lib/python/gladevcp/speedcontrol.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/python/gladevcp/speedcontrol.py b/lib/python/gladevcp/speedcontrol.py index 332f025ab0d..5dda24b65a3 100755 --- a/lib/python/gladevcp/speedcontrol.py +++ b/lib/python/gladevcp/speedcontrol.py @@ -35,6 +35,8 @@ else: from .hal_widgets import _HalSpeedControlBase +from gladevcp.core import Status, Action + class SpeedControl(Gtk.Box, _HalSpeedControlBase): ''' The SpeedControl Widget serves as a slider with button to increment od decrease @@ -98,6 +100,8 @@ class SpeedControl(Gtk.Box, _HalSpeedControlBase): "%.1f", GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), 'do_hide_button' : ( GObject.TYPE_BOOLEAN, 'Hide the button', 'Display the button + and - to alter the values', False, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), + 'type' : ( GObject.TYPE_INT, 'Type of adjustment', 'Set to -1 for general, 0 for jograte', + -1, 0, -1, GObject.ParamFlags.READWRITE|GObject.ParamFlags.CONSTRUCT), } __gproperties = __gproperties__ @@ -112,6 +116,9 @@ class SpeedControl(Gtk.Box, _HalSpeedControlBase): def __init__(self, size = 36, value = 0, min = 0, max = 100, inc_speed = 100, unit = "", color = "#FF8116", template = "%.1f"): super(SpeedControl, self).__init__() + self._action = Action() + self._status = Status() + # basic settings self._size = size self._value = value @@ -123,6 +130,7 @@ def __init__(self, size = 36, value = 0, min = 0, max = 100, inc_speed = 100, un self._increment = (self._max - self._min) / 100.0 self._template = template self._speed = inc_speed + self.type_linear_jog = False self.adjustment = Gtk.Adjustment(value = self._value, lower = self._min, upper = self._max, step_increment = self._increment, page_increment = 0) self.adjustment.connect("value_changed", self._on_value_changed) @@ -175,6 +183,10 @@ def _hal_init(self): self.hal_pin_decrease = self.hal.newpin(self.hal_name+".decrease", hal.HAL_BIT, hal.HAL_IN) self.hal_pin_decrease.connect("value-changed", self._on_minus_changed) + if self.type_linear_jog: + print('->>',self.type_linear_jog) + self._status.connect('jograte-changed', lambda w, data: self.set_value(data)) + # this draws our widget on the screen def expose(self, widget, event): # create the cairo window @@ -269,6 +281,9 @@ def get_value(self): # we are not sync, so def _on_value_changed(self, widget): value = widget.get_value() + if self.type_linear_jog: + self._action.SET_JOG_RATE(value) + if value != self._value: self._value = value self.set_value(self._value) @@ -453,6 +468,10 @@ def do_set_property(self, property, value): self._template = value if name == "do_hide_button": self.hide_button(value) + if name == "type": + print(name,value) + if value == 0: + self.type_linear_jog = True self._draw_widget() else: raise AttributeError('unknown property %s' % property.name) From be114e3d3c7324dcae999b9c0e77703dec0a161f Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 10:05:52 -0700 Subject: [PATCH 17/81] gladevcp -gtk_action: add ability to run new style INI MDI commands named INI MDI commands were not recognised. --- lib/python/gladevcp/gtk_action.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/python/gladevcp/gtk_action.py b/lib/python/gladevcp/gtk_action.py index 247d9df4e93..97a6fc442c5 100644 --- a/lib/python/gladevcp/gtk_action.py +++ b/lib/python/gladevcp/gtk_action.py @@ -191,14 +191,23 @@ def CALL_MDI_WAIT(self, code, time=5, mode_return=False): self.ensure_mode(premode) return 0 - def CALL_INI_MDI(self, number): + def CALL_INI_MDI(self, key): try: - mdi = INFO.MDI_COMMAND_LIST[number] + # prefer named INI MDI commands + mdi = INFO.get_ini_mdi_command(key) + LOG.debug('COMMAND= {}'.format(mdi)) + if mdi is None: raise Exception except: - msg = 'MDI_COMMAND= # {} Not found under [MDI_COMMAND_LIST] in INI file'.format(number) - LOG.error(msg) - self.SET_ERROR_MESSAGE(msg) - return + # fallback to legacy nth line + try: + mdi = INFO.MDI_COMMAND_LIST[key] + except: + msg = 'MDI_COMMAND_{} Not found under [MDI_COMMAND_LIST] in INI file'.format(key) + LOG.error(msg) + self.SET_ERROR_MESSAGE(msg) + return + + mdi_list = mdi.split(';') self.ensure_mode(linuxcnc.MODE_MDI) for code in (mdi_list): From ddd8418e56b5caa1fc7b338e6969c6452b76c464 Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 18:10:59 -0700 Subject: [PATCH 18/81] qtvcp/gladevcp -action: add ability; return to mode after INI mdi --- lib/python/gladevcp/gtk_action.py | 12 +++++++++++- lib/python/qtvcp/qt_action.py | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/python/gladevcp/gtk_action.py b/lib/python/gladevcp/gtk_action.py index 97a6fc442c5..ae56852891a 100644 --- a/lib/python/gladevcp/gtk_action.py +++ b/lib/python/gladevcp/gtk_action.py @@ -191,7 +191,7 @@ def CALL_MDI_WAIT(self, code, time=5, mode_return=False): self.ensure_mode(premode) return 0 - def CALL_INI_MDI(self, key): + def CALL_INI_MDI(self, key, mode_return = False): try: # prefer named INI MDI commands mdi = INFO.get_ini_mdi_command(key) @@ -209,11 +209,21 @@ def CALL_INI_MDI(self, key): mdi_list = mdi.split(';') + if mode_return: + self.RECORD_CURRENT_MODE() + self._a = STATUS.connect('command-stopped', lambda w: self.return_mode_after_finish()) self.ensure_mode(linuxcnc.MODE_MDI) for code in (mdi_list): LOG.debug('CALL_INI_MDI command:{}'.format(code)) self.cmd.mdi('%s' % code) + # when command stops - we try to continue the generator. + # if generator is done - return to recorded mode. + def return_mode_after_finish(self): + print('ini command end') + self.RESTORE_RECORDED_MODE() + STATUS.handler_disconnect(self._a) + def CALL_OWORD(self, code, time=5): LOG.debug('OWORD_COMMAND= {}'.format(code)) self.ensure_mode(linuxcnc.MODE_MDI) diff --git a/lib/python/qtvcp/qt_action.py b/lib/python/qtvcp/qt_action.py index 524282238d2..5c84ae2df89 100644 --- a/lib/python/qtvcp/qt_action.py +++ b/lib/python/qtvcp/qt_action.py @@ -232,7 +232,7 @@ def CALL_MDI_WAIT(self, code, time=5, mode_return=False): self.ensure_mode(premode) return 0 - def CALL_INI_MDI(self, key): + def CALL_INI_MDI(self, key, mode_return = False): try: # prefer named INI MDI commands mdi = INFO.get_ini_mdi_command(key) @@ -249,6 +249,9 @@ def CALL_INI_MDI(self, key): return mdi_list = mdi.split(';') + if mode_return: + self.RECORD_CURRENT_MODE() + self._a = STATUS.connect('command-stopped', lambda w: self.return_mode_after_finish()) self.ensure_mode(linuxcnc.MODE_MDI) for code in (mdi_list): LOG.debug('CALL_INI_MDI command:{}'.format(code)) @@ -288,6 +291,13 @@ def RUN_MACRO( self, data): self.CALL_MDI(command, mode_return=True) return + # when command stops - we try to continue the generator. + # if generator is done - return to recorded mode. + def return_mode_after_finish(self): + print('ini command end') + self.RESTORE_RECORDED_MODE() + STATUS.handler_disconnect(self._a) + def CALL_OWORD(self, code, time=5): LOG.debug('OWORD_COMMAND= {}'.format(code)) self.ensure_mode(linuxcnc.MODE_MDI) From 9fa62113004b827d7d0134e12e137db5eed1c390 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 10 Apr 2026 13:40:16 -0700 Subject: [PATCH 19/81] gladevcp -speedcontrol -add angular jograte type automatically sends and responds to jograte messages --- lib/python/gladevcp/speedcontrol.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/python/gladevcp/speedcontrol.py b/lib/python/gladevcp/speedcontrol.py index 5dda24b65a3..a30cc26dd3b 100755 --- a/lib/python/gladevcp/speedcontrol.py +++ b/lib/python/gladevcp/speedcontrol.py @@ -100,8 +100,8 @@ class SpeedControl(Gtk.Box, _HalSpeedControlBase): "%.1f", GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), 'do_hide_button' : ( GObject.TYPE_BOOLEAN, 'Hide the button', 'Display the button + and - to alter the values', False, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), - 'type' : ( GObject.TYPE_INT, 'Type of adjustment', 'Set to -1 for general, 0 for jograte', - -1, 0, -1, GObject.ParamFlags.READWRITE|GObject.ParamFlags.CONSTRUCT), + 'type' : ( GObject.TYPE_INT, 'Type of adjustment', 'Set to -1 for general, 0 for jograte, 1 for angular jograte', + -1, 1, -1, GObject.ParamFlags.READWRITE|GObject.ParamFlags.CONSTRUCT), } __gproperties = __gproperties__ @@ -131,6 +131,7 @@ def __init__(self, size = 36, value = 0, min = 0, max = 100, inc_speed = 100, un self._template = template self._speed = inc_speed self.type_linear_jog = False + self.type_angular_jog = False self.adjustment = Gtk.Adjustment(value = self._value, lower = self._min, upper = self._max, step_increment = self._increment, page_increment = 0) self.adjustment.connect("value_changed", self._on_value_changed) @@ -184,8 +185,11 @@ def _hal_init(self): self.hal_pin_decrease.connect("value-changed", self._on_minus_changed) if self.type_linear_jog: - print('->>',self.type_linear_jog) + print('linear jograte ->>',self.type_linear_jog) self._status.connect('jograte-changed', lambda w, data: self.set_value(data)) + elif self.type_angular_jog: + print('Angular jograte ->>',self.type_angular_jog) + self._status.connect('jograte-angular-changed', lambda w, data: self.set_value(data)) # this draws our widget on the screen def expose(self, widget, event): @@ -472,6 +476,10 @@ def do_set_property(self, property, value): print(name,value) if value == 0: self.type_linear_jog = True + self.type_angular_jog = False + elif value == 1: + self.type_linear_jog = False + self.type_angular_jog = True self._draw_widget() else: raise AttributeError('unknown property %s' % property.name) From 5804bb4ecf99f360c2b471177570a0c991c32f15 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sun, 12 Apr 2026 10:07:58 -0700 Subject: [PATCH 20/81] gladevcp -speedcontrol: add angular jograte command forgot to add the command to output the rate whenn \the widget is changed --- lib/python/gladevcp/speedcontrol.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/python/gladevcp/speedcontrol.py b/lib/python/gladevcp/speedcontrol.py index a30cc26dd3b..36cde39a08e 100755 --- a/lib/python/gladevcp/speedcontrol.py +++ b/lib/python/gladevcp/speedcontrol.py @@ -285,8 +285,11 @@ def get_value(self): # we are not sync, so def _on_value_changed(self, widget): value = widget.get_value() + if self.type_linear_jog: self._action.SET_JOG_RATE(value) + elif self.type_angular_jog: + self._action.SET_JOG_RATE_ANGULAR(value) if value != self._value: self._value = value From 77126ec381475bc3c1240cd11ad94f4199093c79 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Thu, 9 Jul 2026 14:12:15 -0700 Subject: [PATCH 21/81] gladevcp -hal_gremlin: clear the plot if reloading the screen --- lib/python/gladevcp/hal_gremlin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/python/gladevcp/hal_gremlin.py b/lib/python/gladevcp/hal_gremlin.py index c0cfd5675ea..d7badb66747 100644 --- a/lib/python/gladevcp/hal_gremlin.py +++ b/lib/python/gladevcp/hal_gremlin.py @@ -139,6 +139,7 @@ def __init__(self, *a, **kw): def reloadfile(self,w): try: self.fileloaded(None,self._reload_filename) + self.clear_live_plotter() except: pass self.gstat.emit('graphics-gcode-properties',self.gcode_properties) From 5321a930c07489476f4b935f8685406a80c5cf2c Mon Sep 17 00:00:00 2001 From: Cmorley Date: Fri, 10 Jul 2026 10:20:22 -0700 Subject: [PATCH 22/81] gladevcp gtk_action -get shutdown to work reliably gnome-sessions-quit wil not pop up consistantly withoout the gtk dialog --- lib/python/gladevcp/gtk_action.py | 84 +++++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 14 deletions(-) diff --git a/lib/python/gladevcp/gtk_action.py b/lib/python/gladevcp/gtk_action.py index ae56852891a..0b49e213c93 100644 --- a/lib/python/gladevcp/gtk_action.py +++ b/lib/python/gladevcp/gtk_action.py @@ -616,21 +616,25 @@ def ADJUST_GRAPHICS_PAN(self, x, y): def ADJUST_GRAPHICS_ROTATE(self, x, y): STATUS.emit('graphics-view-changed', 'rotate-view', {'X': x, 'Y': y}) + #TODO without the gtk dialog, gnome-sessions + # does not start reliably def SHUT_SYSTEM_DOWN_PROMPT(self): - import subprocess - try: - try: - subprocess.call('gnome-session-quit --power-off', shell=True) - except: - try: - subprocess.call('xfce4-session-logout', shell=True) - except: - try: - subprocess.call('systemctl poweroff', shell=True) - except: - raise - except Exception as e: - LOG.warning("Couldn't shut system down: {}".format(e)) + import shutil + import time + dialog = YesNoDialog(title='System Shutdown') + dialog.set_keep_above(True) + dialog.format_secondary_text('Unsaved data will be lost') + response = dialog.ask_dialog() + dialog.destroy() + if response == gtk.ResponseType.YES: + + if shutil.which('gnome-session-quit'): + subprocess.run(["gnome-session-quit", "--power-off"]) + elif shutil.which('xfce4-session-logout'): + subprocess.call('xfce4-session-logout', shell=True) + else: + # force a shutdown - no prompt + subprocess.call('systemctl poweroff', shell=True) def SHUT_SYSTEM_DOWN_NOW(self): import subprocess @@ -927,6 +931,58 @@ def error(self, exitcode, stderr): dialog.run() dialog.destroy() +########################################### +# Dialog Class +######################################################################## + +class YesNoDialog(gtk.MessageDialog): + def __init__(self, parent=None, message="Are you sure?", title = "Operator Message"): + super(YesNoDialog, self).__init__( + parent=parent, + flags=gtk.DialogFlags.DESTROY_WITH_PARENT, + type=gtk.MessageType.QUESTION, + buttons=gtk.ButtonsType.NONE, + message_format=message) + + yes_button = gtk.Button.new_with_mnemonic("_Yes") + no_button = gtk.Button.new_with_mnemonic("_No") + yes_button.set_size_request(-1, 56) + no_button.set_size_request(-1, 56) + yes_button.connect("clicked",lambda w:self.response(gtk.ResponseType.YES)) + no_button.connect("clicked",lambda w:self.response(gtk.ResponseType.NO)) + box = gtk.HButtonBox() + box.add(no_button) + box.add(yes_button) + box.set_spacing(10) + box.set_layout(gtk.ButtonBoxStyle.CENTER) + self.action_area.add(box) + self.set_border_width(5) + self.connect("response", self.on_yn_response) + self.set_markup(message) + if title: + self.set_title(str(title)) + + def ask_dialog(self): + self.show_all() + #self.emit("play_sound", "alert") + + # wait but don't block event loop + self.RESPONSE = None + while self.RESPONSE is None: + while gtk.events_pending(): + # read any ZMQ messages + # then update widgets + # till we get a dialog answer + STATUS.readNextMsg() + gtk.main_iteration() + + rtn = self.RESPONSE + self.hide() + return bool(rtn in(gtk.ResponseType.YES, gtk.ResponseType.ACCEPT)) + + # update internal variable so dialog will respond + def on_yn_response(self,dialog, rtn): + self.RESPONSE = rtn # For testing purposes From 0d34fc62b3a5016d753474a3db87e681c41cd52f Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 10:07:38 -0700 Subject: [PATCH 23/81] qtvcp -dialog widget: add status message control of tool change dialog you can use STATUS messages to 'press' ok or cancel --- lib/python/qtvcp/widgets/dialog_widget.py | 46 +++++++++++++++++------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/lib/python/qtvcp/widgets/dialog_widget.py b/lib/python/qtvcp/widgets/dialog_widget.py index e0f9735afb9..25a7e3f5452 100644 --- a/lib/python/qtvcp/widgets/dialog_widget.py +++ b/lib/python/qtvcp/widgets/dialog_widget.py @@ -416,6 +416,7 @@ def __init__(self, parent=None): class ToolDialog(LcncDialog, GeometryMixin): def __init__(self, parent=None): super(ToolDialog, self).__init__(parent) + self._request_name = 'TOOLCHANGE' self.setText('Manual Tool Change Request') self.setInformativeText('Please Insert Tool 0') self.setStandardButtons(QMessageBox.Ok) @@ -458,6 +459,8 @@ def _hal_init(self): self.sound_type = self.PREFS_.getpref('toolDialog_sound_type', 'READY', str, 'DIALOG_OPTIONS') else: self.play_sound = False + # can acknowledge from status messages too + STATUS.connect('dialog-update', self._status_update) # process callback from 'change' HAL pin def tool_change(self, change): @@ -508,7 +511,7 @@ def tool_change(self, change): # process callback for 'change-button' HAL pin # hide the message dialog or desktop notify message def external_acknowledge(self, state): - #print('external acklnowledge: {}'.format(state)) + #print('external acknowledge: {}'.format(state)) if state: if self._useDesktopNotify: self.deskNotice.close() @@ -516,10 +519,29 @@ def external_acknowledge(self, state): self.hide() self._processChange(True) + # callback from status 'update-dialog' + def _status_update(self, w, message): + print(message) + if message.get('NAME') == self._request_name: + if not self.isVisible(): return + print(self._request_name) + response = message.get('response') + if not response is None: + # 'ok' + if response == 1: + if self._useDesktopNotify: + self.deskNotice.close() + elif self.isVisible(): + self.hide() + self._processChange(True) + # 'cancel' + elif response == 0: + self.hide() + self._processChange(False) # This also is called from DesktopDialog def _processChange(self,answer): - #print('proces change: {}'.format(answer)) + print('process change: {}'.format(answer)) if answer == -1: self.changed.set(True) ACTION.ABORT() @@ -535,6 +557,16 @@ def _processChange(self,answer): self.record_geometry() STATUS.emit('focus-overlay-changed', False, None, None) + # decode button presses + def msgbtn(self, i): + LOG.debug('Button pressed is: {}'.format(i.text())) + if self.clickedButton() == self._actionbutton: + self._processChange(-1) + elif self.standardButton(self.clickedButton()) == QMessageBox.Ok: + self._processChange(True) + else: + self._processChange(False) + ###### overridden functions ################ def showdialog(self, message, more_info=None, details=None, @@ -576,16 +608,6 @@ def showEvent(self, event): self.set_geometry() super(LcncDialog, self).showEvent(event) - # decode button presses - def msgbtn(self, i): - LOG.debug('Button pressed is: {}'.format(i.text())) - if self.clickedButton() == self._actionbutton: - self._processChange(-1) - elif self.standardButton(self.clickedButton()) == QMessageBox.Ok: - self._processChange(True) - else: - self._processChange(False) - ############################################ # ********************** From f4e8f2510afa7da99d521f5d01aca0e489cc8572 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 13 Dec 2025 20:42:40 -0800 Subject: [PATCH 24/81] qtvcp -baseclass: register dialogs for later checks like if you want to send responses to the current showing dialog --- lib/python/qtvcp/widgets/widget_baseclass.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/python/qtvcp/widgets/widget_baseclass.py b/lib/python/qtvcp/widgets/widget_baseclass.py index 23bfa396f45..d2012fe8f82 100644 --- a/lib/python/qtvcp/widgets/widget_baseclass.py +++ b/lib/python/qtvcp/widgets/widget_baseclass.py @@ -17,6 +17,7 @@ # the other subclasses are for simple HAL widget functionality import hal + from qtpy.QtCore import Property from qtpy.QtWidgets import QDialog From d58706771fb23abcc6105eccc887851f9394b97f Mon Sep 17 00:00:00 2001 From: Cmorley Date: Wed, 27 May 2026 19:04:21 -0700 Subject: [PATCH 25/81] qtvcp -axis tool button: allow for MPG selection --- lib/python/qtvcp/widgets/axis_tool_button.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/python/qtvcp/widgets/axis_tool_button.py b/lib/python/qtvcp/widgets/axis_tool_button.py index d2c58f7a55b..9080ae29efd 100644 --- a/lib/python/qtvcp/widgets/axis_tool_button.py +++ b/lib/python/qtvcp/widgets/axis_tool_button.py @@ -134,6 +134,8 @@ def homed_on_test(): self.hal_pin_joint = self.HAL_GCOMP_.newpin(str(pname + '-joint'), hal.HAL_BIT, hal.HAL_OUT) self.hal_pin_axis = self.HAL_GCOMP_.newpin(str(pname + '-axis'), hal.HAL_BIT, hal.HAL_OUT) STATUS.connect('general',self.return_value) + if 'MPG' in self._axis.upper(): + self.settingMenu.clear() def _enableGroup(self, state,bstate): for i in(self.zeroButton, self.setButton,self.divideButton, @@ -226,6 +228,7 @@ def _a_from_j(self, axis): return axis, r[jnum] def selectJoint(self): + print(self.objectName(),f'select: j{self._joint} a{self._axis} ck{self.isChecked()}') if self._block_signal or self._joint == -1 or self._axis == '': return if self.isChecked() == True: if STATUS.is_joint_mode(): @@ -251,7 +254,8 @@ def selectJoint(self): self.hal_pin_axis.set(False) def ChangeState(self, joint = None, axis = None): - #print(self.objectName(),'change',joint,axis,self._axis) + #print(self.objectName(),f'change: j{joint} a{axis} type{self._axis} ck{self.isChecked()}') + # joint mode if STATUS.is_joint_mode(): if int(joint) != self._joint: @@ -269,12 +273,18 @@ def ChangeState(self, joint = None, axis = None): # axis mode else: if str(axis) != self._axis and self.isChecked(): + #print(self.objectName(),'Set false') + if not self.group() in (0,None): + self.group().setExclusive(False) self._block_signal = True self.setChecked(False) self._block_signal = False + if not self.group() in (0,None): + self.group().setExclusive(True) if self._halpin_option and self._axis != '': self.hal_pin_joint.set(False) elif str(axis) == self._axis and not self.isChecked(): + #print(self.objectName(),'Set True') self._block_signal = True self.setChecked(True) self._block_signal = False @@ -311,6 +321,8 @@ def set_axis(self, data): self.goToG53Button.setText(text) text = 'Go To G5x Origin in {}'.format(self._axis) self.goToG5xButton.setText(text) + elif data.upper() in('MPG0','MPG1'): + self._axis = str(data.upper()) else: self._axis = str('') From d9f7eddb26a9145cf4d4dffafe5e29f0feb8279b Mon Sep 17 00:00:00 2001 From: Cmorley Date: Wed, 8 Jul 2026 23:06:07 -0700 Subject: [PATCH 26/81] qtvcp -dialog_widget: check ZMQ messages directly When waiting for a dialog answer (with wait but don't block option) --- lib/python/qtvcp/widgets/dialog_widget.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/python/qtvcp/widgets/dialog_widget.py b/lib/python/qtvcp/widgets/dialog_widget.py index 25a7e3f5452..a0fa1028c1b 100644 --- a/lib/python/qtvcp/widgets/dialog_widget.py +++ b/lib/python/qtvcp/widgets/dialog_widget.py @@ -1871,6 +1871,10 @@ def showdialog(self, preload=None, overlay=True, cycle=False, wait=False): if wait: self._flag = True while self._flag: + # read any ZMQ messages + # then update widgets + # till we get a dialog answer + STATUS.readNextMsg() QApplication.processEvents() return (self.display.text(), self._result) else: From e2d8a3f270d3a79b5e58f6d60152f70a43fe856d Mon Sep 17 00:00:00 2001 From: Cmorley Date: Wed, 29 Jul 2026 16:21:41 -0700 Subject: [PATCH 27/81] qtvcp -fix close dialog response when using zmq messages we still used exec, so zmq messages were blocked --- lib/python/qtvcp/widgets/dialog_widget.py | 59 +++++++++++++++-------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/lib/python/qtvcp/widgets/dialog_widget.py b/lib/python/qtvcp/widgets/dialog_widget.py index a0fa1028c1b..ae62c49b55f 100644 --- a/lib/python/qtvcp/widgets/dialog_widget.py +++ b/lib/python/qtvcp/widgets/dialog_widget.py @@ -104,6 +104,7 @@ def __init__(self, parent=None): self.timer = QTimer() self.seconds_left = 0 self.timer.timeout.connect(self.update_timer) + self._waitflag = False def _hal_init(self): self.read_preference_geometry(self._geoName) @@ -251,10 +252,19 @@ def showdialog(self, messagetext, more_info=None, details=None, display_type='OK self.timer.start(1000) if use_exec: - retval = self.exec() + self._waitflag = True + while self._waitflag: + # read any ZMQ messages + # then update widgets + # till we get a dialog answer + STATUS.readNextMsg() + QApplication.processEvents() + retval = self.result() STATUS.emit('focus-overlay-changed', False, None, None) - LOG.debug('Value of pressed button: {}'.format(retval)) - return self.qualifiedReturn(retval) + LOG.debug('^Value of pressed button: {}'.format(retval)) + rtn = self.qualifiedReturn(retval) + print('return:',rtn) + return rtn # hack to force details box to present open on first display def forceDetailsOpen(self): @@ -276,25 +286,21 @@ def forceDetailsOpen(self): def qualifiedReturn(self, retval): if retval in(QMessageBox.No, QMessageBox.Cancel): + #print('no/cancel') return False - elif retval in(QMessageBox.Ok, QMessageBox.Yes): + if retval in(QMessageBox.Ok, QMessageBox.Yes): + #print('ok/yes') return True - else: + if self.buttonRole(self.clickedButton()) != -1: + #print('button role') + # destruction role button return self.buttonRole(self.clickedButton()) + return retval # move dialog when shown def showEvent(self, event): self.set_geometry() super(LcncDialog, self).showEvent(event) - return - if self._nblock: - self.set_geometry() - else: - geom = self.frameGeometry() - geom.moveCenter(QApplication.primaryScreen().availableGeometry().center()) - self.setGeometry(geom) - super(LcncDialog, self).showEvent(event) - def update_timer(self): self.seconds_left -= 1 @@ -307,7 +313,8 @@ def update_timer(self): def btn_callback(self, i): LOG.debug('Button pressed is: {}'.format(i.text())) - + # stop waiting + self._waitflag = False # update the dialog position self.record_geometry() @@ -315,26 +322,30 @@ def btn_callback(self, i): return self.hide() - btn = self.standardButton(self.clickedButton()) result = self.qualifiedReturn(btn) LOG.debug('Value of {} pressed button: {}'.format(self, result)) self.process_result(result) def process_result(self, result): + self._waitflag = False self.timer.stop() # these directly call a function with btn info if not self._return_callback is None: + #print('callback return') self._return_callback(self, result) # these return via status messages elif self._message is not None: + #print('message return') self._message['RETURN'] = result STATUS.emit('general', self._message) STATUS.emit('focus-overlay-changed', False, None, None) self._message = None # just return result else: + #print('just return') LOG.error('No callback or STATUS message specified for: {}'.format(self.objectName())) + return result def setGeometry(self,*args): #print(args,len(args)) @@ -360,12 +371,24 @@ def _external_update(self, w, message): LOG.debug('Response is: {}'.format(response)) # update the dialog position self.record_geometry() - self.hide() + self._message = message if response == 0: self.process_result(False) + self.reject() elif response == 1: self.process_result(True) + self.accept() + + def accept(self): + self.record_geometry() + super().accept() + self._waitflag = False + + def reject(self): + self.record_geometry() + super().reject() + self._waitflag = False # ********************** # Designer properties @@ -521,10 +544,8 @@ def external_acknowledge(self, state): # callback from status 'update-dialog' def _status_update(self, w, message): - print(message) if message.get('NAME') == self._request_name: if not self.isVisible(): return - print(self._request_name) response = message.get('response') if not response is None: # 'ok' From 1c233979324fb0eda5e98ecbf1d459baf629e854 Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 10:04:00 -0700 Subject: [PATCH 28/81] iniinfo -parse ini commands in a better way mdi commands with a comma in it (ie MSG, text) would not be interpeted properly --- lib/python/common/iniinfo.py | 45 ++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/python/common/iniinfo.py b/lib/python/common/iniinfo.py index 57b78707bd7..c81118f6e37 100644 --- a/lib/python/common/iniinfo.py +++ b/lib/python/common/iniinfo.py @@ -18,7 +18,7 @@ def __init__(self, ini=None): global LOG LOG = logger.getLogger(__name__) # Force the log level for this module only - #LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL + LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL inipath = os.environ.get('INI_FILE_NAME', '/dev/null') self.LINUXCNC_IS_RUNNING = bool(inipath != '/dev/null') @@ -570,7 +570,7 @@ def update(self): if self.INI.hassection('MDI_COMMAND_LIST'): try: for key,value in self.INI.getvariables('MDI_COMMAND_LIST'): - + #print(f'key:{key},value:{value}') # legacy way: list of repeat 'MDI_COMMAND=XXXX' # in this case order matters in the INI if key == 'MDI_COMMAND': @@ -597,24 +597,51 @@ def update(self): # new way: 'MDI_COMMAND_SSS = XXXX' (SSS being any string) # order of commands doesn't matter in the INI + + # here are some samples, the last three are difficult + # the third is invalid + # MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero + # cmd: G53 G0 Z0;G53 G0 X0 Y0 label: Goto\nMachn\nZero + + # MDI_COMMAND_MACRO2 = (MSG, macro 2); + # cmd: (MSG, macro 2); label: + + # MDI_COMMAND_MACRO3 = (MSG, macro 2) + # cmd: (MSG label: macro 2) + + # MDI_COMMAND_MACRO4 = (MSG, macro 2),test + # cmd: (MSG, macro 2) label: test + else: self.MDI_COMMAND_LIST.append(None) self.MDI_COMMAND_LABEL_LIST.append(None) try: name = (key.replace('MDI_COMMAND_','')) mdidatadict = {} - for num,k in enumerate(value.split(',')): - if num == 0: - mdidatadict['cmd'] = k - if len(value.split(',')) <2: - mdidatadict['label'] = None - else: - mdidatadict['label'] = k + #print(f'name:{name}') + # find the last colon in string or 0 + lastCmd = value.rfind(';') + #print('l ;:',lastCmd) + if lastCmd == -1: lastCmd = 0 + + # find the last colon in string or use the string length + lastComma = value.rfind(',', lastCmd) + #print('l comma:',lastComma,lastCmd) + if lastComma == -1: lastComma = len(value) + + label = value[lastComma+1:] + cmd = value[:lastComma] + #print(value,' cmd:',cmd,' label:',label) + + mdidatadict['cmd'] = cmd + mdidatadict['label'] = label self.MDI_COMMAND_DICT[name] = mdidatadict + except Exception as e: LOG.error('INI MDI command parse error:{}'.format(e)) except Exception as e: LOG.error('INI MDI command parse error:{}'.format(e)) + print(self.MDI_COMMAND_DICT) ################ # MACRO commands # From 8f0d2c9c8bdb4ce1e4f9bda38b29d81c6c9cf178 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 22 Aug 2025 15:33:19 -0700 Subject: [PATCH 29/81] qtdragon -adjust external pause message to toggle --- share/qtvcp/screens/qtdragon/qtdragon_handler.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/share/qtvcp/screens/qtdragon/qtdragon_handler.py b/share/qtvcp/screens/qtdragon/qtdragon_handler.py index e6460795b0e..9c782d7c002 100644 --- a/share/qtvcp/screens/qtdragon/qtdragon_handler.py +++ b/share/qtvcp/screens/qtdragon/qtdragon_handler.py @@ -154,7 +154,7 @@ def __init__(self, halcomp, widgets, paths): STATUS.connect('status-message', lambda w, d, o: self.add_external_status(d,o)) STATUS.connect('runstop-line-changed', lambda w, l :self.lastRunLine(l)) STATUS.connect('cycle-start-request', lambda w, state :self.btn_start_clicked(state)) - STATUS.connect('cycle-pause-request', lambda w, state: self.btn_pause_clicked(state)) + STATUS.connect('cycle-pause-request', lambda w, state: self.ext_pause_toggled(state)) STATUS.connect('macro-call-request', lambda w, name: self.request_macro_call(name)) STATUS.connect('ok-request', lambda w, state: self.dialog_ext_control(w,1,1)) STATUS.connect('cancel-request', lambda w, state: self.dialog_ext_control(w,1,0)) @@ -1320,6 +1320,12 @@ def btn_spindle_z_down_clicked(self): if self.h['eoffset-clear'] != True: self.h['eoffset-spindle-count'] = int(fval) + def ext_pause_toggled(self, state): + if STATUS.is_auto_paused(): + self.btn_pause_clicked(False) + return + self.btn_pause_clicked(True) + def btn_pause_clicked(self, data): # pause request From 1aa6d2948c44815123adf42f37b9a0ac7f19301a Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 12 Dec 2025 20:15:21 -0800 Subject: [PATCH 30/81] qtdragon -add mpg_select button logic control --- share/qtvcp/screens/qtdragon/qtdragon_handler.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/share/qtvcp/screens/qtdragon/qtdragon_handler.py b/share/qtvcp/screens/qtdragon/qtdragon_handler.py index 9c782d7c002..139250803ab 100644 --- a/share/qtvcp/screens/qtdragon/qtdragon_handler.py +++ b/share/qtvcp/screens/qtdragon/qtdragon_handler.py @@ -1472,9 +1472,12 @@ def MPG_select_changed(self, button): if button == self.w.btn_mpg_scroll: self.removeMPGFocusBorder() + if not self.w.btn_mpg_scroll.isChecked(): + ACTION.SET_SELECTED_AXIS('None') return if button == self.w.btn_mpg_scroll: if self.w.btn_mpg_scroll.isChecked(): + ACTION.SET_SELECTED_AXIS('MPG0') self.recolorMPGFocusBorder() else: self.removeMPGFocusBorder() From 9da7f9561925e736671216e15dc49235681b5945 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 13 Dec 2025 20:46:51 -0800 Subject: [PATCH 31/81] qtdragon -find the currently visible dialog to send messages to halui 'ok' or 'cancel' messages --- share/qtvcp/screens/qtdragon/qtdragon_handler.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/share/qtvcp/screens/qtdragon/qtdragon_handler.py b/share/qtvcp/screens/qtdragon/qtdragon_handler.py index 139250803ab..1398b2be61a 100644 --- a/share/qtvcp/screens/qtdragon/qtdragon_handler.py +++ b/share/qtvcp/screens/qtdragon/qtdragon_handler.py @@ -789,7 +789,7 @@ def dialog_return(self, w, message): self.touchoff('touchplate') elif sensor_code and name == 'MESSAGE' and rtn is True: self.touchoff('sensor') - elif wait_code and name == 'MESSAGE': + elif wait_code and name == 'MESSAGE' and rtn is True: self.lowerSpindle() elif unhome_code and name == 'MESSAGE' and rtn is True: ACTION.SET_MACHINE_UNHOMED(-1) @@ -2208,6 +2208,11 @@ def dialog_ext_control(self, pin, value, answer): STATUS.emit('dialog-update',{'NAME':name,'response':answer}) return + # fallback + for w in QtWidgets.QApplication.topLevelWidgets(): + if isinstance(w, QtWidgets.QDialog) and not w.isHidden(): + LOG.verbose(f'Found unregistered dialog {w.objectName()}') + def log_version(self): if INFO.RIP_FLAG: t = _translate("HandlerClass","(RIP)") From 98800e1923a6e5519dd67c5e686bc6e510c1a26f Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 14 Mar 2026 19:03:05 -0700 Subject: [PATCH 32/81] qtdragon -fix MPG selection from panel sometimes selecting a combination of MPG,None,X would end up with None selected when MPG should have. --- lib/python/qtvcp/widgets/axis_tool_button.py | 1 - share/qtvcp/screens/qtdragon/qtdragon.ui | 149 ++++++++++-------- .../screens/qtdragon/qtdragon_handler.py | 14 +- 3 files changed, 90 insertions(+), 74 deletions(-) diff --git a/lib/python/qtvcp/widgets/axis_tool_button.py b/lib/python/qtvcp/widgets/axis_tool_button.py index 9080ae29efd..ea93e00f62a 100644 --- a/lib/python/qtvcp/widgets/axis_tool_button.py +++ b/lib/python/qtvcp/widgets/axis_tool_button.py @@ -255,7 +255,6 @@ def selectJoint(self): def ChangeState(self, joint = None, axis = None): #print(self.objectName(),f'change: j{joint} a{axis} type{self._axis} ck{self.isChecked()}') - # joint mode if STATUS.is_joint_mode(): if int(joint) != self._joint: diff --git a/share/qtvcp/screens/qtdragon/qtdragon.ui b/share/qtvcp/screens/qtdragon/qtdragon.ui index d853a2198f4..4edf09dff06 100644 --- a/share/qtvcp/screens/qtdragon/qtdragon.ui +++ b/share/qtvcp/screens/qtdragon/qtdragon.ui @@ -3504,7 +3504,7 @@ PLATE QFrame::Raised - 0 + 9 @@ -5800,9 +5800,9 @@ LOG 0 - 0 - 192 - 547 + -351 + 306 + 734 @@ -12677,69 +12677,6 @@ LIFT - - - - - 1 - 0 - - - - - 58 - 36 - - - - - 16777215 - 76 - - - - MPG -SCROLL - - - true - - - - - - true - - - false - - - - 255 - 0 - 255 - - - - 4 - - - 0.300000000000000 - - - 0 - - - 0 - - - false - - - selectButtonGroup - - - @@ -14573,6 +14510,81 @@ ALL + + + + + 1 + 0 + + + + + 58 + 36 + + + + + 16777215 + 76 + + + + MPG +SCROLL + + + true + + + false + + + QToolButton::DelayedPopup + + + true + + + + 255 + 0 + 255 + + + + 4 + + + 0 + + + 0 + + + -2 + + + MPG0 + + + CALCULATOR + + + false + + + false + + + false + + + false + + + @@ -18094,7 +18106,7 @@ ALL 2 - 02:37:25 + 08:01:11 PM Qt::AlignCenter @@ -19231,4 +19243,3 @@ ALL - diff --git a/share/qtvcp/screens/qtdragon/qtdragon_handler.py b/share/qtvcp/screens/qtdragon/qtdragon_handler.py index 1398b2be61a..480340dc048 100644 --- a/share/qtvcp/screens/qtdragon/qtdragon_handler.py +++ b/share/qtvcp/screens/qtdragon/qtdragon_handler.py @@ -158,6 +158,7 @@ def __init__(self, halcomp, widgets, paths): STATUS.connect('macro-call-request', lambda w, name: self.request_macro_call(name)) STATUS.connect('ok-request', lambda w, state: self.dialog_ext_control(w,1,1)) STATUS.connect('cancel-request', lambda w, state: self.dialog_ext_control(w,1,0)) + STATUS.connect('axis-selection-changed', lambda w,data: self.mpg_selection_changed(data)) self.swoopPath = os.path.join(paths.IMAGEDIR,'lcnc_swoop.png') self.swoopURL = QtCore.QUrl.fromLocalFile(self.swoopPath) @@ -1460,6 +1461,12 @@ def btn_systemtool_toggled(self, state): if state: STATUS.emit('dro-reference-change-request', 1) + def mpg_selection_changed(self, data): + if data =='MPG0': + self.recolorMPGFocusBorder() + elif data == 'None': + self.removeMPGFocusBorder() + def MPG_select_changed(self, button): #print(button) # Auto exclusive doesn't allow unchecking all buttons @@ -1472,15 +1479,14 @@ def MPG_select_changed(self, button): if button == self.w.btn_mpg_scroll: self.removeMPGFocusBorder() - if not self.w.btn_mpg_scroll.isChecked(): - ACTION.SET_SELECTED_AXIS('None') return if button == self.w.btn_mpg_scroll: if self.w.btn_mpg_scroll.isChecked(): - ACTION.SET_SELECTED_AXIS('MPG0') self.recolorMPGFocusBorder() - else: + else: self.removeMPGFocusBorder() + else: + self.removeMPGFocusBorder() #self.set_statusbar('MPG output Selected: {}'.format(cmd.toolTip()),DEFAULT,noLog=True) self._lastSelectButton = button From 3c4bab2e0d0142f6117ff1a747838707ec043e7c Mon Sep 17 00:00:00 2001 From: Cmorley Date: Thu, 2 Jul 2026 19:49:35 -0700 Subject: [PATCH 33/81] qtdragon -external run of macros, use cycle start for MDI running --- .../screens/qtdragon/qtdragon_handler.py | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/share/qtvcp/screens/qtdragon/qtdragon_handler.py b/share/qtvcp/screens/qtdragon/qtdragon_handler.py index 480340dc048..653d909a818 100644 --- a/share/qtvcp/screens/qtdragon/qtdragon_handler.py +++ b/share/qtvcp/screens/qtdragon/qtdragon_handler.py @@ -923,20 +923,24 @@ def lastRunLine(self, line): # called from hal_glib to run macros from external event def request_macro_call(self, data): + #print(f'macro call data: {data}') if not self.w.chk_auto_mode_ext_macro.isChecked() and not STATUS.is_mdi_mode(): self.add_status(_translate("HandlerClass",'Machine must be in MDI mode to run macros'), WARNING) return - - if 'ini-macro-cmd' in data: + cmd = INFO.get_ini_mdi_command(data) + #print(f'MDI command:{cmd} data:{data}') + if INFO.get_ini_mdi_command(data) is None: data = data.replace('ini-macro-cmd-','') try: temp = INFO.MACRO_COMMAND_DICT.get(data).get('cmd') + #print(temp) self.run_macro(data=temp) return - except: - self.add_status(_translate(f"HandlerClass",'External requested INI macro data not recognized:{data}'), CRITICAL) + except Exception as e: + print(e) + self.add_status(_translate("HandlerClass",f'External requested INI macro data not recognized:{data}'), CRITICAL) - elif 'ini-mdi-cmd' in data: + elif INFO.get_ini_macro_command(data) is None: for b in range(0,10): button = self.w['macrobutton{}'.format(b)] # prefer named INI MDI commands @@ -959,9 +963,9 @@ def request_macro_call(self, data): self.add_status(_translate("HandlerClass",'Error running macro: {} {}\n{}'.format(key, text, e))) break else: - self.add_status(_translate(f"HandlerClass",'External requested INI mdi {data} does not match button name/number'), CRITICAL) + self.add_status(_translate("HandlerClass",f'External requested INI mdi {data} does not match button name/number'), CRITICAL) else: - self.add_status(_translate(f"HandlerClass",'External requested INI macro data not recognized:{data}'), CRITICAL) + self.add_status(_translate("HandlerClass",f'External requested INI macro data not recognized:{data}'), CRITICAL) ####################### # CALLBACKS FROM FORM # @@ -996,10 +1000,20 @@ def cmb_gcode_history_clicked(self): # program frame def btn_start_clicked(self, obj): + if not STATUS.is_all_homed(): self.add_status(_translate("HandlerClass","Machine must be is homed"), CRITICAL) return - if not os.path.exists(self.last_loaded_program): + if STATUS.is_auto_paused(): + self.command.auto(linuxcnc.AUTO_RESUME) + return + if STATUS.is_mdi_mode(): + self.w.mdiline.submit() + return + if STATUS.is_man_mode(): + self.add_status(_translate("HandlerClass","Can't start cycles or submit MDI commands in manual Mode"),WARNING) + return + if not os.path.exists(self.last_loaded_program): self.add_status(_translate("HandlerClass","No program to execute"), WARNING) return if not STATUS.is_auto_mode() and not self.auto_mode_switch: From 9ba0f115a70f2ded0a43b665379d41ce3b087323 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Mon, 20 Jul 2026 21:00:13 -0700 Subject: [PATCH 34/81] qtdragon -add basic support for softkeys to select main tabs --- share/qtvcp/screens/qtdragon/qtdragon_handler.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/share/qtvcp/screens/qtdragon/qtdragon_handler.py b/share/qtvcp/screens/qtdragon/qtdragon_handler.py index 653d909a818..319a1f9be27 100644 --- a/share/qtvcp/screens/qtdragon/qtdragon_handler.py +++ b/share/qtvcp/screens/qtdragon/qtdragon_handler.py @@ -159,6 +159,7 @@ def __init__(self, halcomp, widgets, paths): STATUS.connect('ok-request', lambda w, state: self.dialog_ext_control(w,1,1)) STATUS.connect('cancel-request', lambda w, state: self.dialog_ext_control(w,1,0)) STATUS.connect('axis-selection-changed', lambda w,data: self.mpg_selection_changed(data)) + STATUS.connect('softkey-pressed', lambda w,data: self.softkey_pressed(data)) self.swoopPath = os.path.join(paths.IMAGEDIR,'lcnc_swoop.png') self.swoopURL = QtCore.QUrl.fromLocalFile(self.swoopPath) @@ -967,6 +968,17 @@ def request_macro_call(self, data): else: self.add_status(_translate("HandlerClass",f'External requested INI macro data not recognized:{data}'), CRITICAL) + # external request for a softkey press from HALUI/halbridge + def softkey_pressed(self, index): + tmp=['main','file','offsets','tool','status', + 'probe','gcodes','setup','settings', + 'utils','user','camera'] + + btn = self.w[f'btn_{tmp[index]}'] + #print(f'index{index}, btn, {btn}') + if btn.isVisible(): + btn.click() + ####################### # CALLBACKS FROM FORM # ####################### From f8dc5f25b6c48cbea5a7d717e7a0735f02ca9c02 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 23 Aug 2025 13:57:34 -0700 Subject: [PATCH 35/81] qtdragon -add halui test sim sim -add scale buttons and MPG dial sim -add MPG button on VCP panel sim -fix goto user macro: raise up to Z0 in g53 rather then user units sim -add a macro command to run from the panel sim -update sim control panel sim update for softkeys qtdragon -update sample panel pin names --- configs/sim/qtdragon/qtdragon_xyz/panel.hal | 54 + configs/sim/qtdragon/qtdragon_xyz/panel.ui | 1327 +++++++++++++++++ .../qtdragon_xyz/qtdragon_halui_test.ini | 274 ++++ 3 files changed, 1655 insertions(+) create mode 100644 configs/sim/qtdragon/qtdragon_xyz/panel.hal create mode 100644 configs/sim/qtdragon/qtdragon_xyz/panel.ui create mode 100644 configs/sim/qtdragon/qtdragon_xyz/qtdragon_halui_test.ini diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.hal b/configs/sim/qtdragon/qtdragon_xyz/panel.hal new file mode 100644 index 00000000000..89b5bef186f --- /dev/null +++ b/configs/sim/qtdragon/qtdragon_xyz/panel.hal @@ -0,0 +1,54 @@ +net rate halui.axis.jog-speed panel.jog-rate + +net sx halui.axis.x.select panel.axis-x +net sx axis.x.jog-enable +net sy halui.axis.y.select panel.axis-y +net sy axis.y.jog-enable +net sz halui.axis.z.select panel.axis-z +net sz axis.z.jog-enable +net sgui halui.gui.mpg-select.0 panel.select-gui0 + + +net mpg-scale axis.x.jog-scale panel.mpg-scale +net mpg-scale axis.y.jog-scale +net mpg-scale axis.z.jog-scale + +net mpg-count panel.mpg-wheel-s +net mpg-count qtdragon.mpg-in +net mpg-count axis.x.jog-counts +net mpg-count axis.y.jog-counts +net mpg-count axis.z.jog-counts + +net jog-p halui.axis.selected.plus panel.jog-pos +net jog-m halui.axis.selected.minus panel.jog-neg + +net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0 +net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1 +net m2 halui.gui.mdi-command-MACRO6 panel.mdi-2 + +net man panel.manual-mode halui.mode.manual +net mdi panel.mdi-mode halui.mode.mdi +net auto panel.auto-mode halui.mode.auto + +net pause halui.gui.cycle.start panel.cycle-start +net start halui.gui.cycle.pause panel.cycle-pause +net abort halui.abort panel.cycle-abort + +net cancel halui.gui.cancel panel.cancel +net ok halui.gui.ok panel.ok + +net softkey0 halui.gui.softkey-00 panel.softkey-0 +net softkey1 halui.gui.softkey-01 panel.softkey-1 +net softkey2 halui.gui.softkey-02 panel.softkey-2 +net softkey3 halui.gui.softkey-03 panel.softkey-3 +net softkey4 halui.gui.softkey-04 panel.softkey-4 +net softkey5 halui.gui.softkey-05 panel.softkey-5 +net softkey6 halui.gui.softkey-06 panel.softkey-6 +net softkey7 halui.gui.softkey-07 panel.softkey-7 +net softkey8 halui.gui.softkey-08 panel.softkey-8 +net softkey9 halui.gui.softkey-09 panel.softkey-9 +net softkey10 halui.gui.softkey-10 panel.softkey-10 +net softkey11 halui.gui.softkey-11 panel.softkey-11 + +net exit halui.gui.shutdown panel.exit +net reload halui.gui.reload-display panel.reload diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.ui b/configs/sim/qtdragon/qtdragon_xyz/panel.ui new file mode 100644 index 00000000000..ecef3135492 --- /dev/null +++ b/configs/sim/qtdragon/qtdragon_xyz/panel.ui @@ -0,0 +1,1327 @@ + + + MainWindow + + + + 0 + 0 + 583 + 694 + + + + MainWindow + + + + + + + + + + + Axis Jog + + + + + + + + + + jog-pos + + + + + + + - + + + jog-neg + + + + + + + + + + linear rate / angular rate + + + + + + Qt::Horizontal + + + jog-rate + + + true + + + + + + + Qt::Horizontal + + + jog-rate-angular + + + false + + + true + + + + + + + + + + + + + + MPG + + + + + + .001 + + + true + + + true + + + true + + + mpg-scale-small + + + true + + + PushButton::FLOAT + + + mpg-scale + + + 0.001000000000000 + + + buttonGroup_mpgscale + + + + + + + .01 + + + true + + + true + + + mpg-scale-med + + + true + + + PushButton::FLOAT + + + mpg-scale + + + 0.010000000000000 + + + buttonGroup_mpgscale + + + + + + + .1 + + + true + + + true + + + mpg-scale-large + + + true + + + PushButton::FLOAT + + + mpg-scale + + + 0.100000000000000 + + + buttonGroup_mpgscale + + + + + + + + + + + 0 + 74 + + + + false + + + true + + + false + + + mpg-wheel + + + + + + + + + MDI Comands + + + + + + 0 + + + mdi-0 + + + + + + + 1 + + + mdi-1 + + + + + + + 2 + + + mdi-2 + + + + + + + + + + Mode Comands + + + + + + Manual + + + false + + + true + + + manual-mode + + + true + + + true + + + true + + + + + + + MDI + + + false + + + true + + + mdi-mode + + + true + + + true + + + true + + + + + + + Auto + + + false + + + true + + + auto-mode + + + true + + + true + + + true + + + + + + + + + + program control + + + + + + start + + + cycle-start + + + + + + + pause + + + cycle-pause + + + + + + + Abort + + + cycle-abort + + + + + + + + + + dialog control + + + + + + ok + + + ok + + + + + + + cancel + + + cancel + + + + + + + + + + + + Axis Selection + + + + 0 + + + 3 + + + 0 + + + 0 + + + 4 + + + + + None + + + true + + + true + + + axis-none + + + + + + + A + + + true + + + true + + + axis-a + + + + + + + Y + + + true + + + true + + + axis-y + + + + + + + X + + + true + + + true + + + axis-x + + + + + + + Z + + + true + + + true + + + axis-z + + + + + + + B + + + true + + + true + + + axis-b + + + + + + + C + + + true + + + true + + + axis-c + + + + + + + GUI + + + true + + + true + + + select-gui0 + + + + + + + + + + + + ESTOP + + + false + + + false + + + false + + + false + + + false + + + + 255 + 0 + 0 + + + + 0 + + + + 0 + 0 + 0 + + + + 0.300000000000000 + + + 10 + + + 0 + + + 0 + + + 5.000000000000000 + + + 0.300000000000000 + + + 0.900000000000000 + + + True + + + False + + + print("true command") + + + print("false command") + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + 0 + + + true + + + false + + + -1 + + + 0.010000000000000 + + + 0.025000000000000 + + + -1.000000000000000 + + + false + + + 0.300000000000000 + + + 50.000000000000000 + + + P + + + + + + 0 + + + %1.3f in + + + %1.2f mm + + + + + + + ON + + + false + + + false + + + false + + + false + + + false + + + + 255 + 0 + 0 + + + + 0 + + + + 0 + 0 + 0 + + + + 0.300000000000000 + + + 10 + + + 0 + + + 0 + + + 5.000000000000000 + + + 0.300000000000000 + + + 0.900000000000000 + + + True + + + False + + + print("true command") + + + print("false command") + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + 0 + + + true + + + false + + + -1 + + + 0.010000000000000 + + + 0.025000000000000 + + + -1.000000000000000 + + + false + + + 0.300000000000000 + + + 50.000000000000000 + + + P + + + + + + 0 + + + %1.3f in + + + %1.2f mm + + + + + + + HOME + + + false + + + false + + + false + + + false + + + false + + + + 255 + 0 + 0 + + + + 0 + + + + 0 + 0 + 0 + + + + 0.300000000000000 + + + 10 + + + 0 + + + 0 + + + 5.000000000000000 + + + 0.300000000000000 + + + 0.900000000000000 + + + True + + + False + + + print("true command") + + + print("false command") + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + 0 + + + true + + + false + + + -1 + + + 0.010000000000000 + + + 0.025000000000000 + + + -1.000000000000000 + + + false + + + 0.300000000000000 + + + 50.000000000000000 + + + P + + + + + + 0 + + + %1.3f in + + + %1.2f mm + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + MainTab + + + softkey-0 + + + PushButton::BIT + + + + + + + FileTab + + + softkey-1 + + + PushButton::BIT + + + + + + + Offsets + + + softkey-2 + + + PushButton::BIT + + + + + + + Tools + + + softkey-3 + + + PushButton::BIT + + + + + + + Status + + + softkey-4 + + + PushButton::BIT + + + + + + + Probe + + + softkey-5 + + + PushButton::BIT + + + + + + + Gcodes + + + softkey-6 + + + PushButton::BIT + + + + + + + Setup + + + softkey-7 + + + PushButton::BIT + + + + + + + Settings + + + softkey-8 + + + PushButton::BIT + + + + + + + Utils + + + softkey-9 + + + PushButton::BIT + + + + + + + User + + + softkey-10 + + + PushButton::BIT + + + + + + + Cam + + + softkey-11 + + + PushButton::BIT + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Reload + + + reload + + + PushButton::BIT + + + + + + + exit + + + exit + + + PushButton::BIT + + + + + + + + + + + IndicatedPushButton + QPushButton +
qtvcp.widgets.simple_widgets
+
+ + PushButton + QPushButton +
qtvcp.widgets.simple_widgets
+
+ + Dial + QDial +
qtvcp.widgets.simple_widgets
+
+ + ActionButton + IndicatedPushButton +
qtvcp.widgets.action_button
+
+ + StatusSlider + QSlider +
qtvcp.widgets.status_slider
+
+
+ + + + + +
diff --git a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_halui_test.ini b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_halui_test.ini new file mode 100644 index 00000000000..5efd17e4785 --- /dev/null +++ b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_halui_test.ini @@ -0,0 +1,274 @@ +# This file was created with the 7i96 Wizard on Jun 10 2019 11:12:47 +# Changes to most things are ok and will be read by the wizard + +[EMC] +VERSION = 1.1 +MACHINE = qtdragon_metric +DEBUG = 0x00000000 + +[DISPLAY] +# sets qtdragon as screen. for debug output to terminal add -d or -v +# sets window title +# sets icon in task manager +DISPLAY = qtvcp -d qtdragon +TITLE = QtDragon XYZ Metric +ICON = silver_dragon.png + +# qtdragon saves most preference to this file +PREFERENCE_FILE_PATH = WORKINGFOLDER/qtdragon.pref + +# min/max percentage overrides allowed in qtdragon 1 = 100% +MAX_FEED_OVERRIDE = 1.2 +MIN_SPINDLE_0_OVERRIDE = 0.5 +MAX_SPINDLE_0_OVERRIDE = 1.2 + +# manual spindle speed will start at this RPM +DEFAULT_SPINDLE_0_SPEED = 12000 + +# spindle up/down increment in RPM +SPINDLE_INCREMENT = 200 + +# min max apindle speed manually allowed +MIN_SPINDLE_0_SPEED = 1000 +MAX_SPINDLE_0_SPEED = 20000 + +# max spindle power in Watts +MAX_SPINDLE_POWER = 2000 + +# min/max/default jog velocities in qtdragon in units/sec +MIN_LINEAR_VELOCITY = 0 +MAX_LINEAR_VELOCITY = 60.00 +DEFAULT_LINEAR_VELOCITY = 50.0 + +# incremental jog step length options +INCREMENTS = 10 mm, 1.0 mm, 0.10 mm, 0.01 mm, 1.0 inch, 0.1 inch, 0.01 inch + +# Display grid increments +GRIDS = 0, .1 mm, 1 mm, 2 mm, 5 mm, 10 mm, .25 in, .5 in + +CYCLE_TIME = 100 +INTRO_GRAPHIC = silver_dragon.png +INTRO_TIME = 2 + +# default program search path +PROGRAM_PREFIX = ~/linuxcnc/nc_files + +# NGCGUI subroutine path. +# Thr path must also be in [RS274NGC] SUBROUTINE_PATH +NGCGUI_SUBFILE_PATH = ../../../nc_files/ngcgui_lib/ +# pre selected programs tabs +# specify filenames only, files must be in the NGCGUI_SUBFILE_PATH +NGCGUI_SUBFILE = slot.ngc +NGCGUI_SUBFILE = qpocket.ngc + +# qtdragon saves MDI cxommands to this file +MDI_HISTORY_FILE = mdi_history.dat +# qtdragon saves rnning logs to this file +LOG_FILE = qtdragon.log + +# optional user dialogs (3), controlled by HAL pins +MESSAGE_BOLDTEXT = Critical and Persistent +MESSAGE_TEXT = This is a persistent dialog test +MESSAGE_DETAILS = There seems to be something wrong\n You must fix it to clear message +MESSAGE_TYPE = nonedialog +MESSAGE_PINNAME = nonedialogtest +MESSAGE_ICON = CRITICAL + +MESSAGE_BOLDTEXT = Do You Want To Make A Choice? +MESSAGE_TEXT = This is a yes no dialog test +MESSAGE_DETAILS = Y/N DETAILS +MESSAGE_TYPE = yesnodialog +MESSAGE_PINNAME = yndialogtest +MESSAGE_ICON = QUESTION + +MESSAGE_BOLDTEXT = This is an information message +MESSAGE_TEXT = This is low priority +MESSAGE_DETAILS = press ok to clear +MESSAGE_TYPE = okdialog status +MESSAGE_PINNAME = bothtest +MESSAGE_ICON = INFO + +# optional tab showing an external qtvcp panel +EMBED_TAB_NAME=Vismach demo +EMBED_TAB_COMMAND=qtvcp vismach_mill_xyz +EMBED_TAB_LOCATION=tabWidget_utilities + +[MDI_COMMAND_LIST] +# for macro buttons on main page up to 10 possible +MDI_COMMAND_MACRO0 = G53 G0 Z0;G0 X0 Y0;Z0, Goto\nUser\nZero +MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero + +[MACROS] +MACRO_COMMAND_MACRO6 = go_to_position x-pos y-pos z-pos, Go To \nPosition +MACRO_COMMAND_MACRO2 = lost, LOST +MACRO_COMMAND_MACRO3 = increment x-incr y-incr, INCR +MACRO_COMMAND_MACRO4 = macro_4 +MACRO_COMMAND_MACRO5 = macro_5 + +[FILTER] +# Controls what programs are shown inqtdragon file manager +PROGRAM_EXTENSION = .ngc,.nc,.tap G-Code File (*.ngc,*.nc,*.tap) +PROGRAM_EXTENSION = .png,.gif,.jpg Greyscale Depth Image +PROGRAM_EXTENSION = .py Python Script + +# specifies what special 'filter' programs runs based on program ending +png = image-to-gcode +gif = image-to-gcode +jpg = image-to-gcode +py = python3 + +[KINS] +KINEMATICS = trivkins coordinates=XYZ +JOINTS = 3 + +[EMCIO] +TOOL_TABLE = tool.tbl + +[RS274NGC] +# motion controller saves parameters to this file +PARAMETER_FILE = qtdragon.var + +# start up G/M codes when first loaded +RS274NGC_STARTUP_CODE = G17 G21 G40 G43H0 G54 G64P0.0127 G80 G90 G94 G97 M5 M9 + +# subroutine/remap path list +SUBROUTINE_PATH = ../../../../nc_files/probe/basic_probe/macros:~/linuxcnc/nc_files/examples/ngcgui_lib:~/linuxcnc/nc_files/examples/ngcgui_lib/utilitysubs:./macros + +# on abort, this ngc file is called. required for basic/versa probe +ON_ABORT_COMMAND=O call + +[EMCMOT] +EMCMOT = motmod +SERVO_PERIOD = 1000000 +COMM_TIMEOUT = 1.0 +COMM_WAIT = 0.010 +BASE_PERIOD = 100000 + +[TASK] +TASK = milltask +CYCLE_TIME = 0.010 + +[TRAJ] +COORDINATES = XYZ +LINEAR_UNITS = metric +ANGULAR_UNITS = degree +MAX_LINEAR_VELOCITY = 60.00 +DEFAULT_LINEAR_VELOCITY = 50.00 +SPINDLES = 1 + +[HAL] +HALUI = halui +#HALBRIDGE = hal_bridge + +# loads the HAL machine simulation +HALFILE = core_sim.hal +HALFILE = simulated_home.hal + +# this file is loaded after qtdragon has made it's HAl pins +# you can add multiple entries +POSTGUI_HALFILE = qtdragon_postgui.hal + +# this command is run after qtdragon has made it's HAl pins +# any HAL conmmand can be used +# you can add multiple entries +# uncomment this one to print all HAL pins that start with qt +#POSTGUI_HALCMD = show pin qt +POSTGUI_HALCMD = show pin halui.gui +POSTGUI_HALCMD = loadusr qtvcp -a -H panel.hal panel +[HALUI] +# no content + +[PROBE] +# pick basic probe or versa probe or remove for none +#USE_PROBE = versaprobe +USE_PROBE = basicprobe + +[AXIS_X] +MIN_LIMIT = -0.001 +MAX_LIMIT = 520.0 +MAX_VELOCITY = 60.0 +MAX_ACCELERATION = 500.0 + +[AXIS_Y] +MIN_LIMIT = -0.001 +MAX_LIMIT = 630.0 +MAX_VELOCITY = 60.0 +MAX_ACCELERATION = 500.0 + +[AXIS_Z] +# used by external offsets for auto spindle lift +OFFSET_AV_RATIO = 0.2 +MIN_LIMIT = -115.0 +MAX_LIMIT = 10.0 +MAX_VELOCITY = 40.0 +MAX_ACCELERATION = 500.0 + +[JOINT_0] +AXIS = X +MIN_LIMIT = -0.001 +MAX_LIMIT = 520.0 +MAX_VELOCITY = 60.0 +MAX_ACCELERATION = 500.0 +TYPE = LINEAR +SCALE = 160.0 +STEPGEN_MAX_VEL = 72.0 +STEPGEN_MAX_ACC = 600.0 +FERROR = 1.0 +MIN_FERROR = 0.5 +MAX_OUTPUT = 0 +MAX_ERROR = 0.0127 +HOME = 20.0 +HOME_OFFSET = 0.00000 +HOME_SEARCH_VEL = 20.000000 +HOME_LATCH_VEL = 10.000 +HOME_SEQUENCE = 1 +HOME_USE_INDEX = False +HOME_IGNORE_LIMITS = False +HOME_IS_SHARED = 1 + +[JOINT_1] +AXIS = Y +MIN_LIMIT = -0.001 +MAX_LIMIT = 630.0 +MAX_VELOCITY = 60.0 +MAX_ACCELERATION = 500.0 +TYPE = LINEAR +SCALE = 160.0 +STEPGEN_MAX_VEL = 72.0 +STEPGEN_MAX_ACC = 600.0 +FERROR = 1.0 +MIN_FERROR = 0.5 +MAX_OUTPUT = 0 +MAX_ERROR = 0.0127 +HOME = 20.0 +HOME_OFFSET = 0.000000 +HOME_SEARCH_VEL = 20.00 +HOME_LATCH_VEL = 10.00 +HOME_SEQUENCE = 2 +HOME_USE_INDEX = False +HOME_IGNORE_LIMITS = False + +[JOINT_2] +AXIS = Z +MIN_LIMIT = -115.0 +MAX_LIMIT = 10.0 +MAX_VELOCITY = 40.0 +MAX_ACCELERATION = 500.0 +TYPE = LINEAR +SCALE = 160.0 +STEPGEN_MAX_VEL = 48.0 +STEPGEN_MAX_ACC = 600.0 +FERROR = 1.0 +MIN_FERROR = 0.5 +MAX_OUTPUT = 0 +MAX_ERROR = 0.0127 +HOME = -10.0 +HOME_OFFSET = 0.000000 +HOME_SEARCH_VEL = 20.000000 +HOME_LATCH_VEL = 10.00 +HOME_SEQUENCE = 0 +HOME_USE_INDEX = False +HOME_IGNORE_LIMITS = False +HOME_IS_SHARED = 1 + + From 6d43058e8489ba8c62668be27fd05e21a879bfcc Mon Sep 17 00:00:00 2001 From: CMorley Date: Sun, 27 Jul 2025 14:05:58 -0700 Subject: [PATCH 36/81] gmoccapy/hal_bridge -allow hal_bridge to call macros in gmoccapy This is a proof of concept to allow 3rd party (hal_bridge) to call macros. The macros are run from Gmoccapy so no timing problems should occur and and pre checks or post changes can be covered. There needs to be agreement on where to put macro definitions in the INI. Gmoccapy puts them under [MACROS], iniinfo under [DISPLAY] python ZMQ module package must be available for this to work --- configs/sim/gmoccapy/gmoccapy_right_panel.ini | 8 +++- src/emc/usr_intf/gmoccapy/getiniinfo.py | 2 +- src/emc/usr_intf/gmoccapy/gmoccapy.py | 42 ++++++++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/configs/sim/gmoccapy/gmoccapy_right_panel.ini b/configs/sim/gmoccapy/gmoccapy_right_panel.ini index 62213d687d5..7b4c5981da8 100644 --- a/configs/sim/gmoccapy/gmoccapy_right_panel.ini +++ b/configs/sim/gmoccapy/gmoccapy_right_panel.ini @@ -31,6 +31,12 @@ INTRO_TIME = 5 # list of selectable jog increments INCREMENTS = 1mm, 0.1mm, 0.01mm, 0.001mm, 1.2345in +MACRO = i_am_lost +MACRO = halo_world +MACRO = jog_around +MACRO = increment xinc yinc +MACRO = go_to_position X-pos Y-pos Z-pos + [FILTER] PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image PROGRAM_EXTENSION = .py Python Script @@ -75,7 +81,7 @@ HALFILE = simulated_home.hal POSTGUI_HALFILE = gmoccapy_postgui.hal HALUI = halui - +HALBRIDGE = hal_bridge -d # Trajectory planner section -------------------------------------------------- [HALUI] #No Content diff --git a/src/emc/usr_intf/gmoccapy/getiniinfo.py b/src/emc/usr_intf/gmoccapy/getiniinfo.py index e9ec0965a84..71dc4db0d92 100644 --- a/src/emc/usr_intf/gmoccapy/getiniinfo.py +++ b/src/emc/usr_intf/gmoccapy/getiniinfo.py @@ -380,7 +380,7 @@ def get_tool_sensor_data(self): def get_macros(self): # lets look in the INI file, if there are any entries - macros = self.inifile.findall("MACROS", "MACRO") + macros = self.inifile.findall("DISPLAY", "MACRO") # If there are no entries we will return False if not macros: return False diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index c7afde2fcbf..ebeb1bd3255 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -431,6 +431,7 @@ def __init__(self, argv): self.GSTAT = Status() self.GSTAT.connect("graphics-gcode-properties", self.on_gcode_properties) self.GSTAT.connect("file-loaded", self.on_hal_status_file_loaded) + self.GSTAT.connect('macro-call-request', lambda w, name: self.request_macro_call(name)) # get if run from line should be used self.run_from_line = self.prefs.getpref("run_from_line", "no_run", str) @@ -1314,6 +1315,33 @@ def _make_joints_button(self): self.joints_button_dic[name] = btn + # call INI macro (from hal_glib message) + def request_macro_call(self, data): + + # some error checking + if not self.GSTAT.is_mdi_mode(): + message = _("You must be in MDI mode to run macros") + self.dialogs.warning_dialog(self, _("Important Warning!"), message) + return + + # look thru the INI macros + macros = self.get_ini_info.get_macros() + num_macros = len(macros) + if num_macros > 14: + num_macros = 14 + for pos in range(0, num_macros): + # extract just the macro name + name = macros[pos].split()[0] + if data == name: + # get the button instance and click it + button = self["button_macro_{0}".format(pos)] + button.emit("clicked") + break + else: + # didn't match a name - give a hint + message = _("Macro {} not found ".format(data)) + self.dialogs.warning_dialog(self, _("Important Warning!"), message) + # check if macros are in the INI file and add them to MDI Button List def _make_macro_button(self): LOG.debug("Entering make macro button") @@ -1355,7 +1383,9 @@ def _make_macro_button(self): btn.set_halign(Gtk.Align.CENTER) btn.set_valign(Gtk.Align.CENTER) btn.set_property("name","macro_{0}".format(pos)) - btn.set_property("tooltip-text", _("Press to run macro {0}").format(name)) + # keep a reference of the button + self["button_macro_{0}".format(pos)] = btn + btn.set_property("tooltip-text", _("Press to run macro {0}".format(name))) btn.connect("clicked", self._on_btn_macro_pressed, name) btn.position = pos btn.show() @@ -6513,6 +6543,16 @@ def _make_hal_pins(self): hal_glib.GPin(pin).connect("value_changed", self._blockdelete) + ############################## + # required class boiler code # + # for subscriptable objects # + ############################## + def __getitem__(self, item): + return getattr(self, item) + + def __setitem__(self, item, value): + return setattr(self, item, value) + # Hal Pin Handling End # ========================================================= From c9abdc771885570733b28714022b10f350995fdd Mon Sep 17 00:00:00 2001 From: CMorley Date: Wed, 30 Jul 2025 20:50:47 -0700 Subject: [PATCH 37/81] gmoccapy -ability to run INI MDI commands using HAL bridge --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index ebeb1bd3255..22e6e6a7dd5 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -198,7 +198,8 @@ def __init__(self, argv): self.error_channel.poll() # set INI path for INI info class before widgets are loaded - INFO = Info(ini=argv[2]) + self.INFO = Info(ini=argv[2]) + self.ACTION = Action() self.builder = Gtk.Builder() # translation of the glade file will be done with @@ -1317,7 +1318,17 @@ def _make_joints_button(self): # call INI macro (from hal_glib message) def request_macro_call(self, data): + # if MDI command change to MDI and run + cmd = self.INFO.get_ini_mdi_command(data) + print('MDI command:',data,cmd) + if not cmd is None: + self.ACTION.RECORD_CURRENT_MODE() + LOG.debug("INI MDI COMMAND #: {} = {}".format(data, cmd)) + self.ACTION.CALL_INI_MDI(data) + self.ACTION.RESTORE_RECORDED_MODE() + return + # run Macros # some error checking if not self.GSTAT.is_mdi_mode(): message = _("You must be in MDI mode to run macros") @@ -6592,6 +6603,7 @@ def __setitem__(self, item, value): # Some of these libraries log when imported so logging level must already be set. import gladevcp.makepins from gladevcp.core import Info, Status + from gladevcp.core import Info, Status, Action from gladevcp.combi_dro import Combi_DRO # we will need it to make the DRO from gmoccapy import widgets # a class to handle the widgets From 8dfe2ae1059892ee68663edd4300643e9769ff82 Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 10:16:35 -0700 Subject: [PATCH 38/81] gmoccapy -add gstat message control of start and pause --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index 22e6e6a7dd5..073490d6f46 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -433,6 +433,8 @@ def __init__(self, argv): self.GSTAT.connect("graphics-gcode-properties", self.on_gcode_properties) self.GSTAT.connect("file-loaded", self.on_hal_status_file_loaded) self.GSTAT.connect('macro-call-request', lambda w, name: self.request_macro_call(name)) + self.GSTAT.connect('cycle-start-request', lambda w, state :self.request_start(state)) + self.GSTAT.connect('cycle-pause-request', lambda w, state: self.request_pause(state)) # get if run from line should be used self.run_from_line = self.prefs.getpref("run_from_line", "no_run", str) @@ -1316,6 +1318,14 @@ def _make_joints_button(self): self.joints_button_dic[name] = btn + def request_start(self,data): + print('start') + self.widgets.btn_run.emit('clicked') + + def request_pause(self,data): + print('pause') + self.widgets.tbtn_pause.emit('clicked') + # call INI macro (from hal_glib message) def request_macro_call(self, data): # if MDI command change to MDI and run From 44d4657ce9f1a50a0d326941a202b984a9aff5e8 Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 21:27:49 -0700 Subject: [PATCH 39/81] gmoccapy -used new mode return INI MDI function --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index 073490d6f46..3c2c43bfc04 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -1332,10 +1332,8 @@ def request_macro_call(self, data): cmd = self.INFO.get_ini_mdi_command(data) print('MDI command:',data,cmd) if not cmd is None: - self.ACTION.RECORD_CURRENT_MODE() LOG.debug("INI MDI COMMAND #: {} = {}".format(data, cmd)) - self.ACTION.CALL_INI_MDI(data) - self.ACTION.RESTORE_RECORDED_MODE() + self.ACTION.CALL_INI_MDI(data,mode_return = True) return # run Macros From bb697edafaa5425eaff25b282d4e9d2d17dbc7b6 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 17 Oct 2025 21:50:03 -0700 Subject: [PATCH 40/81] gmoccapy -getinfo.py: fix macro search title this must have been fix in master after I branched --- src/emc/usr_intf/gmoccapy/getiniinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emc/usr_intf/gmoccapy/getiniinfo.py b/src/emc/usr_intf/gmoccapy/getiniinfo.py index 71dc4db0d92..e9ec0965a84 100644 --- a/src/emc/usr_intf/gmoccapy/getiniinfo.py +++ b/src/emc/usr_intf/gmoccapy/getiniinfo.py @@ -380,7 +380,7 @@ def get_tool_sensor_data(self): def get_macros(self): # lets look in the INI file, if there are any entries - macros = self.inifile.findall("DISPLAY", "MACRO") + macros = self.inifile.findall("MACROS", "MACRO") # If there are no entries we will return False if not macros: return False From 509ede52813c7b292d95611c6385d68cb66fc611 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sun, 14 Dec 2025 01:10:17 -0800 Subject: [PATCH 41/81] gmoccapy -change system dialog to accept halui messages ok and cancel are used by the system unlock dialog as an example. To do this properly for all dialogs would require some more thought. --- src/emc/usr_intf/gmoccapy/dialogs.py | 46 +++++++++++++++++++++------ src/emc/usr_intf/gmoccapy/gmoccapy.py | 29 +++++++++++++++-- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/dialogs.py b/src/emc/usr_intf/gmoccapy/dialogs.py index 08a16ee7249..ab06d809965 100644 --- a/src/emc/usr_intf/gmoccapy/dialogs.py +++ b/src/emc/usr_intf/gmoccapy/dialogs.py @@ -36,10 +36,19 @@ class Dialogs(GObject.GObject): __gsignals__ = { 'play_sound': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)), + 'system-dialog-result': (GObject.SignalFlags.RUN_FIRST , GObject.TYPE_NONE, (GObject.TYPE_INT,)) } - def __init__(self): + def __init__(self, caller): GObject.GObject.__init__(self) + self.sys_dialog = self.system_dialog(caller) + + def dialog_ext_control(self, answer): + if self.sys_dialog.get_visible(): + if answer: + self.sys_dialog.response(Gtk.ResponseType.ACCEPT) + else: + self.sys_dialog.response(Gtk.ResponseType.CANCEL) # This dialog is for unlocking the system tab # The unlock code number is defined at the top of the page @@ -47,9 +56,12 @@ def system_dialog(self, caller): dialog = Gtk.Dialog(_("Enter System Unlock Code"), caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT) + dialog.set_modal(True) label = Gtk.Label(_("Enter System Unlock Code")) label.modify_font(Pango.FontDescription("sans 20")) calc = gladevcp.Calculator() + dialog._calc = calc + dialog._caller = caller dialog.vbox.pack_start(label, False, False, 0) dialog.vbox.add(calc) calc.set_value("") @@ -57,18 +69,32 @@ def system_dialog(self, caller): calc.set_editable(True) calc.integer_entry_only(True) calc.num_pad_only(True) - calc.entry.connect("activate", lambda w : dialog.emit("response", Gtk.ResponseType.ACCEPT)) + calc.entry.connect("activate", lambda w : self.on_system_response(dialog,Gtk.ResponseType.ACCEPT)) dialog.parse_geometry("360x400") dialog.set_decorated(True) - dialog.show_all() + dialog.connect("response", self.on_system_response) + return dialog + + def show_system_dialog(self): + self.sys_dialog._calc.set_value("") + self.sys_dialog.show_all() self.emit("play_sound", "alert") - response = dialog.run() - code = calc.get_value() - dialog.destroy() - if response == Gtk.ResponseType.ACCEPT: - if code == int(caller.unlock_code): - return True - return False + + def on_system_response(self, dialog, result): + code = dialog._calc.get_value() + print('Code:',code) + rtn = -1 + if result == Gtk.ResponseType.ACCEPT: + if code == int(dialog._caller.unlock_code): + print('Yes') + rtn = 1 + else: + print('No') + rtn = 0 + else: + print('Cancelled') + self.emit('system-dialog-result',rtn) + dialog.hide() def entry_dialog(self, caller, data = None, header = _("Enter value") , label = _("Enter the value to set"), integer = False): dialog = Gtk.Dialog(header, diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index 3c2c43bfc04..a2a9aac5196 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -272,8 +272,9 @@ def __init__(self, argv): self.icon_theme.append_search_path(ICON_THEME_DIR) self.icon_theme.append_search_path(USER_ICON_THEME_DIR) - self.dialogs = dialogs.Dialogs() + self.dialogs = dialogs.Dialogs(caller = self) self.dialogs.connect("play_sound", self._on_play_sound) + self.dialogs.connect('system-dialog-result', self.system_dialog_return) # check the arguments given from the command line (Ini file) self.user_mode = False @@ -435,6 +436,8 @@ def __init__(self, argv): self.GSTAT.connect('macro-call-request', lambda w, name: self.request_macro_call(name)) self.GSTAT.connect('cycle-start-request', lambda w, state :self.request_start(state)) self.GSTAT.connect('cycle-pause-request', lambda w, state: self.request_pause(state)) + self.GSTAT.connect('ok-request', lambda w, state: self.dialogs.dialog_ext_control(1)) + self.GSTAT.connect('cancel-request', lambda w, state: self.dialogs.dialog_ext_control(0)) # get if run from line should be used self.run_from_line = self.prefs.getpref("run_from_line", "no_run", str) @@ -4278,8 +4281,9 @@ def on_tbtn_setup_toggled(self, widget, data=None): code = True # else we ask for the code using the system.dialog if self.widgets.rbt_use_unlock.get_active(): - if self.dialogs.system_dialog(self): - code = True + self.dialogs.show_system_dialog() + # we will wait for response + return # Lets see if the user has the right to enter settings if code: self.widgets.ntb_main.set_current_page(1) @@ -4329,6 +4333,25 @@ def on_tbtn_setup_toggled(self, widget, data=None): widget.set_image(self.widgets.img_settings) + # return code from system dialog + def system_dialog_return(self,widget,result): + print(widget,result) + # Lets see if the user has the right to enter settings + if result == 1: + self.widgets.ntb_main.set_current_page(1) + self.widgets.ntb_setup.set_current_page(0) + self.widgets.ntb_button.set_current_page(_BB_SETUP) + #widget.set_image(self.widgets.img_settings_on) + elif result == 0: + if self.widgets.rbt_hal_unlock.get_active(): + message = _("Hal Pin is low, Access denied") + else: + message = _("wrong code entered, Access denied") + self.dialogs.warning_dialog(self, _("Just to warn you"), message) + self.widgets.tbtn_setup.set_active(False) + #widget.set_image(self.widgets.img_settings) + + # Show or hide the user tabs def on_tbtn_user_tabs_toggled(self, widget, data=None): if widget.get_active(): From f63fab75d73a4e50ecead843d33b81eddd0ec86c Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 26 Dec 2025 19:48:56 -0800 Subject: [PATCH 42/81] gmoccapy -change warning dialogs to accept HALUI messages --- src/emc/usr_intf/gmoccapy/dialogs.py | 38 +++++-- src/emc/usr_intf/gmoccapy/gmoccapy.py | 152 ++++++++++++++++---------- 2 files changed, 122 insertions(+), 68 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/dialogs.py b/src/emc/usr_intf/gmoccapy/dialogs.py index ab06d809965..a28408d9abe 100644 --- a/src/emc/usr_intf/gmoccapy/dialogs.py +++ b/src/emc/usr_intf/gmoccapy/dialogs.py @@ -36,19 +36,20 @@ class Dialogs(GObject.GObject): __gsignals__ = { 'play_sound': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)), - 'system-dialog-result': (GObject.SignalFlags.RUN_FIRST , GObject.TYPE_NONE, (GObject.TYPE_INT,)) + 'system-dialog-result': (GObject.SignalFlags.RUN_FIRST , GObject.TYPE_NONE, (GObject.TYPE_INT,)), + 'warning-dialog-result': (GObject.SignalFlags.RUN_FIRST , GObject.TYPE_NONE, (GObject.TYPE_INT, GObject.TYPE_STRING)) } def __init__(self, caller): GObject.GObject.__init__(self) self.sys_dialog = self.system_dialog(caller) + self.warn_dialog = self.warning_dialog(caller) def dialog_ext_control(self, answer): if self.sys_dialog.get_visible(): - if answer: - self.sys_dialog.response(Gtk.ResponseType.ACCEPT) - else: - self.sys_dialog.response(Gtk.ResponseType.CANCEL) + self.sys_dialog.response(answer) + elif self.warn_dialog.get_visible(): + self.warn_dialog.response(answer) # This dialog is for unlocking the system tab # The unlock code number is defined at the top of the page @@ -134,7 +135,7 @@ def entry_dialog(self, caller, data = None, header = _("Enter value") , label = return "CANCEL" # display warning dialog - def warning_dialog(self, caller, message, secondary = None, title = _("Operator Message"),\ + def warning_dialog(self, caller, message = '', secondary = None, title = _("Operator Message"),\ sound = True, confirm_pin = 'warning-confirm', active_pin = None): dialog = Gtk.MessageDialog(caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT, @@ -149,11 +150,10 @@ def warning_dialog(self, caller, message, secondary = None, title = _("Operator box.add(ok_button) dialog.action_area.add(box) dialog.set_border_width(5) - dialog.show_all() if sound: self.emit("play_sound", "alert") dialog.set_title(title) - + dialog.context = [] def periodic(): if caller.halcomp[confirm_pin]: dialog.response(Gtk.ResponseType.OK) @@ -164,10 +164,26 @@ def periodic(): return False return True GLib.timeout_add(100, periodic) + dialog.connect("response", self.on_warning_response) + return dialog - response = dialog.run() - dialog.destroy() - return response == Gtk.ResponseType.OK + def show_warning_dialog(self, title, message, context=None, sound=True,\ + confirm_pin = 'warning-confirm', active_pin = None): + print(message,context) + self.warn_dialog.context.append(context) + self.warn_dialog.set_title(title) + self.warn_dialog.format_secondary_text(message) + self.warn_dialog.set_markup(message) + self.warn_dialog.show_all() + if sound: + self.emit("play_sound", "alert") + print(self.warn_dialog.context) + + def on_warning_response(self, dialog, rtn): + context = dialog.context.pop() + print(context) + self.emit('warning-dialog-result', rtn, context) + dialog.hide() def yesno_dialog(self, caller, message, title = _("Operator Message")): dialog = Gtk.MessageDialog(caller.widgets.window1, diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index a2a9aac5196..84771469140 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -272,10 +272,6 @@ def __init__(self, argv): self.icon_theme.append_search_path(ICON_THEME_DIR) self.icon_theme.append_search_path(USER_ICON_THEME_DIR) - self.dialogs = dialogs.Dialogs(caller = self) - self.dialogs.connect("play_sound", self._on_play_sound) - self.dialogs.connect('system-dialog-result', self.system_dialog_return) - # check the arguments given from the command line (Ini file) self.user_mode = False self.logofile = None @@ -354,6 +350,11 @@ def __init__(self, argv): self.builder.connect_signals(self) + self.dialogs = dialogs.Dialogs(caller = self) + self.dialogs.connect("play_sound", self._on_play_sound) + self.dialogs.connect('system-dialog-result', self.system_dialog_return) + self.dialogs.connect('warning-dialog-result', self.warning_dialog_return) + # this are settings to be done before window show self._init_preferences() @@ -436,8 +437,8 @@ def __init__(self, argv): self.GSTAT.connect('macro-call-request', lambda w, name: self.request_macro_call(name)) self.GSTAT.connect('cycle-start-request', lambda w, state :self.request_start(state)) self.GSTAT.connect('cycle-pause-request', lambda w, state: self.request_pause(state)) - self.GSTAT.connect('ok-request', lambda w, state: self.dialogs.dialog_ext_control(1)) - self.GSTAT.connect('cancel-request', lambda w, state: self.dialogs.dialog_ext_control(0)) + self.GSTAT.connect('ok-request', lambda w, state: self.dialogs.dialog_ext_control(Gtk.ResponseType.ACCEPT)) + self.GSTAT.connect('cancel-request', lambda w, state: self.dialogs.dialog_ext_control(Gtk.ResponseType.CANCEL)) # get if run from line should be used self.run_from_line = self.prefs.getpref("run_from_line", "no_run", str) @@ -1343,7 +1344,8 @@ def request_macro_call(self, data): # some error checking if not self.GSTAT.is_mdi_mode(): message = _("You must be in MDI mode to run macros") - self.dialogs.warning_dialog(self, _("Important Warning!"), message) + self.dialogs.show_warning_dialog( _("Important Warning!"), + message, context=None) return # look thru the INI macros @@ -1362,7 +1364,8 @@ def request_macro_call(self, data): else: # didn't match a name - give a hint message = _("Macro {} not found ".format(data)) - self.dialogs.warning_dialog(self, _("Important Warning!"), message) + self.dialogs.show_warning_dialog( _("Important Warning!"), + message, context=None) # check if macros are in the INI file and add them to MDI Button List def _make_macro_button(self): @@ -1540,8 +1543,9 @@ def _make_lathe(self): message += _("this is not a lathe, as a lathe must have at least\n") message += _("an X and an Z axis\n") message += _("Wrong lathe configuration, we will leave here") - self.dialogs.warning_dialog(self, _("Very critical situation"), message, sound = False) - sys.exit() + self.dialogs.show_warning_dialog( _("Very critical situation"), + message, context='systemexit') + return else: if not len(self.axis_list) == 2 and not len(self.axis_list) < 6: self._arrange_jog_button_by_axis() @@ -1991,8 +1995,9 @@ def _init_tooleditor(self): if not tooltable: message = _("Did not find a toolfile file in [EMCIO] TOOL_TABLE") LOG.error(message) - self.dialogs.warning_dialog(self, _("Very critical situation"), message, sound = False) - sys.exit() + self.dialogs.show_warning_dialog( _("Very critical situation"), + message, context='systemexit') + return toolfile = os.path.join(CONFIGPATH, tooltable) self.widgets.tooledit1.set_filename(toolfile) # first we hide all the axis columns the unhide the ones we want @@ -2451,8 +2456,9 @@ def _init_offsetpage(self): if not parameterfile: message = _("Did not find a parameter file in [RS274NGC] PARAMETER_FILE") LOG.error(message) - self.dialogs.warning_dialog(self, _("Very critical situation"), message, sound = False) - sys.exit() + self.dialogs.show_warning_dialog( _("Very critical situation"), + message, context='systemexit') + return path = os.path.join(CONFIGPATH, parameterfile) self.widgets.offsetpage1.set_filename(path) @@ -4335,21 +4341,48 @@ def on_tbtn_setup_toggled(self, widget, data=None): # return code from system dialog def system_dialog_return(self,widget,result): - print(widget,result) + print('System ->',widget,result) # Lets see if the user has the right to enter settings if result == 1: self.widgets.ntb_main.set_current_page(1) self.widgets.ntb_setup.set_current_page(0) self.widgets.ntb_button.set_current_page(_BB_SETUP) - #widget.set_image(self.widgets.img_settings_on) + self.widgets.tbtn_setup.set_image(self.widgets.img_settings_on) elif result == 0: if self.widgets.rbt_hal_unlock.get_active(): message = _("Hal Pin is low, Access denied") else: message = _("wrong code entered, Access denied") - self.dialogs.warning_dialog(self, _("Just to warn you"), message) + self.dialogs.show_warning_dialog( _("Just to warn you"), message, context='sytemunlockfail') + # we will wait for response + + + + # return code from system dialog + def warning_dialog_return(self,widget,result,context): + print('Warning ->',widget,result,context) + if context is None: + return + + if context == 'systemunlockfail': self.widgets.tbtn_setup.set_active(False) - #widget.set_image(self.widgets.img_settings) + self.widgets.tbtn_setup.set_image(self.widgets.img_settings) + elif context == 'systemexit': + sys.exit() + + elif context == 'mantoolchange': + if result: + self.halcomp["toolchange-changed"] = True + else: + LOG.debug("toolchange abort {0} {1}".format(self.stat.tool_in_spindle, self.halcomp['toolchange-number'])) + self.command.abort() + self.halcomp['toolchange-number'] = self.stat.tool_in_spindle + self.halcomp['toolchange-change'] = False + self.halcomp['toolchange-changed'] = True + message = _("Tool Change has been aborted!\n") + message += _("The old tool will remain set!") + self.dialogs.show_warning_dialog( _("Just to warn you"), + message, context=None) # Show or hide the user tabs @@ -4470,8 +4503,8 @@ def on_btn_classicladder_clicked(self, widget, data=None): if hal.component_exists("classicladder_rt"): p = os.popen("classicladder &", "w") else: - self.dialogs.warning_dialog(self, _("INFO:"), - _("Classicladder real-time component not detected")) + self.dialogs.show_warning_dialog(_("INFO:"), + _("Classicladder real-time component not detected"), context='classicfail') # ========================================================= # spindle stuff @@ -4786,8 +4819,8 @@ def on_btn_show_calc_clicked(self, widget): integer=False) if value == "ERROR": LOG.debug("conversion error") - self.dialogs.warning_dialog(self, _("Conversion error !"), - ("Please enter only numerical values\nValues have not been applied")) + self.dialogs.show_warning_dialog(_("INFO:"), + _("Please enter only numerical values\nValues have not been applied"), context=None) elif value == "CANCEL": return else: @@ -4812,8 +4845,8 @@ def on_btn_show_calc_clicked(self, widget): integer=False) if value == "ERROR": LOG.debug("conversion error") - self.dialogs.warning_dialog(self, _("Conversion error !"), - ("Please enter only numerical values\nValues have not been applied")) + self.dialogs.show_warning_dialog(_("INFO:"), + _("Please enter only numerical values\nValues have not been applied"), context=None) elif value == "CANCEL": return else: @@ -4999,8 +5032,8 @@ def _on_btn_set_value_clicked(self, widget, data=None): return elif offset == "ERROR": LOG.debug("Conversion error in btn_set_value") - self.dialogs.warning_dialog(self, _("Conversion error in btn_set_value!"), - _("Please enter only numerical values. Values have not been applied")) + self.dialogs.show_warning_dialog(_("Conversion error in btn_set_value!"), + _("Please enter only numerical values\nValues have not been applied"), context=None) else: self.command.mode(linuxcnc.MODE_MDI) self.command.wait_complete() @@ -5021,7 +5054,8 @@ def _on_btn_set_selected_clicked(self, widget, data=None): system, name = self.widgets.offsetpage1.get_selected() if system not in ["G54", "G55", "G56", "G57", "G58", "G59", "G59.1", "G59.2", "G59.3"]: message = _("You did not select a system to be changed to, so nothing will be changed") - self.dialogs.warning_dialog(self, _("Important Warning!"), message) + self.dialogs.show_warning_dialog(_("Important Warning!"), + message, context=None) return if system == self.system_list[self.stat.g5x_index]: return @@ -5084,8 +5118,8 @@ def on_btn_block_height_clicked(self, widget, data=None): else: self.prefs.putpref("blockheight", 0.0, float) self.prefs.putpref("probeheight", 0.0, float) - self.dialogs.warning_dialog(self, _("Conversion error in btn_block_height!"), - _("Please enter only numerical values\nValues have not been applied")) + self.dialogs.show_warning_dialog(_("Conversion error in btn_block_height!"), + _("Please enter only numerical values\nValues have not been applied"), context=None) # set coordinate system to new origin origin = self.get_ini_info.get_axis_2_min_limit() + blockheight @@ -5113,7 +5147,9 @@ def _set_icon_theme(self, name): if name is None or name == "none": # Switching to none required a restart (skip entire icon theme stuff) message = "Change to no icon theme requires a restart to take effect." - self.dialogs.warning_dialog(self, _("Just to warn you"), message) + self.dialogs.show_warning_dialog( _("Just to warn you"), + message, context=None) + else: self.icon_theme.set_custom_theme(name) self.notification.set_property('icon_theme_name', name) @@ -5657,28 +5693,20 @@ def on_tool_change(self, widget): except: message = _("Tool\n\n# {0:d}\n\n not in the tool table!").format(toolnumber) - result = self.dialogs.warning_dialog(self, message, title=_("Manual Tool change"),\ - confirm_pin = 'toolchange-confirm', active_pin = 'toolchange-change') - if result: - self.halcomp["toolchange-changed"] = True - else: - LOG.debug("toolchange abort {0} {1}".format(self.stat.tool_in_spindle, self.halcomp['toolchange-number'])) - self.command.abort() - self.halcomp['toolchange-number'] = self.stat.tool_in_spindle - self.halcomp['toolchange-change'] = False - self.halcomp['toolchange-changed'] = True - message = _("Tool Change has been aborted!\n") - message += _("The old tool will remain set!") - self.dialogs.warning_dialog(self, message) + self.dialogs.show_warning_dialog( _("Manual Tool change"), + message, context='mantoolchange', + confirm_pin = 'toolchange-confirm', + active_pin = 'toolchange-change') else: self.halcomp['toolchange-changed'] = False def on_btn_delete_tool_clicked(self, widget, data=None): selected_tool = self.widgets.tooledit1.get_selected_row() if self.stat.tool_in_spindle == selected_tool: - message = _("You are trying to delete the tool mounted in the spindle.\n" - "This is not allowed, please change tool prior to delete it.") - self.dialogs.warning_dialog(self, _("Warning Tool can not be deleted!"), message) + message = _("You are trying to delete the tool mounted in the spindle\n") + message += _("This is not allowed, please change tool prior to delete it") + self.dialogs.show_warning_dialog( _("Warning Tool can not be deleted!"), + message, context=None) return self.widgets.tooledit1.delete_selected_row(widget) self.widgets.tooledit1.edited = True @@ -5706,13 +5734,15 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None): if not tool: message = _("No tool selected in the tool table. ") message += _("Please select only one tool in the table!") - self.dialogs.warning_dialog(self, _("Warning Tool Touch off not possible!"), message) + self.dialogs.show_warning_dialog( _("Warning Tool Touch off not possible!"), + message, context=None) return if tool != self.stat.tool_in_spindle: message = _("You can not touch off a tool, which is not mounted in the spindle! ") message += _("Your selection has been reset to the tool in spindle.") - self.dialogs.warning_dialog(self, _("Warning Tool Touch off not possible!"), message) + self.dialogs.show_warning_dialog( _("Warning Tool Touch off not possible!"), + message, context=None) self.widgets.tooledit1.reload(self) self.widgets.tooledit1.set_selected_tool(self.stat.tool_in_spindle) return @@ -5720,7 +5750,8 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None): if "G41" in self.active_gcodes or "G42" in self.active_gcodes: message = _("Tool touch off is not possible with cutter radius compensation switched on!\n") message += _("Please emit an G40 before tool touch off.") - self.dialogs.warning_dialog(self, _("Warning Tool Touch off not possible!"), message) + self.dialogs.show_warning_dialog( _("Warning Tool Touch off not possible!"), + message, context=None) return if widget == self.widgets.btn_tool_touchoff_x: @@ -5728,8 +5759,8 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None): elif widget == self.widgets.btn_tool_touchoff_z: axis = "z" else: - self.dialogs.warning_dialog(self, _("Real big error!"), - _("You managed to come to a place that is not possible in on_btn_tool_touchoff")) + self.dialogs.show_warning_dialog(_("Real big error!"), + _("You managed to come to a place that is not possible in on_btn_tool_touchoff"), context=None) return value = self.dialogs.entry_dialog(self, data=None, @@ -5739,7 +5770,8 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None): if value == "ERROR": message = _("Conversion error because of wrong entry for touch off axis {0}").format(axis.upper()) - self.dialogs.warning_dialog(self, _("Conversion error !"), message) + self.dialogs.show_warning_dialog( _("Conversion error !"), + message, context=None) return elif value == "CANCEL": return @@ -5767,13 +5799,15 @@ def on_btn_select_tool_by_no_clicked(self, widget, data=None): if value == "ERROR": message = _("Conversion error because of wrong entry for tool number.\n") message += _("Enter only integer numbers!") - self.dialogs.warning_dialog(self, _("Conversion error !"), message) + self.dialogs.show_warning_dialog( _("Conversion error !"), + message, context=None) return elif value == "CANCEL": return elif int(value) == self.stat.tool_in_spindle: message = _("Selected tool is already in spindle, no change needed.") - self.dialogs.warning_dialog(self, _("Important Warning!"), message) + self.dialogs.show_warning_dialog( _("Important Warning!"), + message, context=None) return else: self.tool_change = True @@ -5794,11 +5828,13 @@ def on_btn_selected_tool_clicked(self, widget, data=None): tool = self.widgets.tooledit1.get_selected_row() if tool == None: message = _("you selected no or more than one tool, the tool selection must be unique") - self.dialogs.warning_dialog(self, _("Important Warning!"), message) + self.dialogs.show_warning_dialog( _("Important Warning!"), + message, context=None) return if tool == self.stat.tool_in_spindle: message = _("Selected tool is already in spindle, no change needed.") - self.dialogs.warning_dialog(self, _("Important Warning!"), message) + self.dialogs.show_warning_dialog( _("Important Warning!"), + message, context=None) return if tool or tool == 0: self.tool_change = True @@ -5816,7 +5852,9 @@ def on_btn_selected_tool_clicked(self, widget, data=None): self.command.mdi(command) else: message = _("Could not understand the entered tool number. Will not change anything!") - self.dialogs.warning_dialog(self, _("Important Warning!"), message) + self.dialogs.show_warning_dialog( _("Important Warning!"), + message, context=None) + # ========================================================= # gremlin relevant calls From 01141851738e2d3cd4bdcca8ed137e40cfefb8a7 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sun, 1 Feb 2026 00:00:35 -0800 Subject: [PATCH 43/81] gmoccapy -set jog speed control widget to use messages Sends out a jog rate gobject message --- src/emc/usr_intf/gmoccapy/gmoccapy.glade | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.glade b/src/emc/usr_intf/gmoccapy/gmoccapy.glade index 049c965ed4f..efe301beca8 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.glade +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.glade @@ -1,5 +1,5 @@ - + @@ -1410,6 +1410,7 @@ uncomment selection
False rgb(255,129,22) 10500 + 0 mm/min 1500 @@ -1456,6 +1457,7 @@ uncomment selection rgb(255,129,22) 3600 %.d + 0 °/min 360 From b9a951afba02b8d46b9e610b428723f179a7a9c2 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sun, 1 Feb 2026 00:25:59 -0800 Subject: [PATCH 44/81] gmoccapy -use halui message to cancel notifications --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index 84771469140..2fb03eec134 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -439,6 +439,7 @@ def __init__(self, argv): self.GSTAT.connect('cycle-pause-request', lambda w, state: self.request_pause(state)) self.GSTAT.connect('ok-request', lambda w, state: self.dialogs.dialog_ext_control(Gtk.ResponseType.ACCEPT)) self.GSTAT.connect('cancel-request', lambda w, state: self.dialogs.dialog_ext_control(Gtk.ResponseType.CANCEL)) + self.GSTAT.connect('cancel-request', lambda w, state: self._del_notification()) # get if run from line should be used self.run_from_line = self.prefs.getpref("run_from_line", "no_run", str) @@ -6348,6 +6349,9 @@ def _on_message_deleted(self, widget, messages, checkbox_checked): def _del_message_changed(self, pin): if pin.get(): + self._del_notification() + + def _del_notification(self): if self.halcomp["error"] == True: number = [] messages = self.notification.messages From b7708e542e91328012ffe028fc65bbac30528ba9 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 7 Feb 2026 14:45:11 -0800 Subject: [PATCH 45/81] gmoccapy -can submit the current MDI command from HALUI Pressing HALUI's cycle start when in MDI mode will call the current MDI command in gmoccapy's MDI history widget. --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index 2fb03eec134..3c5bd167d5b 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -1323,12 +1323,21 @@ def _make_joints_button(self): self.joints_button_dic[name] = btn + # halui/external cycle start request def request_start(self,data): - print('start') - self.widgets.btn_run.emit('clicked') + print('start request') + if self.stat.task_mode == linuxcnc.MODE_MDI: + print('Submit MDI') + self.widgets.hal_mdihistory.submit() + elif self.stat.task_mode == linuxcnc.MODE_MANUAL: + self._show_error((13, _("Can't start cycles or submit MDI commands in manual Mode"))) + else: + print('Cycle Start') + self.widgets.btn_run.emit('clicked') + # halui/external pause request def request_pause(self,data): - print('pause') + print('pause request') self.widgets.tbtn_pause.emit('clicked') # call INI macro (from hal_glib message) From 33b20b10a637a08801933f79c73a0faeee5fe3f0 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 3 Apr 2026 18:34:43 -0700 Subject: [PATCH 46/81] gmoccapy -update external pause/start behavior fix pausing in MDI don't toggle pause (only pause not unpause) cycle start will unpause if paused --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index 3c5bd167d5b..05a42ba45d4 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -1326,7 +1326,9 @@ def _make_joints_button(self): # halui/external cycle start request def request_start(self,data): print('start request') - if self.stat.task_mode == linuxcnc.MODE_MDI: + if self.GSTAT.is_auto_paused(): + self.command.auto(linuxcnc.AUTO_RESUME) + elif self.stat.task_mode == linuxcnc.MODE_MDI: print('Submit MDI') self.widgets.hal_mdihistory.submit() elif self.stat.task_mode == linuxcnc.MODE_MANUAL: @@ -1338,7 +1340,15 @@ def request_start(self,data): # halui/external pause request def request_pause(self,data): print('pause request') - self.widgets.tbtn_pause.emit('clicked') + + # don't toggle + if self.GSTAT.is_auto_paused(): + return + + if self.stat.task_mode == linuxcnc.MODE_AUTO: + self.widgets.tbtn_pause.emit('clicked') + else: + self.command.auto(linuxcnc.AUTO_PAUSE) # call INI macro (from hal_glib message) def request_macro_call(self, data): From 1136f17f782538d7db4589d3a59cd5a36ff94693 Mon Sep 17 00:00:00 2001 From: CMorley Date: Thu, 9 Apr 2026 17:00:26 -0700 Subject: [PATCH 47/81] gmoccapy -entry dialog respond to halui, add non blocking wait code wait but don't block code added. entry dialogs now can use halui yes and no responses --- src/emc/usr_intf/gmoccapy/dialogs.py | 172 +++++++++++++++-------- src/emc/usr_intf/gmoccapy/gmoccapy.py | 192 +++++++++++--------------- 2 files changed, 195 insertions(+), 169 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/dialogs.py b/src/emc/usr_intf/gmoccapy/dialogs.py index a28408d9abe..dc9418f37ad 100644 --- a/src/emc/usr_intf/gmoccapy/dialogs.py +++ b/src/emc/usr_intf/gmoccapy/dialogs.py @@ -36,20 +36,27 @@ class Dialogs(GObject.GObject): __gsignals__ = { 'play_sound': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)), - 'system-dialog-result': (GObject.SignalFlags.RUN_FIRST , GObject.TYPE_NONE, (GObject.TYPE_INT,)), - 'warning-dialog-result': (GObject.SignalFlags.RUN_FIRST , GObject.TYPE_NONE, (GObject.TYPE_INT, GObject.TYPE_STRING)) } def __init__(self, caller): GObject.GObject.__init__(self) self.sys_dialog = self.system_dialog(caller) self.warn_dialog = self.warning_dialog(caller) + self.ent_dialog = self.entry_dialog(caller) + self.yn_dialog = self.yesno_dialog(caller) + # sent from Gstat messages + # first one found visibly gets the answer + # maybe we should check focus? def dialog_ext_control(self, answer): if self.sys_dialog.get_visible(): self.sys_dialog.response(answer) elif self.warn_dialog.get_visible(): self.warn_dialog.response(answer) + elif self.ent_dialog.get_visible(): + self.ent_dialog.response(answer) + elif self.yn_dialog.get_visible(): + self.yn_dialog.response(answer) # This dialog is for unlocking the system tab # The unlock code number is defined at the top of the page @@ -77,66 +84,94 @@ def system_dialog(self, caller): return dialog def show_system_dialog(self): - self.sys_dialog._calc.set_value("") - self.sys_dialog.show_all() + dialog = self.sys_dialog + dialog._calc.set_value("") + dialog.show_all() self.emit("play_sound", "alert") - def on_system_response(self, dialog, result): + # wait but don't block event loop + dialog.RESPONSE = None + while dialog.RESPONSE is None: + while Gtk.events_pending(): + Gtk.main_iteration() + + dialog.hide() + code = dialog._calc.get_value() - print('Code:',code) rtn = -1 - if result == Gtk.ResponseType.ACCEPT: + if dialog.RESPONSE == Gtk.ResponseType.ACCEPT: if code == int(dialog._caller.unlock_code): - print('Yes') rtn = 1 else: - print('No') rtn = 0 - else: - print('Cancelled') - self.emit('system-dialog-result',rtn) - dialog.hide() - def entry_dialog(self, caller, data = None, header = _("Enter value") , label = _("Enter the value to set"), integer = False): - dialog = Gtk.Dialog(header, + return rtn + + def on_system_response(self, dialog, rtn): + dialog.RESPONSE = rtn + + def entry_dialog(self, caller): + dialog = Gtk.Dialog('', caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT) - label = Gtk.Label(label) - label.modify_font(Pango.FontDescription("sans 20")) - label.set_margin_top(15) - calc = gladevcp.Calculator() + dialog.label = Gtk.Label('') + dialog.label.modify_font(Pango.FontDescription("sans 20")) + dialog.label.set_margin_top(15) + dialog.calc = gladevcp.Calculator() content_area = dialog.get_content_area() - content_area.pack_start(child=label, expand=False, fill=False, padding=0) - content_area.add(calc) - if data != None: - calc.set_value(data) - else: - calc.set_value("") - calc.set_property("font", "sans 20") - calc.set_editable(True) - calc.entry.connect("activate", lambda w : dialog.emit("response", Gtk.ResponseType.ACCEPT)) + content_area.pack_start(child=dialog.label, expand=False, fill=False, padding=0) + content_area.add(dialog.calc) + dialog.calc.set_property("font", "sans 20") + dialog.calc.set_editable(True) + dialog.calc.entry.connect("activate", lambda w : self.on_entry_response(dialog, Gtk.ResponseType.ACCEPT)) dialog.parse_geometry("460x400") dialog.set_decorated(True) + dialog.connect("response", self.on_entry_response) + return dialog + + def show_entry_dialog(self, data = None, header = _("Enter value") , + label = _("Enter the value to set"), integer = False): + + dialog = self.ent_dialog + if data != None: + dialog.calc.set_value(data) + else: + dialog.calc.set_value("") if integer: # The user is only allowed to enter integer values, we hide some button - calc.integer_entry_only(True) - calc.num_pad_only(True) + dialog.calc.integer_entry_only(True) + dialog.calc.num_pad_only(True) + dialog.label.set_text(label) + dialog.set_title(header) dialog.show_all() - response = dialog.run() - value = calc.get_value() - dialog.destroy() - if response == Gtk.ResponseType.ACCEPT: + + # wait but don't block event loop + dialog.RESPONSE = None + while dialog.RESPONSE is None: + while Gtk.events_pending(): + Gtk.main_iteration() + + dialog.hide() + + value = dialog.calc.get_value() + if dialog.RESPONSE == Gtk.ResponseType.ACCEPT: if value != None: - if integer: - return int(value) + if dialog.calc.integer_only: + qv = int(value) else: - return float(value) + qv = float(value) else: - return "ERROR" - return "CANCEL" + qv = "ERROR" + else: + qv = "CANCEL" + return qv + + def on_entry_response(self, dialog, rtn): + dialog.RESPONSE = rtn # display warning dialog def warning_dialog(self, caller, message = '', secondary = None, title = _("Operator Message"),\ sound = True, confirm_pin = 'warning-confirm', active_pin = None): + dialog = Gtk.MessageDialog(caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.NONE, message) @@ -167,32 +202,34 @@ def periodic(): dialog.connect("response", self.on_warning_response) return dialog - def show_warning_dialog(self, title, message, context=None, sound=True,\ + def show_warning_dialog(self, title, message, sound=True, confirm_pin = 'warning-confirm', active_pin = None): - print(message,context) - self.warn_dialog.context.append(context) - self.warn_dialog.set_title(title) - self.warn_dialog.format_secondary_text(message) - self.warn_dialog.set_markup(message) - self.warn_dialog.show_all() + dialog = self.warn_dialog + dialog.set_title(title) + dialog.format_secondary_text(message) + dialog.set_markup(message) + dialog.show_all() if sound: self.emit("play_sound", "alert") - print(self.warn_dialog.context) - def on_warning_response(self, dialog, rtn): - context = dialog.context.pop() - print(context) - self.emit('warning-dialog-result', rtn, context) + # wait but don't block event loop + dialog.RESPONSE = None + while dialog.RESPONSE is None: + while Gtk.events_pending(): + Gtk.main_iteration() + dialog.hide() - def yesno_dialog(self, caller, message, title = _("Operator Message")): + return dialog.RESPONSE + + def on_warning_response(self, dialog, rtn): + dialog.RESPONSE = rtn + + def yesno_dialog(self, caller): dialog = Gtk.MessageDialog(caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE) - if title: - dialog.set_title(str(title)) - dialog.set_markup(message) yes_button = Gtk.Button.new_with_mnemonic(_("_Yes")) no_button = Gtk.Button.new_with_mnemonic(_("_No")) yes_button.set_size_request(-1, 56) @@ -206,11 +243,30 @@ def yesno_dialog(self, caller, message, title = _("Operator Message")): box.set_layout(Gtk.ButtonBoxStyle.CENTER) dialog.action_area.add(box) dialog.set_border_width(5) + dialog.connect("response", self.on_yn_response) + return dialog + + def show_yesno_dialog(self, caller, message, title = _("Operator Message")): + dialog = self.yn_dialog + dialog.set_markup(message) + if title: + dialog.set_title(str(title)) dialog.show_all() self.emit("play_sound", "alert") - response = dialog.run() - dialog.destroy() - return response == Gtk.ResponseType.YES + + # wait but don't block event loop + dialog.RESPONSE = None + while dialog.RESPONSE is None: + while Gtk.events_pending(): + Gtk.main_iteration() + + rtn = dialog.RESPONSE + dialog.hide() + return bool(rtn in(Gtk.ResponseType.YES, Gtk.ResponseType.ACCEPT)) + + # update internal variable so dialog will respond + def on_yn_response(self,dialog, rtn): + dialog.RESPONSE = rtn def show_user_message(self, caller, message, title = _("Operator Message")): dialog = Gtk.MessageDialog(caller.widgets.window1, diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index 05a42ba45d4..34026f8e210 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -352,8 +352,6 @@ def __init__(self, argv): self.dialogs = dialogs.Dialogs(caller = self) self.dialogs.connect("play_sound", self._on_play_sound) - self.dialogs.connect('system-dialog-result', self.system_dialog_return) - self.dialogs.connect('warning-dialog-result', self.warning_dialog_return) # this are settings to be done before window show self._init_preferences() @@ -1354,7 +1352,7 @@ def request_pause(self,data): def request_macro_call(self, data): # if MDI command change to MDI and run cmd = self.INFO.get_ini_mdi_command(data) - print('MDI command:',data,cmd) + print(f'MDI command:{cmd} data:{data}') if not cmd is None: LOG.debug("INI MDI COMMAND #: {} = {}".format(data, cmd)) self.ACTION.CALL_INI_MDI(data,mode_return = True) @@ -1365,7 +1363,7 @@ def request_macro_call(self, data): if not self.GSTAT.is_mdi_mode(): message = _("You must be in MDI mode to run macros") self.dialogs.show_warning_dialog( _("Important Warning!"), - message, context=None) + message) return # look thru the INI macros @@ -1385,7 +1383,7 @@ def request_macro_call(self, data): # didn't match a name - give a hint message = _("Macro {} not found ".format(data)) self.dialogs.show_warning_dialog( _("Important Warning!"), - message, context=None) + message) # check if macros are in the INI file and add them to MDI Button List def _make_macro_button(self): @@ -1564,8 +1562,8 @@ def _make_lathe(self): message += _("an X and an Z axis\n") message += _("Wrong lathe configuration, we will leave here") self.dialogs.show_warning_dialog( _("Very critical situation"), - message, context='systemexit') - return + message) + sys.exit() else: if not len(self.axis_list) == 2 and not len(self.axis_list) < 6: self._arrange_jog_button_by_axis() @@ -2016,8 +2014,8 @@ def _init_tooleditor(self): message = _("Did not find a toolfile file in [EMCIO] TOOL_TABLE") LOG.error(message) self.dialogs.show_warning_dialog( _("Very critical situation"), - message, context='systemexit') - return + message) + sys.exit() toolfile = os.path.join(CONFIGPATH, tooltable) self.widgets.tooledit1.set_filename(toolfile) # first we hide all the axis columns the unhide the ones we want @@ -2173,15 +2171,15 @@ def on_tool_col_edit_started(self, widget, filtered_path, new_text, col): toolpage = self.widgets.tooledit1 toolview = toolpage.view1 model, treeiter = toolview.get_selection().get_selected() - value = self.dialogs.entry_dialog(self, - data=model[treeiter][col], + value = self.dialogs.show_entry_dialog(data=model[treeiter][col], header=_("Enter value"), label=_("Tool") + f" {model[treeiter][1]}, {captations[col]}:", integer=col in [1,2,15]) + if value == "ERROR": LOG.debug("conversion error") - self.dialogs.warning_dialog(self, _("Conversion error !"), - ("Please enter only numerical values\nValues have not been applied")) + self.dialogs.warning_dialog(_("Conversion error !"), + ("Please enter only numerical values\nValues have not been applied")) elif value == "CANCEL": pass else: @@ -2477,8 +2475,8 @@ def _init_offsetpage(self): message = _("Did not find a parameter file in [RS274NGC] PARAMETER_FILE") LOG.error(message) self.dialogs.show_warning_dialog( _("Very critical situation"), - message, context='systemexit') - return + message) + sys.exit() path = os.path.join(CONFIGPATH, parameterfile) self.widgets.offsetpage1.set_filename(path) @@ -2576,12 +2574,13 @@ def on_offset_col_edit_started(self, widget, filtered_path, new_text, col): path = offsetpage.modelfilter.get_path(treeiter) (store_path,) = offsetpage.modelfilter.convert_path_to_child_path(path) row = store_path - if self.widgets.offsetpage1.btn_edit_offsets.get_active(): - offset = self.dialogs.entry_dialog(self, - data=offsetpage.store[row][col], + if self.widgets.offsetpage1.btn_edit_offsets.get_active() or \ + self.touch_button_dic["edit_offsets"].get_active(): + value = self.dialogs.show_entry_dialog(data=offsetpage.store[row][col], header=_("Enter value for offset"), label=f"{offsetpage.store[row][0]} {AXISLIST[col]}-" + _("offset:"), integer=False) + if offset == "ERROR": LOG.debug("conversion error") self.dialogs.warning_dialog(self, _("Conversion error !"), @@ -2733,7 +2732,7 @@ def _show_user_message(self, pin, message): self.halcomp["messages." + message[2] + "-waiting"] = 1 self.halcomp["messages." + message[2] + "-response"] = 0 title = "Pin " + message[2] + " message" - response = self.dialogs.yesno_dialog(self, message[0], title) + response = self.dialogs.show_yesno_dialog(self, message[0], title) self.halcomp["messages." + message[2] + "-waiting"] = 0 self.halcomp["messages." + message[2] + "-response"] = response else: @@ -3241,13 +3240,14 @@ def on_hal_status_mode_mdi(self, widget): self.last_key_event = None, 0 def on_mdi_calculation_start(self, *args): - position = self.widgets.hal_mdihistory.entry.get_position() - print("position: ", position) - value = self.dialogs.entry_dialog(self, + value = self.dialogs.show_entry_dialog( data=self.widgets.hal_mdihistory.entry.get_text(), header=_("Enter value"), label=_("Calculate value to insert"), integer=False) + + position = self.widgets.hal_mdihistory.entry.get_position() + if value == "ERROR": LOG.debug("conversion error") self.dialogs.warning_dialog(self, _("Conversion error !"), @@ -3453,8 +3453,9 @@ def _on_btn_macro_pressed( self, widget = None, data = None ): command = str( "O<" + o_codes[0] + "> call" ) for code in o_codes[1:]: - parameter = self.dialogs.entry_dialog(self, data=None, header=_("Enter value:"), + parameter = self.dialogs.show_entry_dialog(data=None, header=_("Enter value:"), label=f"{code}:", integer=False) + if parameter == "ERROR": LOG.debug("conversion error") self.dialogs.warning_dialog(self, _("Conversion error !"), @@ -4307,9 +4308,12 @@ def on_tbtn_setup_toggled(self, widget, data=None): code = True # else we ask for the code using the system.dialog if self.widgets.rbt_use_unlock.get_active(): - self.dialogs.show_system_dialog() - # we will wait for response - return + code = self.dialogs.show_system_dialog() + # cancelled? + if code == -1: + self.widgets.tbtn_setup.set_active(False) + widget.set_image(self.widgets.img_settings) + return # Lets see if the user has the right to enter settings if code: self.widgets.ntb_main.set_current_page(1) @@ -4321,7 +4325,7 @@ def on_tbtn_setup_toggled(self, widget, data=None): message = _("Hal Pin is low, Access denied") else: message = _("wrong code entered, Access denied") - self.dialogs.warning_dialog(self, _("Just to warn you"), message) + self.dialogs.show_warning_dialog(_("Just to warn you"), message) self.widgets.tbtn_setup.set_active(False) widget.set_image(self.widgets.img_settings) else: @@ -4359,52 +4363,6 @@ def on_tbtn_setup_toggled(self, widget, data=None): widget.set_image(self.widgets.img_settings) - # return code from system dialog - def system_dialog_return(self,widget,result): - print('System ->',widget,result) - # Lets see if the user has the right to enter settings - if result == 1: - self.widgets.ntb_main.set_current_page(1) - self.widgets.ntb_setup.set_current_page(0) - self.widgets.ntb_button.set_current_page(_BB_SETUP) - self.widgets.tbtn_setup.set_image(self.widgets.img_settings_on) - elif result == 0: - if self.widgets.rbt_hal_unlock.get_active(): - message = _("Hal Pin is low, Access denied") - else: - message = _("wrong code entered, Access denied") - self.dialogs.show_warning_dialog( _("Just to warn you"), message, context='sytemunlockfail') - # we will wait for response - - - - # return code from system dialog - def warning_dialog_return(self,widget,result,context): - print('Warning ->',widget,result,context) - if context is None: - return - - if context == 'systemunlockfail': - self.widgets.tbtn_setup.set_active(False) - self.widgets.tbtn_setup.set_image(self.widgets.img_settings) - elif context == 'systemexit': - sys.exit() - - elif context == 'mantoolchange': - if result: - self.halcomp["toolchange-changed"] = True - else: - LOG.debug("toolchange abort {0} {1}".format(self.stat.tool_in_spindle, self.halcomp['toolchange-number'])) - self.command.abort() - self.halcomp['toolchange-number'] = self.stat.tool_in_spindle - self.halcomp['toolchange-change'] = False - self.halcomp['toolchange-changed'] = True - message = _("Tool Change has been aborted!\n") - message += _("The old tool will remain set!") - self.dialogs.show_warning_dialog( _("Just to warn you"), - message, context=None) - - # Show or hide the user tabs def on_tbtn_user_tabs_toggled(self, widget, data=None): if widget.get_active(): @@ -4524,7 +4482,7 @@ def on_btn_classicladder_clicked(self, widget, data=None): p = os.popen("classicladder &", "w") else: self.dialogs.show_warning_dialog(_("INFO:"), - _("Classicladder real-time component not detected"), context='classicfail') + _("Classicladder real-time component not detected")) # ========================================================= # spindle stuff @@ -4818,7 +4776,7 @@ def on_btn_delete_clicked(self, widget, data=None): message = _("Do you really want to delete the MDI history?\n") message += _("This will not delete the MDI History file, but will\n" "delete the listbox entries for this session.") - result = self.dialogs.yesno_dialog(self, message, _("Attention!!")) + result = self.dialogs.show_yesno_dialog(self, message, _("Attention!!")) if result: self.widgets.hal_mdihistory.model.clear() @@ -4832,15 +4790,14 @@ def on_btn_show_calc_clicked(self, widget): text = mdi_entry.get_text() data = text[bounds[0]:bounds[1]] has_selection = True - value = self.dialogs.entry_dialog(self, - data=data, + value = self.dialogs.show_entry_dialog(data=data, header=_("Enter value"), label=_("Calculate value to insert"), integer=False) if value == "ERROR": LOG.debug("conversion error") - self.dialogs.show_warning_dialog(_("INFO:"), - _("Please enter only numerical values\nValues have not been applied"), context=None) + self.dialogs.show_warning_dialog(_("Conversion error !"), + ("Please enter only numerical values\nValues have not been applied")) elif value == "CANCEL": return else: @@ -4858,15 +4815,14 @@ def on_btn_show_calc_clicked(self, widget): bounds = buffer.get_selection_bounds() data = buffer.get_text(bounds[0],bounds[1],False) has_selection = True - value = self.dialogs.entry_dialog(self, - data=data, + value = self.dialogs.show_entry_dialog(data=data, header=_("Enter value"), label=_("Calculate value to insert"), integer=False) if value == "ERROR": LOG.debug("conversion error") - self.dialogs.show_warning_dialog(_("INFO:"), - _("Please enter only numerical values\nValues have not been applied"), context=None) + self.dialogs.show_warning_dialog(_("Conversion error !"), + ("Please enter only numerical values\nValues have not been applied")) elif value == "CANCEL": return else: @@ -4925,7 +4881,7 @@ def on_btn_back_clicked(self, widget, data=None): if self.widgets.ntb_button.get_current_page() == _BB_EDIT: # edit mode, go back to auto_buttons if self.file_changed: message = _("Exit and discard changes?") - result = self.dialogs.yesno_dialog(self, message, _("Attention!")) + result = self.dialogs.show_yesno_dialog(self, message, _("Attention!")) if not result: # user says no, he want to save return self.widgets.ntb_button.set_current_page(_BB_AUTO) @@ -4938,7 +4894,7 @@ def on_btn_back_clicked(self, widget, data=None): else: # else we go to main button on manual if self.widgets.tooledit1.edited: message = _("Discard unsaved changes and exit?") - result = self.dialogs.yesno_dialog(self, message, _("Attention!")) + result = self.dialogs.show_yesno_dialog(self, message, _("Attention!")) if not result: # user says no, he want to save return # check if offset values for current tool have been changed @@ -4960,7 +4916,7 @@ def on_btn_back_clicked(self, widget, data=None): "Do you want to activate tool compensation (G43)\n" \ "using the currently active tool offset?") if message: - result = self.dialogs.yesno_dialog(self, message, _("Attention!")) + result = self.dialogs.show_yesno_dialog(self, message, _("Attention!")) if result: # user says YES self.command.mode(linuxcnc.MODE_MDI) self.command.wait_complete() @@ -5038,22 +4994,22 @@ def _on_btn_set_value_clicked(self, widget, data=None): if self.lathe_mode and axis =="x": if self.diameter_mode: preset = self.prefs.getpref("diameter offset_axis_{0}".format(axis), 0, float) - offset = self.dialogs.entry_dialog(self, data=preset, header=_("Enter value for diameter"), + offset = self.dialogs.show_entry_dialog(data=preset, header=_("Enter value for diameter"), label=_("Set diameter to:"), integer=False) else: preset = self.prefs.getpref("radius offset_axis_{0}".format(axis), 0, float) - offset = self.dialogs.entry_dialog(self, data=preset, header=_("Enter value for radius"), + offset = self.dialogs.show_entry_dialog(data=preset, header=_("Enter value for radius"), label=_("Set radius to:"), integer=False) else: preset = self.prefs.getpref("offset_axis_{0}".format(axis), 0, float) - offset = self.dialogs.entry_dialog(self, data=preset, header=_("Enter value for axis {0}").format(axis.upper()), + offset = self.dialogs.show_entry_dialog(data=preset, header=_("Enter value for axis {0}").format(axis.upper()), label=_("Set axis {0} to:").format(axis.upper()), integer=False) if offset == "CANCEL": return elif offset == "ERROR": LOG.debug("Conversion error in btn_set_value") self.dialogs.show_warning_dialog(_("Conversion error in btn_set_value!"), - _("Please enter only numerical values\nValues have not been applied"), context=None) + _("Please enter only numerical values\nValues have not been applied")) else: self.command.mode(linuxcnc.MODE_MDI) self.command.wait_complete() @@ -5075,7 +5031,7 @@ def _on_btn_set_selected_clicked(self, widget, data=None): if system not in ["G54", "G55", "G56", "G57", "G58", "G59", "G59.1", "G59.2", "G59.3"]: message = _("You did not select a system to be changed to, so nothing will be changed") self.dialogs.show_warning_dialog(_("Important Warning!"), - message, context=None) + message) return if system == self.system_list[self.stat.g5x_index]: return @@ -5125,7 +5081,7 @@ def on_chk_reload_tool_toggled(self, widget, data=None): def on_btn_block_height_clicked(self, widget, data=None): probeheight = self.widgets.spbtn_probe_height.get_value() preset = self.prefs.getpref("blockheight", 0.0, float) - blockheight = self.dialogs.entry_dialog(self, data=preset, header=_("Enter the block height"), + blockheight = self.dialogs.show_entry_dialog(data=preset, header=_("Enter the block height"), label=_("Block height measured from base table"), integer=False) if blockheight == "CANCEL" or blockheight == "ERROR": @@ -5139,7 +5095,7 @@ def on_btn_block_height_clicked(self, widget, data=None): self.prefs.putpref("blockheight", 0.0, float) self.prefs.putpref("probeheight", 0.0, float) self.dialogs.show_warning_dialog(_("Conversion error in btn_block_height!"), - _("Please enter only numerical values\nValues have not been applied"), context=None) + _("Please enter only numerical values\nValues have not been applied")) # set coordinate system to new origin origin = self.get_ini_info.get_axis_2_min_limit() + blockheight @@ -5168,7 +5124,7 @@ def _set_icon_theme(self, name): # Switching to none required a restart (skip entire icon theme stuff) message = "Change to no icon theme requires a restart to take effect." self.dialogs.show_warning_dialog( _("Just to warn you"), - message, context=None) + message) else: self.icon_theme.set_custom_theme(name) @@ -5713,10 +5669,24 @@ def on_tool_change(self, widget): except: message = _("Tool\n\n# {0:d}\n\n not in the tool table!").format(toolnumber) - self.dialogs.show_warning_dialog( _("Manual Tool change"), + result = self.dialogs.show_warning_dialog( _("Manual Tool change"), message, context='mantoolchange', confirm_pin = 'toolchange-confirm', active_pin = 'toolchange-change') + + if result: + self.halcomp["toolchange-changed"] = True + else: + LOG.debug("toolchange abort {0} {1}".format(self.stat.tool_in_spindle, self.halcomp['toolchange-number'])) + self.command.abort() + self.halcomp['toolchange-number'] = self.stat.tool_in_spindle + self.halcomp['toolchange-change'] = False + self.halcomp['toolchange-changed'] = True + message = _("Tool Change has been aborted!\n") + message += _("The old tool will remain set!") + self.dialogs.show_warning_dialog( _("Just to warn you"), + message) + else: self.halcomp['toolchange-changed'] = False @@ -5726,7 +5696,7 @@ def on_btn_delete_tool_clicked(self, widget, data=None): message = _("You are trying to delete the tool mounted in the spindle\n") message += _("This is not allowed, please change tool prior to delete it") self.dialogs.show_warning_dialog( _("Warning Tool can not be deleted!"), - message, context=None) + message) return self.widgets.tooledit1.delete_selected_row(widget) self.widgets.tooledit1.edited = True @@ -5738,7 +5708,7 @@ def on_btn_add_tool_clicked(self, widget, data=None): def on_btn_reload_tooltable_clicked(self, widget, data=None): if self.widgets.tooledit1.edited: message = _("Discard unsaved changes and reload the table?") - result = self.dialogs.yesno_dialog(self, message, _("Attention!")) + result = self.dialogs.show_yesno_dialog(self, message, _("Attention!")) if not result: # user says no, he want to save return self.widgets.tooledit1.reload(None) @@ -5755,14 +5725,14 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None): message = _("No tool selected in the tool table. ") message += _("Please select only one tool in the table!") self.dialogs.show_warning_dialog( _("Warning Tool Touch off not possible!"), - message, context=None) + message) return if tool != self.stat.tool_in_spindle: message = _("You can not touch off a tool, which is not mounted in the spindle! ") message += _("Your selection has been reset to the tool in spindle.") self.dialogs.show_warning_dialog( _("Warning Tool Touch off not possible!"), - message, context=None) + message) self.widgets.tooledit1.reload(self) self.widgets.tooledit1.set_selected_tool(self.stat.tool_in_spindle) return @@ -5771,7 +5741,7 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None): message = _("Tool touch off is not possible with cutter radius compensation switched on!\n") message += _("Please emit an G40 before tool touch off.") self.dialogs.show_warning_dialog( _("Warning Tool Touch off not possible!"), - message, context=None) + message) return if widget == self.widgets.btn_tool_touchoff_x: @@ -5780,10 +5750,10 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None): axis = "z" else: self.dialogs.show_warning_dialog(_("Real big error!"), - _("You managed to come to a place that is not possible in on_btn_tool_touchoff"), context=None) + _("You managed to come to a place that is not possible in on_btn_tool_touchoff")) return - value = self.dialogs.entry_dialog(self, data=None, + value = self.dialogs.show_entry_dialog(data=None, header=_("Enter value for axis {0} to set:").format(axis.upper()), label=_("Set parameter of tool {0:d} and axis {1} to:").format(self.stat.tool_in_spindle, axis.upper()), integer=False) @@ -5791,7 +5761,7 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None): if value == "ERROR": message = _("Conversion error because of wrong entry for touch off axis {0}").format(axis.upper()) self.dialogs.show_warning_dialog( _("Conversion error !"), - message, context=None) + message) return elif value == "CANCEL": return @@ -5811,23 +5781,23 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None): def on_btn_select_tool_by_no_clicked(self, widget, data=None): if self.widgets.tooledit1.edited: message = _("Discard unsaved changes and change tool?") - result = self.dialogs.yesno_dialog(self, message, _("Attention!")) + result = self.dialogs.show_yesno_dialog(self, message, _("Attention!")) if not result: # user says no, he want to save return - value = self.dialogs.entry_dialog(self, data=None, header=_("Enter the tool number as integer "), + value = self.dialogs.show_entry_dialog(data=None, header=_("Enter the tool number as integer "), label=_("Select the tool to change"), integer=True) if value == "ERROR": message = _("Conversion error because of wrong entry for tool number.\n") message += _("Enter only integer numbers!") self.dialogs.show_warning_dialog( _("Conversion error !"), - message, context=None) + message) return elif value == "CANCEL": return elif int(value) == self.stat.tool_in_spindle: message = _("Selected tool is already in spindle, no change needed.") self.dialogs.show_warning_dialog( _("Important Warning!"), - message, context=None) + message) return else: self.tool_change = True @@ -5842,19 +5812,19 @@ def on_btn_select_tool_by_no_clicked(self, widget, data=None): def on_btn_selected_tool_clicked(self, widget, data=None): if self.widgets.tooledit1.edited: message = _("Discard unsaved changes and change tool?") - result = self.dialogs.yesno_dialog(self, message, _("Attention!")) + result = self.dialogs.show_yesno_dialog(self, message, _("Attention!")) if not result: # user says no, he want to save return tool = self.widgets.tooledit1.get_selected_row() if tool == None: message = _("you selected no or more than one tool, the tool selection must be unique") self.dialogs.show_warning_dialog( _("Important Warning!"), - message, context=None) + message) return if tool == self.stat.tool_in_spindle: message = _("Selected tool is already in spindle, no change needed.") self.dialogs.show_warning_dialog( _("Important Warning!"), - message, context=None) + message) return if tool or tool == 0: self.tool_change = True @@ -5873,7 +5843,7 @@ def on_btn_selected_tool_clicked(self, widget, data=None): else: message = _("Could not understand the entered tool number. Will not change anything!") self.dialogs.show_warning_dialog( _("Important Warning!"), - message, context=None) + message) # ========================================================= From ff3e2f2551c49c852ecf504fcf1fee993a841735 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Thu, 30 Jul 2026 09:44:20 -0700 Subject: [PATCH 48/81] gmoccapy -dialogs: search through focused dialogs, clean up code --- src/emc/usr_intf/gmoccapy/dialogs.py | 84 ++++++++++++++++++---------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/dialogs.py b/src/emc/usr_intf/gmoccapy/dialogs.py index dc9418f37ad..f09744d41bb 100644 --- a/src/emc/usr_intf/gmoccapy/dialogs.py +++ b/src/emc/usr_intf/gmoccapy/dialogs.py @@ -40,10 +40,11 @@ class Dialogs(GObject.GObject): def __init__(self, caller): GObject.GObject.__init__(self) - self.sys_dialog = self.system_dialog(caller) - self.warn_dialog = self.warning_dialog(caller) - self.ent_dialog = self.entry_dialog(caller) - self.yn_dialog = self.yesno_dialog(caller) + self._caller = caller + self.sys_dialog = self.system_dialog() + self.warn_dialog = self.warning_dialog() + self.ent_dialog = self.entry_dialog() + self.yn_dialog = self.yesno_dialog() # sent from Gstat messages # first one found visibly gets the answer @@ -57,19 +58,30 @@ def dialog_ext_control(self, answer): self.ent_dialog.response(answer) elif self.yn_dialog.get_visible(): self.yn_dialog.response(answer) + else: + # Get the widget that currently has user focus + focused_widget = self._caller.widgets.window1.get_focus() + + # To inspect all open windows (including dialogs) in your application: + for window in Gtk.Window.list_toplevels(): + if window.get_visible() and isinstance(window, Gtk.Dialog): + print("Found active dialog title:", window.get_title()) + window.response(answer) + return + + print("Can't send external response: No dialog found") # This dialog is for unlocking the system tab # The unlock code number is defined at the top of the page - def system_dialog(self, caller): + def system_dialog(self): dialog = Gtk.Dialog(_("Enter System Unlock Code"), - caller.widgets.window1, + self._caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT) dialog.set_modal(True) label = Gtk.Label(_("Enter System Unlock Code")) label.modify_font(Pango.FontDescription("sans 20")) calc = gladevcp.Calculator() dialog._calc = calc - dialog._caller = caller dialog.vbox.pack_start(label, False, False, 0) dialog.vbox.add(calc) calc.set_value("") @@ -93,6 +105,10 @@ def show_system_dialog(self): dialog.RESPONSE = None while dialog.RESPONSE is None: while Gtk.events_pending(): + # read any ZMQ messages + # then update widgets + # till we get a dialog answer + self._caller.GSTAT.readNextMsg() Gtk.main_iteration() dialog.hide() @@ -100,7 +116,7 @@ def show_system_dialog(self): code = dialog._calc.get_value() rtn = -1 if dialog.RESPONSE == Gtk.ResponseType.ACCEPT: - if code == int(dialog._caller.unlock_code): + if code == int(self._caller.unlock_code): rtn = 1 else: rtn = 0 @@ -110,9 +126,9 @@ def show_system_dialog(self): def on_system_response(self, dialog, rtn): dialog.RESPONSE = rtn - def entry_dialog(self, caller): + def entry_dialog(self): dialog = Gtk.Dialog('', - caller.widgets.window1, + self._caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT) dialog.label = Gtk.Label('') dialog.label.modify_font(Pango.FontDescription("sans 20")) @@ -148,6 +164,10 @@ def show_entry_dialog(self, data = None, header = _("Enter value") , dialog.RESPONSE = None while dialog.RESPONSE is None: while Gtk.events_pending(): + # read any ZMQ messages + # then update widgets + # till we get a dialog answer + self._caller.GSTAT.readNextMsg() Gtk.main_iteration() dialog.hide() @@ -169,10 +189,10 @@ def on_entry_response(self, dialog, rtn): dialog.RESPONSE = rtn # display warning dialog - def warning_dialog(self, caller, message = '', secondary = None, title = _("Operator Message"),\ + def warning_dialog(self, message = '', secondary = None, title = _("Operator Message"),\ sound = True, confirm_pin = 'warning-confirm', active_pin = None): - dialog = Gtk.MessageDialog(caller.widgets.window1, + dialog = Gtk.MessageDialog(self._caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.NONE, message) # if there is a secondary message then the first message text is bold @@ -190,11 +210,11 @@ def warning_dialog(self, caller, message = '', secondary = None, title = _("Oper dialog.set_title(title) dialog.context = [] def periodic(): - if caller.halcomp[confirm_pin]: + if self._caller.halcomp[confirm_pin]: dialog.response(Gtk.ResponseType.OK) return False if active_pin is not None: - if not caller.halcomp[active_pin]: + if not self._caller.halcomp[active_pin]: dialog.response(Gtk.ResponseType.CANCEL) return False return True @@ -216,6 +236,10 @@ def show_warning_dialog(self, title, message, sound=True, dialog.RESPONSE = None while dialog.RESPONSE is None: while Gtk.events_pending(): + # read any ZMQ messages + # then update widgets + # till we get a dialog answer + self._caller.GSTAT.readNextMsg() Gtk.main_iteration() dialog.hide() @@ -225,8 +249,8 @@ def show_warning_dialog(self, title, message, sound=True, def on_warning_response(self, dialog, rtn): dialog.RESPONSE = rtn - def yesno_dialog(self, caller): - dialog = Gtk.MessageDialog(caller.widgets.window1, + def yesno_dialog(self): + dialog = Gtk.MessageDialog(self._caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE) @@ -246,7 +270,7 @@ def yesno_dialog(self, caller): dialog.connect("response", self.on_yn_response) return dialog - def show_yesno_dialog(self, caller, message, title = _("Operator Message")): + def show_yesno_dialog(self, message, title = _("Operator Message")): dialog = self.yn_dialog dialog.set_markup(message) if title: @@ -258,6 +282,10 @@ def show_yesno_dialog(self, caller, message, title = _("Operator Message")): dialog.RESPONSE = None while dialog.RESPONSE is None: while Gtk.events_pending(): + # read any ZMQ messages + # then update widgets + # till we get a dialog answer + self._caller.GSTAT.readNextMsg() Gtk.main_iteration() rtn = dialog.RESPONSE @@ -268,8 +296,8 @@ def show_yesno_dialog(self, caller, message, title = _("Operator Message")): def on_yn_response(self,dialog, rtn): dialog.RESPONSE = rtn - def show_user_message(self, caller, message, title = _("Operator Message")): - dialog = Gtk.MessageDialog(caller.widgets.window1, + def show_user_message(self, message, title = _("Operator Message")): + dialog = Gtk.MessageDialog(self._caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.NONE) @@ -290,7 +318,7 @@ def show_user_message(self, caller, message, title = _("Operator Message")): return response == Gtk.ResponseType.OK # dialog for run from line - def restart_dialog(self, caller): + def restart_dialog(self): # highlight the gcode down one line lower # used for run-at-line restart @@ -314,16 +342,16 @@ def on_enter_button(widget, obj, calc): obj.widgets.gcode_view.set_line_number(line) restart_dialog = Gtk.Dialog(_("Restart Entry"), - caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT) + self._caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT) label = Gtk.Label(_("Restart Entry")) label.modify_font(Pango.FontDescription("sans 20")) restart_dialog.vbox.pack_start(label, False, False, 0) calc = gladevcp.Calculator() restart_dialog.vbox.add(calc) - calc.set_value("%d" % caller.widgets.gcode_view.get_line_number()) + calc.set_value("%d" % self._caller.widgets.gcode_view.get_line_number()) calc.set_property("font", "sans 20") calc.set_editable(True) - calc.entry.connect("activate", on_enter_button, caller, calc) + calc.entry.connect("activate", on_enter_button, self._caller, calc) calc.integer_entry_only(True) calc.num_pad_only(True) # add additional buttons @@ -336,9 +364,9 @@ def on_enter_button(widget, obj, calc): calc.table.attach(upbutton,3,1,1,1) calc.table.attach(downbutton,3,2,1,1) calc.table.attach(enterbutton,3,3,1,1) - upbutton.connect("clicked", restart_up, caller, calc) - downbutton.connect("clicked", restart_down, caller, calc) - enterbutton.connect("clicked", on_enter_button, caller, calc) + upbutton.connect("clicked", restart_up, self._caller, calc) + downbutton.connect("clicked", restart_down, self._caller, calc) + enterbutton.connect("clicked", on_enter_button, self._caller, calc) restart_dialog.parse_geometry("410x400+0+0") restart_dialog.show_all() @@ -350,5 +378,5 @@ def on_enter_button(widget, obj, calc): line = int(calc.get_value()) if line == None: line = 0 - caller.widgets.gcode_view.set_line_number(line) - caller.start_line = line + self._caller.widgets.gcode_view.set_line_number(line) + self._caller.start_line = line From 403d5b4cdc2cd42c7232ce4fccaf3974816d38ed Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 10 Apr 2026 13:41:47 -0700 Subject: [PATCH 49/81] gmoccapy -update the angular speedcontrol to new angular type --- src/emc/usr_intf/gmoccapy/gmoccapy.glade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.glade b/src/emc/usr_intf/gmoccapy/gmoccapy.glade index efe301beca8..622a1cc2c4c 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.glade +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.glade @@ -1457,7 +1457,7 @@ uncomment selection rgb(255,129,22) 3600 %.d - 0 + 1 °/min 360 From a7271314913bbce3dc0afff62dd27072b7de4c63 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Thu, 9 Jul 2026 14:13:15 -0700 Subject: [PATCH 50/81] gmoccapy -honour shutdown request --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index 34026f8e210..b67234d36d5 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -438,6 +438,7 @@ def __init__(self, argv): self.GSTAT.connect('ok-request', lambda w, state: self.dialogs.dialog_ext_control(Gtk.ResponseType.ACCEPT)) self.GSTAT.connect('cancel-request', lambda w, state: self.dialogs.dialog_ext_control(Gtk.ResponseType.CANCEL)) self.GSTAT.connect('cancel-request', lambda w, state: self._del_notification()) + self.GSTAT.connect('shutdown-request', lambda w: self.on_btn_exit_clicked(w)) # get if run from line should be used self.run_from_line = self.prefs.getpref("run_from_line", "no_run", str) From 21676c19963633c7b259a7daeae4a49e551c3659 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Fri, 10 Jul 2026 10:21:38 -0700 Subject: [PATCH 51/81] gmoccapy -respond to softkeys and make exit do shutdown --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index b67234d36d5..6829053fc21 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -438,7 +438,8 @@ def __init__(self, argv): self.GSTAT.connect('ok-request', lambda w, state: self.dialogs.dialog_ext_control(Gtk.ResponseType.ACCEPT)) self.GSTAT.connect('cancel-request', lambda w, state: self.dialogs.dialog_ext_control(Gtk.ResponseType.CANCEL)) self.GSTAT.connect('cancel-request', lambda w, state: self._del_notification()) - self.GSTAT.connect('shutdown-request', lambda w: self.on_btn_exit_clicked(w)) + self.GSTAT.connect('shutdown-request', lambda w: self.system_shutdown(w)) + self.GSTAT.connect('softkey-pressed', lambda w,data: self.softkey_pressed(data)) # get if run from line should be used self.run_from_line = self.prefs.getpref("run_from_line", "no_run", str) @@ -2934,6 +2935,9 @@ def on_rbt_auto_pressed(self, widget, data=None): def on_btn_exit_clicked(self, widget, data=None): self.widgets.window1.destroy() + def system_shutdown(self, widget): + self.ACTION.SHUT_SYSTEM_DOWN_PROMPT() + # button handlers End # ========================================================= @@ -6400,6 +6404,20 @@ def _blockdelete(self, pin): self.command.set_optional_stop(pin.get()) # ========================================================= + # external request for a softkey press from HALUI/halbridge + def softkey_pressed(self, index): + if index > 9: + location = "bottom" + number = index -10 + elif index < 7: + location = "right" + number = index + else: + LOG.debug(f"Could not translate softkey {index} to button number") + LOG.debug(f"softkey index: {index}, location: {location}, number: {number}") + button = self._get_child_button(location, number) + self.process_button(button) + # The actions of the buttons def _button_pin_changed(self, pin): # we check if the button is pressed or released, @@ -6423,6 +6441,9 @@ def _button_pin_changed(self, pin): return button = self._get_child_button(location, number) + self.process_button(button) + + def process_button(self, button): if not button: LOG.debug("no button here") return From cf938b5cf3c0df33dff6f3e7fbafc86f5fe4a1a3 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Fri, 10 Jul 2026 13:23:52 -0700 Subject: [PATCH 52/81] gmoccapy -add MPG input to zoom and scroll scroll the gcode if in auto and not running otherwise zoom the gcode display. Only if the MPG is enabled from HALUI --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index 6829053fc21..58ba3707705 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -228,6 +228,8 @@ def __init__(self, argv): self.so_counts = 0 # need to calculate difference in counts to change the spindle override slider self.jv_counts = 0 # need to calculate difference in counts to change the jog_vel slider self.ro_counts = 0 # need to calculate difference in counts to change the rapid override slider + self._last_count = 0 # need to calculate MPG count difference + self.mpg_enabled = False self.spindle_override = 1 # holds the feed override value and is needed to be able to react to halui pin self.feed_override = 1 # holds the spindle override value and is needed to be able to react to halui pin @@ -440,6 +442,7 @@ def __init__(self, argv): self.GSTAT.connect('cancel-request', lambda w, state: self._del_notification()) self.GSTAT.connect('shutdown-request', lambda w: self.system_shutdown(w)) self.GSTAT.connect('softkey-pressed', lambda w,data: self.softkey_pressed(data)) + self.GSTAT.connect('axis-selection-changed', lambda w,data: self.mpg_selection_changed(data)) # get if run from line should be used self.run_from_line = self.prefs.getpref("run_from_line", "no_run", str) @@ -6403,6 +6406,31 @@ def _blockdelete(self, pin): LOG.debug("Received a signal from pin {0} with state = {1}".format(pin.name, pin.get())) self.command.set_optional_stop(pin.get()) + # from gmoccapy's MPG input pin + def _external_mpg(self, pin): + count = pin.get() + diff = count - self._last_count + if self.mpg_enabled: + if diff <0: + if self.GSTAT.is_auto_mode() and not self.GSTAT.is_auto_running(): + self.widgets.gcode_view.line_up() + else: + self.widgets.gremlin.zoom_out() + else: + if self.GSTAT.is_auto_mode() and not self.GSTAT.is_auto_running(): + self.widgets.gcode_view.line_down() + self.widgets.gcode_view.line_down() + else: + self.widgets.gremlin.zoom_in() + self._last_count = count + + # enabled externally by halui/hal_bridge + def mpg_selection_changed(self, data): + if data =='MPG0': + self.mpg_enabled = True + else: + self.mpg_enabled = False + # ========================================================= # external request for a softkey press from HALUI/halbridge def softkey_pressed(self, index): @@ -6637,6 +6665,8 @@ def _make_hal_pins(self): pin = self.halcomp.newpin("blockdelete", hal.HAL_BIT, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self._blockdelete) + pin = self.halcomp.newpin('mpg-in',hal.HAL_S32, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._external_mpg) ############################## # required class boiler code # From 826b184abd33ce369a6b9c0c57bfb2f7e33ba948 Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 10:19:59 -0700 Subject: [PATCH 53/81] gmoccapy -add a halui test config with a sim test panel gmoccapy -add a 4 axis halui test config to confirm angular jograte works --- configs/sim/gmoccapy/gmoccapy_halui_test.ini | 225 +++ .../gmoccapy/gmoccapy_halui_test_4_axis.ini | 218 +++ configs/sim/gmoccapy/panel-4axis.hal | 65 + configs/sim/gmoccapy/panel.hal | 59 + configs/sim/gmoccapy/panel.ui | 1407 +++++++++++++++++ 5 files changed, 1974 insertions(+) create mode 100644 configs/sim/gmoccapy/gmoccapy_halui_test.ini create mode 100644 configs/sim/gmoccapy/gmoccapy_halui_test_4_axis.ini create mode 100644 configs/sim/gmoccapy/panel-4axis.hal create mode 100644 configs/sim/gmoccapy/panel.hal create mode 100644 configs/sim/gmoccapy/panel.ui diff --git a/configs/sim/gmoccapy/gmoccapy_halui_test.ini b/configs/sim/gmoccapy/gmoccapy_halui_test.ini new file mode 100644 index 00000000000..27579c0b389 --- /dev/null +++ b/configs/sim/gmoccapy/gmoccapy_halui_test.ini @@ -0,0 +1,225 @@ +# EMC controller parameters for a simulated machine. +# General note: Comments can either be preceded with a # or ; - either is +# acceptable, although # is in keeping with most linux config files. + +# General section ------------------------------------------------------------- +[EMC] +VERSION = 1.1 +MACHINE = gmoccapy +DEBUG = 0 + +# Sections for display options ------------------------------------------------ +[DISPLAY] +DISPLAY = gmoccapy -i +# Log level: +# DEBUG -d +# INFO -i +# VERBOSE -v +# ERROR -q + +# Cycle time, in milliseconds, that display will sleep between polls +CYCLE_TIME = 100 + +# Values that will be allowed for override, 1.0 = 100% +MAX_FEED_OVERRIDE = 1.5 +MAX_SPINDLE_OVERRIDE = 1.2 +MIN_SPINDLE_OVERRIDE = 0.5 + +# Initial value for spindle speed +DEFAULT_SPINDLE_SPEED = 450 + +# The following are not used, added here to suppress warnings (from qt_istat/logger). +DEFAULT_LINEAR_VELOCITY = 35 +MIN_LINEAR_VELOCITY = 0 +MAX_LINEAR_VELOCITY = 234 +DEFAULT_SPINDLE_0_SPEED = 500 +MIN_SPINDLE_0_SPEED = 0 +MAX_SPINDLE_0_SPEED = 3000 +MAX_SPINDLE_0_OVERRIDE = 1.2 +MIN_SPINDLE_0_OVERRIDE = 0.5 + +# Prefix to be used +PROGRAM_PREFIX = ../../nc_files/ + +# Introductory graphic +INTRO_GRAPHIC = linuxcnc.gif +INTRO_TIME = 5 + +# list of selectable jog increments +INCREMENTS = 1.000 mm, 0.100 mm, 0.010 mm, 0.001 mm, 1.2345 inch + +# for details see nc_files/subroutines/maco_instructions.txt +[FILTER] +PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image +PROGRAM_EXTENSION = .py Python Script +png = image-to-gcode +gif = image-to-gcode +jpg = image-to-gcode +py = python3 + +# Task controller section ----------------------------------------------------- +[RS274NGC] +RS274NGC_STARTUP_CODE = G17 G21 G40 G43H0 G54 G64P0.005 G80 G90 G94 G97 M5 M9 +PARAMETER_FILE = sim.var +SUBROUTINE_PATH = ./macros +REMAP=M6 modalgroup=6 prolog=change_prolog ngc=change_g43 epilog=change_epilog +REMAP=M61 modalgroup=6 prolog=settool_prolog ngc=settool_g43 epilog=settool_epilog + +# the Python plugins serves interpreter and task +[PYTHON] +PATH_PREPEND = ./python +TOPLEVEL = ./python/toplevel.py +LOG_LEVEL = 0 + +# Motion control section ------------------------------------------------------ +[EMCMOT] +EMCMOT = motmod +COMM_TIMEOUT = 1.0 +BASE_PERIOD = 100000 +SERVO_PERIOD = 1000000 + +# Hardware Abstraction Layer section -------------------------------------------------- +[TASK] +TASK = milltask +CYCLE_TIME = 0.001 + +# Part program interpreter section -------------------------------------------- +[HAL] +HALFILE = core_sim.hal +HALFILE = spindle_sim.hal +HALFILE = simulated_home.hal + +# Single file that is executed after the GUI has started. +POSTGUI_HALFILE = gmoccapy_postgui.hal +POSTGUI_HALCMD = loadusr qtvcp -a -H panel.hal panel + +HALUI = halui + +# Trajectory planner section -------------------------------------------------- +[HALUI] +#No Content + +[MDI_COMMAND_LIST] +# for macro buttons on main oage up to 10 possible +MDI_COMMAND_MACRO0 = G0 Z1;X0 Y0;Z0, Goto\nUser\nZero +MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero +MDI_COMMAND_MACRO2 = (MSG, macro 2); +MDI_COMMAND_MACRO3 = (MSG, macro 2) +MDI_COMMAND_MACRO4 = (MSG, macro 2),test +[TRAJ] +COORDINATES = X Y Z +LINEAR_UNITS = mm +ANGULAR_UNITS = degree +DEFAULT_LINEAR_VELOCITY = 35 +MAX_LINEAR_VELOCITY = 234 +POSITION_FILE = position.txt +#NO_FORCE_HOMING = 1 + +[EMCIO] +# tool table file +TOOL_TABLE = tool.tbl +TOOL_CHANGE_POSITION = 100 100 -10 +TOOL_CHANGE_QUILL_UP = 1 + +[KINS] +KINEMATICS = trivkins coordinates=xyz +JOINTS = 3 + +[AXIS_X] +MIN_LIMIT = -400.0 +MAX_LIMIT = 400.0 +MAX_VELOCITY = 166 +MAX_ACCELERATION = 1500.0 + +[JOINT_0] +TYPE = LINEAR +MAX_VELOCITY = 166 +MAX_ACCELERATION = 1500.0 +BACKLASH = 0.000 +INPUT_SCALE = 4000 +OUTPUT_SCALE = 1.000 +MIN_LIMIT = -400.0 +MAX_LIMIT = 400.0 +FERROR = 0.050 +MIN_FERROR = 0.010 +HOME_OFFSET = 0.0 +HOME = 10 +HOME_SEARCH_VEL = 200.0 +HOME_LATCH_VEL = 20.0 +HOME_USE_INDEX = NO +HOME_IGNORE_LIMITS = NO +HOME_SEQUENCE = 1 +HOME_IS_SHARED = 1 + +# Second axis +[AXIS_Y] +MIN_LIMIT = -400.0 +MAX_LIMIT = 400.0 +MAX_VELOCITY = 166 +MAX_ACCELERATION = 1500.0 + +[JOINT_1] +TYPE = LINEAR +MAX_VELOCITY = 166 +MAX_ACCELERATION = 1500.0 +BACKLASH = 0.000 +INPUT_SCALE = 4000 +OUTPUT_SCALE = 1.000 +MIN_LIMIT = -400.0 +MAX_LIMIT = 400.0 +FERROR = 0.050 +MIN_FERROR = 0.010 +HOME_OFFSET = 0.0 +HOME = 10 +HOME_SEARCH_VEL = 200.0 +HOME_LATCH_VEL = 20.0 +HOME_USE_INDEX = NO +HOME_IGNORE_LIMITS = NO +HOME_SEQUENCE = 1 + +# Third axis +[AXIS_Z] +MIN_LIMIT = -400.0 +MAX_LIMIT = 0.001 +MAX_VELOCITY = 166 +MAX_ACCELERATION = 1500.0 + +[JOINT_2] +TYPE = LINEAR +MAX_VELOCITY = 166 +MAX_ACCELERATION = 1500.0 +BACKLASH = 0.000 +INPUT_SCALE = 4000 +OUTPUT_SCALE = 1.000 +MIN_LIMIT = -400.0 +MAX_LIMIT = 0.001 +FERROR = 0.050 +MIN_FERROR = 0.010 +HOME_OFFSET = 1.0 +HOME = -10 +HOME_SEARCH_VEL = 200.0 +HOME_LATCH_VEL = 20.0 +HOME_USE_INDEX = NO +HOME_IGNORE_LIMITS = NO +HOME_SEQUENCE = 0 +HOME_IS_SHARED = 1 + +# section for main IO controller parameters ----------------------------------- +[MACROS] +MACRO = go_to_position x-pos y-pos z-pos +MACRO = i_am_lost +MACRO = increment x-incr y-incr +MACRO = macro_4 +MACRO = macro_5 +MACRO = macro_6 +MACRO = macro_7 +MACRO = macro_8 +MACRO = macro_9 +MACRO = macro_10 +MACRO = macro_11 +MACRO = macro_12 +MACRO = macro_13 +MACRO = macro_14 +MACRO = macro_15 + + diff --git a/configs/sim/gmoccapy/gmoccapy_halui_test_4_axis.ini b/configs/sim/gmoccapy/gmoccapy_halui_test_4_axis.ini new file mode 100644 index 00000000000..f722d2e6654 --- /dev/null +++ b/configs/sim/gmoccapy/gmoccapy_halui_test_4_axis.ini @@ -0,0 +1,218 @@ +# General section ------------------------------------------------------------- +[EMC] +VERSION = 1.1 +MACHINE = gmoccapy 4 axis +DEBUG = 0 + +# for details see nc_files/subroutines/maco_instructions.txt +[DISPLAY] +DISPLAY = gmoccapy + +# Cycle time, in milliseconds, that display will sleep between polls +CYCLE_TIME = 100 + +# Highest value that will be allowed for feed override, 1.0 = 100% +MAX_FEED_OVERRIDE = 1.5 +MAX_SPINDLE_OVERRIDE = 1.2 +MIN_SPINDLE_OVERRIDE = .5 + +# Prefix to be used +PROGRAM_PREFIX = ../../nc_files/ + +# Introductory graphic +INTRO_GRAPHIC = linuxcnc.gif +INTRO_TIME = 5 + +# list of selectable jog increments +INCREMENTS = 1.000 mm, 0.100 mm, 0.010 mm, 0.001 mm, 90.000 ° + +[FILTER] +PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image +PROGRAM_EXTENSION = .py Python Script +png = image-to-gcode +gif = image-to-gcode +jpg = image-to-gcode +py = python3 + +# Task controller section ----------------------------------------------------- +[RS274NGC] +RS274NGC_STARTUP_CODE = G17 G21 G40 G43H0 G54 G64P0.005 G80 G90 G94 G97 M5 M9 +PARAMETER_FILE = sim.var +SUBROUTINE_PATH = ./macros +REMAP=M6 modalgroup=6 prolog=change_prolog ngc=change_g43 epilog=change_epilog +REMAP=M61 modalgroup=6 prolog=settool_prolog ngc=settool_g43 epilog=settool_epilog + +# the Python plugins serves interpreter and task +[PYTHON] +PATH_PREPEND = ./python +TOPLEVEL = ./python/toplevel.py +LOG_LEVEL = 0 + +# Motion control section ------------------------------------------------------ +[EMCMOT] +EMCMOT = motmod +COMM_TIMEOUT = 1.0 +BASE_PERIOD = 100000 +SERVO_PERIOD = 1000000 + +# Hardware Abstraction Layer section -------------------------------------------------- +[TASK] +TASK = milltask +CYCLE_TIME = 0.001 + +# Part program interpreter section -------------------------------------------- +[HAL] +HALFILE = core_sim4.hal +HALFILE = spindle_sim.hal +HALFILE = simulated_home.hal + +# Single file that is executed after the GUI has started. +POSTGUI_HALFILE = gmoccapy_postgui.hal +POSTGUI_HALCMD = loadusr qtvcp -a -H panel-4axis.hal panel +HALUI = halui + +# Trajectory planner section -------------------------------------------------- +[HALUI] +#No Content +[MDI_COMMAND_LIST] +# for macro buttons on main oage up to 10 possible +MDI_COMMAND_MACRO0 = G0 Z1;X0 Y0;Z0, Goto\nUser\nZero +MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero +MDI_COMMAND_MACRO2 = (MSG, macro 2); +MDI_COMMAND_MACRO3 = (MSG, macro 2) +MDI_COMMAND_MACRO4 = (MSG, macro 2),test + + +[TRAJ] +COORDINATES = X Y Z C +LINEAR_UNITS = mm +ANGULAR_UNITS = degree +DEFAULT_LINEAR_VELOCITY = 50 +MAX_LINEAR_VELOCITY = 234 +POSITION_FILE = position.txt + +# First axis = X +[EMCIO] +# tool table file +TOOL_TABLE = tool.tbl +TOOL_CHANGE_POSITION = 100 100 -10 +TOOL_CHANGE_QUILL_UP = 1 + +[KINS] +KINEMATICS = trivkins coordinates=xyzc +JOINTS = 4 + +[AXIS_X] +MIN_LIMIT = -400.0 +MAX_LIMIT = 400.0 +MAX_VELOCITY = 166 +MAX_ACCELERATION = 1500.0 + +[JOINT_0] +TYPE = LINEAR +MAX_VELOCITY = 166 +MAX_ACCELERATION = 1500.0 +BACKLASH = 0.000 +INPUT_SCALE = 4000 +OUTPUT_SCALE = 1.000 +MIN_LIMIT = -400.0 +MAX_LIMIT = 400.0 +FERROR = 0.050 +MIN_FERROR = 0.010 +HOME_OFFSET = 0.0 +HOME = 10 +HOME_SEARCH_VEL = 200.0 +HOME_LATCH_VEL = 20.0 +HOME_USE_INDEX = NO +HOME_IGNORE_LIMITS = NO +HOME_SEQUENCE = 1 +HOME_IS_SHARED = 1 + +# Second axis = Y +[AXIS_Y] +MIN_LIMIT = -400.0 +MAX_LIMIT = 400.0 +MAX_VELOCITY = 166 +MAX_ACCELERATION = 1500.0 + +[JOINT_1] +TYPE = LINEAR +MAX_VELOCITY = 166 +MAX_ACCELERATION = 1500.0 +BACKLASH = 0.000 +INPUT_SCALE = 4000 +OUTPUT_SCALE = 1.000 +MIN_LIMIT = -400.0 +MAX_LIMIT = 400.0 +FERROR = 0.050 +MIN_FERROR = 0.010 +HOME_OFFSET = 0.0 +HOME = 10 +HOME_SEARCH_VEL = 200.0 +HOME_LATCH_VEL = 20.0 +HOME_USE_INDEX = NO +HOME_IGNORE_LIMITS = NO +HOME_SEQUENCE = 1 +HOME_IS_SHARED = 1 + +# Third axis = Z +[AXIS_Z] +MIN_LIMIT = -400.0 +MAX_LIMIT = 0.001 +MAX_VELOCITY = 166 +MAX_ACCELERATION = 1500.0 + +[JOINT_2] +TYPE = LINEAR +MAX_VELOCITY = 166 +MAX_ACCELERATION = 1500.0 +BACKLASH = 0.000 +INPUT_SCALE = 4000 +OUTPUT_SCALE = 1.000 +MIN_LIMIT = -400.0 +MAX_LIMIT = 0.001 +FERROR = 0.050 +MIN_FERROR = 0.010 +HOME_OFFSET = 1.0 +HOME = -10 +HOME_SEARCH_VEL = 200.0 +HOME_LATCH_VEL = 20.0 +HOME_USE_INDEX = NO +HOME_IGNORE_LIMITS = NO +HOME_SEQUENCE = 0 +HOME_IS_SHARED = 1 + +# Fourth axis = A +# Fifth axis = B + +# Sixt axis = C +[AXIS_C] +MAX_VELOCITY = 90.0 +MAX_ACCELERATION = 1200.0 + +[JOINT_3] +TYPE = ANGULAR +HOME = 0.0 +MAX_VELOCITY = 90.0 +MAX_ACCELERATION = 1200.0 +BACKLASH = 0.000 +INPUT_SCALE = 40 +OUTPUT_SCALE = 1.000 +FERROR = 5.0 +MIN_FERROR = 1.0 +HOME_OFFSET = 0.0 +HOME_SEARCH_VEL = 0.0 +HOME_LATCH_VEL = 0.0 +HOME_USE_INDEX = NO +HOME_IGNORE_LIMITS = NO +HOME_SEQUENCE = 1 + +# section for main IO controller parameters ----------------------------------- +[MACROS] +MACRO = i_am_lost +MACRO = halo_world +MACRO = jog_around +MACRO = increment xinc yinc +MACRO = go_to_position X-pos Y-pos Z-pos + +# Sections for display options ------------------------------------------------ diff --git a/configs/sim/gmoccapy/panel-4axis.hal b/configs/sim/gmoccapy/panel-4axis.hal new file mode 100644 index 00000000000..8120d678518 --- /dev/null +++ b/configs/sim/gmoccapy/panel-4axis.hal @@ -0,0 +1,65 @@ +net rate halui.axis.jog-speed panel.jog-rate +net angular-rate halui.axis.jog-speed-angular panel.jog-rate-angular + +net sx halui.axis.x.select panel.axis-x +net sy halui.axis.y.select panel.axis-y +net sz halui.axis.z.select panel.axis-z +net sc halui.axis.c.select panel.axis-c + +net sx axis.x.jog-enable +net sy axis.y.jog-enable +net sz axis.z.jog-enable +net sc axis.c.jog-enable +net sgui halui.gui.mpg-select.0 panel.select-gui0 + +net jog-p halui.axis.selected.plus panel.jog-pos +net jog-m halui.axis.selected.minus panel.jog-neg + +net mpg-scale axis.x.jog-scale panel.mpg-scale +net mpg-scale axis.y.jog-scale +net mpg-scale axis.z.jog-scale +net mpg-scale axis.c.jog-scale + +net mpg-count panel.mpg-wheel-s +net mpg-count gmoccapy.mpg-in +net mpg-count axis.x.jog-counts +net mpg-count axis.y.jog-counts +net mpg-count axis.z.jog-counts +net mpg-count axis.c.jog-counts + +net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0 +net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1 +net m2 halui.gui.mdi-command-MACRO2 panel.mdi-2 + +net man panel.manual-mode halui.mode.manual +net mdi panel.mdi-mode halui.mode.mdi +net auto panel.auto-mode halui.mode.auto + +net pause halui.gui.cycle.start panel.cycle-start +net start halui.gui.cycle.pause panel.cycle-pause +net abort halui.abort panel.cycle-abort + +net cancel halui.gui.cancel panel.cancel +net ok halui.gui.ok panel.ok + + +net softkey0 halui.gui.softkey-00 panel.softkey-v0 +net softkey1 halui.gui.softkey-01 panel.softkey-v1 +net softkey2 halui.gui.softkey-02 panel.softkey-v2 +net softkey3 halui.gui.softkey-03 panel.softkey-v3 +net softkey4 halui.gui.softkey-04 panel.softkey-v4 +net softkey5 halui.gui.softkey-05 panel.softkey-v5 +net softkey6 halui.gui.softkey-06 panel.softkey-v6 +net softkeyh0 halui.gui.softkey-10 panel.softkey-h0 +net softkeyh1 halui.gui.softkey-11 panel.softkey-h1 +net softkeyh2 halui.gui.softkey-12 panel.softkey-h2 +net softkeyh3 halui.gui.softkey-13 panel.softkey-h3 +net softkeyh4 halui.gui.softkey-14 panel.softkey-h4 +net softkeyh5 halui.gui.softkey-15 panel.softkey-h5 +net softkeyh6 halui.gui.softkey-16 panel.softkey-h6 +net softkeyh7 halui.gui.softkey-17 panel.softkey-h7 +net softkeyh8 halui.gui.softkey-18 panel.softkey-h8 +net softkeyh9 halui.gui.softkey-19 panel.softkey-h9 + +net exit halui.gui.shutdown panel.exit +net reload halui.gui.reload-display panel.reload diff --git a/configs/sim/gmoccapy/panel.hal b/configs/sim/gmoccapy/panel.hal new file mode 100644 index 00000000000..0c80e60010c --- /dev/null +++ b/configs/sim/gmoccapy/panel.hal @@ -0,0 +1,59 @@ +net rate halui.axis.jog-speed panel.jog-rate + +net sx halui.axis.x.select panel.axis-x +net sy halui.axis.y.select panel.axis-y +net sz halui.axis.z.select panel.axis-z + +net sx axis.x.jog-enable +net sy axis.y.jog-enable +net sz axis.z.jog-enable +net sgui halui.gui.mpg-select.0 panel.select-gui0 + +net jog-p halui.axis.selected.plus panel.jog-pos +net jog-m halui.axis.selected.minus panel.jog-neg + +net mpg-scale axis.x.jog-scale panel.mpg-scale +net mpg-scale axis.y.jog-scale +net mpg-scale axis.z.jog-scale + +net mpg-count panel.mpg-wheel-s +net mpg-count gmoccapy.mpg-in +net mpg-count axis.x.jog-counts +net mpg-count axis.y.jog-counts +net mpg-count axis.z.jog-counts + +net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0 +net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1 +net m2 halui.gui.mdi-command-MACRO2 panel.mdi-2 + +net man panel.manual-mode halui.mode.manual +net mdi panel.mdi-mode halui.mode.mdi +net auto panel.auto-mode halui.mode.auto + +net pause halui.gui.cycle.start panel.cycle-start +net start halui.gui.cycle.pause panel.cycle-pause +net abort halui.abort panel.cycle-abort + +net cancel halui.gui.cancel panel.cancel +net ok halui.gui.ok panel.ok + +net softkey0 halui.gui.softkey-00 panel.softkey-v0 +net softkey1 halui.gui.softkey-01 panel.softkey-v1 +net softkey2 halui.gui.softkey-02 panel.softkey-v2 +net softkey3 halui.gui.softkey-03 panel.softkey-v3 +net softkey4 halui.gui.softkey-04 panel.softkey-v4 +net softkey5 halui.gui.softkey-05 panel.softkey-v5 +net softkey6 halui.gui.softkey-06 panel.softkey-v6 +net softkeyh0 halui.gui.softkey-10 panel.softkey-h0 +net softkeyh1 halui.gui.softkey-11 panel.softkey-h1 +net softkeyh2 halui.gui.softkey-12 panel.softkey-h2 +net softkeyh3 halui.gui.softkey-13 panel.softkey-h3 +net softkeyh4 halui.gui.softkey-14 panel.softkey-h4 +net softkeyh5 halui.gui.softkey-15 panel.softkey-h5 +net softkeyh6 halui.gui.softkey-16 panel.softkey-h6 +net softkeyh7 halui.gui.softkey-17 panel.softkey-h7 +net softkeyh8 halui.gui.softkey-18 panel.softkey-h8 +net softkeyh9 halui.gui.softkey-19 panel.softkey-h9 + +net exit halui.gui.shutdown panel.exit +net reload halui.gui.reload-display panel.reload diff --git a/configs/sim/gmoccapy/panel.ui b/configs/sim/gmoccapy/panel.ui new file mode 100644 index 00000000000..cf614f0bb7f --- /dev/null +++ b/configs/sim/gmoccapy/panel.ui @@ -0,0 +1,1407 @@ + + + MainWindow + + + + 0 + 0 + 874 + 756 + + + + MainWindow + + + + + + + + + + + + ESTOP + + + false + + + false + + + false + + + false + + + false + + + + 255 + 0 + 0 + + + + 0 + + + + 0 + 0 + 0 + + + + 0.300000000000000 + + + 10 + + + 0 + + + 0 + + + 5.000000000000000 + + + 0.300000000000000 + + + 0.900000000000000 + + + True + + + False + + + print("true command") + + + print("false command") + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + 0 + + + true + + + false + + + -1 + + + 0.010000000000000 + + + 0.025000000000000 + + + -1.000000000000000 + + + false + + + 0.300000000000000 + + + 50.000000000000000 + + + P + + + + + + 0 + + + %1.3f in + + + %1.2f mm + + + + + + + ON + + + false + + + false + + + false + + + false + + + false + + + + 255 + 0 + 0 + + + + 0 + + + + 0 + 0 + 0 + + + + 0.300000000000000 + + + 10 + + + 0 + + + 0 + + + 5.000000000000000 + + + 0.300000000000000 + + + 0.900000000000000 + + + True + + + False + + + print("true command") + + + print("false command") + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + 0 + + + true + + + false + + + -1 + + + 0.010000000000000 + + + 0.025000000000000 + + + -1.000000000000000 + + + false + + + 0.300000000000000 + + + 50.000000000000000 + + + P + + + + + + 0 + + + %1.3f in + + + %1.2f mm + + + + + + + HOME + + + false + + + false + + + false + + + false + + + false + + + + 255 + 0 + 0 + + + + 0 + + + + 0 + 0 + 0 + + + + 0.300000000000000 + + + 10 + + + 0 + + + 0 + + + 5.000000000000000 + + + 0.300000000000000 + + + 0.900000000000000 + + + True + + + False + + + print("true command") + + + print("false command") + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + 0 + + + true + + + false + + + -1 + + + 0.010000000000000 + + + 0.025000000000000 + + + -1.000000000000000 + + + false + + + 0.300000000000000 + + + 50.000000000000000 + + + P + + + + + + 0 + + + %1.3f in + + + %1.2f mm + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + softKey +V0 + + + softkey-v0 + + + PushButton::BIT + + + + + + + SoftKey +V1 + + + softkey-v1 + + + PushButton::BIT + + + + + + + SoftKey +V2 + + + softkey-v2 + + + PushButton::BIT + + + + + + + SoftKey +V3 + + + softkey-v3 + + + PushButton::BIT + + + + + + + SoftKey +V4 + + + softkey-v4 + + + PushButton::BIT + + + + + + + SoftKey +V5 + + + softkey-v5 + + + PushButton::BIT + + + + + + + SoftKey +V6 + + + softkey-v6 + + + PushButton::BIT + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Reload + + + reload + + + PushButton::BIT + + + + + + + SYSTEM +Exit + + + exit + + + PushButton::BIT + + + + + + + + + + + + + Axis Jog + + + + + + + + + + jog-pos + + + + + + + - + + + jog-neg + + + + + + + + + + linear rate / angular rate + + + + + + Qt::Horizontal + + + jog-rate + + + true + + + + + + + Qt::Horizontal + + + jog-rate-angular + + + false + + + true + + + + + + + + + + + + + + MPG + + + + + + .001 + + + true + + + true + + + true + + + mpg-scale-small + + + true + + + PushButton::FLOAT + + + mpg-scale + + + 0.001000000000000 + + + buttonGroup_mpgscale + + + + + + + .01 + + + true + + + true + + + mpg-scale-med + + + true + + + PushButton::FLOAT + + + mpg-scale + + + 0.010000000000000 + + + buttonGroup_mpgscale + + + + + + + .1 + + + true + + + true + + + mpg-scale-large + + + true + + + PushButton::FLOAT + + + mpg-scale + + + 0.100000000000000 + + + buttonGroup_mpgscale + + + + + + + + + + + 0 + 74 + + + + false + + + true + + + false + + + mpg-wheel + + + + + + + + + MDI Comands + + + + + + 0 + + + mdi-0 + + + + + + + 1 + + + mdi-1 + + + + + + + 2 + + + mdi-2 + + + + + + + + + + Mode Comands + + + + + + Manual + + + false + + + true + + + manual-mode + + + true + + + true + + + true + + + + + + + MDI + + + false + + + true + + + mdi-mode + + + true + + + true + + + true + + + + + + + Auto + + + false + + + true + + + auto-mode + + + true + + + true + + + true + + + + + + + + + + program control + + + + + + start + + + cycle-start + + + + + + + pause + + + cycle-pause + + + + + + + Abort + + + cycle-abort + + + + + + + + + + dialog control + + + + + + ok + + + ok + + + + + + + cancel + + + cancel + + + + + + + + + + + + Axis Selection + + + + 0 + + + 3 + + + 0 + + + 0 + + + 4 + + + + + None + + + true + + + true + + + axis-none + + + + + + + A + + + true + + + true + + + axis-a + + + + + + + Y + + + true + + + true + + + axis-y + + + + + + + X + + + true + + + true + + + axis-x + + + + + + + Z + + + true + + + true + + + axis-z + + + + + + + B + + + true + + + true + + + axis-b + + + + + + + C + + + true + + + true + + + axis-c + + + + + + + GUI + + + true + + + true + + + select-gui0 + + + + + + + + + + + + H0 + + + softkey-h0 + + + PushButton::BIT + + + + + + + H1 + + + softkey-h1 + + + PushButton::BIT + + + + + + + H2 + + + softkey-h2 + + + PushButton::BIT + + + + + + + H3 + + + softkey-h3 + + + PushButton::BIT + + + + + + + H4 + + + softkey-h4 + + + PushButton::BIT + + + + + + + H5 + + + softkey-h5 + + + PushButton::BIT + + + + + + + H6 + + + softkey-h6 + + + PushButton::BIT + + + + + + + H7 + + + softkey-h7 + + + PushButton::BIT + + + + + + + H8 + + + softkey-h8 + + + PushButton::BIT + + + + + + + H9 + + + softkey-h9 + + + PushButton::BIT + + + + + + + + + + + IndicatedPushButton + QPushButton +
qtvcp.widgets.simple_widgets
+
+ + PushButton + QPushButton +
qtvcp.widgets.simple_widgets
+
+ + Dial + QDial +
qtvcp.widgets.simple_widgets
+
+ + ActionButton + IndicatedPushButton +
qtvcp.widgets.action_button
+
+ + StatusSlider + QSlider +
qtvcp.widgets.status_slider
+
+
+ + + + + +
From 9e4657e9a3f78a8002114be9884998d1e8bd61b5 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 26 Sep 2025 22:27:26 -0700 Subject: [PATCH 54/81] axis sim -add a sim test for halui messages axis -add softkey, exit and reload actions axis -allow MPG scrolling of gcode axis -add mdi/macro calls from halui --- configs/sim/axis/axis_halui_test.ini | 237 +++ configs/sim/axis/gstatmessages.py | 163 ++ configs/sim/axis/macros/go_to_position.ngc | 24 + configs/sim/axis/macros/hello_world.ngc | 24 + configs/sim/axis/macros/increment.ngc | 22 + configs/sim/axis/macros/lost.ngc | 24 + .../sim/axis/macros/macro_Instructions.txt | 39 + configs/sim/axis/macros/move_around.ngc | 28 + configs/sim/axis/panel.hal | 53 + configs/sim/axis/panel.ui | 1328 +++++++++++++++++ configs/sim/gmoccapy/gmoccapy_halui_test.ini | 2 +- 11 files changed, 1943 insertions(+), 1 deletion(-) create mode 100644 configs/sim/axis/axis_halui_test.ini create mode 100644 configs/sim/axis/gstatmessages.py create mode 100644 configs/sim/axis/macros/go_to_position.ngc create mode 100644 configs/sim/axis/macros/hello_world.ngc create mode 100644 configs/sim/axis/macros/increment.ngc create mode 100644 configs/sim/axis/macros/lost.ngc create mode 100644 configs/sim/axis/macros/macro_Instructions.txt create mode 100644 configs/sim/axis/macros/move_around.ngc create mode 100644 configs/sim/axis/panel.hal create mode 100644 configs/sim/axis/panel.ui diff --git a/configs/sim/axis/axis_halui_test.ini b/configs/sim/axis/axis_halui_test.ini new file mode 100644 index 00000000000..2e53fb8275a --- /dev/null +++ b/configs/sim/axis/axis_halui_test.ini @@ -0,0 +1,237 @@ +# EMC controller parameters for a simulated machine. + +# General note: Comments can either be preceded with a # or ; - either is +# acceptable, although # is in keeping with most linux config files. + +# General section ------------------------------------------------------------- +[EMC] + +# Version of this INI file +VERSION = 1.1 + +# Name of machine, for use with display, etc. +MACHINE = LinuxCNC-HAL-SIM-AXIS + +# Debug level, 0 means no messages. See src/emc/nml_int/emcglb.h for others +#DEBUG = 0x7FFFFFFF +DEBUG = 0 + +# Sections for display options ------------------------------------------------ +[DISPLAY] + +# Name of display program, e.g., axis +DISPLAY = axis + +# Cycle time, in seconds, that display will sleep between polls +CYCLE_TIME = 0.100 + +# Path to help file +HELP_FILE = doc/help.txt + +# Initial display setting for position, RELATIVE or MACHINE +POSITION_OFFSET = RELATIVE + +# Initial display setting for position, COMMANDED or ACTUAL +POSITION_FEEDBACK = ACTUAL + +# Highest value that will be allowed for feed override, 1.0 = 100% +MAX_FEED_OVERRIDE = 1.2 +MAX_SPINDLE_OVERRIDE = 1.0 + +MAX_LINEAR_VELOCITY = 5 +DEFAULT_LINEAR_VELOCITY = .25 +DEFAULT_SPINDLE_SPEED = 200 +# Prefix to be used +PROGRAM_PREFIX = ../../nc_files/ + +# Introductory graphic +INTRO_GRAPHIC = linuxcnc.gif +INTRO_TIME = 5 + +#EDITOR = geany +TOOL_EDITOR = tooledit + +INCREMENTS = 1 in, 0.1 in, 10 mil, 1 mil, 1mm, .1mm, 1/8000 in + +USER_COMMAND_FILE=gstatmessages.py + +[MDI_COMMAND_LIST] +MDI_COMMAND_MACRO0 = G53 G0 Z0;G0 X0 Y0;Z0, Goto\nUser\nZero +MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero + +[MACROS] +MACRO_COMMAND_MACRO6 = go_to_position x-pos y-pos z-pos, Go To \nPosition +MACRO_COMMAND_MACRO2 = lost, LOST +MACRO_COMMAND_MACRO3 = increment x-incr y-incr, INCR +MACRO_COMMAND_MACRO4 = macro_4 +MACRO_COMMAND_MACRO5 = macro_5 + +[FILTER] +PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image +PROGRAM_EXTENSION = .py Python Script + +png = image-to-gcode +gif = image-to-gcode +jpg = image-to-gcode +py = python3 + +# Task controller section ----------------------------------------------------- +[TASK] + +# Name of task controller program, e.g., milltask +TASK = milltask + +# Cycle time, in seconds, that task controller will sleep between polls +CYCLE_TIME = 0.001 + +# Part program interpreter section -------------------------------------------- +[RS274NGC] + +# File containing interpreter variables +PARAMETER_FILE = sim.var +SUBROUTINE_PATH = ./macros + +# Motion control section ------------------------------------------------------ +[EMCMOT] + +EMCMOT = motmod + +# Timeout for comm to emcmot, in seconds +COMM_TIMEOUT = 1.0 + +# BASE_PERIOD is unused in this configuration but specified in core_sim.hal +BASE_PERIOD = 0 +# Servo task period, in nano-seconds +SERVO_PERIOD = 1000000 + +# section for main IO controller parameters ----------------------------------- +[EMCIO] +# tool table file +TOOL_TABLE = sim.tbl +TOOL_CHANGE_POSITION = 0 0 0 +TOOL_CHANGE_QUILL_UP = 1 + +# Hardware Abstraction Layer section -------------------------------------------------- +[HAL] + +# The run script first uses halcmd to execute any HALFILE +# files, and then to execute any individual HALCMD commands. +# + +# list of hal config files to run through halcmd +# files are executed in the order in which they appear +HALFILE = core_sim.hal +HALFILE = sim_spindle_encoder.hal +HALFILE = axis_manualtoolchange.hal +HALFILE = simulated_home.hal +HALFILE = check_xyz_constraints.hal +HALFILE = cooling.hal + +# list of halcmd commands to execute +# commands are executed in the order in which they appear +#HALCMD = save neta + +# Single file that is executed after the GUI has started. Only supported by +# AXIS at this time (only AXIS creates a HAL component of its own) +#POSTGUI_HALFILE = test_postgui.hal +POSTGUI_HALCMD = loadusr qtvcp -a -H panel.hal panel + +HALUI = halui + +# Trajectory planner section -------------------------------------------------- +[TRAJ] +COORDINATES = X Y Z +LINEAR_UNITS = inch +ANGULAR_UNITS = degree +MAX_LINEAR_VELOCITY = 4 +DEFAULT_LINEAR_ACCELERATION = 100 +MAX_LINEAR_ACCELERATION = 100 +POSITION_FILE = position.txt + +[KINS] +KINEMATICS = trivkins +JOINTS = 3 + +# Axes sections --------------- +[AXIS_X] +MAX_VELOCITY = 4 +MAX_ACCELERATION = 100.0 +MIN_LIMIT = -10.0 +MAX_LIMIT = 10.0 + +[AXIS_Y] +MAX_VELOCITY = 4 +MAX_ACCELERATION = 100.0 +MIN_LIMIT = -10.0 +MAX_LIMIT = 10.0 + +[AXIS_Z] +MAX_VELOCITY = 4 +MAX_ACCELERATION = 100.0 +MIN_LIMIT = -8.0 +MAX_LIMIT = 0.12 + +# Joints sections ------------- +[JOINT_0] +TYPE = LINEAR +HOME = 0.000 +MAX_VELOCITY = 5 +MAX_ACCELERATION = 50.0 +BACKLASH = 0.000 +INPUT_SCALE = 4000 +OUTPUT_SCALE = 1.000 +FERROR = 0.050 +MIN_FERROR = 0.010 +MIN_LIMIT = -10.0 +MAX_LIMIT = 10.0 +HOME_OFFSET = 0.0 +HOME_SEARCH_VEL = 20.0 +HOME_LATCH_VEL = 20.0 +HOME_USE_INDEX = NO +HOME_IGNORE_LIMITS = NO +HOME_SEQUENCE = 1 +HOME_IS_SHARED = 1 + +[JOINT_1] +TYPE = LINEAR +HOME = 0.000 +MAX_VELOCITY = 5 +MAX_ACCELERATION = 50.0 +BACKLASH = 0.000 +INPUT_SCALE = 4000 +OUTPUT_SCALE = 1.000 +FERROR = 0.050 +MIN_FERROR = 0.010 +MIN_LIMIT = -10.0 +MAX_LIMIT = 10.0 +HOME_OFFSET = 0.0 +HOME_SEARCH_VEL = 20.0 +HOME_LATCH_VEL = 20.0 +HOME_USE_INDEX = NO +HOME_IGNORE_LIMITS = NO +HOME_SEQUENCE = 1 + +[JOINT_2] +TYPE = LINEAR +HOME = 0.0 +MAX_VELOCITY = 5 +MAX_ACCELERATION = 50.0 +BACKLASH = 0.000 +INPUT_SCALE = 4000 +OUTPUT_SCALE = 1.000 +MIN_LIMIT = -8.0 + +# Normally the Z max should be 0.000! +# The only reason it's greater than 0 here is so that the splash screen +# gcode will run. +MAX_LIMIT = 0.12 + +FERROR = 0.050 +MIN_FERROR = 0.010 +HOME_OFFSET = 1.0 +HOME_SEARCH_VEL = 20.0 +HOME_LATCH_VEL = 20.0 +HOME_USE_INDEX = NO +HOME_IGNORE_LIMITS = NO +HOME_SEQUENCE = 0 +HOME_IS_SHARED = 1 diff --git a/configs/sim/axis/gstatmessages.py b/configs/sim/axis/gstatmessages.py new file mode 100644 index 00000000000..702c1b6e0ba --- /dev/null +++ b/configs/sim/axis/gstatmessages.py @@ -0,0 +1,163 @@ + +from hal_glib import GStat +from common.iniinfo import _IStat as IStatParent + +class Info(IStatParent): + _instance = None + _instanceNum = 0 + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = IStatParent.__new__(cls, *args, **kwargs) + return cls._instance + +INFO = Info() + +GSTAT = GStat() +GSTAT.forced_update() +GSTAT.connect('jograte-changed', lambda w, data: vars.jog_speed.set(data)) +GSTAT.connect('axis-selection-changed', lambda w,data: select_axis(data)) +GSTAT.connect('cycle-start-request', lambda w, state : cycle_start_request(state)) +GSTAT.connect('cycle-pause-request', lambda w, state: pause_request(state)) +GSTAT.connect('ok-request', lambda w, state: dialog_ext_control(w,1,1)) +GSTAT.connect('cancel-request', lambda w, state: dialog_ext_control(w,1,0)) +GSTAT.connect('macro-call-request', lambda w, name: request_macro_call(name)) +GSTAT.connect('softkey-pressed', lambda w,data: softkey_pressed(data)) +GSTAT.connect('shutdown-request', lambda w : General_Halt()) +GSTAT.connect('reload-display', lambda w : commands.clear_live_plot()) + +global last_mpg +last_mpg = 0 +mpg_enabled = 0 + + +def user_hal_pins(): + comp.newpin('mpg-enable', hal.HAL_BIT, hal.HAL_IN) + comp.newpin('mpg-in', hal.HAL_S32, hal.HAL_IN) + comp.ready() + +def user_live_update(): + GSTAT.run_iteration() + global mpg_enabled + try: + if comp['mpg-enable'] or mpg_enabled: + global last_mpg + if comp['mpg-in'] == last_mpg: return + if comp['mpg-in'] > last_mpg:scroll_up(None) + if comp['mpg-in'] < last_mpg:scroll_down(None) + last_mpg = comp['mpg-in'] + except Exception as e: + print(e) + +def select_axis(data): + global mpg_enabled + if data is None: return + if data =='MPG0': + mpg_enabled = True + return + mpg_enabled = False + if data.upper() =='NONE': + return + try: + widget = getattr(widgets, "axis_%s" % data.lower()) + widget.focus() + widget.invoke() + except: + pass + +def cycle_start_request(state): + print('cycle start',state) + commands.task_run(None) + +def pause_request(state): + print('cycle pause',state) + commands.task_pauseresume(None) + +def dialog_ext_control(widget,t,state): + print('dialog control',widget,state) + + flag = False + for child in root_window.winfo_children(): + #print(child) + if isinstance(child, Tkinter.Toplevel): + #print(f"Found a Toplevel window: {child}") + if '.!toplevel' in str(child): + #print('sending command:',child) + for child2 in child.winfo_children(): + #print(child2) + if isinstance(child2, Tkinter.Frame): + for child3 in child2.winfo_children(): + #print(child3) + if isinstance(child3, Tkinter.Button): + #print(dir(child3)) + txt = child3.cget("text") + if txt.lower() == 'ok' and state: + #print('Ok') + child3.invoke() + flag = True + break + elif txt.lower() == 'cancel' and not state: + #print('Cancel') + child3.invoke() + flag = True + break + if flag: break + if flag: break + else: + #print('No window') + # remove one error message + if state == 0: + notifications.clear_one() + +def request_macro_call(name): + #print('request macro:',name) + cmd = INFO.get_ini_mdi_command(name) + #print(f'MDI command:{cmd} name:{name}') + if not INFO.get_ini_mdi_command(name) is None: + run_mdi(data=cmd) + else: + #print(INFO.get_ini_macro_command(name)) + try: + temp = INFO.MACRO_COMMAND_DICT.get(name).get('cmd') + #print(temp) + run_macro(data=temp) + except Exception as e: + print(e) + +def run_mdi(data): + #print(f'run mdi command:{data}') + mdi_list = data.split(';') + for code in (mdi_list): + commands.send_mdi_command(code) + +def run_macro(data): + #print(f'run macro:{data}') + o_codes = data.split() + command = str( "O<" + o_codes[0] + "> call" ) + # check for oword and confirm path exists + #if not self.check_macro_path(command): + # return + for code in o_codes[1:]: + if vars.metric.get(): unit_str = " " + _("mm") + else: unit_str = " " + _("in") + param = prompt_float("Macro", f"Enter a value for: {code}:", + "", unit_str) + if param <= 0: return + if vars.metric.get(): param /= 25.4 + command = command + " [" + str(param) + "] " + commands.send_mdi_command(command) + +def softkey_pressed(index): + + if index == 0: + root_window.tk.call('.pane.top.tabs','raise','manual') + elif index == 1: + root_window.tk.call('.pane.top.tabs','raise','mdi') + elif index == 2: + root_window.tk.call('.pane.top.right','raise','preview') + elif index == 3: + root_window.tk.call('.pane.top.right','raise','numbers') + elif index == 4: + root_window.tk.call('.pane.top.right','raise','user_0') + else: + print(f'Softkey index:{index}') diff --git a/configs/sim/axis/macros/go_to_position.ngc b/configs/sim/axis/macros/go_to_position.ngc new file mode 100644 index 00000000000..19b79e2a1a3 --- /dev/null +++ b/configs/sim/axis/macros/go_to_position.ngc @@ -0,0 +1,24 @@ +; Testfile go to position +; will jog the machine to a given position + +O sub + +G17 +G21 +G54 +G61 +G40 +G49 +G80 +G90 + +;#1 = +;#2 = +;#3 = + +(DEBUG, Will now move machine to %fX = #1 , Y = #2 , Z = #3) +G0 X #1 Y #2 Z #3 + +O endsub + +M2 diff --git a/configs/sim/axis/macros/hello_world.ngc b/configs/sim/axis/macros/hello_world.ngc new file mode 100644 index 00000000000..c24424e0695 --- /dev/null +++ b/configs/sim/axis/macros/hello_world.ngc @@ -0,0 +1,24 @@ +; Testfile "hello world" +; will just give messages + +O sub + +G17 +G21 +G54 +G61 +G40 +G49 +G80 +G90 + +G0 X10 + +(MSG, Hello World) + +G0X-10 + +O endsub + +M2 + diff --git a/configs/sim/axis/macros/increment.ngc b/configs/sim/axis/macros/increment.ngc new file mode 100644 index 00000000000..af0da2376f3 --- /dev/null +++ b/configs/sim/axis/macros/increment.ngc @@ -0,0 +1,22 @@ +; Testfile "increment" +; will move the machine in relative coordinates + +O sub + +G17 +G21 +G54 +G61 +G40 +G49 +G80 +G90 + +G91 G0 X#1 Y#2 +G90 + +(DEBUG, %fX was [#1] and Y was [#2]) + +O endsub + +M2 diff --git a/configs/sim/axis/macros/lost.ngc b/configs/sim/axis/macros/lost.ngc new file mode 100644 index 00000000000..51897355866 --- /dev/null +++ b/configs/sim/axis/macros/lost.ngc @@ -0,0 +1,24 @@ +; Testfile Lost +; will jog to machine zero and set all axis to zero + +O sub + +G17 +G21 +G54 +G61 +G40 +G49 +G80 +G90 + +(MSG, Will now move to machine zero) +G53 G0 X0 Y0 Z0 +(MSG, will now set all axis to zero) +G10 L20 P0 X0 Y0 Z0 +(MSG, all done) + + +O endsub + +M2 diff --git a/configs/sim/axis/macros/macro_Instructions.txt b/configs/sim/axis/macros/macro_Instructions.txt new file mode 100644 index 00000000000..6bb07a5d7a6 --- /dev/null +++ b/configs/sim/axis/macros/macro_Instructions.txt @@ -0,0 +1,39 @@ +This is a small instruction to include macros in Qtdragon. + +In your INI File you need to introduce a section called [MACROS] +and for every macro you'll need to include a one-liner like so: + +MACRO = jog_around ,MOVE\nTHERE +or +MACRO = increment xinc yinc ,INCR + +where xinc and yinc are placeholders +Anything after the comma is the button text. + +During execution of the macro, you will be asked to enter the values. + +You are allowed to introduce 9 macros! +If you enter more macros, only the first 9 will appear with button in gmoccapy. + +In the [RS274NGC] section you may want to give a path to your macros like so: + +[RS274NGC] +SUBROUTINE_PATH = nc_files/subroutines + +or you place your macros in the nc_files folder. + +Each macro must have it's own file in one of the mentioned folders and they are normal subroutines, so the must begin with: + +O sub + +and end with + +O endsub +M2 + +The name of the file must be jog_around.ngc. +And an macro must contain at least one movement of one axis. + +macro name in INI file have to be the same as the file name and the sub must have also the same name (case sensitive!)! + + diff --git a/configs/sim/axis/macros/move_around.ngc b/configs/sim/axis/macros/move_around.ngc new file mode 100644 index 00000000000..0c8e3b69322 --- /dev/null +++ b/configs/sim/axis/macros/move_around.ngc @@ -0,0 +1,28 @@ +; Testfile "move around" +; will just move a little bit around + +O sub + +G17 +G21 +G54 +G61 +G40 +G49 +G80 +G90 + +G91 G0 X 25 +Y-25 +Z-25 +Y25 +X-25 +Z25 +F250 +G2 I 25 + +(MSG, It is done!) + +O endsub + +M2 diff --git a/configs/sim/axis/panel.hal b/configs/sim/axis/panel.hal new file mode 100644 index 00000000000..04ffc3a6167 --- /dev/null +++ b/configs/sim/axis/panel.hal @@ -0,0 +1,53 @@ +net rate halui.axis.jog-speed panel.jog-rate + +net sx halui.axis.x.select panel.axis-x +net sx axis.x.jog-enable +net sy halui.axis.y.select panel.axis-y +net sy axis.y.jog-enable +net sz halui.axis.z.select panel.axis-z +net sz axis.z.jog-enable +net sgui halui.gui.mpg-select.0 panel.select-gui0 + +net mpg-scale axis.x.jog-scale panel.mpg-scale +net mpg-scale axis.y.jog-scale +net mpg-scale axis.z.jog-scale + +net mpg-count panel.mpg-wheel-s +net mpg-count axisui.mpg-in +net mpg-count axis.x.jog-counts +net mpg-count axis.y.jog-counts +net mpg-count axis.z.jog-counts + +net jog-p halui.axis.selected.plus panel.jog-pos +net jog-m halui.axis.selected.minus panel.jog-neg + +net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0 +net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1 +net m2 halui.gui.mdi-command-MACRO3 panel.mdi-2 + +net man panel.manual-mode halui.mode.manual +net mdi panel.mdi-mode halui.mode.mdi +net auto panel.auto-mode halui.mode.auto + +net pause halui.gui.cycle.start panel.cycle-start +net start halui.gui.cycle.pause panel.cycle-pause +net abort halui.abort panel.cycle-abort + +net cancel halui.gui.cancel panel.cancel +net ok halui.gui.ok panel.ok + +net softkey0 halui.gui.softkey-00 panel.softkey-0 +net softkey1 halui.gui.softkey-01 panel.softkey-1 +net softkey2 halui.gui.softkey-02 panel.softkey-2 +net softkey3 halui.gui.softkey-03 panel.softkey-3 +net softkey4 halui.gui.softkey-04 panel.softkey-4 +net softkey5 halui.gui.softkey-05 panel.softkey-5 +net softkey6 halui.gui.softkey-06 panel.softkey-6 +net softkey7 halui.gui.softkey-07 panel.softkey-7 +net softkey8 halui.gui.softkey-08 panel.softkey-8 +net softkey9 halui.gui.softkey-09 panel.softkey-9 +net softkey10 halui.gui.softkey-10 panel.softkey-10 +net softkey11 halui.gui.softkey-11 panel.softkey-11 + +net exit halui.gui.shutdown panel.exit +net reload halui.gui.reload-display panel.reload diff --git a/configs/sim/axis/panel.ui b/configs/sim/axis/panel.ui new file mode 100644 index 00000000000..164338d0144 --- /dev/null +++ b/configs/sim/axis/panel.ui @@ -0,0 +1,1328 @@ + + + MainWindow + + + + 0 + 0 + 588 + 706 + + + + MainWindow + + + + + + + + + + + Axis Jog + + + + + + + + + + jog-pos + + + + + + + - + + + jog-neg + + + + + + + + + + linear rate / angular rate + + + + + + Qt::Horizontal + + + jog-rate + + + true + + + + + + + Qt::Horizontal + + + jog-rate-angular + + + false + + + true + + + + + + + + + + + + + + MPG + + + + + + .001 + + + true + + + true + + + true + + + mpg-scale-small + + + true + + + PushButton::FLOAT + + + mpg-scale + + + 0.001000000000000 + + + buttonGroup_mpgscale + + + + + + + .01 + + + true + + + true + + + mpg-scale-med + + + true + + + PushButton::FLOAT + + + mpg-scale + + + 0.010000000000000 + + + buttonGroup_mpgscale + + + + + + + .1 + + + true + + + true + + + mpg-scale-large + + + true + + + PushButton::FLOAT + + + mpg-scale + + + 0.100000000000000 + + + buttonGroup_mpgscale + + + + + + + + + + + 0 + 74 + + + + false + + + true + + + false + + + mpg-wheel + + + + + + + + + MDI Comands + + + + + + 0 + + + mdi-0 + + + + + + + 1 + + + mdi-1 + + + + + + + 2 + + + mdi-2 + + + + + + + + + + Mode Comands + + + + + + Manual + + + false + + + true + + + manual-mode + + + true + + + true + + + true + + + + + + + MDI + + + false + + + true + + + mdi-mode + + + true + + + true + + + true + + + + + + + Auto + + + false + + + true + + + auto-mode + + + true + + + true + + + true + + + + + + + + + + program control + + + + + + start + + + cycle-start + + + + + + + pause + + + cycle-pause + + + + + + + Abort + + + cycle-abort + + + + + + + + + + dialog control + + + + + + ok + + + ok + + + + + + + cancel + + + cancel + + + + + + + + + + + + Axis Selection + + + + 0 + + + 3 + + + 0 + + + 0 + + + 4 + + + + + None + + + true + + + true + + + axis-none + + + + + + + A + + + true + + + true + + + axis-a + + + + + + + Y + + + true + + + true + + + axis-y + + + + + + + X + + + true + + + true + + + axis-x + + + + + + + Z + + + true + + + true + + + axis-z + + + + + + + B + + + true + + + true + + + axis-b + + + + + + + C + + + true + + + true + + + axis-c + + + + + + + GUI + + + true + + + true + + + select-gui0 + + + + + + + + + + + + ESTOP + + + false + + + false + + + false + + + false + + + false + + + + 255 + 0 + 0 + + + + 0 + + + + 0 + 0 + 0 + + + + 0.300000000000000 + + + 10 + + + 0 + + + 0 + + + 5.000000000000000 + + + 0.300000000000000 + + + 0.900000000000000 + + + True + + + False + + + print("true command") + + + print("false command") + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + 0 + + + true + + + false + + + -1 + + + 0.010000000000000 + + + 0.025000000000000 + + + -1.000000000000000 + + + false + + + 0.300000000000000 + + + 50.000000000000000 + + + P + + + + + + 0 + + + %1.3f in + + + %1.2f mm + + + + + + + ON + + + false + + + false + + + false + + + false + + + false + + + + 255 + 0 + 0 + + + + 0 + + + + 0 + 0 + 0 + + + + 0.300000000000000 + + + 10 + + + 0 + + + 0 + + + 5.000000000000000 + + + 0.300000000000000 + + + 0.900000000000000 + + + True + + + False + + + print("true command") + + + print("false command") + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + 0 + + + true + + + false + + + -1 + + + 0.010000000000000 + + + 0.025000000000000 + + + -1.000000000000000 + + + false + + + 0.300000000000000 + + + 50.000000000000000 + + + P + + + + + + 0 + + + %1.3f in + + + %1.2f mm + + + + + + + HOME + + + false + + + false + + + false + + + false + + + false + + + + 255 + 0 + 0 + + + + 0 + + + + 0 + 0 + 0 + + + + 0.300000000000000 + + + 10 + + + 0 + + + 0 + + + 5.000000000000000 + + + 0.300000000000000 + + + 0.900000000000000 + + + True + + + False + + + print("true command") + + + print("false command") + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + 0 + + + true + + + false + + + -1 + + + 0.010000000000000 + + + 0.025000000000000 + + + -1.000000000000000 + + + false + + + 0.300000000000000 + + + 50.000000000000000 + + + P + + + + + + 0 + + + %1.3f in + + + %1.2f mm + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Manual + + + softkey-0 + + + PushButton::BIT + + + + + + + MDI + + + softkey-1 + + + PushButton::BIT + + + + + + + Preview + + + softkey-2 + + + PushButton::BIT + + + + + + + DRO + + + softkey-3 + + + PushButton::BIT + + + + + + + User +Tab + + + softkey-4 + + + PushButton::BIT + + + + + + + + + + softkey-5 + + + PushButton::BIT + + + + + + + + + + softkey-6 + + + PushButton::BIT + + + + + + + + + + softkey-7 + + + PushButton::BIT + + + + + + + + + + softkey-8 + + + PushButton::BIT + + + + + + + + + + softkey-9 + + + PushButton::BIT + + + + + + + + + + softkey-10 + + + PushButton::BIT + + + + + + + + + + softkey-11 + + + PushButton::BIT + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Reload + + + reload + + + PushButton::BIT + + + + + + + exit + + + exit + + + PushButton::BIT + + + + + + + + + + + IndicatedPushButton + QPushButton +
qtvcp.widgets.simple_widgets
+
+ + PushButton + QPushButton +
qtvcp.widgets.simple_widgets
+
+ + Dial + QDial +
qtvcp.widgets.simple_widgets
+
+ + ActionButton + IndicatedPushButton +
qtvcp.widgets.action_button
+
+ + StatusSlider + QSlider +
qtvcp.widgets.status_slider
+
+
+ + + + + +
diff --git a/configs/sim/gmoccapy/gmoccapy_halui_test.ini b/configs/sim/gmoccapy/gmoccapy_halui_test.ini index 27579c0b389..8f628a1c1f0 100644 --- a/configs/sim/gmoccapy/gmoccapy_halui_test.ini +++ b/configs/sim/gmoccapy/gmoccapy_halui_test.ini @@ -101,7 +101,7 @@ HALUI = halui [MDI_COMMAND_LIST] # for macro buttons on main oage up to 10 possible -MDI_COMMAND_MACRO0 = G0 Z1;X0 Y0;Z0, Goto\nUser\nZero +MDI_COMMAND_MACRO0 = G53 G0 Z0;X0 Y0;Z0, Goto\nUser\nZero MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero MDI_COMMAND_MACRO2 = (MSG, macro 2); MDI_COMMAND_MACRO3 = (MSG, macro 2) From def65f79d6247ee606c47d38c2ce4fcc99d7bc58 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Sun, 19 Jul 2026 21:27:51 -0700 Subject: [PATCH 55/81] gmoccapy -add docs for halui messages --- docs/src/gui/gmoccapy.adoc | 58 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/docs/src/gui/gmoccapy.adoc b/docs/src/gui/gmoccapy.adoc index 67cd9331bd3..c49e30bfb96 100644 --- a/docs/src/gui/gmoccapy.adoc +++ b/docs/src/gui/gmoccapy.adoc @@ -935,6 +935,64 @@ net tooloffset-z gmoccapy.tooloffset-z <= motion.tooloffset.z Please note, that GMOCCAPY takes care of its own to update the offsets, sending an G43 after any tool change, *but not in auto mode!* + So writing a program makes you responsible to include an G43 after each tool change! +== HALUI +HALUI will create halui.gui pins that interface with Gmoccapy. + +- There is a cycle start and pause pin - these call the code in Gmoccapy rather then the motion controller. ie if in MDI mode the MDI command will be run with a cycle start.+ +- If there are macros defined in the INI there will be (up to 64) pins available to initiate them. + +- clear/reload the display +- shutdown the linux system +- ok/cancel of dialogs and notify (error) messages +- softkeys 0 - 11 control the main tab keys: + +halui.gui.softkey-00 - halui.gui.softkey-06 are the vertical keys. + +halui.gui.softkey-10 - halui.gui.softkey-09 are the horizontal keys. + +also: + +- enabling halui.gui.mpg-select.0, will allow zooming of the gcode plot with the gmoccapy.mpg-in pin. +- HALUI selected Axis will be reported to Gmoccapy. + +- HALUI selected jog rate will be reported to Gmoccapy. + +In either case the last changed rate/axis (either HALUI or Gmoccapy) will be used for screen button jogging or HALUI pin jogging. + +.Typical HAL pins avaialble: +---- +Component Pins: +Owner Type Dir Value Name + 25 bit IN FALSE halui.gui.cancel + 25 bit IN FALSE halui.gui.cycle.pause + 25 bit IN FALSE halui.gui.cycle.start + 25 bit IN FALSE halui.gui.mdi-command-MACRO0 + 25 bit IN FALSE halui.gui.mdi-command-MACRO1 + 25 bit IN FALSE halui.gui.mdi-command-MACRO2 + 25 bit IN FALSE halui.gui.mdi-command-MACRO3 + 25 bit IN FALSE halui.gui.mdi-command-MACRO4 + 25 bit IN FALSE halui.gui.mdi-command-MACRO5 + 25 bit IN FALSE halui.gui.mpg-select.0 + 25 bit IN FALSE halui.gui.ok + 25 bit IN FALSE halui.gui.reload-display + 25 bit IN FALSE halui.gui.shutdown + 25 bit IN FALSE halui.gui.softkey-00 + 25 bit IN FALSE halui.gui.softkey-01 + 25 bit IN FALSE halui.gui.softkey-02 + 25 bit IN FALSE halui.gui.softkey-03 + 25 bit IN FALSE halui.gui.softkey-04 + 25 bit IN FALSE halui.gui.softkey-05 + 25 bit IN FALSE halui.gui.softkey-06 + 25 bit IN FALSE halui.gui.softkey-07 + 25 bit IN FALSE halui.gui.softkey-08 + 25 bit IN FALSE halui.gui.softkey-09 + 25 bit IN FALSE halui.gui.softkey-10 + 25 bit IN FALSE halui.gui.softkey-11 + 25 bit IN FALSE halui.gui.softkey-12 + 25 bit IN FALSE halui.gui.softkey-13 + 25 bit IN FALSE halui.gui.softkey-14 + 25 bit IN FALSE halui.gui.softkey-15 + 25 bit IN FALSE halui.gui.softkey-16 + 25 bit IN FALSE halui.gui.softkey-17 + 25 bit IN FALSE halui.gui.softkey-18 + 25 bit IN FALSE halui.gui.softkey-19 + +---- [[gmoccapy:auto-tool-measurement]] == Auto Tool Measurement From 3be6de9e34d837e04111206b0a0e09a9f1a19dd7 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Sun, 19 Jul 2026 21:28:23 -0700 Subject: [PATCH 56/81] qtdragon -add docs for halui messages --- docs/src/gui/qtdragon.adoc | 60 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/docs/src/gui/qtdragon.adoc b/docs/src/gui/qtdragon.adoc index 692eac20a5d..1281916c206 100644 --- a/docs/src/gui/qtdragon.adoc +++ b/docs/src/gui/qtdragon.adoc @@ -462,6 +462,66 @@ Any HAL command can be used. POSTGUI_HALCMD = loadusr qtvcp test_probe POSTGUI_HALCMD = loadusr qtvcp test_led POSTGUI_HALCMD = loadusr halmeter +---- +=== HALUI +HALUI will create halui.gui pins that interface with QtDragon. + +- There is a cycle start and pause pin - these call the code in QtDragon rather then the motion controller. + + This allows custom behaviour, such as spindle lift to work with external buttons. +- If there are macros defined in the INI (see: <>), there will be (up to 64) pins available to initiate them. + + These macros can require the mode to be preset to MDI or to automatically switch from MANUAL to MDI mode by +setting preferences on the settings page. +- clear/reload the display +- shutdown the screen +- ok/cancel of dialogs and notify (error) messages +- softkeys 0 - 11 control the main tab keys: + +'MAIN','FILE','OFFSETS','TOOL','STATUS','PROBE','GCODES','SETUP','SETTINGS','UTILITIES','USER','CAMERA' + +also: + +- enabling halui.gui.mpg-select.0, will allow zooming/scrolling of the gcode plot or text display with an MPG. +- HALUI selected axis will be reported to QtDragon. + +- HALUI selected jog rates/increments will be reported to QtDragon. + +In either case the last changed rate/axis (either HALUI or QtDragon) will be used for screen button jogging or HALUI pin jogging. + +.Typical HAL pins avaialble: +---- +Component Pins: +Owner Type Dir Value Name + 25 bit IN FALSE halui.gui.cancel + 25 bit IN FALSE halui.gui.cycle.pause + 25 bit IN FALSE halui.gui.cycle.start + 25 bit IN FALSE halui.gui.mdi-command-MACRO0 + 25 bit IN FALSE halui.gui.mdi-command-MACRO1 + 25 bit IN FALSE halui.gui.mdi-command-MACRO2 + 25 bit IN FALSE halui.gui.mdi-command-MACRO3 + 25 bit IN FALSE halui.gui.mdi-command-MACRO4 + 25 bit IN FALSE halui.gui.mdi-command-MACRO5 + 25 bit IN FALSE halui.gui.mpg-select.0 + 25 bit IN FALSE halui.gui.ok + 25 bit IN FALSE halui.gui.reload-display + 25 bit IN FALSE halui.gui.shutdown + 25 bit IN FALSE halui.gui.softkey-00 + 25 bit IN FALSE halui.gui.softkey-01 + 25 bit IN FALSE halui.gui.softkey-02 + 25 bit IN FALSE halui.gui.softkey-03 + 25 bit IN FALSE halui.gui.softkey-04 + 25 bit IN FALSE halui.gui.softkey-05 + 25 bit IN FALSE halui.gui.softkey-06 + 25 bit IN FALSE halui.gui.softkey-07 + 25 bit IN FALSE halui.gui.softkey-08 + 25 bit IN FALSE halui.gui.softkey-09 + 25 bit IN FALSE halui.gui.softkey-10 + 25 bit IN FALSE halui.gui.softkey-11 + 25 bit IN FALSE halui.gui.softkey-12 + 25 bit IN FALSE halui.gui.softkey-13 + 25 bit IN FALSE halui.gui.softkey-14 + 25 bit IN FALSE halui.gui.softkey-15 + 25 bit IN FALSE halui.gui.softkey-16 + 25 bit IN FALSE halui.gui.softkey-17 + 25 bit IN FALSE halui.gui.softkey-18 + 25 bit IN FALSE halui.gui.softkey-19 + ---- [[sec:bridge]] === HAL Bridge From 5648e49f2a68b8bdf5511809046e717f4dfbf5c2 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Sun, 19 Jul 2026 21:28:54 -0700 Subject: [PATCH 57/81] halui - add docs for halui zmq messages and pins --- docs/src/gui/halui.adoc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/src/gui/halui.adoc b/docs/src/gui/halui.adoc index 37d92b8ff73..dc2895a1b83 100644 --- a/docs/src/gui/halui.adoc +++ b/docs/src/gui/halui.adoc @@ -117,6 +117,23 @@ Or see http://linuxcnc.org/docs/devel/html/man/man1/halui.1.html * 'halui.feed-override.scale' (float, in) - pin for setting the scale for increase and decrease of 'feed-override'. * 'halui.feed-override.value' (float, out) - current FO value +=== Gui +These are pins used to 'request' a function to run in a screen. + +What these exactly do is dependant on the (GUI) screen used. + +* 'halui.gui.cycle.start' (bit, in) - pin for requesting the screen to run a cycle +* 'halui.gui.cycle.pause' (bit, in) - pin for requesting the screen to pause a cycle +* 'halui.gui.cancel' (bit, in) - pin for cancel/closing dialogs +* 'halui.gui.ok' (bit, in) - pin for ok/apply dialogs +* 'halui.gui.mdi-command-MACRO__' (bit, in) - pin for calling mdi/macro commands +__ is a one or two digit number starting from 0 to 63 + +There can be up to 64 commands defined in the INI under the [MDI_COMMAND_LIST] or [MACROS] headings. + +* 'halui.gui.reload-display' (bit, in) - pin for reloading the screen plot +* 'halui.gui.shutdown' (bit, in) - pin for shutting down linuxcnc or system +* 'halui.gui.softkey__' (bit, in) - pins for screen defined functions +__ is a two digit number starting from 00 to 19 + +* 'halui.gui.mpg-select.0' (bit, in) - pin for selecting MPG based control of the screen, such as scrolling + === Mist * 'halui.mist.is-on' (bit, out) - indicates mist is on From 5763d978ddb48a5b1dc6122f8d3434f131ac8cf9 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Mon, 10 Aug 2026 20:04:40 -0700 Subject: [PATCH 58/81] bridge -quiet print statements --- lib/python/bridgeui/bridge.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/python/bridgeui/bridge.py b/lib/python/bridgeui/bridge.py index e04ccb7837d..beef042a758 100644 --- a/lib/python/bridgeui/bridge.py +++ b/lib/python/bridgeui/bridge.py @@ -16,7 +16,7 @@ log_level=logger.WARNING, logToFile=False) # Force the log level for this module -LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL +#LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL try: import zmq @@ -112,7 +112,7 @@ def action(self, msg, data): elif msg == 'joint-selection-changed': self.activeJoint = int(data[0]) elif msg == 'axis-selection-changed': - print ('pre axis state', self.axesSelected,self.currentSelectedAxis) + #print ('pre axis state', self.axesSelected,self.currentSelectedAxis) flag = 1 if data[0] == 'MPG0': self.currentSelectedAxis = data[0] @@ -131,7 +131,7 @@ def action(self, msg, data): if flag: self.currentSelectedAxis = 'None' - print ('axis state', self.axesSelected,self.currentSelectedAxis) + #print ('axis state', self.axesSelected,self.currentSelectedAxis) # send msg to hal_glib def writeMsg(self, msg, data=''): @@ -169,7 +169,7 @@ def shutdownController(self): self.writeMsg('request_shutdown') def softkey(self, index): - print(f'Softkey index {index}') + #print(f'Softkey index {index}') self.writeMsg('request_softkey', index) # if the number is bigger then MDI command list From 059ff985d8a9aa26ee7bac02e64db78cc3e91379 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Mon, 10 Aug 2026 20:05:45 -0700 Subject: [PATCH 59/81] gladevcp -speedcontrol: quiet print debugs --- lib/python/gladevcp/speedcontrol.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/python/gladevcp/speedcontrol.py b/lib/python/gladevcp/speedcontrol.py index 36cde39a08e..61475d8bcd7 100755 --- a/lib/python/gladevcp/speedcontrol.py +++ b/lib/python/gladevcp/speedcontrol.py @@ -185,10 +185,8 @@ def _hal_init(self): self.hal_pin_decrease.connect("value-changed", self._on_minus_changed) if self.type_linear_jog: - print('linear jograte ->>',self.type_linear_jog) self._status.connect('jograte-changed', lambda w, data: self.set_value(data)) elif self.type_angular_jog: - print('Angular jograte ->>',self.type_angular_jog) self._status.connect('jograte-angular-changed', lambda w, data: self.set_value(data)) # this draws our widget on the screen @@ -476,7 +474,6 @@ def do_set_property(self, property, value): if name == "do_hide_button": self.hide_button(value) if name == "type": - print(name,value) if value == 0: self.type_linear_jog = True self.type_angular_jog = False From 553bbe1d2a8a105e6e6bc46ba078860a5a636a21 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Mon, 10 Aug 2026 20:06:43 -0700 Subject: [PATCH 60/81] iniinfo -fix search for nth INI MDI command ie search by integer --- lib/python/common/iniinfo.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/python/common/iniinfo.py b/lib/python/common/iniinfo.py index c81118f6e37..653a4c1eaf9 100644 --- a/lib/python/common/iniinfo.py +++ b/lib/python/common/iniinfo.py @@ -18,7 +18,7 @@ def __init__(self, ini=None): global LOG LOG = logger.getLogger(__name__) # Force the log level for this module only - LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL + #LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL inipath = os.environ.get('INI_FILE_NAME', '/dev/null') self.LINUXCNC_IS_RUNNING = bool(inipath != '/dev/null') @@ -641,7 +641,7 @@ def update(self): LOG.error('INI MDI command parse error:{}'.format(e)) except Exception as e: LOG.error('INI MDI command parse error:{}'.format(e)) - print(self.MDI_COMMAND_DICT) + #print(self.MDI_COMMAND_DICT) ################ # MACRO commands # @@ -940,16 +940,25 @@ def get_ini_mdi_command(self, key): using an integer is the legacy way to refer to the nth line. using a string will refer to the specific command regardless what line it is on.""" + # try the string: try: - # should fail if not string return self.MDI_COMMAND_DICT[key]['cmd'] - except: - # fallback to legacy variable - try: - # should fail if not int - return self.MDI_COMMAND_LIST[key] - except: - return None + except Exception as e: + LOG.verbose(e) + # try an integer by finding the nth string: + try: + keystr=list(self.MDI_COMMAND_DICT.keys())[int(key)] + return self.MDI_COMMAND_DICT[keystr]['cmd'] + except Exception as e: + LOG.verbose(e) + + # fallback to legacy variable + try: + # should fail if not int + return self.MDI_COMMAND_LIST[int(key)] + except Exception as e: + LOG.verbose(e) + return None def get_ini_mdi_label(self, key): """ returns A MDI label string from the INI heading [MDI_COMMAND_LIST] or None From c4341bcbabef283cbe7c4e16d4459ab149e8335e Mon Sep 17 00:00:00 2001 From: Cmorley Date: Mon, 10 Aug 2026 20:08:11 -0700 Subject: [PATCH 61/81] gmoccapy -remove test code from right_panel.ini --- configs/sim/gmoccapy/gmoccapy_right_panel.ini | 6 ------ 1 file changed, 6 deletions(-) diff --git a/configs/sim/gmoccapy/gmoccapy_right_panel.ini b/configs/sim/gmoccapy/gmoccapy_right_panel.ini index 7b4c5981da8..8a3b51ed480 100644 --- a/configs/sim/gmoccapy/gmoccapy_right_panel.ini +++ b/configs/sim/gmoccapy/gmoccapy_right_panel.ini @@ -31,12 +31,6 @@ INTRO_TIME = 5 # list of selectable jog increments INCREMENTS = 1mm, 0.1mm, 0.01mm, 0.001mm, 1.2345in -MACRO = i_am_lost -MACRO = halo_world -MACRO = jog_around -MACRO = increment xinc yinc -MACRO = go_to_position X-pos Y-pos Z-pos - [FILTER] PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image PROGRAM_EXTENSION = .py Python Script From e66a9f548f30ed42dee2689bf40f752e49a9c44c Mon Sep 17 00:00:00 2001 From: Cmorley Date: Mon, 10 Aug 2026 20:25:12 -0700 Subject: [PATCH 62/81] halui -change gui pin names as discussed on github --- src/emc/usr_intf/halui.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index edb7a8bed29..aac28bea317 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -861,8 +861,8 @@ int halui_hal_init(void) CHK(halui_export_pin_IN_bit(&(halui_data->mist_off), "halui.mist.off")); CHK(halui_export_pin_IN_bit(&(halui_data->flood_on), "halui.flood.on")); CHK(halui_export_pin_IN_bit(&(halui_data->flood_off), "halui.flood.off")); - CHK(halui_export_pin_IN_bit(&(halui_data->cycle_start), "halui.gui.cycle.start")); - CHK(halui_export_pin_IN_bit(&(halui_data->cycle_pause), "halui.gui.cycle.pause")); + CHK(halui_export_pin_IN_bit(&(halui_data->cycle_start), "halui.gui.cycle-start")); + CHK(halui_export_pin_IN_bit(&(halui_data->cycle_pause), "halui.gui.cycle-pause")); CHK(halui_export_pin_IN_bit(&(halui_data->program_run), "halui.program.run")); CHK(halui_export_pin_IN_bit(&(halui_data->program_pause), "halui.program.pause")); CHK(halui_export_pin_IN_bit(&(halui_data->program_resume), "halui.program.resume")); @@ -955,18 +955,18 @@ int halui_hal_init(void) for (int n=0; ngui_mdi_commands[n]), 0, "halui.gui.mdi-command-%s", py_call_get_mdi_name(n))); + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_mdi_commands[n]), 0, "halui.gui.mdi-command.%s", py_call_get_mdi_name(n))); } for (int n=0; ngui_soft_keys[n]), 0, "halui.gui.softkey-%02d", n)); + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_soft_keys[n]), 0, "halui.gui.softkey.%02d", n)); } CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_ok), 0, "halui.gui.ok")); CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_cancel), 0, "halui.gui.cancel")); - CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_reload), 0, "halui.gui.reload-display")); + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_reload), 0, "halui.gui.reload-preview")); CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_shutdown), 0, "halui.gui.shutdown")); From 013be29f6fc8fd564eaaa8318b54eb44e8cce328 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Mon, 10 Aug 2026 20:27:12 -0700 Subject: [PATCH 63/81] sims -update pin names --- configs/sim/axis/panel.hal | 36 ++++++++-------- configs/sim/gmoccapy/panel-4axis.hal | 46 ++++++++++----------- configs/sim/qtdragon/qtdragon_xyz/panel.hal | 36 ++++++++-------- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/configs/sim/axis/panel.hal b/configs/sim/axis/panel.hal index 04ffc3a6167..28cbb492905 100644 --- a/configs/sim/axis/panel.hal +++ b/configs/sim/axis/panel.hal @@ -21,33 +21,33 @@ net mpg-count axis.z.jog-counts net jog-p halui.axis.selected.plus panel.jog-pos net jog-m halui.axis.selected.minus panel.jog-neg -net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0 -net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1 -net m2 halui.gui.mdi-command-MACRO3 panel.mdi-2 +net m0 halui.gui.mdi-command.MACRO0 panel.mdi-0 +net m1 halui.gui.mdi-command.MACRO1 panel.mdi-1 +net m2 halui.gui.mdi-command.MACRO3 panel.mdi-2 net man panel.manual-mode halui.mode.manual net mdi panel.mdi-mode halui.mode.mdi net auto panel.auto-mode halui.mode.auto -net pause halui.gui.cycle.start panel.cycle-start -net start halui.gui.cycle.pause panel.cycle-pause +net pause halui.gui.cycle-start panel.cycle-start +net start halui.gui.cycle-pause panel.cycle-pause net abort halui.abort panel.cycle-abort net cancel halui.gui.cancel panel.cancel net ok halui.gui.ok panel.ok -net softkey0 halui.gui.softkey-00 panel.softkey-0 -net softkey1 halui.gui.softkey-01 panel.softkey-1 -net softkey2 halui.gui.softkey-02 panel.softkey-2 -net softkey3 halui.gui.softkey-03 panel.softkey-3 -net softkey4 halui.gui.softkey-04 panel.softkey-4 -net softkey5 halui.gui.softkey-05 panel.softkey-5 -net softkey6 halui.gui.softkey-06 panel.softkey-6 -net softkey7 halui.gui.softkey-07 panel.softkey-7 -net softkey8 halui.gui.softkey-08 panel.softkey-8 -net softkey9 halui.gui.softkey-09 panel.softkey-9 -net softkey10 halui.gui.softkey-10 panel.softkey-10 -net softkey11 halui.gui.softkey-11 panel.softkey-11 +net softkey0 halui.gui.softkey.00 panel.softkey-0 +net softkey1 halui.gui.softkey.01 panel.softkey-1 +net softkey2 halui.gui.softkey.02 panel.softkey-2 +net softkey3 halui.gui.softkey.03 panel.softkey-3 +net softkey4 halui.gui.softkey.04 panel.softkey-4 +net softkey5 halui.gui.softkey.05 panel.softkey-5 +net softkey6 halui.gui.softkey.06 panel.softkey-6 +net softkey7 halui.gui.softkey.07 panel.softkey-7 +net softkey8 halui.gui.softkey.08 panel.softkey-8 +net softkey9 halui.gui.softkey.09 panel.softkey-9 +net softkey10 halui.gui.softkey.10 panel.softkey-10 +net softkey11 halui.gui.softkey.11 panel.softkey-11 net exit halui.gui.shutdown panel.exit -net reload halui.gui.reload-display panel.reload +net reload halui.gui.reload-preview panel.reload diff --git a/configs/sim/gmoccapy/panel-4axis.hal b/configs/sim/gmoccapy/panel-4axis.hal index 8120d678518..42685ace762 100644 --- a/configs/sim/gmoccapy/panel-4axis.hal +++ b/configs/sim/gmoccapy/panel-4axis.hal @@ -27,39 +27,39 @@ net mpg-count axis.y.jog-counts net mpg-count axis.z.jog-counts net mpg-count axis.c.jog-counts -net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0 -net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1 -net m2 halui.gui.mdi-command-MACRO2 panel.mdi-2 +net m0 halui.gui.mdi-command.MACRO0 panel.mdi-0 +net m1 halui.gui.mdi-command.MACRO1 panel.mdi-1 +net m2 halui.gui.mdi-command.MACRO2 panel.mdi-2 net man panel.manual-mode halui.mode.manual net mdi panel.mdi-mode halui.mode.mdi net auto panel.auto-mode halui.mode.auto -net pause halui.gui.cycle.start panel.cycle-start -net start halui.gui.cycle.pause panel.cycle-pause +net pause halui.gui.cycle-start panel.cycle-start +net start halui.gui.cycle-pause panel.cycle-pause net abort halui.abort panel.cycle-abort net cancel halui.gui.cancel panel.cancel net ok halui.gui.ok panel.ok -net softkey0 halui.gui.softkey-00 panel.softkey-v0 -net softkey1 halui.gui.softkey-01 panel.softkey-v1 -net softkey2 halui.gui.softkey-02 panel.softkey-v2 -net softkey3 halui.gui.softkey-03 panel.softkey-v3 -net softkey4 halui.gui.softkey-04 panel.softkey-v4 -net softkey5 halui.gui.softkey-05 panel.softkey-v5 -net softkey6 halui.gui.softkey-06 panel.softkey-v6 -net softkeyh0 halui.gui.softkey-10 panel.softkey-h0 -net softkeyh1 halui.gui.softkey-11 panel.softkey-h1 -net softkeyh2 halui.gui.softkey-12 panel.softkey-h2 -net softkeyh3 halui.gui.softkey-13 panel.softkey-h3 -net softkeyh4 halui.gui.softkey-14 panel.softkey-h4 -net softkeyh5 halui.gui.softkey-15 panel.softkey-h5 -net softkeyh6 halui.gui.softkey-16 panel.softkey-h6 -net softkeyh7 halui.gui.softkey-17 panel.softkey-h7 -net softkeyh8 halui.gui.softkey-18 panel.softkey-h8 -net softkeyh9 halui.gui.softkey-19 panel.softkey-h9 +net softkey0 halui.gui.softkey.00 panel.softkey-v0 +net softkey1 halui.gui.softkey.01 panel.softkey-v1 +net softkey2 halui.gui.softkey.02 panel.softkey-v2 +net softkey3 halui.gui.softkey.03 panel.softkey-v3 +net softkey4 halui.gui.softkey.04 panel.softkey-v4 +net softkey5 halui.gui.softkey.05 panel.softkey-v5 +net softkey6 halui.gui.softkey.06 panel.softkey-v6 +net softkeyh0 halui.gui.softkey.10 panel.softkey-h0 +net softkeyh1 halui.gui.softkey.11 panel.softkey-h1 +net softkeyh2 halui.gui.softkey.12 panel.softkey-h2 +net softkeyh3 halui.gui.softkey.13 panel.softkey-h3 +net softkeyh4 halui.gui.softkey.14 panel.softkey-h4 +net softkeyh5 halui.gui.softkey.15 panel.softkey-h5 +net softkeyh6 halui.gui.softkey.16 panel.softkey-h6 +net softkeyh7 halui.gui.softkey.17 panel.softkey-h7 +net softkeyh8 halui.gui.softkey.18 panel.softkey-h8 +net softkeyh9 halui.gui.softkey.19 panel.softkey-h9 net exit halui.gui.shutdown panel.exit -net reload halui.gui.reload-display panel.reload +net reload halui.gui.reload-preview panel.reload diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.hal b/configs/sim/qtdragon/qtdragon_xyz/panel.hal index 89b5bef186f..1d1f8628816 100644 --- a/configs/sim/qtdragon/qtdragon_xyz/panel.hal +++ b/configs/sim/qtdragon/qtdragon_xyz/panel.hal @@ -22,33 +22,33 @@ net mpg-count axis.z.jog-counts net jog-p halui.axis.selected.plus panel.jog-pos net jog-m halui.axis.selected.minus panel.jog-neg -net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0 -net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1 -net m2 halui.gui.mdi-command-MACRO6 panel.mdi-2 +net m0 halui.gui.mdi-command.MACRO0 panel.mdi-0 +net m1 halui.gui.mdi-command.MACRO1 panel.mdi-1 +net m2 halui.gui.mdi-command.MACRO6 panel.mdi-2 net man panel.manual-mode halui.mode.manual net mdi panel.mdi-mode halui.mode.mdi net auto panel.auto-mode halui.mode.auto -net pause halui.gui.cycle.start panel.cycle-start -net start halui.gui.cycle.pause panel.cycle-pause +net pause halui.gui.cycle-start panel.cycle-start +net start halui.gui.cycle-pause panel.cycle-pause net abort halui.abort panel.cycle-abort net cancel halui.gui.cancel panel.cancel net ok halui.gui.ok panel.ok -net softkey0 halui.gui.softkey-00 panel.softkey-0 -net softkey1 halui.gui.softkey-01 panel.softkey-1 -net softkey2 halui.gui.softkey-02 panel.softkey-2 -net softkey3 halui.gui.softkey-03 panel.softkey-3 -net softkey4 halui.gui.softkey-04 panel.softkey-4 -net softkey5 halui.gui.softkey-05 panel.softkey-5 -net softkey6 halui.gui.softkey-06 panel.softkey-6 -net softkey7 halui.gui.softkey-07 panel.softkey-7 -net softkey8 halui.gui.softkey-08 panel.softkey-8 -net softkey9 halui.gui.softkey-09 panel.softkey-9 -net softkey10 halui.gui.softkey-10 panel.softkey-10 -net softkey11 halui.gui.softkey-11 panel.softkey-11 +net softkey0 halui.gui.softkey.00 panel.softkey-0 +net softkey1 halui.gui.softkey.01 panel.softkey-1 +net softkey2 halui.gui.softkey.02 panel.softkey-2 +net softkey3 halui.gui.softkey.03 panel.softkey-3 +net softkey4 halui.gui.softkey.04 panel.softkey-4 +net softkey5 halui.gui.softkey.05 panel.softkey-5 +net softkey6 halui.gui.softkey.06 panel.softkey-6 +net softkey7 halui.gui.softkey.07 panel.softkey-7 +net softkey8 halui.gui.softkey.08 panel.softkey-8 +net softkey9 halui.gui.softkey.09 panel.softkey-9 +net softkey10 halui.gui.softkey.10 panel.softkey-10 +net softkey11 halui.gui.softkey.11 panel.softkey-11 net exit halui.gui.shutdown panel.exit -net reload halui.gui.reload-display panel.reload +net reload halui.gui.reload-preview panel.reload From 0f7b249bcb5fd01a592c78b1a8338af9f88dd4b1 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Mon, 10 Aug 2026 20:29:06 -0700 Subject: [PATCH 64/81] gmoccapy -remove one of the halui sims --- configs/sim/gmoccapy/gmoccapy_halui_test.ini | 225 ------------------- configs/sim/gmoccapy/panel.hal | 59 ----- 2 files changed, 284 deletions(-) delete mode 100644 configs/sim/gmoccapy/gmoccapy_halui_test.ini delete mode 100644 configs/sim/gmoccapy/panel.hal diff --git a/configs/sim/gmoccapy/gmoccapy_halui_test.ini b/configs/sim/gmoccapy/gmoccapy_halui_test.ini deleted file mode 100644 index 8f628a1c1f0..00000000000 --- a/configs/sim/gmoccapy/gmoccapy_halui_test.ini +++ /dev/null @@ -1,225 +0,0 @@ -# EMC controller parameters for a simulated machine. -# General note: Comments can either be preceded with a # or ; - either is -# acceptable, although # is in keeping with most linux config files. - -# General section ------------------------------------------------------------- -[EMC] -VERSION = 1.1 -MACHINE = gmoccapy -DEBUG = 0 - -# Sections for display options ------------------------------------------------ -[DISPLAY] -DISPLAY = gmoccapy -i -# Log level: -# DEBUG -d -# INFO -i -# VERBOSE -v -# ERROR -q - -# Cycle time, in milliseconds, that display will sleep between polls -CYCLE_TIME = 100 - -# Values that will be allowed for override, 1.0 = 100% -MAX_FEED_OVERRIDE = 1.5 -MAX_SPINDLE_OVERRIDE = 1.2 -MIN_SPINDLE_OVERRIDE = 0.5 - -# Initial value for spindle speed -DEFAULT_SPINDLE_SPEED = 450 - -# The following are not used, added here to suppress warnings (from qt_istat/logger). -DEFAULT_LINEAR_VELOCITY = 35 -MIN_LINEAR_VELOCITY = 0 -MAX_LINEAR_VELOCITY = 234 -DEFAULT_SPINDLE_0_SPEED = 500 -MIN_SPINDLE_0_SPEED = 0 -MAX_SPINDLE_0_SPEED = 3000 -MAX_SPINDLE_0_OVERRIDE = 1.2 -MIN_SPINDLE_0_OVERRIDE = 0.5 - -# Prefix to be used -PROGRAM_PREFIX = ../../nc_files/ - -# Introductory graphic -INTRO_GRAPHIC = linuxcnc.gif -INTRO_TIME = 5 - -# list of selectable jog increments -INCREMENTS = 1.000 mm, 0.100 mm, 0.010 mm, 0.001 mm, 1.2345 inch - -# for details see nc_files/subroutines/maco_instructions.txt -[FILTER] -PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image -PROGRAM_EXTENSION = .py Python Script -png = image-to-gcode -gif = image-to-gcode -jpg = image-to-gcode -py = python3 - -# Task controller section ----------------------------------------------------- -[RS274NGC] -RS274NGC_STARTUP_CODE = G17 G21 G40 G43H0 G54 G64P0.005 G80 G90 G94 G97 M5 M9 -PARAMETER_FILE = sim.var -SUBROUTINE_PATH = ./macros -REMAP=M6 modalgroup=6 prolog=change_prolog ngc=change_g43 epilog=change_epilog -REMAP=M61 modalgroup=6 prolog=settool_prolog ngc=settool_g43 epilog=settool_epilog - -# the Python plugins serves interpreter and task -[PYTHON] -PATH_PREPEND = ./python -TOPLEVEL = ./python/toplevel.py -LOG_LEVEL = 0 - -# Motion control section ------------------------------------------------------ -[EMCMOT] -EMCMOT = motmod -COMM_TIMEOUT = 1.0 -BASE_PERIOD = 100000 -SERVO_PERIOD = 1000000 - -# Hardware Abstraction Layer section -------------------------------------------------- -[TASK] -TASK = milltask -CYCLE_TIME = 0.001 - -# Part program interpreter section -------------------------------------------- -[HAL] -HALFILE = core_sim.hal -HALFILE = spindle_sim.hal -HALFILE = simulated_home.hal - -# Single file that is executed after the GUI has started. -POSTGUI_HALFILE = gmoccapy_postgui.hal -POSTGUI_HALCMD = loadusr qtvcp -a -H panel.hal panel - -HALUI = halui - -# Trajectory planner section -------------------------------------------------- -[HALUI] -#No Content - -[MDI_COMMAND_LIST] -# for macro buttons on main oage up to 10 possible -MDI_COMMAND_MACRO0 = G53 G0 Z0;X0 Y0;Z0, Goto\nUser\nZero -MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero -MDI_COMMAND_MACRO2 = (MSG, macro 2); -MDI_COMMAND_MACRO3 = (MSG, macro 2) -MDI_COMMAND_MACRO4 = (MSG, macro 2),test -[TRAJ] -COORDINATES = X Y Z -LINEAR_UNITS = mm -ANGULAR_UNITS = degree -DEFAULT_LINEAR_VELOCITY = 35 -MAX_LINEAR_VELOCITY = 234 -POSITION_FILE = position.txt -#NO_FORCE_HOMING = 1 - -[EMCIO] -# tool table file -TOOL_TABLE = tool.tbl -TOOL_CHANGE_POSITION = 100 100 -10 -TOOL_CHANGE_QUILL_UP = 1 - -[KINS] -KINEMATICS = trivkins coordinates=xyz -JOINTS = 3 - -[AXIS_X] -MIN_LIMIT = -400.0 -MAX_LIMIT = 400.0 -MAX_VELOCITY = 166 -MAX_ACCELERATION = 1500.0 - -[JOINT_0] -TYPE = LINEAR -MAX_VELOCITY = 166 -MAX_ACCELERATION = 1500.0 -BACKLASH = 0.000 -INPUT_SCALE = 4000 -OUTPUT_SCALE = 1.000 -MIN_LIMIT = -400.0 -MAX_LIMIT = 400.0 -FERROR = 0.050 -MIN_FERROR = 0.010 -HOME_OFFSET = 0.0 -HOME = 10 -HOME_SEARCH_VEL = 200.0 -HOME_LATCH_VEL = 20.0 -HOME_USE_INDEX = NO -HOME_IGNORE_LIMITS = NO -HOME_SEQUENCE = 1 -HOME_IS_SHARED = 1 - -# Second axis -[AXIS_Y] -MIN_LIMIT = -400.0 -MAX_LIMIT = 400.0 -MAX_VELOCITY = 166 -MAX_ACCELERATION = 1500.0 - -[JOINT_1] -TYPE = LINEAR -MAX_VELOCITY = 166 -MAX_ACCELERATION = 1500.0 -BACKLASH = 0.000 -INPUT_SCALE = 4000 -OUTPUT_SCALE = 1.000 -MIN_LIMIT = -400.0 -MAX_LIMIT = 400.0 -FERROR = 0.050 -MIN_FERROR = 0.010 -HOME_OFFSET = 0.0 -HOME = 10 -HOME_SEARCH_VEL = 200.0 -HOME_LATCH_VEL = 20.0 -HOME_USE_INDEX = NO -HOME_IGNORE_LIMITS = NO -HOME_SEQUENCE = 1 - -# Third axis -[AXIS_Z] -MIN_LIMIT = -400.0 -MAX_LIMIT = 0.001 -MAX_VELOCITY = 166 -MAX_ACCELERATION = 1500.0 - -[JOINT_2] -TYPE = LINEAR -MAX_VELOCITY = 166 -MAX_ACCELERATION = 1500.0 -BACKLASH = 0.000 -INPUT_SCALE = 4000 -OUTPUT_SCALE = 1.000 -MIN_LIMIT = -400.0 -MAX_LIMIT = 0.001 -FERROR = 0.050 -MIN_FERROR = 0.010 -HOME_OFFSET = 1.0 -HOME = -10 -HOME_SEARCH_VEL = 200.0 -HOME_LATCH_VEL = 20.0 -HOME_USE_INDEX = NO -HOME_IGNORE_LIMITS = NO -HOME_SEQUENCE = 0 -HOME_IS_SHARED = 1 - -# section for main IO controller parameters ----------------------------------- -[MACROS] -MACRO = go_to_position x-pos y-pos z-pos -MACRO = i_am_lost -MACRO = increment x-incr y-incr -MACRO = macro_4 -MACRO = macro_5 -MACRO = macro_6 -MACRO = macro_7 -MACRO = macro_8 -MACRO = macro_9 -MACRO = macro_10 -MACRO = macro_11 -MACRO = macro_12 -MACRO = macro_13 -MACRO = macro_14 -MACRO = macro_15 - - diff --git a/configs/sim/gmoccapy/panel.hal b/configs/sim/gmoccapy/panel.hal deleted file mode 100644 index 0c80e60010c..00000000000 --- a/configs/sim/gmoccapy/panel.hal +++ /dev/null @@ -1,59 +0,0 @@ -net rate halui.axis.jog-speed panel.jog-rate - -net sx halui.axis.x.select panel.axis-x -net sy halui.axis.y.select panel.axis-y -net sz halui.axis.z.select panel.axis-z - -net sx axis.x.jog-enable -net sy axis.y.jog-enable -net sz axis.z.jog-enable -net sgui halui.gui.mpg-select.0 panel.select-gui0 - -net jog-p halui.axis.selected.plus panel.jog-pos -net jog-m halui.axis.selected.minus panel.jog-neg - -net mpg-scale axis.x.jog-scale panel.mpg-scale -net mpg-scale axis.y.jog-scale -net mpg-scale axis.z.jog-scale - -net mpg-count panel.mpg-wheel-s -net mpg-count gmoccapy.mpg-in -net mpg-count axis.x.jog-counts -net mpg-count axis.y.jog-counts -net mpg-count axis.z.jog-counts - -net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0 -net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1 -net m2 halui.gui.mdi-command-MACRO2 panel.mdi-2 - -net man panel.manual-mode halui.mode.manual -net mdi panel.mdi-mode halui.mode.mdi -net auto panel.auto-mode halui.mode.auto - -net pause halui.gui.cycle.start panel.cycle-start -net start halui.gui.cycle.pause panel.cycle-pause -net abort halui.abort panel.cycle-abort - -net cancel halui.gui.cancel panel.cancel -net ok halui.gui.ok panel.ok - -net softkey0 halui.gui.softkey-00 panel.softkey-v0 -net softkey1 halui.gui.softkey-01 panel.softkey-v1 -net softkey2 halui.gui.softkey-02 panel.softkey-v2 -net softkey3 halui.gui.softkey-03 panel.softkey-v3 -net softkey4 halui.gui.softkey-04 panel.softkey-v4 -net softkey5 halui.gui.softkey-05 panel.softkey-v5 -net softkey6 halui.gui.softkey-06 panel.softkey-v6 -net softkeyh0 halui.gui.softkey-10 panel.softkey-h0 -net softkeyh1 halui.gui.softkey-11 panel.softkey-h1 -net softkeyh2 halui.gui.softkey-12 panel.softkey-h2 -net softkeyh3 halui.gui.softkey-13 panel.softkey-h3 -net softkeyh4 halui.gui.softkey-14 panel.softkey-h4 -net softkeyh5 halui.gui.softkey-15 panel.softkey-h5 -net softkeyh6 halui.gui.softkey-16 panel.softkey-h6 -net softkeyh7 halui.gui.softkey-17 panel.softkey-h7 -net softkeyh8 halui.gui.softkey-18 panel.softkey-h8 -net softkeyh9 halui.gui.softkey-19 panel.softkey-h9 - -net exit halui.gui.shutdown panel.exit -net reload halui.gui.reload-display panel.reload From 7a775f1ecc0eaa12f7ff4c2d4c56320324edcb09 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Tue, 11 Aug 2026 05:33:22 -0700 Subject: [PATCH 65/81] gmoccapy -fix error from y/n dialog wrong function signature --- src/emc/usr_intf/gmoccapy/dialogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emc/usr_intf/gmoccapy/dialogs.py b/src/emc/usr_intf/gmoccapy/dialogs.py index f09744d41bb..17c9105b435 100644 --- a/src/emc/usr_intf/gmoccapy/dialogs.py +++ b/src/emc/usr_intf/gmoccapy/dialogs.py @@ -270,7 +270,7 @@ def yesno_dialog(self): dialog.connect("response", self.on_yn_response) return dialog - def show_yesno_dialog(self, message, title = _("Operator Message")): + def show_yesno_dialog(self, _caller,message, title = _("Operator Message")): dialog = self.yn_dialog dialog.set_markup(message) if title: From cb9fef68707060444dde2208c2ab6d37aa351183 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Tue, 11 Aug 2026 05:35:30 -0700 Subject: [PATCH 66/81] gmoccapy -fix a 'dialog not found' error message --- src/emc/usr_intf/gmoccapy/dialogs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/emc/usr_intf/gmoccapy/dialogs.py b/src/emc/usr_intf/gmoccapy/dialogs.py index 17c9105b435..f24176c7636 100644 --- a/src/emc/usr_intf/gmoccapy/dialogs.py +++ b/src/emc/usr_intf/gmoccapy/dialogs.py @@ -52,12 +52,16 @@ def __init__(self, caller): def dialog_ext_control(self, answer): if self.sys_dialog.get_visible(): self.sys_dialog.response(answer) + return elif self.warn_dialog.get_visible(): self.warn_dialog.response(answer) + return elif self.ent_dialog.get_visible(): self.ent_dialog.response(answer) + return elif self.yn_dialog.get_visible(): self.yn_dialog.response(answer) + return else: # Get the widget that currently has user focus focused_widget = self._caller.widgets.window1.get_focus() From ca62692c40a0efa94e3ab8f91fc6b833cf346701 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Tue, 11 Aug 2026 19:53:23 -0700 Subject: [PATCH 67/81] gladevcp -speedcontrol: normalise jograte to machine units hal_glib uses machine units for jograte. we need to convert in speedcontrol is in a different unit. --- lib/python/gladevcp/speedcontrol.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/python/gladevcp/speedcontrol.py b/lib/python/gladevcp/speedcontrol.py index 61475d8bcd7..c9c6d95149d 100755 --- a/lib/python/gladevcp/speedcontrol.py +++ b/lib/python/gladevcp/speedcontrol.py @@ -35,7 +35,7 @@ else: from .hal_widgets import _HalSpeedControlBase -from gladevcp.core import Status, Action +from gladevcp.core import Status, Action, Info class SpeedControl(Gtk.Box, _HalSpeedControlBase): ''' @@ -118,6 +118,7 @@ def __init__(self, size = 36, value = 0, min = 0, max = 100, inc_speed = 100, un self._action = Action() self._status = Status() + self._info = Info() # basic settings self._size = size @@ -185,7 +186,7 @@ def _hal_init(self): self.hal_pin_decrease.connect("value-changed", self._on_minus_changed) if self.type_linear_jog: - self._status.connect('jograte-changed', lambda w, data: self.set_value(data)) + self._status.connect('jograte-changed', lambda w, data: self.status_set_value(data)) elif self.type_angular_jog: self._status.connect('jograte-angular-changed', lambda w, data: self.set_value(data)) @@ -264,6 +265,14 @@ def _draw_widget(self): self.cr.show_text(label) self.cr.stroke() + # hal_glib (_status) sends jograte in machine units + def status_set_value(self, value): + if self._status.is_metric_mode(): + v = self._info.convert_machine_to_metric(value) + else: + v = self._info.convert_machine_to_imperial(value) + self.set_value(v) + # This allows to set the value from external, i.e. propertys def set_value(self, value): self.adjustment.set_value(value) @@ -285,7 +294,12 @@ def _on_value_changed(self, widget): value = widget.get_value() if self.type_linear_jog: - self._action.SET_JOG_RATE(value) + # hal_glib (_status) expects jograte in machine units + if self._status.is_metric_mode(): + v = self._info.convert_metric_to_machine(value) + else: + v = self._info.convert_imperial_to_machine(value) + self._action.SET_JOG_RATE(v) elif self.type_angular_jog: self._action.SET_JOG_RATE_ANGULAR(value) From 5132da5590193bc80568472f71e44c3b80973c14 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Tue, 11 Aug 2026 20:39:39 -0700 Subject: [PATCH 68/81] gmoccapy -dialogs: hide the close icon --- src/emc/usr_intf/gmoccapy/dialogs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/emc/usr_intf/gmoccapy/dialogs.py b/src/emc/usr_intf/gmoccapy/dialogs.py index f24176c7636..67004138c96 100644 --- a/src/emc/usr_intf/gmoccapy/dialogs.py +++ b/src/emc/usr_intf/gmoccapy/dialogs.py @@ -102,6 +102,7 @@ def system_dialog(self): def show_system_dialog(self): dialog = self.sys_dialog dialog._calc.set_value("") + dialog.set_deletable(False) dialog.show_all() self.emit("play_sound", "alert") @@ -162,6 +163,7 @@ def show_entry_dialog(self, data = None, header = _("Enter value") , dialog.calc.num_pad_only(True) dialog.label.set_text(label) dialog.set_title(header) + dialog.set_deletable(False) dialog.show_all() # wait but don't block event loop @@ -232,6 +234,7 @@ def show_warning_dialog(self, title, message, sound=True, dialog.set_title(title) dialog.format_secondary_text(message) dialog.set_markup(message) + dialog.set_deletable(False) dialog.show_all() if sound: self.emit("play_sound", "alert") @@ -279,6 +282,7 @@ def show_yesno_dialog(self, _caller,message, title = _("Operator Message")): dialog.set_markup(message) if title: dialog.set_title(str(title)) + dialog.set_deletable(False) dialog.show_all() self.emit("play_sound", "alert") @@ -315,6 +319,7 @@ def show_user_message(self, message, title = _("Operator Message")): box.add(ok_button) dialog.action_area.add(box) dialog.set_border_width(5) + dialog.set_deletable(False) dialog.show_all() self.emit("play_sound", "alert") response = dialog.run() From 56807f619ca3667a4d31bb2398bf06c196eccae1 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Tue, 11 Aug 2026 21:39:49 -0700 Subject: [PATCH 69/81] gmoccapy -fix warning dialog bold text It seems gtk has a bug, if you set the markup text twice the text is not bold. Now we force it bold --- src/emc/usr_intf/gmoccapy/dialogs.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/dialogs.py b/src/emc/usr_intf/gmoccapy/dialogs.py index 67004138c96..3f229527bf6 100644 --- a/src/emc/usr_intf/gmoccapy/dialogs.py +++ b/src/emc/usr_intf/gmoccapy/dialogs.py @@ -232,8 +232,7 @@ def show_warning_dialog(self, title, message, sound=True, confirm_pin = 'warning-confirm', active_pin = None): dialog = self.warn_dialog dialog.set_title(title) - dialog.format_secondary_text(message) - dialog.set_markup(message) + dialog.set_markup(''+message+'') dialog.set_deletable(False) dialog.show_all() if sound: From 4b0e6c917be337b4f0266e5e09c3abd6499d484a Mon Sep 17 00:00:00 2001 From: Cmorley Date: Wed, 12 Aug 2026 18:50:51 -0700 Subject: [PATCH 70/81] gmoccapy -clean up warning dialog code --- src/emc/usr_intf/gmoccapy/dialogs.py | 27 +++++++++++++-------------- src/emc/usr_intf/gmoccapy/gmoccapy.py | 3 +-- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/dialogs.py b/src/emc/usr_intf/gmoccapy/dialogs.py index 3f229527bf6..04e8308cf29 100644 --- a/src/emc/usr_intf/gmoccapy/dialogs.py +++ b/src/emc/usr_intf/gmoccapy/dialogs.py @@ -195,15 +195,11 @@ def on_entry_response(self, dialog, rtn): dialog.RESPONSE = rtn # display warning dialog - def warning_dialog(self, message = '', secondary = None, title = _("Operator Message"),\ - sound = True, confirm_pin = 'warning-confirm', active_pin = None): - + def warning_dialog(self): dialog = Gtk.MessageDialog(self._caller.widgets.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT, - Gtk.MessageType.INFO, Gtk.ButtonsType.NONE, message) - # if there is a secondary message then the first message text is bold - if secondary: - dialog.format_secondary_text(secondary) + Gtk.MessageType.INFO, + Gtk.ButtonsType.NONE) ok_button = Gtk.Button.new_with_mnemonic("_Ok") ok_button.set_size_request(-1, 56) ok_button.connect("clicked",lambda w:dialog.response(Gtk.ResponseType.OK)) @@ -211,16 +207,16 @@ def warning_dialog(self, message = '', secondary = None, title = _("Operator Mes box.add(ok_button) dialog.action_area.add(box) dialog.set_border_width(5) - if sound: - self.emit("play_sound", "alert") - dialog.set_title(title) - dialog.context = [] + # HAL pin names + dialog.confirm_pin = 'warning-confirm' + dialog.active_pin = None + def periodic(): - if self._caller.halcomp[confirm_pin]: + if self._caller.halcomp[dialog.confirm_pin]: dialog.response(Gtk.ResponseType.OK) return False - if active_pin is not None: - if not self._caller.halcomp[active_pin]: + if dialog.active_pin is not None: + if not self._caller.halcomp[dialog.active_pin]: dialog.response(Gtk.ResponseType.CANCEL) return False return True @@ -234,6 +230,9 @@ def show_warning_dialog(self, title, message, sound=True, dialog.set_title(title) dialog.set_markup(''+message+'') dialog.set_deletable(False) + dialog.confirm_pin = confirm_pin + dialog.active_pin = active_pin + dialog.show_all() if sound: self.emit("play_sound", "alert") diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index 58ba3707705..a1d49540226 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -5678,8 +5678,7 @@ def on_tool_change(self, widget): message = _("Tool\n\n# {0:d}\n\n not in the tool table!").format(toolnumber) result = self.dialogs.show_warning_dialog( _("Manual Tool change"), - message, context='mantoolchange', - confirm_pin = 'toolchange-confirm', + message, confirm_pin = 'toolchange-confirm', active_pin = 'toolchange-change') if result: From 5129937fa58e03d4a5acb053793c060f2c618651 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Wed, 12 Aug 2026 19:14:51 -0700 Subject: [PATCH 71/81] qtdragon -check if softkey number is invalid --- share/qtvcp/screens/qtdragon/qtdragon_handler.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/share/qtvcp/screens/qtdragon/qtdragon_handler.py b/share/qtvcp/screens/qtdragon/qtdragon_handler.py index 319a1f9be27..dbcacef1d23 100644 --- a/share/qtvcp/screens/qtdragon/qtdragon_handler.py +++ b/share/qtvcp/screens/qtdragon/qtdragon_handler.py @@ -973,9 +973,11 @@ def softkey_pressed(self, index): tmp=['main','file','offsets','tool','status', 'probe','gcodes','setup','settings', 'utils','user','camera'] - - btn = self.w[f'btn_{tmp[index]}'] - #print(f'index{index}, btn, {btn}') + try: + btn = self.w[f'btn_{tmp[index]}'] + #print(f'index{index}, btn, {btn}') + except: + pass if btn.isVisible(): btn.click() From c2ce7b5fcd433314d9d17c610956149422486e35 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Wed, 12 Aug 2026 19:15:38 -0700 Subject: [PATCH 72/81] halui -change gui ok/cancel pin names yo gui.response.ok gui.response.cancel --- src/emc/usr_intf/halui.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index aac28bea317..2e1e80ea18e 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -962,9 +962,9 @@ int halui_hal_init(void) CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_soft_keys[n]), 0, "halui.gui.softkey.%02d", n)); } - CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_ok), 0, "halui.gui.ok")); + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_ok), 0, "halui.gui.response.ok")); - CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_cancel), 0, "halui.gui.cancel")); + CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_cancel), 0, "halui.gui.response.cancel")); CHK(hal_pin_new_bool(comp_id, HAL_IN, &(halui_data->gui_reload), 0, "halui.gui.reload-preview")); From 7044423f8a91792bdd5a368194941bb3149a0a6d Mon Sep 17 00:00:00 2001 From: Cmorley Date: Wed, 12 Aug 2026 19:18:25 -0700 Subject: [PATCH 73/81] axis sim -change pin names do to halui name change --- configs/sim/axis/panel.hal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/sim/axis/panel.hal b/configs/sim/axis/panel.hal index 28cbb492905..980ad7b1d21 100644 --- a/configs/sim/axis/panel.hal +++ b/configs/sim/axis/panel.hal @@ -33,8 +33,8 @@ net pause halui.gui.cycle-start panel.cycle-start net start halui.gui.cycle-pause panel.cycle-pause net abort halui.abort panel.cycle-abort -net cancel halui.gui.cancel panel.cancel -net ok halui.gui.ok panel.ok +net cancel halui.gui.response.cancel panel.cancel +net ok halui.gui.response.ok panel.ok net softkey0 halui.gui.softkey.00 panel.softkey-0 net softkey1 halui.gui.softkey.01 panel.softkey-1 From 07a2084931e23d283b4e288292982043e7f2cbcb Mon Sep 17 00:00:00 2001 From: Cmorley Date: Wed, 12 Aug 2026 19:19:23 -0700 Subject: [PATCH 74/81] qtdragon sim -change pin names do to halui name change --- configs/sim/qtdragon/qtdragon_xyz/panel.hal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.hal b/configs/sim/qtdragon/qtdragon_xyz/panel.hal index 1d1f8628816..eb1b4046f5d 100644 --- a/configs/sim/qtdragon/qtdragon_xyz/panel.hal +++ b/configs/sim/qtdragon/qtdragon_xyz/panel.hal @@ -34,8 +34,8 @@ net pause halui.gui.cycle-start panel.cycle-start net start halui.gui.cycle-pause panel.cycle-pause net abort halui.abort panel.cycle-abort -net cancel halui.gui.cancel panel.cancel -net ok halui.gui.ok panel.ok +net cancel halui.gui.response.cancel panel.cancel +net ok halui.gui.response.ok panel.ok net softkey0 halui.gui.softkey.00 panel.softkey-0 net softkey1 halui.gui.softkey.01 panel.softkey-1 From 817cf066f0e67652e7d4d9c704b12e5e50703626 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Wed, 12 Aug 2026 19:19:53 -0700 Subject: [PATCH 75/81] gmoccapy sim -change pin names do to halui name change --- configs/sim/gmoccapy/panel-4axis.hal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/sim/gmoccapy/panel-4axis.hal b/configs/sim/gmoccapy/panel-4axis.hal index 42685ace762..0dd30d02c33 100644 --- a/configs/sim/gmoccapy/panel-4axis.hal +++ b/configs/sim/gmoccapy/panel-4axis.hal @@ -39,8 +39,8 @@ net pause halui.gui.cycle-start panel.cycle-start net start halui.gui.cycle-pause panel.cycle-pause net abort halui.abort panel.cycle-abort -net cancel halui.gui.cancel panel.cancel -net ok halui.gui.ok panel.ok +net cancel halui.gui.response.cancel panel.cancel +net ok halui.gui.response.ok panel.ok net softkey0 halui.gui.softkey.00 panel.softkey-v0 From 9f6857ce280ccc4ecd62ef440dd9d73589a6b00c Mon Sep 17 00:00:00 2001 From: Cmorley Date: Wed, 12 Aug 2026 21:26:35 -0700 Subject: [PATCH 76/81] axis -incorporate zmq messages pulls in gstat and iniinfo libraries --- src/emc/usr_intf/axis/scripts/axis.py | 173 ++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/src/emc/usr_intf/axis/scripts/axis.py b/src/emc/usr_intf/axis/scripts/axis.py index ddc9baa4335..896757bbb01 100755 --- a/src/emc/usr_intf/axis/scripts/axis.py +++ b/src/emc/usr_intf/axis/scripts/axis.py @@ -70,6 +70,9 @@ def tkerror(self, arg): import bwidget from math import hypot, atan2, sin, cos, pi, sqrt import linuxcnc +from hal_glib import GStat +from common.iniinfo import _IStat as IStatParent + from glnav import * if "AXIS_NO_HAL" in os.environ: @@ -113,6 +116,15 @@ def putpref(self, option, value, type=bool): self.set("DEFAULT", option, str(value)) self.write(open(self.fn, "w")) +class Info(IStatParent): + _instance = None + _instanceNum = 0 + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = IStatParent.__new__(cls, *args, **kwargs) + return cls._instance + if sys.argv[1] != "-ini": raise SystemExit("-ini must be first argument") @@ -120,6 +132,24 @@ def putpref(self, option, value, type=bool): ap = AxisPreferences() +INFO = Info() +GSTAT = GStat() +GSTAT.forced_update() +GSTAT.connect('jograte-changed', lambda w, data: vars.jog_speed.set(data)) +GSTAT.connect('axis-selection-changed', lambda w,data: select_axis(data)) +GSTAT.connect('cycle-start-request', lambda w, state : cycle_start_request(state)) +GSTAT.connect('cycle-pause-request', lambda w, state: pause_request(state)) +GSTAT.connect('ok-request', lambda w, state: dialog_ext_control(w,1,1)) +GSTAT.connect('cancel-request', lambda w, state: dialog_ext_control(w,1,0)) +GSTAT.connect('macro-call-request', lambda w, name: request_macro_call(name)) +GSTAT.connect('softkey-pressed', lambda w,data: softkey_pressed(data)) +GSTAT.connect('shutdown-request', lambda w : General_Halt()) +GSTAT.connect('reload-display', lambda w : commands.clear_live_plot()) + +global last_mpg +last_mpg = 0 +mpg_enabled = 0 + # Handle repeated key press events pressed_keys_list = [] def key_pressed(ev): @@ -977,6 +1007,27 @@ def update(self): else: state="normal" root_window.call(jname,"configure","-state",state) + GSTAT.run_iteration() + global mpg_enabled + try: + if comp['mpg-enable'] or mpg_enabled: + global last_mpg + if comp['mpg-in'] == last_mpg: return + if comp['mpg-in'] > last_mpg: + if s.task_mode == linuxcnc.MODE_MDI: + commands._mdi_up_cmd() + else: + scroll_up(None) + if comp['mpg-in'] < last_mpg: + if s.task_mode == linuxcnc.MODE_MDI: + commands._mdi_down_cmd() + else: + scroll_down(None) + + last_mpg = comp['mpg-in'] + except Exception as e: + print(e) + user_live_update() def clear(self): @@ -2452,7 +2503,11 @@ def task_stop(*event): comp["abort"] = False def mdi_up_cmd(*args): + print(args) if args and args[0].char: return # e.g., for KP_Up with numlock on + _mdi_up_cmd() + + def _mdi_up_cmd(): global mdi_history_index if widgets.mdi_command.cget("state") == "disabled": return @@ -2470,6 +2525,9 @@ def mdi_up_cmd(*args): def mdi_down_cmd(*args): if args and args[0].char: return # e.g., for KP_Up with numlock on + _mdi_down_cmd() + + def _mdi_down_cmd(): global mdi_history_index if widgets.mdi_command.cget("state") == "disabled": return @@ -3964,6 +4022,8 @@ def select_run_from(e): comp.newpin("resume-inhibit",hal.HAL_BIT,hal.HAL_IN) comp.newpin("error", hal.HAL_BIT, hal.HAL_OUT) comp.newpin("abort", hal.HAL_BIT, hal.HAL_OUT) + comp.newpin('mpg-enable', hal.HAL_BIT, hal.HAL_IN) + comp.newpin('mpg-in', hal.HAL_S32, hal.HAL_IN) vars.has_ladder.set(hal.component_exists('classicladder_rt')) @@ -4064,6 +4124,119 @@ def remove_tempdir(t): z = (o.canon.min_extents[2] + o.canon.max_extents[2])/2 o.set_centerpoint(x, y, z) +def select_axis(data): + global mpg_enabled + if data is None: return + if data =='MPG0': + mpg_enabled = True + return + mpg_enabled = False + if data.upper() =='NONE': + return + try: + widget = getattr(widgets, "axis_%s" % data.lower()) + widget.focus() + widget.invoke() + except: + pass + +def cycle_start_request(state): + if s.task_mode == linuxcnc.MODE_MDI: + command = vars.mdi_command.get() + commands.send_mdi_command(command) + else: + commands.task_run(None) + +def pause_request(state): + commands.task_pauseresume(None) + +def dialog_ext_control(widget,t,state): + flag = False + for child in root_window.winfo_children(): + #print(child) + if isinstance(child, Tkinter.Toplevel): + #print(f"Found a Toplevel window: {child}") + if '.!toplevel' in str(child): + #print('sending command:',child) + for child2 in child.winfo_children(): + #print(child2) + if isinstance(child2, Tkinter.Frame): + for child3 in child2.winfo_children(): + #print(child3) + if isinstance(child3, Tkinter.Button): + #print(dir(child3)) + txt = child3.cget("text") + if txt.lower() == 'ok' and state: + #print('Ok') + child3.invoke() + flag = True + break + elif txt.lower() == 'cancel' and not state: + #print('Cancel') + child3.invoke() + flag = True + break + if flag: break + if flag: break + else: + #print('No window') + # remove one error message + if state == 0: + notifications.clear_one() + +def request_macro_call(name): + #print('request macro:',name) + cmd = INFO.get_ini_mdi_command(name) + #print(f'MDI command:{cmd} name:{name}') + if not INFO.get_ini_mdi_command(name) is None: + run_mdi(data=cmd) + else: + #print(INFO.get_ini_macro_command(name)) + try: + temp = INFO.MACRO_COMMAND_DICT.get(name).get('cmd') + #print(temp) + run_macro(data=temp) + except Exception as e: + print(e) + +def run_mdi(data): + #print(f'run mdi command:{data}') + mdi_list = data.split(';') + for code in (mdi_list): + commands.send_mdi_command(code) + +def run_macro(data): + #print(f'run macro:{data}') + o_codes = data.split() + command = str( "O<" + o_codes[0] + "> call" ) + # check for oword and confirm path exists + #if not self.check_macro_path(command): + # return + for code in o_codes[1:]: + if vars.metric.get(): unit_str = " " + _("mm") + else: unit_str = " " + _("in") + param = prompt_float("Macro", f"Enter a value for: {code}:", + "", unit_str) + if param <= 0: return + if vars.metric.get(): param /= 25.4 + command = command + " [" + str(param) + "] " + commands.send_mdi_command(command) + +def softkey_pressed(index): + + if index == 0: + root_window.tk.call('.pane.top.tabs','raise','manual') + elif index == 1: + root_window.tk.call('.pane.top.tabs','raise','mdi') + elif index == 2: + root_window.tk.call('.pane.top.right','raise','preview') + elif index == 3: + root_window.tk.call('.pane.top.right','raise','numbers') + elif index == 4: + root_window.tk.call('.pane.top.right','raise','user_0') + else: + print(f'Softkey index:{index}') + def destroy_splash(): try: root_window.send("popimage", "destroy", ".") From d4c1dda52842d771eff298cf28554d7235301ada Mon Sep 17 00:00:00 2001 From: Cmorley Date: Wed, 12 Aug 2026 21:28:23 -0700 Subject: [PATCH 77/81] axis sim -remove user command file got zmq messages incorporated in axis now --- configs/sim/axis/axis_halui_test.ini | 2 - configs/sim/axis/gstatmessages.py | 163 --------------------------- 2 files changed, 165 deletions(-) delete mode 100644 configs/sim/axis/gstatmessages.py diff --git a/configs/sim/axis/axis_halui_test.ini b/configs/sim/axis/axis_halui_test.ini index 2e53fb8275a..9f0d56b9346 100644 --- a/configs/sim/axis/axis_halui_test.ini +++ b/configs/sim/axis/axis_halui_test.ini @@ -53,8 +53,6 @@ TOOL_EDITOR = tooledit INCREMENTS = 1 in, 0.1 in, 10 mil, 1 mil, 1mm, .1mm, 1/8000 in -USER_COMMAND_FILE=gstatmessages.py - [MDI_COMMAND_LIST] MDI_COMMAND_MACRO0 = G53 G0 Z0;G0 X0 Y0;Z0, Goto\nUser\nZero MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero diff --git a/configs/sim/axis/gstatmessages.py b/configs/sim/axis/gstatmessages.py deleted file mode 100644 index 702c1b6e0ba..00000000000 --- a/configs/sim/axis/gstatmessages.py +++ /dev/null @@ -1,163 +0,0 @@ - -from hal_glib import GStat -from common.iniinfo import _IStat as IStatParent - -class Info(IStatParent): - _instance = None - _instanceNum = 0 - - def __new__(cls, *args, **kwargs): - if not cls._instance: - cls._instance = IStatParent.__new__(cls, *args, **kwargs) - return cls._instance - -INFO = Info() - -GSTAT = GStat() -GSTAT.forced_update() -GSTAT.connect('jograte-changed', lambda w, data: vars.jog_speed.set(data)) -GSTAT.connect('axis-selection-changed', lambda w,data: select_axis(data)) -GSTAT.connect('cycle-start-request', lambda w, state : cycle_start_request(state)) -GSTAT.connect('cycle-pause-request', lambda w, state: pause_request(state)) -GSTAT.connect('ok-request', lambda w, state: dialog_ext_control(w,1,1)) -GSTAT.connect('cancel-request', lambda w, state: dialog_ext_control(w,1,0)) -GSTAT.connect('macro-call-request', lambda w, name: request_macro_call(name)) -GSTAT.connect('softkey-pressed', lambda w,data: softkey_pressed(data)) -GSTAT.connect('shutdown-request', lambda w : General_Halt()) -GSTAT.connect('reload-display', lambda w : commands.clear_live_plot()) - -global last_mpg -last_mpg = 0 -mpg_enabled = 0 - - -def user_hal_pins(): - comp.newpin('mpg-enable', hal.HAL_BIT, hal.HAL_IN) - comp.newpin('mpg-in', hal.HAL_S32, hal.HAL_IN) - comp.ready() - -def user_live_update(): - GSTAT.run_iteration() - global mpg_enabled - try: - if comp['mpg-enable'] or mpg_enabled: - global last_mpg - if comp['mpg-in'] == last_mpg: return - if comp['mpg-in'] > last_mpg:scroll_up(None) - if comp['mpg-in'] < last_mpg:scroll_down(None) - last_mpg = comp['mpg-in'] - except Exception as e: - print(e) - -def select_axis(data): - global mpg_enabled - if data is None: return - if data =='MPG0': - mpg_enabled = True - return - mpg_enabled = False - if data.upper() =='NONE': - return - try: - widget = getattr(widgets, "axis_%s" % data.lower()) - widget.focus() - widget.invoke() - except: - pass - -def cycle_start_request(state): - print('cycle start',state) - commands.task_run(None) - -def pause_request(state): - print('cycle pause',state) - commands.task_pauseresume(None) - -def dialog_ext_control(widget,t,state): - print('dialog control',widget,state) - - flag = False - for child in root_window.winfo_children(): - #print(child) - if isinstance(child, Tkinter.Toplevel): - #print(f"Found a Toplevel window: {child}") - if '.!toplevel' in str(child): - #print('sending command:',child) - for child2 in child.winfo_children(): - #print(child2) - if isinstance(child2, Tkinter.Frame): - for child3 in child2.winfo_children(): - #print(child3) - if isinstance(child3, Tkinter.Button): - #print(dir(child3)) - txt = child3.cget("text") - if txt.lower() == 'ok' and state: - #print('Ok') - child3.invoke() - flag = True - break - elif txt.lower() == 'cancel' and not state: - #print('Cancel') - child3.invoke() - flag = True - break - if flag: break - if flag: break - else: - #print('No window') - # remove one error message - if state == 0: - notifications.clear_one() - -def request_macro_call(name): - #print('request macro:',name) - cmd = INFO.get_ini_mdi_command(name) - #print(f'MDI command:{cmd} name:{name}') - if not INFO.get_ini_mdi_command(name) is None: - run_mdi(data=cmd) - else: - #print(INFO.get_ini_macro_command(name)) - try: - temp = INFO.MACRO_COMMAND_DICT.get(name).get('cmd') - #print(temp) - run_macro(data=temp) - except Exception as e: - print(e) - -def run_mdi(data): - #print(f'run mdi command:{data}') - mdi_list = data.split(';') - for code in (mdi_list): - commands.send_mdi_command(code) - -def run_macro(data): - #print(f'run macro:{data}') - o_codes = data.split() - command = str( "O<" + o_codes[0] + "> call" ) - # check for oword and confirm path exists - #if not self.check_macro_path(command): - # return - for code in o_codes[1:]: - if vars.metric.get(): unit_str = " " + _("mm") - else: unit_str = " " + _("in") - param = prompt_float("Macro", f"Enter a value for: {code}:", - "", unit_str) - if param <= 0: return - if vars.metric.get(): param /= 25.4 - command = command + " [" + str(param) + "] " - commands.send_mdi_command(command) - -def softkey_pressed(index): - - if index == 0: - root_window.tk.call('.pane.top.tabs','raise','manual') - elif index == 1: - root_window.tk.call('.pane.top.tabs','raise','mdi') - elif index == 2: - root_window.tk.call('.pane.top.right','raise','preview') - elif index == 3: - root_window.tk.call('.pane.top.right','raise','numbers') - elif index == 4: - root_window.tk.call('.pane.top.right','raise','user_0') - else: - print(f'Softkey index:{index}') From a075f4c439824b60defa0bb324fc5d176cbcb6a5 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Thu, 13 Aug 2026 05:53:49 -0700 Subject: [PATCH 78/81] axis -fix macro entry dialog error --- src/emc/usr_intf/axis/scripts/axis.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/emc/usr_intf/axis/scripts/axis.py b/src/emc/usr_intf/axis/scripts/axis.py index 896757bbb01..bf41ab4396e 100755 --- a/src/emc/usr_intf/axis/scripts/axis.py +++ b/src/emc/usr_intf/axis/scripts/axis.py @@ -1800,6 +1800,7 @@ def result(self): return None def run(self): + self.t.wait_visibility() self.t.grab_set() self._after = self.t.after_idle(self.do_focus) self.t.wait_window() From e7f1776a392a3b4e6599d6de0feac59bb4f31fd0 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Thu, 13 Aug 2026 18:43:01 -0700 Subject: [PATCH 79/81] bridge -Don't break everything if there is no ZMQ library selection by HAL pins should keep working. --- lib/python/bridgeui/bridge.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/python/bridgeui/bridge.py b/lib/python/bridgeui/bridge.py index beef042a758..dceb4f8cf7a 100644 --- a/lib/python/bridgeui/bridge.py +++ b/lib/python/bridgeui/bridge.py @@ -90,6 +90,9 @@ def init_read(self): # callback from ZMQ read socket def readMsg(self): + # no ZMQ library. return so HALUI works otherwise + if not ZMQ: return + if self.readSocket.getsockopt(zmq.EVENTS) & zmq.POLLIN: while self.readSocket.getsockopt(zmq.EVENTS) & zmq.POLLIN: # get raw message From 07c8698c7ec317bc2a127607a5218dc8a4729b76 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Sat, 15 Aug 2026 05:35:26 -0700 Subject: [PATCH 80/81] halui -quiet debugging prints --- src/emc/usr_intf/halui.cc | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index 2e1e80ea18e..866a9fac5a2 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -696,7 +696,7 @@ static char* py_call_get_mdi_name( int num) { PyObject *pBytes = PyUnicode_AsUTF8String(pValue); char *result_string = PyBytes_AsString(pBytes); Py_XDECREF(pBytes); - printf("Python function returned: %s %i\n", result_string,num); + //printf("Python function returned: %s %i\n", result_string,num); Py_DECREF(pValue); Py_DECREF(pFuncWrite); return result_string; @@ -954,7 +954,7 @@ int halui_hal_init(void) } for (int n=0; ngui_mdi_commands[n]), 0, "halui.gui.mdi-command.%s", py_call_get_mdi_name(n))); } @@ -1541,7 +1541,6 @@ static int iniLoad(const char *filename) int temp = py_call_get_mdi_count(); for (int n=0; n= 0) { - fprintf(stderr, "halui Bridge: axis selected %d\n",aselect_changed); + //fprintf(stderr, "halui Bridge: axis selected %d\n",aselect_changed); for (axis_num = 0; axis_num < EMCMOT_MAX_AXIS; axis_num++) { if ( !(axis_mask & (1 << axis_num)) ) { continue; } if (axis_num != aselect_changed) { @@ -2393,7 +2388,6 @@ static void check_hal_changes() hal_set_bool(halui_data->axis_is_selected[axis_num], 1); if (hal_get_bool(halui_data->ajog_plus[num_axes])) { - fprintf(stderr, "halui: jog plus: %d\n",num_axes); sendJogCont(axis_num, tempjogspeed,JOGTELEOP); } else if (hal_get_bool(halui_data->ajog_minus[num_axes])) { sendJogCont(axis_num, -tempjogspeed,JOGTELEOP); @@ -2507,7 +2501,6 @@ static void check_hal_changes() // request GUI to run MDI commands for(int n = 0; n < num_gui_mdi_commands; n++) { if (check_bit_changed(new_halui_data.gui_mdi_commands[n], old_halui_data.gui_mdi_commands[n]) != 0){ - fprintf(stderr,"GUI MDI command called index: %i\n", n); py_call_request_MDI(n); } } @@ -2515,28 +2508,23 @@ static void check_hal_changes() // request GUI soft keys for(int n = 0; n < num_gui_soft_keys; n++) { if (check_bit_changed(new_halui_data.gui_soft_keys[n], old_halui_data.gui_soft_keys[n]) != 0){ - fprintf(stderr,"GUI SOFTKEY called index: %i\n", n); py_call_request_softkey(n); } } if (check_bit_changed(new_halui_data.gui_ok, old_halui_data.gui_ok) != 0) { - fprintf(stderr,"GUI OK command called\n"); py_call_ok(); } if (check_bit_changed(new_halui_data.gui_cancel, old_halui_data.gui_cancel) != 0) { - fprintf(stderr,"GUI CANCEL command called\n"); py_call_cancel(); } if (check_bit_changed(new_halui_data.gui_reload, old_halui_data.gui_reload) != 0) { - fprintf(stderr,"GUI RELOAD DISPLAY command called\n"); py_call_reload_display(); } if (check_bit_changed(new_halui_data.gui_shutdown, old_halui_data.gui_shutdown) != 0) { - fprintf(stderr,"GUI SHUTDOWN command called\n"); py_call_shutdown_controller(); } From 7fb2896dbef635334534d7b32338157ffa983c61 Mon Sep 17 00:00:00 2001 From: Cmorley Date: Sat, 15 Aug 2026 05:36:39 -0700 Subject: [PATCH 81/81] qtvcp -axis tool button: quiet debugging print --- lib/python/qtvcp/widgets/axis_tool_button.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/python/qtvcp/widgets/axis_tool_button.py b/lib/python/qtvcp/widgets/axis_tool_button.py index ea93e00f62a..41a749a32d7 100644 --- a/lib/python/qtvcp/widgets/axis_tool_button.py +++ b/lib/python/qtvcp/widgets/axis_tool_button.py @@ -228,7 +228,7 @@ def _a_from_j(self, axis): return axis, r[jnum] def selectJoint(self): - print(self.objectName(),f'select: j{self._joint} a{self._axis} ck{self.isChecked()}') + #print(self.objectName(),f'select: j{self._joint} a{self._axis} ck{self.isChecked()}') if self._block_signal or self._joint == -1 or self._axis == '': return if self.isChecked() == True: if STATUS.is_joint_mode():