#!/usr/bin/env python3 # GladeVcp Widget - DRO widget, showing all 3 reference types # This widgets displays linuxcnc axis position information. # # Copyright (c) 2013 Norbert Schechner # Based on the drowidget from Chris Morley # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. import gi gi.require_version("Gtk","3.0") gi.require_version("Gdk","3.0") from gi.repository import Gtk from gi.repository import Gdk from gi.repository import GObject from gi.repository import Pango from gi.repository import GLib import os import sys import math import linuxcnc from hal_glib import GStat import re # constants _INCH = 0 _MM = 1 _AXISLETTERS = ["X", "Y", "Z", "A", "B", "C", "U", "V", "W"] # we put this in a try so there is no error in the glade editor # linuxcnc is probably not running then try: INIPATH = os.environ['INI_FILE_NAME'] except: # INIPATH = '/home/emcmesa/linuxcnc-dev/configs/sim/gmoccapy/gmoccapy.ini' pass # This is the main class class Combi_DRO(Gtk.VBox): ''' Combi_DRO will display an linuxcnc DRO with all three types at ones Combi_DRO = Combi_DRO(joint_number) joint_number is an integer in the range from 0 to 8 where 0 = X, 1 = Y, 2 = Z, etc. ''' __gtype_name__ = 'Combi_DRO' __gproperties__ = { 'joint_number' : (GObject.TYPE_INT, 'Joint Number', '0:X 1:Y 2:Z etc', 0, 8, 0, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), 'actual' : (GObject.TYPE_BOOLEAN, 'Actual Position', 'Display Actual or Commanded Position', True, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), 'metric_units' : (GObject.TYPE_BOOLEAN, 'Display in metric units', 'Display in metric or not', True, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), 'auto_units' : (GObject.TYPE_BOOLEAN, 'Change units according gcode', 'Units will toggle between metric and imperial according to gcode.', True, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), 'diameter' : (GObject.TYPE_BOOLEAN, 'Diameter Adjustment', 'Display Position As Diameter', False, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), 'mm_text_template' : (GObject.TYPE_STRING, 'Text template for Metric Units', 'Text template to display. Python formatting may be used for one variable', "%10.3f", GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), 'imperial_text_template' : (GObject.TYPE_STRING, 'Text template for Imperial Units', 'Text template to display. Python formatting may be used for one variable', "%9.4f", GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), 'homed_color' : (Gdk.RGBA.__gtype__, 'homed color', 'Sets the color of the display when the axis is homed', GObject.ParamFlags.READWRITE), 'unhomed_color' : (Gdk.RGBA.__gtype__, 'unhomed color', 'Sets the color of the display when the axis is not homed', GObject.ParamFlags.READWRITE), 'abs_color' : (Gdk.RGBA.__gtype__, 'Absolute color', 'Sets the color of the display when absolute coordinates are used', GObject.ParamFlags.READWRITE), 'rel_color' : (Gdk.RGBA.__gtype__, 'Relative color', 'Sets the color of the display when relative coordinates are used', GObject.ParamFlags.READWRITE), 'dtg_color' : (Gdk.RGBA.__gtype__, 'DTG color', 'Sets the color of the display when dtg coordinates are used', GObject.ParamFlags.READWRITE), 'font_size' : (GObject.TYPE_INT, 'Font Size', 'The font size of the big numbers, the small ones will be 2.5 times smaller', 8, 96, 25, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), 'toggle_readout' : (GObject.TYPE_BOOLEAN, 'Enable toggling readout with click', 'The DRO will toggle between Absolute, Relative and DTG with each mouse click.', True, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), 'cycle_time' : (GObject.TYPE_INT, 'Cycle Time', 'Time, in milliseconds, that display will sleep between polls', 100, 1000, 150, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT), } __gproperties = __gproperties__ __gsignals__ = { 'clicked': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING, GObject.TYPE_PYOBJECT)), 'units_changed': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_BOOLEAN,)), 'system_changed': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)), 'axis_clicked': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)), 'exit': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ()), } # Init the class def __init__(self, joint_number = 0): super(Combi_DRO, self).__init__() # we have to distinguish this, as we use the joints number to check homing # and we do need the axis to check for the positions # this is needed if non trivial kinematics are used or just a lathe, # as the lathe has only two joints, but Z will be the third value in position feedback self.axis_no = self.joint_number = joint_number # get the necessary connections to linuxcnc self.linuxcnc = linuxcnc self.status = linuxcnc.stat() self.gstat = GStat() # set some default values' self._ORDER = ["Rel", "Abs", "DTG"] self.system = "Rel" self.homed = False self.homed_color = "#00FF00" self.unhomed_color = "#FF0000" self.abs_color = "#0000FF" self.rel_color = "#000000" self.dtg_color = "#FFFF00" self.mm_text_template = "%10.3f" self.imperial_text_template = "%9.4f" self.font_size = 25 self.metric_units = True self.machine_units = _MM self.unit_convert = 1 self._auto_units = True self.toggle_readout_enable = True self.cycle_time = 150 self.diameter = False self.actual = True self.dtg = 0 self.abs_pos = 0 self.rel_pos = 0 self.widgets = {} # will hold all our widgets we need to style # Make the GUI and connect signals self.css_text = """ .background {background-color: #000000;} .labelcolor {color: #FF0000;} .size_big {font-size: 25px;font-weight: bold;} .size_small {font-size: 10px;font-weight: bold;} """ self.css = Gtk.CssProvider() self.css.load_from_data(bytes(self.css_text, 'utf-8')) eventbox = Gtk.EventBox() eventbox.get_style_context().add_provider(self.css,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) eventbox.get_style_context().add_class('background') self.add(eventbox) vbox_main = Gtk.VBox(homogeneous = False, spacing = 0) eventbox.add(vbox_main) hbox_up = Gtk.HBox(homogeneous = False, spacing = 5) vbox_main.pack_start(hbox_up, True, True, 0) self.widgets["eventbox"] = eventbox lbl_axisletter = Gtk.Label(label = _AXISLETTERS[self.axis_no]) lbl_axisletter.get_style_context().add_provider(self.css,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) lbl_axisletter.get_style_context().add_class('background') lbl_axisletter.get_style_context().add_class('labelcolor') lbl_axisletter.get_style_context().add_class('size_big') hbox_up.pack_start(lbl_axisletter, False, False, 0) self.widgets["lbl_axisletter"] = lbl_axisletter vbox_ref_type = Gtk.VBox(homogeneous = False, spacing = 0) hbox_up.pack_start(vbox_ref_type, False, False, 0) # This label is needed to press the main index (rel,Abs;Dtg) to the upper part lbl_space = Gtk.Label(label = "") vbox_ref_type.pack_start(lbl_space, True, True, 0) lbl_sys_main = Gtk.Label(label = self.system) lbl_sys_main.get_style_context().add_provider(self.css,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) lbl_sys_main.get_style_context().add_class('background') lbl_sys_main.get_style_context().add_class('labelcolor') lbl_sys_main.get_style_context().add_class("size_small") vbox_ref_type.pack_start(lbl_sys_main, False, False, 0) self.widgets["lbl_sys_main"] = lbl_sys_main main_dro = Gtk.Label(label = "9999.999") main_dro.get_style_context().add_provider(self.css,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) main_dro.get_style_context().add_class('background') main_dro.get_style_context().add_class('labelcolor') main_dro.get_style_context().add_class("size_big") main_dro.set_xalign(1.0) hbox_up.pack_start(main_dro, True, True, 0) self.widgets["main_dro"] = main_dro hbox_down = Gtk.HBox(homogeneous = True, spacing = 5) vbox_main.pack_start(hbox_down, False, False, 0) lbl_sys_left = Gtk.Label(label = "Abs") lbl_sys_left.set_xalign(0.0) lbl_sys_left.get_style_context().add_provider(self.css,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) lbl_sys_left.get_style_context().add_class('background') lbl_sys_left.get_style_context().add_class('labelcolor') lbl_sys_left.get_style_context().add_class('size_small') hbox_down.pack_start(lbl_sys_left, True, True, 0) self.widgets["lbl_sys_left"] = lbl_sys_left dro_left = Gtk.Label(label = "-11.111") dro_left.get_style_context().add_provider(self.css,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) dro_left.get_style_context().add_class('background') dro_left.get_style_context().add_class('labelcolor') dro_left.get_style_context().add_class('size_small') dro_left.set_xalign(1.0) hbox_down.pack_start(dro_left, True, True, 0) self.widgets["dro_left"] = dro_left lbl_sys_right = Gtk.Label(label = "DTG") lbl_sys_right.set_xalign(0.0) lbl_sys_right.get_style_context().add_provider(self.css,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) lbl_sys_right.get_style_context().add_class('background') lbl_sys_right.get_style_context().add_class('labelcolor') lbl_sys_right.get_style_context().add_class('size_small') hbox_down.pack_start(lbl_sys_right, False, False, 0) self.widgets["lbl_sys_right"] = lbl_sys_right dro_right = Gtk.Label(label = "22.222") dro_right.get_style_context().add_provider(self.css,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) dro_right.get_style_context().add_class('background') dro_right.get_style_context().add_class('labelcolor') dro_right.get_style_context().add_class('size_small') dro_right.set_xalign(1.0) hbox_down.pack_start(dro_right, True, True, 0) self.widgets["dro_right"] = dro_right eventbox.connect("button_press_event", self._on_eventbox_clicked) self.show_all() self.gstat.connect('not-all-homed', self._not_all_homed ) self.gstat.connect('all-homed', self._all_homed ) self.gstat.connect('homed', self._homed ) self.gstat.connect('current-position', self._position) self.gstat.connect('user-system-changed', self._user_system_changed) self._set_labels() # This try is only needed because while working with glade # linuxcnc may not be working try: self.inifile = self.linuxcnc.ini(INIPATH) # check the INI file if UNITS are set to mm" # first check the global settings units = self.inifile.find("TRAJ", "LINEAR_UNITS") if units == None: # else then the X axis units units = self.inifile.find("AXIS_0", "UNITS") except: units = "inch" if units == "mm" or units == "metric" or units == "1.0": self.machine_units = _MM else: self.machine_units = _INCH # if the eventbox has been clicked, we like to toggle the DRO's # or just emit a signal to allow GUI to do what ever they want with that # signal- gmoccapy uses this signal to open the touch off dialog def _on_eventbox_clicked(self, widget, event): if event.x <= self.widgets["lbl_axisletter"].get_allocation().width + self.widgets["lbl_sys_main"].get_allocation().width: self.emit('axis_clicked', self.widgets["lbl_axisletter"].get_text().lower()) #self.set_style("labelcolor", "#00FF00") else: if not self.toggle_readout_enable: return self.toggle_readout() # Get propertys def do_get_property(self, property): name = property.name.replace('-', '_') if name in list(self.__gproperties.keys()): return getattr(self, name) else: raise AttributeError('unknown property %s' % property.name) def convert_color(self, color): colortuple = ((int(color.red * 255.0), int(color.green * 255.0), int(color.blue * 255.0))) return ('#' + ''.join(f'{i:02X}' for i in colortuple)) # Set propertys def do_set_property(self, property, value): try: name = property.name.replace('-', '_') if name in list(self.__gproperties.keys()): # setattr(self, name, value) # self.queue_draw() if name in ('mm_text_template', 'imperial_text_template'): try: v = value % 0.0 except Exception as e: print("Invalid format string '%s': %s" % (value, e)) return False if name == "homed_color": self.homed_color = self.convert_color(value) if self.homed: self.set_style("labelcolor", self.homed_color) else: self.set_style("labelcolor", self.unhomed_color) if name == "unhomed_color": self.unhomed_color = self.convert_color(value) if self.homed: self.set_style("labelcolor", self.homed_color) else: self.set_style("labelcolor", self.unhomed_color) if name == "abs_color": self.abs_color = self.convert_color(value) self.toggle_readout(True) if name == "rel_color": self.rel_color = self.convert_color(value) self.toggle_readout(True) if name == "dtg_color": self.dtg_color = self.convert_color(value) self.toggle_readout(True) if name == "auto_units": self._auto_units = value self._set_labels() if name == "joint_number": self.axis_no = self.joint = value self.change_axisletter(_AXISLETTERS[self.axis_no]) if name == "font_size": self.font_size = int(value) self.set_style("size", self.font_size) if name == "toggle_readout": self.toggle_readout_enable = value if name == "cycle_time": self.cycle_time = value if name in ('metric_units', 'actual', 'diameter'): setattr(self, name, value) self.queue_draw() else: raise AttributeError('unknown property %s' % property.name) except: pass # get the actual coordinate system to display it on the DRO def _get_current_system(self): gcode = self.status.gcodes[1:] for code in gcode: if code >= 540 and code <= 590: return "G%s" % int((code / 10)) elif code > 590 and code <= 593: return "G%s" % int((code / 10.0)) return "Rel" # Get the units used according to gcode def _get_current_units(self): self.status.poll() gcode = self.status.gcodes[1:] for code in gcode: if code >= 200 and code <= 210: return (code // 10) return False # update the labels def _set_labels(self): self.status.poll() if self._ORDER[0] == "Rel": self.widgets["lbl_sys_main"].set_text(self._get_current_system()) else: self.widgets["lbl_sys_main"].set_text(self._ORDER[0]) if self._ORDER[1] == "Rel": self.widgets["lbl_sys_left"].set_text(self._get_current_system()) else: self.widgets["lbl_sys_left"].set_text(self._ORDER[1]) if self._ORDER[2] == "Rel": self.widgets["lbl_sys_right"].set_text(self._get_current_system()) else: self.widgets["lbl_sys_right"].set_text(self._ORDER[2]) self.system = self._get_current_system() def set_style(self, property, Data): # self.css_text = """ # .background {background-color: black;} # .labelcolor {color: red;} # .size_big {font-size: 25px;font-weight: bold;} # .size_small {font-size: 10px;font-weight: bold;} # """ if property == "background": for widget in self.widgets: self.widgets[widget].get_style_context().remove_class('background') replacement_string = ".background {background-color: " + Data + ";}" self.css_text = re.sub(r'[.][b][a][c][k][g][r][o][u][n][d].*', replacement_string, self.css_text, re.IGNORECASE) elif property == "labelcolor": for widget in self.widgets: self.widgets[widget].get_style_context().remove_class('labelcolor') replacement_string = ".labelcolor {color: " + Data + ";}" self.css_text = re.sub(r'[.][l][a][b][e][l][c][o][l][o][r].*', replacement_string, self.css_text, re.IGNORECASE) elif property == "size": for widget in self.widgets: self.widgets[widget].get_style_context().remove_class('size_big') self.widgets[widget].get_style_context().remove_class('size_small') replacement_string = ".size_big {font-size: " + str(Data) + "px;font-weight: bold;}" self.css_text = re.sub(r'[.][s][i][z][e][_][b][i][g].*', replacement_string, self.css_text, re.IGNORECASE) replacement_string = ".size_small {font-size: " + str(int(Data / 2.5)) + "px;font-weight: bold;}" self.css_text = re.sub(r'[.][s][i][z][e][_][s][m][a][l][l].*', replacement_string, self.css_text, re.IGNORECASE) else: print("Got unknown property in <>") return self.css.load_from_data(bytes(self.css_text, 'utf-8')) for widget in self.widgets: self.widgets[widget].get_style_context().add_class('background') self.widgets[widget].get_style_context().add_class('labelcolor') if widget in ("lbl_axisletter", "main_dro"): self.widgets[widget].get_style_context().add_class('size_big') else: self.widgets[widget].get_style_context().add_class('size_small') self.queue_draw() def _user_system_changed(self, object, system): self._set_labels() def _position(self, object, p, rel_p, dtg, joint_actual_position): # object = hal_glib Object # p = self.stat.actual_position # rel_p = relative position # dtg = distance to go # joint_actual_position = joint positions, not needed here try: dtg = dtg[self.axis_no] abs_pos = p[self.axis_no] rel_pos = rel_p[self.axis_no] except: return if (self._get_current_units() == 20 and self.metric_units) or (self._get_current_units() == 21 and not self.metric_units): if self._auto_units: self.metric_units = not self.metric_units self.emit("units_changed", self.metric_units) if self.metric_units and self.machine_units == _INCH: if self.axis_no not in (3, 4, 5): abs_pos = abs_pos * 25.4 rel_pos = rel_pos * 25.4 dtg = dtg * 25.4 if not self.metric_units and self.machine_units == _MM: if self.axis_no not in (3, 4, 5): abs_pos = abs_pos / 25.4 rel_pos = rel_pos / 25.4 dtg = dtg / 25.4 if self._ORDER == ["Rel", "Abs", "DTG"]: main, left, right = rel_pos, abs_pos, dtg if self._ORDER == ["DTG", "Rel", "Abs"]: main, left, right = dtg, rel_pos, abs_pos if self._ORDER == ["Abs", "DTG", "Rel"]: main, left, right = abs_pos, dtg, rel_pos if self.metric_units: tmpl = lambda s: self.mm_text_template % s else: tmpl = lambda s: self.imperial_text_template % s if self.diameter: scale = 2.0 else: scale = 1.0 main_dro = tmpl(main * scale) left_dro = tmpl(left * scale) right_dro = tmpl(right * scale) self.widgets["main_dro"].set_label(main_dro) self.widgets["dro_left"].set_label(left_dro) self.widgets["dro_right"].set_label(right_dro) self.dtg = dtg * scale self.abs_pos = abs_pos * scale self.rel_pos = rel_pos * scale def _not_all_homed(self, widget, data = None): if self.status.kinematics_type == linuxcnc.KINEMATICS_IDENTITY: self.status.poll() self.homed = self.status.homed[self.joint_no] else: self.homed = False if self.homed: self.set_style("labelcolor", self.homed_color) else: self.set_style("labelcolor", self.unhomed_color) def _all_homed(self, widget, data = None): if self.status.kinematics_type == linuxcnc.KINEMATICS_IDENTITY: return if not self.homed: self.homed = True self.set_style("labelcolor", self.homed_color) def _homed(self, widget, data = None): if self.status.kinematics_type != linuxcnc.KINEMATICS_IDENTITY: return else: self.status.poll() self.homed = self.status.homed[self.joint_no] self.set_style("labelcolor", self.homed_color) # sets the DRO explicitly to inch or mm # attentions auto_units also takes effect on that! def set_to_inch(self, state): ''' sets the DRO to show imperial units Combi_DRO.set_to_inch(state) state = boolean (true or False) ''' if state: self.metric_units = False else: self.metric_units = True # If auto_units is true, the DRO will change according to the # active gcode (G20 / G21) def set_auto_units(self, state): ''' if True the DRO will change units according to active gcode (G20 / G21) Combi_DRO.set_auto_units(state) state = boolean (true or False) ''' self._auto_units = state # Set the axis to diameter mode, the DRO value will be # multiplied by 2 def set_to_diameter(self, state): ''' if True the DRO will show the diameter not the radius, specially needed for lathes the DRO value will be multiplied by 2 Combi_DRO.set_to_diameter(state) state = boolean (true or False) ''' self.diameter = state # this will toggle the DRO around, mainly used to maintain all DRO # at the same state, because a click on one will only change that DRO # This can be used to change also the others def toggle_readout(self, Data = None): ''' toggles the order of the DRO in the widget Combi_DRO.toggle_readout() ''' if not Data: self._ORDER = [self._ORDER[2], self._ORDER[0], self._ORDER[1]] if self._ORDER[0] == "Abs": bg_color = self.abs_color elif self._ORDER[0] == "DTG": bg_color = self.dtg_color else: bg_color = self.rel_color self.set_style("background", bg_color) self._set_labels() # if Data is True, we only update the colors of the background # so we won't emit a click event if Data: return self.emit("clicked", self.joint_number, self._ORDER) # You can change the automatic given axisletter using this function # i.e. to use an axis as R or D insteadt of X on a lathe def change_axisletter(self, letter): ''' changes the automatically given axis-letter very useful to change an lathe DRO from X to R or D Combi_DRO.change_axis-letter(letter) letter = string ''' self.widgets["lbl_axisletter"].set_text(letter) def set_joint_no(self, joint): ''' changes the joint, not the joint number. This is handy for special cases, like Gantry configs, i.e. XYYZ, where joint 0 = X, joint 1 = Y1 joint 2 = Y2 and joint 3 = Z, so the Z axis can be set to joint_number 2 giving the axis letter Z and joint 3 being in this case the corresponding joint, joint 3 instead of 2 ''' self.joint_no = joint def set_axis(self, axis): ''' changes the axis, not the joint number. This is handy for special cases, like Lathe configs, i.e. XZ, where joint 0 = X, joint 1 = Z so the Z axis must be set to joint_number 1 for homing, but we need the axis letter Z to give the correct position feedback ''' self.axis_no = "xyzabcuvws".index(axis.lower()) # returns the order of the DRO, mainly used to maintain them consistent # the order will also be transmitted with the clicked signal def get_order(self): ''' returns the order of the DRO in the widget mainly used to maintain them consistent the order will also be transmitted with the clicked signal Combi_DRO.get_order() returns a list containing the order ''' return self._ORDER # sets the order of the DRO, mainly used to maintain them consistent def set_order(self, order): ''' sets the order of the DRO, mainly used to maintain them consistent Combi_DRO.set_order(order) order = list object, must be one of ["Rel", "Abs", "DTG"] ["DTG", "Rel", "Abs"] ["Abs", "DTG", "Rel"] ''' self._ORDER = order self.toggle_readout(Data=True) # This will return the position information of all three DRO # it will be in the order Abs, Rel, DTG def get_position(self): ''' returns the positions of the DRO Combi_DRO.get_position() returns the position of the DRO as a list of floats the order is independent of the order shown on the DRO and will be given as [Absolute , relative , DTG] Absolute = the machine coordinates, depends on the actual property will give actual or commanded position Relative = will be the coordinates of the actual coordinate system DTG = the distance to go, will mosltly be 0, as this function should not be used while the machine is moving, because of time delays ''' return self.abs_pos, self.rel_pos, self.dtg # for testing without glade editor: # to show some behavior and setting options def main(): window = Gtk.Window(type = Gtk.WindowType.TOPLEVEL) vbox = Gtk.VBox(homogeneous = False, spacing = 5) MDRO_X = Combi_DRO(0) MDRO_Y = Combi_DRO(1) MDRO_Z = Combi_DRO(2) MDRO_C = Combi_DRO(5) vbox.add(MDRO_X) vbox.add(MDRO_Y) vbox.add(MDRO_Z) vbox.add(MDRO_C) window.add(vbox) window.connect("destroy", Gtk.main_quit) MDRO_X.connect("clicked", clicked) MDRO_Y.connect("clicked", clicked) MDRO_Y.set_auto_units(False) MDRO_Y.set_to_inch(True) # MDRO_Y.set_to_diameter(True) # MDRO_Y.set_property('joint_number',0) # MDRO_Y.change_axisletter("D") MDRO_Z.connect("clicked", clicked) MDRO_C.connect("clicked", clicked) MDRO_C.set_property('mm_text_template', '%10.2f') MDRO_C.set_property('imperial_text_template', '%10.3f') MDRO_C.set_property('toggle_readout', False) window.show_all() Gtk.main() def clicked(self, axis_number, order): ''' This signal will be emitted if the user clicked on the DRO axis_number = the joint number of the widget order = the actual order of the DRO in the widget ''' print("Click received from ", axis_number) #print("Order = ", order) #print(self.get_position()) # self.set_property("joint_number", 0) # other_widget.set_order(order) # so they may be maintained consistent if __name__ == "__main__": main()