diff --git a/configs/sim/axis/axis_halui_test.ini b/configs/sim/axis/axis_halui_test.ini
new file mode 100644
index 00000000000..877a91db9d8
--- /dev/null
+++ b/configs/sim/axis/axis_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 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
+
+[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
+
+# 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..5c9e1e95d16
--- /dev/null
+++ b/configs/sim/axis/gstatmessages.py
@@ -0,0 +1,66 @@
+
+from hal_glib import GStat
+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))
+def user_live_update():
+ GSTAT.run_iteration()
+
+def select_axis(data):
+ if data is None: return
+ widget = getattr(widgets, "axis_%s" % data.lower())
+ widget.focus()
+ widget.invoke()
+
+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)
diff --git a/configs/sim/axis/panel.hal b/configs/sim/axis/panel.hal
new file mode 100644
index 00000000000..5db03ed71da
--- /dev/null
+++ b/configs/sim/axis/panel.hal
@@ -0,0 +1,24 @@
+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 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-0 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.cycle.start panel.cycle-start
+net start halui.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
+
diff --git a/configs/sim/axis/panel.ui b/configs/sim/axis/panel.ui
new file mode 100644
index 00000000000..7264d58f6d0
--- /dev/null
+++ b/configs/sim/axis/panel.ui
@@ -0,0 +1,365 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 404
+ 560
+
+
+
+ MainWindow
+
+
+
+ -
+
+
-
+
+
+ Axis Selection
+
+
+
-
+
+
+ None
+
+
+ true
+
+
+ true
+
+
+ axis-none
+
+
+
+ -
+
+
+ X
+
+
+ true
+
+
+ true
+
+
+ axis-x
+
+
+
+ -
+
+
+ Y
+
+
+ true
+
+
+ true
+
+
+ axis-y
+
+
+
+ -
+
+
+ Z
+
+
+ true
+
+
+ true
+
+
+ axis-z
+
+
+
+
+
+
+ -
+
+
+ Axis Jog
+
+
+
-
+
+
+ +
+
+
+ jog-pos
+
+
+
+ -
+
+
+ -
+
+
+ jog-neg
+
+
+
+
+
+
+ -
+
+
+ jog rate
+
+
+
-
+
+
+ Qt::Horizontal
+
+
+ jog-rate
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PushButton
+ QPushButton
+ qtvcp.widgets.simple_widgets
+
+
+ StatusSlider
+ QSlider
+ qtvcp.widgets.status_slider
+
+
+
+
+
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_right_panel.ini b/configs/sim/gmoccapy/gmoccapy_right_panel.ini
index 2b7c12274c0..9704bec48f9 100644
--- a/configs/sim/gmoccapy/gmoccapy_right_panel.ini
+++ b/configs/sim/gmoccapy/gmoccapy_right_panel.ini
@@ -31,6 +31,17 @@ 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
+
+[MDI_COMMAND_LIST]
+# for macro buttons on main oage up to 10 possible
+MDI_COMMAND = G0 Z1;X0 Y0;Z0, Goto\nUser\nZero
+MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero
+
[FILTER]
PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image
PROGRAM_EXTENSION = .py Python Script
@@ -75,7 +86,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/configs/sim/gmoccapy/panel.hal b/configs/sim/gmoccapy/panel.hal
new file mode 100644
index 00000000000..d48117a20ff
--- /dev/null
+++ b/configs/sim/gmoccapy/panel.hal
@@ -0,0 +1,24 @@
+net rate halui.axis.jog-speed panel.Jog-rate-f
+
+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 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-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.cycle.start panel.cycle-start
+net start halui.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
+
diff --git a/configs/sim/gmoccapy/panel.ui b/configs/sim/gmoccapy/panel.ui
new file mode 100644
index 00000000000..6ed71542f13
--- /dev/null
+++ b/configs/sim/gmoccapy/panel.ui
@@ -0,0 +1,365 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 404
+ 537
+
+
+
+ MainWindow
+
+
+
+ -
+
+
-
+
+
+ Axis Selection
+
+
+
-
+
+
+ None
+
+
+ true
+
+
+ true
+
+
+ axis-none
+
+
+
+ -
+
+
+ X
+
+
+ true
+
+
+ true
+
+
+ axis-x
+
+
+
+ -
+
+
+ Y
+
+
+ true
+
+
+ true
+
+
+ axis-y
+
+
+
+ -
+
+
+ Z
+
+
+ true
+
+
+ true
+
+
+ axis-z
+
+
+
+
+
+
+ -
+
+
+ Axis Jog
+
+
+
-
+
+
+ +
+
+
+ jog-pos
+
+
+
+ -
+
+
+ -
+
+
+ jog-neg
+
+
+
+
+
+
+ -
+
+
+ jog rate
+
+
+
-
+
+
+ 300
+
+
+ Qt::Horizontal
+
+
+ Jog-rate
+
+
+
+
+
+
+ -
+
+
+ MDI Comands
+
+
+
-
+
+
+ 0
+
+
+ mdi-0
+
+
+
+ -
+
+
+ 1
+
+
+ mdi-1
+
+
+
+ -
+
+
+ 2
+
+
+ mdi-2
+
+
+
+
+
+
+ -
+
+
+ Mode Comands
+
+
+
-
+
+
+ Manual
+
+
+ true
+
+
+ false
+
+
+ manual-mode
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ MDI
+
+
+ true
+
+
+ false
+
+
+ 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PushButton
+ QPushButton
+ qtvcp.widgets.simple_widgets
+
+
+ Slider
+ QSlider
+ qtvcp.widgets.simple_widgets
+
+
+
+
+
diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.hal b/configs/sim/qtdragon/qtdragon_xyz/panel.hal
new file mode 100644
index 00000000000..04bc14e8e26
--- /dev/null
+++ b/configs/sim/qtdragon/qtdragon_xyz/panel.hal
@@ -0,0 +1,24 @@
+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 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-0 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.cycle.start panel.cycle-start
+net start halui.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
+
diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.ui b/configs/sim/qtdragon/qtdragon_xyz/panel.ui
new file mode 100644
index 00000000000..7264d58f6d0
--- /dev/null
+++ b/configs/sim/qtdragon/qtdragon_xyz/panel.ui
@@ -0,0 +1,365 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 404
+ 560
+
+
+
+ MainWindow
+
+
+
+ -
+
+
-
+
+
+ Axis Selection
+
+
+
-
+
+
+ None
+
+
+ true
+
+
+ true
+
+
+ axis-none
+
+
+
+ -
+
+
+ X
+
+
+ true
+
+
+ true
+
+
+ axis-x
+
+
+
+ -
+
+
+ Y
+
+
+ true
+
+
+ true
+
+
+ axis-y
+
+
+
+ -
+
+
+ Z
+
+
+ true
+
+
+ true
+
+
+ axis-z
+
+
+
+
+
+
+ -
+
+
+ Axis Jog
+
+
+
-
+
+
+ +
+
+
+ jog-pos
+
+
+
+ -
+
+
+ -
+
+
+ jog-neg
+
+
+
+
+
+
+ -
+
+
+ jog rate
+
+
+
-
+
+
+ Qt::Horizontal
+
+
+ jog-rate
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PushButton
+ QPushButton
+ qtvcp.widgets.simple_widgets
+
+
+ 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..e7a674f81b2
--- /dev/null
+++ b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_halui_test.ini
@@ -0,0 +1,267 @@
+# 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 oage up to 10 possible
+MDI_COMMAND_MACRO0 = G0 Z25;X0 Y0;Z0, Goto\nUser\nZero
+MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero
+
+[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
+
+# 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
+
+
diff --git a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini
index 327d051bd5b..a00b52ab427 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..f2377f26100
--- /dev/null
+++ b/lib/python/bridgeui/bridge.py
@@ -0,0 +1,253 @@
+#!/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}
+ 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))
+ 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':
+ flag = 1
+ 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)
+
+ # 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
+ else:
+ index = 'XYZABCUVW'.index(name)
+ return index
+ def setSelectedAxis(self, value):
+ if value < 0:
+ letter = 'None'
+ else:
+ letter ='XYZABCUVW'[value]
+ self.writeMsg('set_selected_axis', letter)
+
+ def isAxisSelected(self, index):
+ 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/lib/python/common/hal_glib.py b/lib/python/common/hal_glib.py
index 52eb0bae3c0..9d45601c7bc 100644
--- a/lib/python/common/hal_glib.py
+++ b/lib/python/common/hal_glib.py
@@ -243,6 +243,8 @@ class _GStat(GObject.GObject):
'following-error': (GObject.SignalFlags.RUN_FIRST , GObject.TYPE_NONE,(GObject.TYPE_PYOBJECT,)),
'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,)),
+ 'ok-request': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_BOOLEAN,)),
+ 'cancel-request': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_BOOLEAN,)),
'macro-call-request': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)),
}
@@ -345,6 +347,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()
@@ -353,8 +360,8 @@ def init_write_socket(self):
self.write_socket.bind(self.writeAddress)
LOG.debug('hal_glib write socket available: {}'.format(self.writeAddress))
self.write_available = True
- except:
- LOG.debug('hal_glib write socket not available')
+ except Exception as e:
+ LOG.debug('hal_glib write socket not available\n {}'.format(e))
self.write_available = False
# convert and actually send out the message
@@ -1449,6 +1456,12 @@ def request_cycle_pause(self, data):
def request_macro_call(self, data):
self.emit('macro-call-request', data)
+ def request_ok(self, data):
+ self.emit('ok-request', data)
+
+ def request_cancel(self, data):
+ self.emit('cancel-request', data)
+
#############################################
def shutdown(self):
diff --git a/lib/python/common/iniinfo.py b/lib/python/common/iniinfo.py
index 8cdabb3a291..177548849a9 100644
--- a/lib/python/common/iniinfo.py
+++ b/lib/python/common/iniinfo.py
@@ -19,7 +19,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')
@@ -571,7 +571,6 @@ def update(self):
if self.parser.has_section('MDI_COMMAND_LIST'):
try:
for key in self.parser['MDI_COMMAND_LIST']:
-
# legacy way: list of repeat 'MDI_COMMAND=XXXX'
# in this case order matters in the INI
if key == 'MDI_COMMAND':
@@ -594,10 +593,24 @@ def update(self):
self.MDI_COMMAND_LABEL_LIST.append(k)
mdidatadict['label'] = k
self.MDI_COMMAND_DICT[str(count)] = mdidatadict
- break
# 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)
@@ -605,14 +618,25 @@ def update(self):
temp = self.INI.find("MDI_COMMAND_LIST",key)
name = (key.replace('MDI_COMMAND_',''))
mdidatadict = {}
- for num,k in enumerate(temp.split(',')):
- if num == 0:
- mdidatadict['cmd'] = k
- if len(temp.split(',')) <2:
- mdidatadict['label'] = None
- else:
- mdidatadict['label'] = k
+
+ # find the last colon in string or 0
+ lastCmd = temp.rfind(';')
+ #print('l ;:',lastCmd)
+ if lastCmd == -1: lastCmd = 0
+
+ # find the last colon in string or use the string length
+ lastComma = temp.rfind(',', lastCmd)
+ #print('l comma:',lastComma,lastCmd)
+ if lastComma == -1: lastComma = len(temp)
+
+ label = temp[lastComma+1:]
+ cmd = temp[:lastComma]
+ #print(temp,' 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:
diff --git a/lib/python/gladevcp/gtk_action.py b/lib/python/gladevcp/gtk_action.py
index c7eb96d3671..ab22f063f7b 100644
--- a/lib/python/gladevcp/gtk_action.py
+++ b/lib/python/gladevcp/gtk_action.py
@@ -191,20 +191,39 @@ 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, mode_return = False):
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(';')
+ 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/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)
diff --git a/lib/python/qtvcp/qt_action.py b/lib/python/qtvcp/qt_action.py
index 2c5f18cedb0..3d57ef050c8 100644
--- a/lib/python/qtvcp/qt_action.py
+++ b/lib/python/qtvcp/qt_action.py
@@ -227,7 +227,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)
@@ -244,11 +244,21 @@ 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))
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/widgets/dialog_widget.py b/lib/python/qtvcp/widgets/dialog_widget.py
index 55c199baf8f..958ba61f4bb 100644
--- a/lib/python/qtvcp/widgets/dialog_widget.py
+++ b/lib/python/qtvcp/widgets/dialog_widget.py
@@ -398,6 +398,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)
@@ -440,6 +441,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):
@@ -490,7 +493,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()
@@ -498,10 +501,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()
@@ -517,6 +539,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,
@@ -558,16 +590,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)
-
############################################
# **********************
diff --git a/share/qtvcp/screens/qtdragon/qtdragon_handler.py b/share/qtvcp/screens/qtdragon/qtdragon_handler.py
index e911f8d54a5..adc2cc604cb 100644
--- a/share/qtvcp/screens/qtdragon/qtdragon_handler.py
+++ b/share/qtvcp/screens/qtdragon/qtdragon_handler.py
@@ -151,8 +151,10 @@ 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))
self.swoopPath = os.path.join(paths.IMAGEDIR,'lcnc_swoop.png')
self.swoopURL = QtCore.QUrl.fromLocalFile(self.swoopPath)
@@ -878,6 +880,7 @@ def lastRunLine(self, line):
# called from hal_glib to run macros from external event
def request_macro_call(self, data):
+ print('macro request:',data)
if not STATUS.is_mdi_mode():
self.add_status(_translate("HandlerClass",'Machine must be in MDI mode to run macros'), CRITICAL)
return
@@ -902,6 +905,8 @@ def request_macro_call(self, data):
self.add_status(_translate("HandlerClass",'Running macro: {} {}'.format(key, text)))
button.click()
break
+ else:
+ self.add_status(_translate("HandlerClass","can't find button for macro: {}".format(data)))
#######################
# CALLBACKS FROM FORM #
@@ -1252,6 +1257,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
@@ -2041,9 +2052,14 @@ def external_mpg(self, count):
def dialog_ext_control(self, pin, value, answer):
if value:
+ # handler defined dialog?
if not self._dialog_message is None:
name = self._dialog_message.get('NAME')
STATUS.emit('dialog-update',{'NAME':name,'response':answer})
+ else:
+ # tool change dialog?
+ if self.w.toolDialog_.isVisible():
+ STATUS.emit('dialog-update',{'NAME':'TOOLCHANGE','response':answer})
def log_version(self):
if INFO.RIP_FLAG:
@@ -2059,33 +2075,6 @@ def log_version(self):
self.add_status(mess, CRITICAL,noLog=True)
STATUS.emit('update-machine-log', mess, None)
- # called from hal_glib to run macros from external event
- def request_macro_call(self, data):
- if not STATUS.is_mdi_mode():
- self.add_status(_translate("HandlerClass",'Machine must be in MDI mode to run macros'), CRITICAL)
- return
-
- for b in range(0,10):
- button = self.w['macrobutton{}'.format(b)]
- # prefer named INI MDI commands
- key = button.property('ini_mdi_key')
- code = INFO.get_ini_mdi_command(key)
- if key == '' or code is None:
- # fallback to legacy nth line
- key = button.property('ini_mdi_number')
- code = INFO.get_ini_mdi_command(key)
- if code is None:
- continue
- if str(key) == data:
- #print('match',button.objectName())
- text = button.text().replace('\n',' ')
- self.add_status(_translate("HandlerClass",'Running macro: {} {}'.format(key, text)))
- try:
- button.click()
- except Exception as e:
- self.add_status(_translate("HandlerClass",'Running macro: {} {}\n{}'.format(key, text, e)))
- break
-
#####################
# KEY BINDING CALLS #
#####################
diff --git a/src/emc/usr_intf/Submakefile b/src/emc/usr_intf/Submakefile
index 0b1df44be40..2efe36b90a9 100644
--- a/src/emc/usr_intf/Submakefile
+++ b/src/emc/usr_intf/Submakefile
@@ -37,5 +37,6 @@ TARGETS += ../bin/linuxcnclcd
../bin/halui: $(call TOOBJS, $(HALUISRCS)) ../lib/liblinuxcnc.a ../lib/liblinuxcncini.so.0 ../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/gmoccapy/gmoccapy.glade b/src/emc/usr_intf/gmoccapy/gmoccapy.glade
index 9b5c2ee18df..1d720f12588 100644
--- a/src/emc/usr_intf/gmoccapy/gmoccapy.glade
+++ b/src/emc/usr_intf/gmoccapy/gmoccapy.glade
@@ -1,5 +1,5 @@
-
+
@@ -236,14 +236,14 @@
False
go-up
-