From 1cafec691ac099b4795baf49cbca6b43d05b03a2 Mon Sep 17 00:00:00 2001 From: electrolab Date: Sun, 6 Dec 2020 21:39:31 +0100 Subject: [PATCH] Make a local copy of gmoccapy for homing patch --- maurice_gmoccapy.py | 5205 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5205 insertions(+) create mode 100755 maurice_gmoccapy.py diff --git a/maurice_gmoccapy.py b/maurice_gmoccapy.py new file mode 100755 index 0000000..250ecd5 --- /dev/null +++ b/maurice_gmoccapy.py @@ -0,0 +1,5205 @@ +#! /usr/bin/python2 +# -*- coding:UTF-8 -*- +""" + A GUI for LinuxCNC based on gladevcp and Python + Based on the design of moccagui from Tom + and with a lot of code from gscreen from Chris Morley + and with the help from Michael Haberler + and Chris Morley and some more + + Copyright 2012 / 2017 Norbert Schechner + nieson@web.de + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" + +import traceback # needed to launch traceback errors +import hal # base hal class to react to hal signals +import hal_glib # needed to make our own hal pins +import gtk # base for pygtk widgets and constants +import sys # handle system calls +import os # needed to get the paths and directories +import gladevcp.makepins # needed for the dialog"s calculator widget +import atexit # needed to register child's to be closed on closing the GUI +import subprocess # to launch onboard and other processes +import tempfile # needed only if the user click new in edit mode to open a new empty file +import linuxcnc # to get our own error system +import gobject # needed to add the timer for periodic +import locale # for setting the language of the GUI +import gettext # to extract the strings to be translated +from collections import OrderedDict # needed for proper jog button arrangement + +from gladevcp.gladebuilder import GladeBuilder + +from gladevcp.combi_dro import Combi_DRO # we will need it to make the DRO + +from time import sleep # needed to get time in case of non wanted mode switch +from time import strftime # needed for the clock in the GUI +from gtk._gtk import main_quit + +# Throws up a dialog with debug info when an error is encountered +def excepthook(exc_type, exc_obj, exc_tb): + try: + w = app.widgets.window1 + except KeyboardInterrupt: + sys.exit() + except NameError: + w = None + lines = traceback.format_exception(exc_type, exc_obj, exc_tb) + m = gtk.MessageDialog(w, + gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, + ("Found an error!\nThe following information may be useful in troubleshooting:\n\n") + + "".join(lines)) + m.show() + m.run() + m.destroy() + + +sys.excepthook = excepthook + +debug = False + +if debug: + pydevdir = '/home/emcmesa/liclipse/plugins/org.python.pydev_4.5.4.201601292050/pysrc' + + if os.path.isdir(pydevdir): # and 'emctask' in sys.builtin_module_names: + sys.path.append(pydevdir) + sys.path.insert(0, pydevdir) + try: + import pydevd + + print("pydevd imported, connecting to Eclipse debug server...") + pydevd.settrace() + except: + print("no pydevd module found") + pass + +# constants +# # gmoccapy #" +_RELEASE = " 3.0.9.1" +_INCH = 0 # imperial units are active +_MM = 1 # metric units are active + +# set names for the tab numbers, its easier to understand the code +# Bottom Button Tabs +_BB_MANUAL = 0 +_BB_MDI = 1 +_BB_AUTO = 2 +_BB_HOME = 3 +_BB_TOUCH_OFF = 4 +_BB_SETUP = 5 +_BB_EDIT = 6 +_BB_TOOL = 7 +_BB_LOAD_FILE = 8 +#_BB_HOME_JOINTS will not be used, we will reorder the notebooks to get the correct page shown + +_TEMPDIR = tempfile.gettempdir() # Now we know where the tempdir is, usualy /tmp + +# set up paths to files +BASE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..")) +LIBDIR = os.path.join(BASE, "lib", "python") +sys.path.insert(0, LIBDIR) + +# as now we know the libdir path we can import our own modules +from gmoccapy import widgets # a class to handle the widgets +from gmoccapy import notification # this is the module we use for our error handling +from gmoccapy import preferences # this handles the preferences +from gmoccapy import getiniinfo # this handles the INI File reading so checking is done in that module +from gmoccapy import dialogs # this takes the code of all our dialogs + +_AUDIO_AVAILABLE = False +try: + import gst + from gmoccapy import player # a class to handle sounds + _AUDIO_AVAILABLE = True +except: + pass +# set up paths to files, part two +CONFIGPATH = os.environ['CONFIG_DIR'] +DATADIR = os.path.join(BASE, "share", "gmoccapy") +IMAGEDIR = os.path.join(DATADIR, "images") +XMLNAME = os.path.join(DATADIR, "gmoccapy.glade") +THEMEDIR = "/usr/share/themes" +USERTHEMEDIR = os.path.join(os.path.expanduser("~"), ".themes") +LOCALEDIR = os.path.join(BASE, "share", "locale") + +# path to TCL for external programs eg. halshow +TCLPATH = os.environ['LINUXCNC_TCL_DIR'] + +# the ICONS should must be in share/gmoccapy/images +ALERT_ICON = os.path.join(IMAGEDIR, "applet-critical.png") +INFO_ICON = os.path.join(IMAGEDIR, "std_info.gif") + +# this is for hiding the pointer when using a touch screen +pixmap = gtk.gdk.Pixmap(None, 1, 1, 1) +color = gtk.gdk.Color() +INVISABLE = gtk.gdk.Cursor(pixmap, pixmap, color, color, 0, 0) + + +class gmoccapy(object): + def __init__(self, argv): + + # prepare for translation / internationalisation + locale.setlocale(locale.LC_ALL, '') + locale.bindtextdomain("gmoccapy", LOCALEDIR) + gettext.install("gmoccapy", localedir=LOCALEDIR, unicode=True) + gettext.bindtextdomain("gmoccapy", LOCALEDIR) + + # needed components to comunicate with hal and linuxcnc + self.halcomp = hal.component("gmoccapy") + self.command = linuxcnc.command() + self.stat = linuxcnc.stat() + + self.error_channel = linuxcnc.error_channel() + # initial poll, so all is up to date + self.stat.poll() + self.error_channel.poll() + + self.builder = gtk.Builder() + # translation of the glade file will be done with + self.builder.set_translation_domain("gmoccapy") + self.builder.add_from_file(XMLNAME) + + self.widgets = widgets.Widgets(self.builder) + + self.initialized = False # will be set True after the window has been shown and all + # basic settings has been finished, so we avoid some actions + # because we cause click or toggle events when initializing + # widget states. + + self.start_line = 0 # needed for start from line + + self.active_gcodes = [] # this are the formated G code values + self.active_mcodes = [] # this are the formated M code values + self.gcodes = [] # this are the unformatted G code values to check if an update is required + self.mcodes = [] # this are the unformatted M code values to check if an update is required + + self.distance = 0 # This global will hold the jog distance + self.tool_change = False # this is needed to get back to manual mode after a tool change + self.load_tool = False # We use this avoid mode switching on reloading the tool on start up of the GUI + self.macrobuttons = [] # The list of all macros defined in the INI file + self.fo_counts = 0 # need to calculate difference in counts to change the feed override slider + 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.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 + self.rapidrate = 1 # holds the rapid override value and is needed to be able to react to halui pin + + self.incr_rbt_list = [] # we use this list to add hal pin to the button later + self.jog_increments = [] # This holds the increment values + self.unlock = False # this value will be set using the hal pin unlock settings + + # needed to display the labels + self.system_list = ("0", "G54", "G55", "G56", "G57", "G58", "G59", "G59.1", "G59.2", "G59.3") + self.dro_size = 28 # The size of the DRO, user may want them bigger on bigger screen + self.axisnumber_four = "" # we use this to get the number of the 4-th axis + self.axisletter_four = None # we use this to get the letter of the 4-th axis + self.axisnumber_five = "" # we use this to get the number of the 5-th axis + self.axisletter_five = None # we use this to get the letter of the 5-th axis + + self.notification = notification.Notification() # Our own message system + self.notification.connect("message_deleted", self._on_message_deleted) + self.last_key_event = None, 0 # needed to avoid the auto repeat function of the keyboard + self.all_homed = False # will hold True if all axis are homed + self.faktor = 1.0 # needed to calculate velocities + + self.xpos = 40 # The X Position of the main Window + self.ypos = 30 # The Y Position of the main Window + self.width = 979 # The width of the main Window + self.height = 750 # The height of the main Window + + self.gcodeerror = "" # we need this to avoid multiple messages of the same error + + self.lathe_mode = None # we need this to check if we have a lathe config + self.backtool_lathe = False + self.diameter_mode = False + + # the default theme = System Theme we store here to be able to go back to that one later + self.default_theme = gtk.settings_get_default().get_property("gtk-theme-name") + + self.dialogs = dialogs.Dialogs() + self.dialogs.connect("play_sound", self._on_play_sound) + + # check the arguments given from the command line (Ini file) + self.user_mode = False + self.logofile = None + for index, arg in enumerate(argv): + print(index, " = ", arg) + if arg == "-user_mode": + self.user_mode = True + self.widgets.tbtn_setup.set_sensitive(False) + message = _("**** GMOCCAPY INI Entry **** \n") + message += _("user mode selected") + print (message) + if arg == "-logo": + self.logofile = str(argv[ index + 1 ]) + message = _("**** GMOCCAPY INI Entry **** \n") + message += _("logo entry found = {0}").format(self.logofile) + print (message) + self.logofile = self.logofile.strip("\"\'") + if not os.path.isfile(self.logofile): + self.logofile = None + message = _("**** GMOCCAPY INI Entry Error **** \n") + message += _("Logofile entry found, but could not be converted to path.\n") + message += _("The file path should not contain any spaces") + print(message) + + # check if the user want a Logo (given as command line argument) + if self.logofile: + self.widgets.img_logo.set_from_file(self.logofile) + self.widgets.img_logo.show() + + page2 = self.widgets.ntb_jog_JA.get_nth_page(2) + self.widgets.ntb_jog_JA.reorder_child(page2, 0) + page1 = self.widgets.ntb_jog_JA.get_nth_page(1) + self.widgets.ntb_jog_JA.reorder_child(page1, -1) + + # Our own class to get information from ini the file we use this way, to be sure + # to get a valid result, as the checks are done in that module + self._get_ini_data() + + self._get_pref_data() + + self.tool_measure_OK = self._check_toolmeasurement() + + # make all widgets we create dynamically + self._make_DRO() + self._make_ref_axis_button() + self._make_touch_button() + self._make_jog_increments() + self._make_jog_button() + if not self.trivial_kinematics: + # we need joint jogging button + self._make_joints_button() + self._arrange_joint_button() + self._make_macro_button() + + # if we have a lathe, we need to rearrange some stuff + # we will do that in a separate function + if self.lathe_mode: + self._make_lathe() + else: + self.widgets.rbt_view_y2.hide() + # X Offset is not necessary on a mill + self.widgets.lbl_tool_offset_x.hide() + self.widgets.lbl_offset_x.hide() + self.widgets.btn_tool_touchoff_x.hide() + self.widgets.lbl_hide_tto_x.show() + + self._arrange_dro() + self._arrange_jog_button() + + self._make_hal_pins() + + self._init_user_messages() + + # set the title of the window, to show the release + self.widgets.window1.set_title("gmoccapy for LinuxCNC {0}".format(_RELEASE)) + self.widgets.lbl_version.set_label("gmoccapy\n{0}".format(_RELEASE)) + + panel = gladevcp.makepins.GladePanel(self.halcomp, XMLNAME, self.builder, None) + + self.halcomp.ready() + + self.builder.connect_signals(self) + + # this are settings to be done before window show + self._init_preferences() + + # finally show the window + self.widgets.window1.show() + + self._init_dynamic_tabs() + self._init_tooleditor() + self._init_themes() + self._init_audio() + self._init_gremlin() + self._init_kinematics_type() + self._init_hide_cursor() + self._init_offsetpage() + self._init_keybindings() + self._init_IconFileSelection() + self._init_keyboard() + + # now we initialize the file to load widget + self._init_file_to_load() + + self._show_offset_tab(False) + self._show_tooledit_tab(False) + self._show_iconview_tab(False) + + # the velocity settings + self.widgets.adj_spindle_bar_min.set_value(self.min_spindle_rev) + self.widgets.adj_spindle_bar_max.set_value(self.max_spindle_rev) + self.widgets.spindle_feedback_bar.set_property("min", float(self.min_spindle_rev)) + self.widgets.spindle_feedback_bar.set_property("max", float(self.max_spindle_rev)) + + # Popup Messages position and size + self.widgets.adj_x_pos_popup.set_value(self.prefs.getpref("x_pos_popup", 45, float)) + self.widgets.adj_y_pos_popup.set_value(self.prefs.getpref("y_pos_popup", 55, float)) + self.widgets.adj_width_popup.set_value(self.prefs.getpref("width_popup", 250, float)) + self.widgets.adj_max_messages.set_value(self.prefs.getpref("max_messages", 10, float)) + self.widgets.fontbutton_popup.set_font_name(self.prefs.getpref("message_font", "sans 10", str)) + self.widgets.chk_use_frames.set_active(self.prefs.getpref("use_frames", True, bool)) + + # this sets the background colors of several buttons + # the colors are different for the states of the button + self.widgets.tbtn_on.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.tbtn_estop.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FF0000")) + self.widgets.tbtn_estop.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#00FF00")) + self.widgets.rbt_manual.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.rbt_mdi.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.rbt_auto.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.tbtn_setup.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.rbt_forward.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#00FF00")) + self.widgets.rbt_reverse.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#00FF00")) + self.widgets.rbt_stop.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.rbt_view_p.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.rbt_view_x.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.rbt_view_y.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.rbt_view_y2.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.rbt_view_z.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.tbtn_flood.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#00FF00")) + self.widgets.tbtn_fullsize_preview0.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.tbtn_fullsize_preview1.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.tbtn_mist.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#00FF00")) + self.widgets.tbtn_optional_blocks.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.tbtn_user_tabs.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.tbtn_view_dimension.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.tbtn_view_tool_path.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.tbtn_switch_mode.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + + # should the tool in spindle be reloaded on startup? + self.widgets.chk_reload_tool.set_active(self.prefs.getpref("reload_tool", True, bool)) + + # and the rest of the widgets + self.widgets.rbt_manual.set_active(True) + self.widgets.ntb_jog.set_current_page(0) + + opt_blocks = self.prefs.getpref("blockdel", False, bool) + self.widgets.tbtn_optional_blocks.set_active(opt_blocks) + self.command.set_block_delete(opt_blocks) + + #optional_stops = self.prefs.getpref( "opstop", False, bool ) + #self.widgets.tbtn_optional_stops.set_active( optional_stops ) + #self.command.set_optional_stop( optional_stops ) + + self.widgets.chk_show_dro.set_active(self.prefs.getpref("enable_dro", False, bool)) + self.widgets.chk_show_offsets.set_active(self.prefs.getpref("show_offsets", False, bool)) + self.widgets.chk_show_dtg.set_active(self.prefs.getpref("show_dtg", False, bool)) + self.widgets.chk_show_offsets.set_sensitive(self.widgets.chk_show_dro.get_active()) + self.widgets.chk_show_dtg.set_sensitive(self.widgets.chk_show_dro.get_active()) + self.widgets.cmb_mouse_button_mode.set_active(self.prefs.getpref("mouse_btn_mode", 4, int)) + + self.widgets.tbtn_view_tool_path.set_active(self.prefs.getpref("view_tool_path", True, bool)) + self.widgets.tbtn_view_dimension.set_active(self.prefs.getpref("view_dimension", True, bool)) + view = view = self.prefs.getpref("view", "p", str) + self.widgets["rbt_view_{0}".format(view)].set_active(True) + + # get if run from line should be used + rfl = self.prefs.getpref("run_from_line", "no_run", str) + # and set the corresponding button active + self.widgets["rbtn_{0}_from_line".format(rfl)].set_active(True) + if rfl == "no_run": + self.widgets.btn_from_line.set_sensitive(False) + else: + self.widgets.btn_from_line.set_sensitive(True) + + # get the way to unlock the setting + unlock = self.prefs.getpref("unlock_way", "use", str) + # and set the corresponding button active + self.widgets["rbt_{0}_unlock".format(unlock)].set_active(True) + # if Hal pin should be used, only set the button active, if the pin is high + if unlock == "hal" and not self.halcomp["unlock-settings"]: + self.widgets.tbtn_setup.set_sensitive(False) + + # check if the user want to display preview window instead of offsetpage widget + state = self.prefs.getpref("show_preview_on_offset", False, bool) + if state: + self.widgets.rbtn_show_preview.set_active(True) + else: + self.widgets.rbtn_show_offsets.set_active(True) + + # check if keyboard shortcuts should be used and set the chkbox widget + self.widgets.chk_use_kb_shortcuts.set_active(self.prefs.getpref("use_keyboard_shortcuts", + False, bool)) + + # check the highlighting type + # the following would load the python language + # self.widgets.gcode_view.set_language("python") + LANGDIR = os.path.join(BASE, "share", "gtksourceview-2.0", "language-specs") + file_path = os.path.join(LANGDIR, "gcode.lang") + if os.path.isfile(file_path): + print "**** GMOCCAPY INFO: Gcode.lang found ****" + self.widgets.gcode_view.set_language("gcode", LANGDIR) + + # set the user colors and digits of the DRO + self.widgets.abs_colorbutton.set_color(gtk.gdk.color_parse(self.abs_color)) + self.widgets.rel_colorbutton.set_color(gtk.gdk.color_parse(self.rel_color)) + self.widgets.dtg_colorbutton.set_color(gtk.gdk.color_parse(self.dtg_color)) + self.widgets.homed_colorbtn.set_color(gtk.gdk.color_parse(self.homed_color)) + self.widgets.unhomed_colorbtn.set_color(gtk.gdk.color_parse(self.unhomed_color)) + + self.widgets.adj_dro_digits.set_value(self.dro_digits) + # the adjustment change signal will set the dro_digits correct, so no extra need here. + + self.widgets.chk_toggle_readout.set_active(self.toggle_readout) + + self.widgets.adj_start_spindle_RPM.set_value(self.spindle_start_rpm) + self.widgets.gcode_view.set_sensitive(False) + self.widgets.ntb_user_tabs.remove_page(0) + + if not self.get_ini_info.get_embedded_tabs()[2]: + self.widgets.tbtn_user_tabs.set_sensitive(False) + + # call the function to change the button status + # so every thing is ready to start + widgetlist = ["rbt_manual", "rbt_mdi", "rbt_auto", "btn_homing", "btn_touch", "btn_tool", + "ntb_jog", "spc_feed", "btn_feed_100", "rbt_forward", "btn_index_tool", + "rbt_reverse", "rbt_stop", "tbtn_flood", "tbtn_mist", "btn_change_tool", + "btn_select_tool_by_no", "btn_spindle_100", "spc_rapid", "spc_spindle", + "btn_tool_touchoff_x", "btn_tool_touchoff_z" + ] + # + self._sensitize_widgets(widgetlist, False) + + # this must be done last, otherwise we will get wrong values + # because the window is not fully realized + self._init_notification() + + # since the main loop is needed to handle the UI and its events, blocking calls like sleep() + # will block the UI as well, so everything goes through event handlers (aka callbacks) + # The gobject.timeout_add() function sets a function to be called at regular intervals + # the time between calls to the function, in milliseconds + # CYCLE_TIME = time, in milliseconds, that display will sleep between polls + cycle_time = self.get_ini_info.get_cycle_time() + gobject.timeout_add( cycle_time, self._periodic ) # time between calls to the function, in milliseconds + + def _get_ini_data(self): + self.get_ini_info = getiniinfo.GetIniInfo() + # get the axis list from INI + self.axis_list = self.get_ini_info.get_axis_list() + # get the joint axis relation from INI + self.joint_axis_dic, self.double_axis_letter = self.get_ini_info.get_joint_axis_relation() + # if it's a lathe config, set the tool editor style + self.lathe_mode = self.get_ini_info.get_lathe() + if self.lathe_mode: + # we do need to know also if we have a backtool lathe + self.backtool_lathe = self.get_ini_info.get_backtool_lathe() + + # check if the user want actual or commanded for the DRO + self.dro_actual = self.get_ini_info.get_position_feedback_actual() + # the given Jog Increments + self.jog_increments = self.get_ini_info.get_increments() + # check if NO_FORCE_HOMING is used in ini + self.no_force_homing = self.get_ini_info.get_no_force_homing() + # do we use a identity kinematics or do we have to distingish + # JOINT and Axis modes? + self.trivial_kinematics = self.get_ini_info.get_trivial_kinematics() + units = self.get_ini_info.get_machine_units() + if units == "mm" or units == "cm": + self.metric = True + else: + self.metric = False + self.no_force_homing = self.get_ini_info.get_no_force_homing() + + # get the values for the sliders + self.rabbit_jog = self.get_ini_info.get_jog_vel() + self.jog_rate_max = self.get_ini_info.get_max_jog_vel() + + self.min_ang_vel = self.get_ini_info.get_min_ang_jog_vel() + self.default_ang_vel = self.get_ini_info.get_default_ang_jog_vel() + self.max_ang_vel = self.get_ini_info.get_max_ang_jog_vel() + self.spindle_override_max = self.get_ini_info.get_max_spindle_override() + self.spindle_override_min = self.get_ini_info.get_min_spindle_override() + self.feed_override_max = self.get_ini_info.get_max_feed_override() + self.rapid_override_max = self.get_ini_info.get_max_rapid_override() + self.dro_actual = self.get_ini_info.get_position_feedback_actual() + + def _get_pref_data(self): + self.prefs = preferences.preferences(self.get_ini_info.get_preference_file_path()) + + # the size and digits of the DRO + # set default values according to the machine units + digits = 3 + if self.stat.linear_units != _MM: + digits = 4 + self.dro_digits = self.prefs.getpref("dro_digits", digits, int) + self.dro_size = self.prefs.getpref("dro_size", 28, int) + + # the colors of the DRO + self.abs_color = self.prefs.getpref("abs_color", "#0000FF", str) # blue + self.rel_color = self.prefs.getpref("rel_color", "#000000", str) # black + self.dtg_color = self.prefs.getpref("dtg_color", "#FFFF00", str) # yellow + self.homed_color = self.prefs.getpref("homed_color", "#00FF00", str) # green + self.unhomed_color = self.prefs.getpref("unhomed_color", "#FF0000", str) # red + + # do we want gremlin dro ? + self.enable_gremlin_dro = self.prefs.getpref("enable_dro", False, bool) + + # the scale to be applied to the counts of the hardware mpg wheel, to avoid to much turning + self.scale_jog_vel = self.prefs.getpref("scale_jog_vel", self.jog_rate_max / 100, float) + self.scale_spindle_override = self.prefs.getpref("scale_spindle_override", 1, float) + self.scale_feed_override = self.prefs.getpref("scale_feed_override", 1, float) + self.scale_rapid_override = self.prefs.getpref("scale_rapid_override", 1, float) + + # holds the max velocity value and is needed to be able to jog at + # at max velocity if is hold during jogging + self.max_velocity = self.stat.max_velocity + + # the velocity settings + self.min_spindle_rev = self.prefs.getpref("spindle_bar_min", 0.0, float) + self.max_spindle_rev = self.prefs.getpref("spindle_bar_max", 6000.0, float) + + self.turtle_jog_factor = self.prefs.getpref('turtle_jog_factor', 20, int) + self.hide_turtle_jog_button = self.prefs.getpref("hide_turtle_jog_button", False, bool) + + self.unlock_code = self.prefs.getpref("unlock_code", "123", str) # get unlock code + + self.toggle_readout = self.prefs.getpref("toggle_readout", True, bool) + + # if there is a INI Entry for default spindle speed, we will use that one as default + # but if there is a setting in our preference file, that one will beet the INI entry + default_spindle_speed = self.get_ini_info.get_default_spindle_speed() + self.spindle_start_rpm = self.prefs.getpref( 'spindle_start_rpm', default_spindle_speed, float ) + +############################################################################### +## create widgets dynamically ## +############################################################################### + + def _make_DRO(self): + print("**** GMOCCAPY INFO ****") + print("**** Entering make_DRO") + print("axis_list = {0}".format(self.axis_list)) + + # we build one DRO for each axis + self.dro_dic = {} + for pos, axis in enumerate(self.axis_list): + joint = self._get_joint_from_joint_axis_dic(axis) + dro = Combi_DRO() + dro.set_joint_no(joint) + dro.set_axis(axis) + dro.change_axisletter(axis.upper()) + dro.show() + dro.set_property("name", "Combi_DRO_{0}".format(pos)) + dro.set_property("abs_color", gtk.gdk.color_parse(self.abs_color)) + dro.set_property("rel_color", gtk.gdk.color_parse(self.rel_color)) + dro.set_property("dtg_color", gtk.gdk.color_parse(self.dtg_color)) + dro.set_property("homed_color", gtk.gdk.color_parse(self.homed_color)) + dro.set_property("unhomed_color", gtk.gdk.color_parse(self.unhomed_color)) + dro.set_property("actual", self.dro_actual) + dro.connect("clicked", self._on_DRO_clicked) + dro.connect('axis_clicked', self._on_DRO_axis_clicked) + self.dro_dic[dro.name] = dro +# print dro.name + + def _get_joint_from_joint_axis_dic(self, value): + # if the selected axis is a double axis we will get the joint from the + # master axis, witch should end with 0 + if value in self.double_axis_letter: + value = value + "0" + return self.joint_axis_dic.keys()[self.joint_axis_dic.values().index(value)] + + def _make_ref_axis_button(self): + print("**** GMOCCAPY INFO ****") + print("**** Entering make ref axis button") + + # check if we need axis or joint homing button + if self.trivial_kinematics: + # lets find out, how many axis we got + dic = self.axis_list + name_prefix = "axis" + else: + # lets find out, how many joints we got + dic = self.joint_axis_dic + name_prefix = "joint" + num_elements = len(dic) + + # as long as the number of axis is less 6 we can use the standard layout + # we can display 6 axis without the second space label + # and 7 axis if we do not display the first space label either + # if we have more than 7 axis, we need arrows to switch the visible ones + if num_elements < 7: + lbl = self._get_space_label("lbl_space_0") + self.widgets.hbtb_ref.pack_start(lbl) + + file = "ref_all.png" + filepath = os.path.join(IMAGEDIR, file) + print("Filepath = ", filepath) + btn = self._get_button_with_image("ref_all", filepath, None) + btn.set_property("tooltip-text", _("Press to home all {0}".format(name_prefix))) + btn.connect("clicked", self._on_btn_home_clicked) + # we use pack_start, so the widgets will be moved from right to left + # and are displayed the way we want + self.widgets.hbtb_ref.pack_start(btn) + + if num_elements > 7: + # show the previous arrow to switch visible homing button) + btn = self._get_button_with_image("previous_button", None, gtk.STOCK_GO_BACK) + btn.set_property("tooltip-text", _("Press to display previous homing button")) + btn.connect("clicked", self._on_btn_previous_clicked) + self.widgets.hbtb_ref.pack_start(btn) + btn.hide() + + # do not use this label, to allow one more axis + if num_elements < 6: + lbl = self._get_space_label("lbl_space_2") + self.widgets.hbtb_ref.pack_start(lbl) + + for pos, elem in enumerate(dic): + + file = "ref_{0}.png".format(elem) + filepath = os.path.join(IMAGEDIR, file) + print("Filepath = ", filepath) + + name = "home_{0}_{1}".format(name_prefix, elem) + btn = self._get_button_with_image(name, filepath, None) + btn.set_property("tooltip-text", _("Press to home {0} {1}".format(name_prefix, elem))) + btn.connect("clicked", self._on_btn_home_clicked) + + self.widgets.hbtb_ref.pack_start(btn) + + # if we have more than 7 axis we need to hide some button + if num_elements > 7: + if pos > 5: + btn.hide() + + if num_elements > 7: + # show the next arrow to switch visible homing button) + btn = self._get_button_with_image("next_button", None, gtk.STOCK_GO_FORWARD) + btn.set_property("tooltip-text", _("Press to display next homing button")) + btn.connect("clicked", self._on_btn_next_clicked) + self.widgets.hbtb_ref.pack_start(btn) + + # if there is space left, fill it with space labels + start = self.widgets.hbtb_ref.child_get_property(btn,"position") + for count in range(start + 1 , 8): + lbl = self._get_space_label("lbl_space_{0}".format(count)) + self.widgets.hbtb_ref.pack_start(lbl) + + file = "unhome.png" + filepath = os.path.join(IMAGEDIR, file) + print("Filepath = ", filepath) + name = "unref_all" + btn = self._get_button_with_image(name, filepath, None) + btn.set_property("tooltip-text", _("Press to unhome all {0}".format(name_prefix))) + btn.connect("clicked", self._on_btn_unhome_clicked) + self.widgets.hbtb_ref.pack_start(btn) + + name = "home_back" + btn = self._get_button_with_image(name, None, gtk.STOCK_UNDO) + btn.set_property("tooltip-text", _("Press to return to main button list")) + btn.connect("clicked", self._on_btn_home_back_clicked) + self.widgets.hbtb_ref.pack_start(btn) + + self.ref_button_dic = {} + children = self.widgets.hbtb_ref.get_children() + for child in children: + self.ref_button_dic[child.name] = child + + def _get_space_label(self, name): + lbl = gtk.Label("") + lbl.set_property("name", name) + lbl.set_size_request(85,56) + lbl.show() + return lbl + + def _get_button_with_image(self, name, filepath, stock): + image = gtk.Image() + image.set_size_request(72,48) + btn = gtk.Button() + btn.set_size_request(85,56) + btn.set_property("name", name) + try: + if filepath: + pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(filepath, 72, 48) + image.set_from_pixbuf(pixbuf) + else: + image.set_from_stock(stock, 48) + btn.add(image) + except: + message = _("**** GMOCCAPY ERROR ****\n") + message += _("**** could not resolv the image path '{0}' given for macro button '{1}' ****".format(filepath, name)) + print(message) + image.set_from_stock(gtk.STOCK_MISSING_IMAGE, 48) + btn.add(image) + + btn.show_all() + return btn + + def _remove_button(self, dic, box): + for child in dic: + box.remove(dic[child]) + + def _on_btn_next_clicked(self, widget): + # remove all buttons from container + self._remove_button(self.ref_button_dic, self.widgets.hbtb_ref) + + self.widgets.hbtb_ref.pack_start(self.ref_button_dic["ref_all"], True, True, 0) + self.ref_button_dic["ref_all"].show() + self.widgets.hbtb_ref.pack_start(self.ref_button_dic["previous_button"], True, True, 0) + self.ref_button_dic["previous_button"].show() + + start = len(self.axis_list) - 6 + end = len(self.axis_list) + + # now put the needed widgets in the container + for axis in self.axis_list[start : end]: + name = "home_axis_{0}".format(axis.lower()) + self.ref_button_dic[name].show() + self.widgets.hbtb_ref.pack_start(self.ref_button_dic[name], True, True, 0) + + self._put_unref_and_back() + + def _on_btn_next_touch_clicked(self, widget): + self._remove_button(self.touch_button_dic, self.widgets.hbtb_touch_off) + + self.widgets.hbtb_touch_off.pack_start(self.touch_button_dic["edit_offsets"]) + self.widgets.hbtb_touch_off.pack_start(self.touch_button_dic["previous_button"]) + self.touch_button_dic["previous_button"].show() + + start = len(self.axis_list) - 5 + end = len(self.axis_list) + + # now put the needed widgets in the container + for axis in self.axis_list[start : end]: + name = "touch_{0}".format(axis.lower()) + self.touch_button_dic[name].show() + self.widgets.hbtb_touch_off.pack_start(self.touch_button_dic[name], True, True, 0) + + self._put_set_active_and_back() + + def _on_btn_next_macro_clicked(self, widget): + # remove all buttons from container + self._remove_button(self.macro_dic, self.widgets.hbtb_MDI) + + self.widgets.hbtb_MDI.pack_start(self.macro_dic["previous_button"]) + self.macro_dic["previous_button"].show() + + end = len(self.macro_dic) - 3 # reduced by next, previous and keyboard + start = end - 8 + + # now put the needed widgets in the container + for pos in range(start, end): + name = "macro_{0}".format(pos) + self.widgets.hbtb_MDI.pack_start(self.macro_dic[name], True, True, 0) + self.macro_dic[name].show() + + self.widgets.hbtb_MDI.pack_start(self.macro_dic["keyboard"]) + self.macro_dic["keyboard"].show() + + def _on_btn_previous_clicked(self, widget): + print("previous") + self._remove_button(self.ref_button_dic, self.widgets.hbtb_ref) + + self.widgets.hbtb_ref.pack_start(self.ref_button_dic["ref_all"], True, True, 0) + self.ref_button_dic["ref_all"].show() + + start = 0 + end = 6 + + # now put the needed widgets in the container + for axis in self.axis_list[start : end]: + name = "home_axis_{0}".format(axis.lower()) + self.ref_button_dic[name].show() + self.widgets.hbtb_ref.pack_start(self.ref_button_dic[name], True, True, 0) + + self.widgets.hbtb_ref.pack_start(self.ref_button_dic["next_button"], True, True, 0) + self.ref_button_dic["next_button"].show() + + self._put_unref_and_back() + + def _on_btn_previous_touch_clicked(self, widget): + self._remove_button(self.touch_button_dic, self.widgets.hbtb_touch_off) + + self.widgets.hbtb_touch_off.pack_start(self.touch_button_dic["edit_offsets"]) + + if self.tool_measure_OK: + end = 4 + else: + end = 5 + + start = 0 + # now put the needed widgets in the container + for axis in self.axis_list[start : end]: + name = "touch_{0}".format(axis.lower()) + self.touch_button_dic[name].show() + self.widgets.hbtb_touch_off.pack_start(self.touch_button_dic[name], True, True, 0) + + self.widgets.hbtb_touch_off.pack_start(self.touch_button_dic["next_button"]) + self.touch_button_dic["next_button"].show() + + if self.tool_measure_OK: + self.widgets.hbtb_touch_off.pack_start(self.touch_button_dic["block_height"]) + + self.widgets.hbtb_touch_off.pack_start(self.touch_button_dic["zero_offsets"]) + self._put_set_active_and_back() + + def _on_btn_previous_macro_clicked(self, widget): + # remove all buttons from container + self._remove_button(self.macro_dic, self.widgets.hbtb_MDI) + + start = 0 + end = 8 + + # now put the needed widgets in the container + for pos in range(start, end): + name = "macro_{0}".format(pos) + self.widgets.hbtb_MDI.pack_start(self.macro_dic[name], True, True, 0) + self.macro_dic[name].show() + + self.widgets.hbtb_MDI.pack_start(self.macro_dic["next_button"]) + self.macro_dic["next_button"].show() + + self.widgets.hbtb_MDI.pack_start(self.macro_dic["keyboard"]) + self.macro_dic["keyboard"].show() + + def _put_set_active_and_back(self): + self.widgets.hbtb_touch_off.pack_start(self.touch_button_dic["zero_offsets"], True, True, 0) + self.widgets.hbtb_touch_off.pack_start(self.touch_button_dic["set_active"], True, True, 0) + self.widgets.hbtb_touch_off.pack_start(self.touch_button_dic["touch_back"], True, True, 0) + + def _put_unref_and_back(self): + self.widgets.hbtb_ref.pack_start(self.ref_button_dic["unref_all"], True, True, 0) + self.widgets.hbtb_ref.pack_start(self.ref_button_dic["home_back"], True, True, 0) + + def _make_touch_button(self): + print("**** GMOCCAPY INFO ****") + print("**** Entering make touch button") + + dic = self.axis_list + num_elements = len(dic) + end = 7 + + if self.tool_measure_OK: + # we will have 3 buttons on the right side + end -= 1 + + btn = gtk.ToggleButton(_(" edit\noffsets")) + btn.connect("toggled", self.on_tbtn_edit_offsets_toggled) + btn.set_property("tooltip-text", _("Press to edit the offsets")) + btn.set_property("name", "edit_offsets") + btn.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.widgets.hbtb_touch_off.pack_start(btn) + btn.show() + + if num_elements > 6: + # show the previous arrow to switch visible touch button) + btn = self._get_button_with_image("previous_button", None, gtk.STOCK_GO_BACK) + btn.set_property("tooltip-text", _("Press to display previous homing button")) + btn.connect("clicked", self._on_btn_previous_touch_clicked) + self.widgets.hbtb_touch_off.pack_start(btn) + end -= 1 + btn.hide() + + for pos, elem in enumerate(dic): + file = "touch_{0}.png".format(elem) + filepath = os.path.join(IMAGEDIR, file) + name = "touch_{0}".format(elem) + btn = self._get_button_with_image(name, filepath, None) + btn.set_property("tooltip-text", _("Press to set touch off value for axis {0}".format(elem.upper()))) + btn.connect("clicked", self._on_btn_set_value_clicked) + + #print("Touch button Name = ",name) + + self.widgets.hbtb_touch_off.pack_start(btn) + + if pos > end - 2: + btn.hide() + + if num_elements > (end - 1): + # show the next arrow to switch visible homing button) + btn = self._get_button_with_image("next_button", None, gtk.STOCK_GO_FORWARD) + btn.set_property("tooltip-text", _("Press to display next homing button")) + btn.connect("clicked", self._on_btn_next_touch_clicked) + self.widgets.hbtb_touch_off.pack_start(btn) + btn.show() + end -= 1 + + # if there is space left, fill it with space labels + start = self.widgets.hbtb_touch_off.child_get_property(btn,"position") + for count in range(start + 1 , end): + lbl = self._get_space_label("lbl_space_{0}".format(count)) + self.widgets.hbtb_touch_off.pack_start(lbl) + lbl.show() + + btn = gtk.Button(_("zero\n G92")) + btn.connect("clicked", self.on_btn_zero_g92_clicked) + btn.set_property("tooltip-text", _("Press to reset all G92 offsets")) + btn.set_property("name", "zero_offsets") + self.widgets.hbtb_touch_off.pack_start(btn) + btn.show() + + if self.tool_measure_OK: + btn = gtk.Button(_(" Block\nHeight")) + btn.connect("clicked", self.on_btn_block_height_clicked) + btn.set_property("tooltip-text", _("Press to enter new value for block height")) + btn.set_property("name", "block_height") + self.widgets.hbtb_touch_off.pack_start(btn) + btn.show() + + btn = gtk.Button(_(" set\nselected")) + btn.connect("clicked", self._on_btn_set_selected_clicked) + btn.set_property("tooltip-text", _("Press to set the selected coordinate system to be the active one")) + btn.set_property("name", "set_active") + self.widgets.hbtb_touch_off.pack_start(btn) + btn.show() + + btn = self._get_button_with_image("touch_back", None, gtk.STOCK_UNDO) + btn.set_property("tooltip-text", _("Press to return to main button list")) + btn.connect("clicked", self._on_btn_home_back_clicked) + self.widgets.hbtb_touch_off.pack_start(btn) + btn.show() + + self.touch_button_dic = {} + children = self.widgets.hbtb_touch_off.get_children() + for child in children: + self.touch_button_dic[child.name] = child + + def _check_toolmeasurement(self): + # tool measurement probe settings + xpos, ypos, zpos, maxprobe = self.get_ini_info.get_tool_sensor_data() + if not xpos or not ypos or not zpos or not maxprobe: + self.widgets.lbl_tool_measurement.show() + print(_("**** GMOCCAPY INFO ****")) + print(_("**** no valid probe config in INI File ****")) + print(_("**** disabled tool measurement ****")) + self.widgets.chk_use_tool_measurement.set_active(False) + self.widgets.chk_use_tool_measurement.set_sensitive(False) + return False + else: + self.widgets.lbl_tool_measurement.hide() + self.widgets.spbtn_probe_height.set_value(self.prefs.getpref("probeheight", -1.0, float)) + self.widgets.spbtn_search_vel.set_value(self.prefs.getpref("searchvel", 75.0, float)) + self.widgets.spbtn_probe_vel.set_value(self.prefs.getpref("probevel", 10.0, float)) + self.widgets.chk_use_tool_measurement.set_active(self.prefs.getpref("use_toolmeasurement", False, bool)) + self.widgets.lbl_x_probe.set_label(str(xpos)) + self.widgets.lbl_y_probe.set_label(str(ypos)) + self.widgets.lbl_z_probe.set_label(str(zpos)) + self.widgets.lbl_maxprobe.set_label(str(maxprobe)) + print(_("**** GMOCCAPY INFO ****")) + print(_("**** found valid probe config in INI File ****")) + print(_("**** will use auto tool measurement ****")) + return True + + def _make_jog_increments(self): + print("**** GMOCCAPY INFO ****") + print("**** Entering make jog increments") + # Now we will build the option buttons to select the Jog-rates + # We do this dynamically, because users are able to set them in INI File + # because of space on the screen only 10 items are allowed + # jogging increments + + self.incr_rbt_dic = {} + + # We get the increments from INI File + if len(self.jog_increments) > 10: + print(_("**** GMOCCAPY build_GUI INFO ****")) + print(_("**** To many increments given in INI File for this screen ****")) + print(_("**** Only the first 10 will be reachable through this screen ****")) + # we shorten the increment list to 10 (first is default = 0) + self.jog_increments = self.jog_increments[0:11] + + # The first radio button is created to get a radio button group + # The group is called according the name off the first button + # We use the pressed signal, not the toggled, otherwise two signals will be emitted + # One from the released button and one from the pressed button + # we make a list of the buttons to later add the hardware pins to them + label = _("Continuous") + rbt = gtk.RadioButton(None, label) + rbt.set_property("name","rbt_0") + rbt.connect("pressed", self._jog_increment_changed) + self.widgets.vbtb_jog_incr.pack_start(rbt, True, True, 0) + rbt.set_property("draw_indicator", False) + rbt.show() + rbt.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.incr_rbt_dic[rbt.name] = rbt + # the rest of the buttons are now added to the group + # self.no_increments is set while setting the hal pins with self._check_len_increments + for item in range(1, len(self.jog_increments)): + name = "rbt_{0}".format(item) + rbt = gtk.RadioButton(self.incr_rbt_dic["rbt_0"], self.jog_increments[item]) + rbt.set_property("name",name) + rbt.connect("pressed", self._jog_increment_changed) + self.widgets.vbtb_jog_incr.pack_start(rbt, True, True, 0) + rbt.set_property("draw_indicator", False) + rbt.show() + rbt.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + self.incr_rbt_dic[rbt.name] = rbt + self.incr_rbt_dic["rbt_0"].set_active(True) + self.active_increment = "rbt_0" + + def _jog_increment_changed(self, widget,): + # first cancel any joints jogging + JOGMODE = self._get_jog_mode() + for jnum in range(self.stat.joints): + self.command.jog(linuxcnc.JOG_STOP, JOGMODE, jnum) + self.distance = self._parse_increment(widget.name) + self.halcomp["jog.jog-increment"] = self.distance + self.active_increment = widget.name + + def _on_btn_jog_pressed(self, widget, button_name, shift=False): + print("Jog Button pressed = {0}".format(button_name)) + + # only in manual mode we will allow jogging the axis at this development state + # needed to avoid error on start up, machine not on + if not self.stat.enabled or self.stat.task_mode != linuxcnc.MODE_MANUAL: + return + + joint_no_or_axis_index = self._get_joint_no_or_axis_index(button_name) + if joint_no_or_axis_index is None: + print("Did not get an axis number in jog part of the code") + return + + # if shift = True, then the user pressed SHIFT for Jogging and + # want's to jog at full speed + # This can only happen on keyboard jogging, not with the on screen jog button + # We just only use one function for both cases + if shift: + # There are no keyboard shortcuts to home angular axis, but + # we implement the possibility for future options + if button_name[0] in "abc": + value = self.widgets.spc_ang_jog_vel.get_property("max") / 60 + else: + value = self.stat.max_velocity + else: + if button_name[0] in "abc": + value = self.widgets.spc_ang_jog_vel.get_value() / 60 + else: + value = self.widgets.spc_lin_jog_vel.get_value() / 60 + + velocity = value * (1 / self.faktor) + + if button_name[1] == "+": + dir = 1 + else: + dir = -1 + + JOGMODE = self._get_jog_mode() + + if self.distance <> 0: # incremental jogging + distance = self.distance + if self.lathe_mode and self.diameter_mode and button_name[0] == "x": + distance = self.distance/2 + self.command.jog(linuxcnc.JOG_INCREMENT, JOGMODE, joint_no_or_axis_index, dir * velocity, distance) + else: # continuous jogging + self.command.jog(linuxcnc.JOG_CONTINUOUS, JOGMODE, joint_no_or_axis_index, dir * velocity) + + def _on_btn_jog_released(self, widget, button_name, shift=False): + print ("Jog Button released = {0}".format(button_name)) + # only in manual mode we will allow jogging the axis at this development state + if not self.stat.enabled or self.stat.task_mode != linuxcnc.MODE_MANUAL: + return + + joint_no_or_axis_index = self._get_joint_no_or_axis_index(button_name) + + JOGMODE = self._get_jog_mode() + + # Otherwise the movement would stop before the desired distance was moved + if self.distance <> 0: + pass + else: + self.command.jog(linuxcnc.JOG_STOP, JOGMODE, joint_no_or_axis_index) + + def _get_jog_mode(self): + # self.stat.motion_mode == + # 1 = Joint + # 2 = MDI + # 3 = TELOP + if self.stat.motion_mode == 1: + JOGMODE = 1 + else : + JOGMODE = 0 + return JOGMODE + + def _get_joint_no_or_axis_index(self, button_name): + joint_btn = False + if not button_name[0] in "xyzabcuvw": + print("Axis button") + # OK, it may be a Joints button + if button_name[0] in "012345678": + print("joint button") + joint_btn = True + else: + print(_("**** GMOCCAPY INFO ****")) + print (_("unknown jog command {0}".format(button_name))) + return None + + if not joint_btn: + # get the axisnumber from the index as specified in python interface documentation + if self.all_homed: + joint_no_or_axis_index = "xyzabcuvw".index(button_name[0]) + else: + joint_no_or_axis_index = self._get_joint_from_joint_axis_dic(button_name[0]) + else: + joint_no_or_axis_index = int(button_name[0]) + + return joint_no_or_axis_index + + def _make_jog_button(self): + print("**** GMOCCAPY INFO ****") + print("**** Entering make jog button") + + self.jog_button_dic = OrderedDict() + + for axis in self.axis_list: + for direction in ["+","-"]: + name = "{0}{1}".format(str(axis), direction) + btn = gtk.Button(name.upper()) + btn.set_property("name", name) + btn.connect("pressed", self._on_btn_jog_pressed, name) + btn.connect("released", self._on_btn_jog_released, name) + btn.set_property("tooltip-text", _("Press to jog axis {0}".format(axis))) + btn.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + btn.set_size_request(48,48) + + self.jog_button_dic[name] = btn + + def _make_joints_button(self): + print("**** GMOCCAPY INFO ****") + print("**** Entering make joints button") + + self.joints_button_dic = {} + + for joint in range(0, self.stat.joints): + for direction in ["+","-"]: + name = "{0}{1}".format(str(joint), direction) + btn = gtk.Button(name.upper()) + btn.set_property("name", name) + btn.connect("pressed", self._on_btn_jog_pressed, name) + btn.connect("released", self._on_btn_jog_released, name) + btn.set_property("tooltip-text", _("Press to jog joint {0}".format(joint))) + btn.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.color_parse("#FFFF00")) + btn.set_size_request(48,48) + + self.joints_button_dic[name] = btn + + # check if macros are in the INI file and add them to MDI Button List + def _make_macro_button(self): + print("**** GMOCCAPY INFO ****") + print("**** Entering make macro button") + + macros = self.get_ini_info.get_macros() + + # if no macros at all are found, we receive a NONE, so we have to check: + if not macros: + num_macros = 0 + # no return here, otherwise we will not get filling labels + else: + num_macros = len(macros) + + print("found {0} Macros".format(num_macros)) + + if num_macros > 16: + message = _("**** GMOCCAPY INFO ****\n") + message += _("**** found more than 16 macros, will use only the first 16 ****") + print(message) + + num_macros = 16 + + btn = self._get_button_with_image("previous_button", None, gtk.STOCK_GO_BACK) + btn.hide() + btn.set_property("tooltip-text", _("Press to display previous macro button")) + btn.connect("clicked", self._on_btn_previous_macro_clicked) + self.widgets.hbtb_MDI.pack_start(btn) + + for pos in range(0, num_macros): + name = macros[pos] + + image = self._check_macro_for_image(name) + if image: + print("Macro {0} has image link".format(name)) + print("Image = {0}".format(image)) + btn = self._get_button_with_image("macro_{0}".format(pos), image, None) + else: + lbl = name.split()[0] + # shorten / break line of the name if it is to long + if len(lbl) > 11: + lbl = lbl[0:10] + "\n" + lbl[11:20] + btn = gtk.Button(lbl, None, False) + btn.set_property("name","macro_{0}".format(pos)) + btn.set_property("tooltip-text", _("Press to run macro {0}".format(name))) + btn.connect("pressed", self._on_btn_macro_pressed, name) + btn.position = pos + btn.show() + self.widgets.hbtb_MDI.pack_start(btn, True, True, 0) + + btn = self._get_button_with_image("next_button", None, gtk.STOCK_GO_FORWARD) + btn.set_property("tooltip-text", _("Press to display next macro button")) + btn.connect("clicked", self._on_btn_next_macro_clicked) + btn.hide() + self.widgets.hbtb_MDI.pack_start(btn) + + file = "keyboard.png" + filepath = os.path.join(IMAGEDIR, file) + + # if there is still place, we fill it with empty labels, to be sure the button will not be on different + # places if the amount of macros change. + if num_macros < 9: + for pos in range(num_macros, 9): + lbl = gtk.Label() + lbl.set_property("name","lbl_space_{0}".format(pos)) + lbl.set_text("") + self.widgets.hbtb_MDI.pack_start(lbl, True, True, 0) + lbl.show() + + name = "keyboard" + btn = self._get_button_with_image(name, filepath, None) + btn.set_property("tooltip-text", _("Press to display the virtual keyboard")) + btn.connect("clicked", self.on_btn_show_kbd_clicked) + btn.set_property("name", name) + self.widgets.hbtb_MDI.pack_start(btn) + + self.macro_dic = {} + + children = self.widgets.hbtb_MDI.get_children() + for child in children: + #print(child.name) + self.macro_dic[child.name] = child + + if num_macros >= 9: + self.macro_dic["next_button"].show() + for pos in range(8, num_macros): + self.macro_dic["macro_{0}".format(pos)].hide() + + def _check_macro_for_image(self, name): + image = False + for path in self.get_ini_info.get_subroutine_paths().split(":"): + file = path + "/" + name.split()[0] + ".ngc" + if os.path.isfile(file): + macrofile = open(file, "r") + lines = macrofile.readlines() + macrofile.close() + for line in lines: + if line[0] == ";": + continue + if "image" in line.lower(): + image = True + break + + # should be like that in ngc file + # (IMAGE, /home/my_home/my_image_dir/my_image.png) + # so we need to get the correct image path + if image: + image = line.split(",")[1] + image = image.strip() + image = image.replace(")","") + if "~" in image: + image = image.replace("~", os.path.expanduser("~")) + image = os.path.abspath(image) + + return image + + # if this is a lathe we need to rearrange some button and add a additional DRO + def _make_lathe(self): + print("**** GMOCCAPY INFO ****") + print("**** we have a lathe here") + + # if we have a lathe, we will need an additional DRO to display + # diameter and radius simultaneous, we will call that one Combi_DRO_9, as that value + # should never be used due to the limit in axis from 0 to 8 + dro = Combi_DRO() + dro.set_property("name", "Combi_DRO_9") + dro.set_property("abs_color", gtk.gdk.color_parse(self.abs_color)) + dro.set_property("rel_color", gtk.gdk.color_parse(self.rel_color)) + dro.set_property("dtg_color", gtk.gdk.color_parse(self.dtg_color)) + dro.set_property("homed_color", gtk.gdk.color_parse(self.homed_color)) + dro.set_property("unhomed_color", gtk.gdk.color_parse(self.unhomed_color)) + dro.set_property("actual", self.dro_actual) + + joint = self._get_joint_from_joint_axis_dic("x") + dro.set_joint_no(joint) + dro.set_axis("x") + dro.change_axisletter("D") + dro.set_property("diameter", True) + dro.show() + + dro.connect("clicked", self._on_DRO_clicked) + dro.connect('axis_clicked', self._on_DRO_axis_clicked) + self.dro_dic[dro.name] = dro + + self.dro_dic["Combi_DRO_0"].change_axisletter("R") + + # For a lathe we don"t need the following button + self.widgets.rbt_view_p.hide() + self.widgets.rbt_view_x.hide() + self.widgets.rbt_view_z.hide() + self.widgets.lbl_hide_tto_x.hide() + self.widgets.lbl_blockheight.hide() + + # but we have to show this one + self.widgets.rbt_view_y2.show() + + if self.backtool_lathe: + view = "y2" + else: + view = "y" + self.prefs.putpref("view", view) + + self.widgets["rbt_view_{0}".format(view)].set_active(True) + self.widgets.gremlin.set_property("view", view) + + self._switch_to_g7(False) + + # we need to arrange the jog button, + # a lathe should have at least X and Z axis + if not "x" in self.axis_list or not "z" in self.axis_list: + message = _("***** GMOCCAPY ERROR *****") + 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() + else: + if len(self.axis_list) == 2: + self.widgets.tbl_jog_btn_axes.resize(3,3) + elif len(self.axis_list) < 6: + self.widgets.tbl_jog_btn_axes.resize(3,4) + else: + self._arrange_jog_button_by_axis() + return + count = 0 + for btn_name in self.jog_button_dic: + if btn_name == "x+": + col = 1 + row = 2 + if self.backtool_lathe: + row = 0 + elif btn_name == "x-": + col = 1 + row = 0 + if self.backtool_lathe: + row = 2 + elif btn_name == "z+": + col = 2 + row = 1 + elif btn_name == "z-": + col = 0 + row = 1 + else: + if count < 2: + col = 3 + elif count < 4: + col = 2 + else: + col = 0 + if "+" in btn_name: + row = 0 + else: + row = 2 + count +=1 + + self.widgets.tbl_jog_btn_axes.attach(self.jog_button_dic[btn_name], col, col + 1, row, row + 1) + self.jog_button_dic[btn_name].show() + + def _arrange_dro(self): + print("**** GMOCCAPY INFO ****") + print("**** arrange DRO") + print(len(self.dro_dic)) + # if we have less than 4 axis, we can resize the table, as we have + # enough space to display each one in it's own line + + if len(self.dro_dic) < 4: + self._place_in_table(len(self.dro_dic),1, self.dro_size) + + # having 4 DRO we need to reduce the size, to fit the available space + elif len(self.dro_dic) == 4: + self._place_in_table(len(self.dro_dic),1, self.dro_size * 0.75) + + # having 5 axis we will display 3 in an one line and two DRO share + # the last line, the size of the DRO must be reduced also + # this is a special case so we do not use _place_in_table + elif len(self.dro_dic) == 5: + self.widgets.tbl_DRO.resize(4,2) + dro_order = self._get_DRO_order() + + for dro, dro_name in enumerate(dro_order): + # as a lathe might not have all Axis, we check if the key is in directory + if dro < 3: + size = self.dro_size * 0.75 + self.widgets.tbl_DRO.attach(self.dro_dic[dro_name], + 0, 2, int(dro), int(dro + 1), ypadding = 0) + else: + size = self.dro_size * 0.65 + if dro == 3: + self.widgets.tbl_DRO.attach(self.dro_dic[dro_name], + 0, 1, int(dro), int(dro + 1), ypadding = 0) + else: + self.widgets.tbl_DRO.attach(self.dro_dic[dro_name], + 1, 2, int(dro-1), int(dro), ypadding = 0) + self.dro_dic[dro_name].set_property("font_size", size) + + else: + print("**** GMOCCAPY build_GUI INFO ****") + print("**** more than 5 axis ") + # check if amount of axis is an even number, adapt the needed lines + if len(self.dro_dic) % 2 == 0: + rows = len(self.dro_dic) / 2 + else: + rows = (len(self.dro_dic) + 1) / 2 + self._place_in_table(rows, 2, self.dro_size * 0.65) + + # set values to dro size adjustments + self.widgets.adj_dro_size.set_value(self.dro_size) + + def _place_in_table(self, rows, cols, dro_size): + print("**** GMOCCAPY INFO ****") + print("**** Place in table") + + self.widgets.tbl_DRO.resize(rows, cols) + col = 0 + row = 0 + + dro_order = self._get_DRO_order() + + for dro, dro_name in enumerate(dro_order): + # as a lathe might not have all Axis, we check if the key is in directory + if dro_name not in self.dro_dic.keys(): + continue + self.dro_dic[dro_name].set_property("font_size", dro_size) + + self.widgets.tbl_DRO.attach(self.dro_dic[dro_name], + col, col+1, row, row + 1, ypadding = 0) + if cols > 1: + # calculate if we have to place in the first or the second column + if (dro % 2 == 1): + col = 0 + row +=1 + else: + col += 1 + else: + row += 1 + + def _get_DRO_order(self): + print("**** GMOCCAPY INFO ****") + print("**** get DRO order") + dro_order = [] + # if Combi_DRO_9 exist we have a lathe with an additional DRO for diameter mode + if "Combi_DRO_9" in self.dro_dic.keys(): + for dro_name in ["Combi_DRO_0", "Combi_DRO_9", "Combi_DRO_1", "Combi_DRO_2", "Combi_DRO_3", + "Combi_DRO_4", "Combi_DRO_5", "Combi_DRO_6", "Combi_DRO_7", "Combi_DRO_8"]: + if dro_name in self.dro_dic.keys(): + dro_order.append(dro_name) + else: + dro_order = sorted(self.dro_dic.keys()) + return dro_order + + def _arrange_jog_button(self): + print("**** GMOCCAPY INFO ****") + print("**** arrange JOG button") + + # if we have a lathe, we have done the arrangement in _make_lathe + # but if the lathe has more than 5 axis we will use standard + if self.lathe_mode: + return + + if len(self.axis_list) > 5: + self._arrange_jog_button_by_axis() + return + + if not "x" in self.axis_list or not "y" in self.axis_list or not "z" in self.axis_list: + message = _("***** GMOCCAPY INFO *****") + message += _("this is not a usual config\n") + message += _("we miss one of X , Y or Z axis\n") + message += _("We will use by axisletter ordered jog button") + print(message) + self._arrange_jog_button_by_axis() + return + + if len(self.axis_list) < 3: + print("Less than 3 axis") + # we can resize the jog_btn_table + self.widgets.tbl_jog_btn_axes.resize(3, 3) + else: + print("less than 6 axis") + # we can resize the jog_btn_table + self.widgets.tbl_jog_btn_axes.resize(3, 4) + + count = 0 + for btn_name in self.jog_button_dic: + if btn_name == "x+": + col = 2 + row = 1 + elif btn_name == "x-": + col = 0 + row = 1 + elif btn_name == "y+": + col = 1 + row = 0 + elif btn_name =="y-": + col = 1 + row = 2 + elif btn_name == "z+": + col = 3 + row = 0 + elif btn_name == "z-": + col = 3 + row = 2 + # order of the data in the dict matters for extra buttons. + # This is why we use ordered dict for self.jog_button_dic + else: + if count < 2: + col = 2 + else: + col = 0 + if "+" in btn_name: + row = 0 + else: + row = 2 + count +=1 + self.widgets.tbl_jog_btn_axes.attach(self.jog_button_dic[btn_name], col, col + 1, row, row + 1) + self.widgets.tbl_jog_btn_axes.show_all() + + def _arrange_jog_button_by_axis(self): + print("**** GMOCCAPY INFO ****") + print("**** arrange JOG button by axis") + print sorted(self.jog_button_dic.keys()) + # check if amount of axis is an even number, adapt the needed lines + cols = 4 + if len(self.dro_dic) % 2 == 0: + rows = len(self.dro_dic) / 2 + else: + rows = (len(self.dro_dic) + 1) / 2 + + self.widgets.tbl_jog_btn_axes.resize(rows, cols) + #print (cols,rows) + + col = 0 + row = 0 + + for pos, btn in enumerate("xyzabcuvw"): + btn_name = "{0}-".format(btn) + if btn_name not in self.jog_button_dic.keys(): + continue + + self.widgets.tbl_jog_btn_axes.attach(self.jog_button_dic[btn_name], + col, col+1, row, row + 1, ypadding = 0) + btn_name = "{0}+".format(btn) + self.widgets.tbl_jog_btn_axes.attach(self.jog_button_dic[btn_name], + col+1, col+2, row, row + 1, ypadding = 0) + + row +=1 + + # calculate if we have to place in the first or the second column + if row >= rows: + col = 2 + row = 0 + self.widgets.tbl_jog_btn_axes.show_all() + + def _arrange_joint_button(self): + print("**** GMOCCAPY INFO ****") + print("**** arrange JOINTS button") + print("Found {0} Joints Button".format(len(self.joints_button_dic))) + + cols = 4 + if self.stat.joints % 2 == 0: + rows = self.stat.joints / 2 + else: + rows = (self.stat.joints + 1) / 2 + + self.widgets.tbl_jog_btn_joints.resize(rows, cols) + + col = 0 + row = 0 + + for joint in range(0, self.stat.joints): + #print(joint) + + joint_name = "{0}-".format(joint) + self.widgets.tbl_jog_btn_joints.attach(self.joints_button_dic[joint_name], + col, col+1, row, row + 1, ypadding = 0) + + joint_name = "{0}+".format(joint) + self.widgets.tbl_jog_btn_joints.attach(self.joints_button_dic[joint_name], + col+1, col+2, row, row + 1, ypadding = 0) + + row +=1 + + # calculate if we have to place in the first or the second column + if row >= rows: + col = 2 + row = 0 + + self.widgets.tbl_jog_btn_joints.show_all() + + def _init_preferences(self): + # check if NO_FORCE_HOMING is used in ini + # disable reload tool on start up, if True + if self.no_force_homing: + self.widgets.chk_reload_tool.set_sensitive(False) + self.widgets.chk_reload_tool.set_active(False) + self.widgets.lbl_reload_tool.set_visible(True) + + # set the slider limits + self.widgets.spc_lin_jog_vel.set_property("min", 0) + self.widgets.spc_lin_jog_vel.set_property("max", self.jog_rate_max) + self.widgets.spc_lin_jog_vel.set_value(self.rabbit_jog) + + self.widgets.spc_spindle.set_property("min", self.spindle_override_min * 100) + self.widgets.spc_spindle.set_property("max", self.spindle_override_max * 100) + self.widgets.spc_spindle.set_value(100) + + self.widgets.spc_rapid.set_property("min", 0) + self.widgets.spc_rapid.set_property("max", self.rapid_override_max * 100) + self.widgets.spc_rapid.set_value(100) + + self.widgets.spc_feed.set_property("min", 0) + self.widgets.spc_feed.set_property("max", self.feed_override_max * 100) + self.widgets.spc_feed.set_value(100) + + # the scales to apply to the count of the hardware mpg wheel, to avoid to much turning + self.widgets.adj_scale_jog_vel.set_value(self.scale_jog_vel) + self.widgets.adj_scale_spindle_override.set_value(self.scale_spindle_override) + self.widgets.adj_scale_feed_override.set_value(self.scale_feed_override) + self.widgets.adj_scale_rapid_override.set_value(self.scale_rapid_override) + + # set and get all information for turtle jogging + # self.rabbit_jog will be used in future to store the last value + # so it can be recovered after jog_vel_mode switch + self.widgets.chk_turtle_jog.set_active(self.hide_turtle_jog_button) + self.widgets.adj_turtle_jog_factor.configure(self.turtle_jog_factor, 1, + 100, 1, 0, 0) + if self.hide_turtle_jog_button: + self.widgets.tbtn_turtle_jog.hide() + self.turtle_jog_factor = 1 + self.turtle_jog = self.rabbit_jog / self.turtle_jog_factor + + # and according to machine units the digits to display + if self.stat.linear_units == _MM: + self.widgets.spc_lin_jog_vel.set_digits(0) + self.widgets.spc_lin_jog_vel.set_property("unit", _("mm/min")) + else: + self.widgets.spc_lin_jog_vel.set_digits(2) + self.widgets.spc_lin_jog_vel.set_property("unit", _("inch/min")) + + # the size of the DRO ; self.dro_size is initialized in _get_pref_data + self.widgets.adj_dro_size.set_value(self.dro_size) + + # hide the angular jog vel if no angular joint is used + if not "a" in self.axis_list and not "b" in self.axis_list and not "c" in self.axis_list: + self.widgets.spc_ang_jog_vel.hide() + else: + self.widgets.spc_ang_jog_vel.set_property("min", self.min_ang_vel) + self.widgets.spc_ang_jog_vel.set_property("max", self.max_ang_vel) + self.widgets.spc_ang_jog_vel.set_value(self.default_ang_vel) + +# ============================================================= +# Dynamic tabs handling Start + + def _init_dynamic_tabs(self): + # dynamic tabs setup + self._dynamic_childs = {} + # register all tabs, so they will be closed together with the GUI + atexit.register(self._kill_dynamic_childs) + + tab_names, tab_locations, tab_cmd = self.get_ini_info.get_embedded_tabs() + if not tab_names: + print (_("**** GMOCCAPY INFO ****")) + print (_("**** Invalid embedded tab configuration ****")) + print (_("**** No tabs will be added! ****")) + return + + try: + for t, c, name in zip(tab_names, tab_cmd, tab_locations): + nb = self.widgets[name] + xid = self._dynamic_tab(nb, t) + if not xid: continue + cmd = c.replace('{XID}', str(xid)) + child = subprocess.Popen(cmd.split()) + self._dynamic_childs[xid] = child + nb.show_all() + except: + print(_("ERROR, trying to initialize the user tabs or panels, check for typos")) + self.set_up_user_tab_widgets(tab_locations) + + # adds the embedded object to a notebook tab or box + def _dynamic_tab(self, widget, text): + s = gtk.Socket() + try: + widget.append_page(s, gtk.Label(" " + text + " ")) + except: + try: + widget.pack_end(s, True, True, 0) + except: + return None + return s.get_id() + + # Gotta kill the embedded processes when gmoccapy closes + def _kill_dynamic_childs(self): + for child in self._dynamic_childs.values(): + child.terminate() + + def set_up_user_tab_widgets(self, tab_locations): + print(tab_locations) + if tab_locations: + # make sure the user tabs button is disabled + # if no ntb_user_tabs in location is given + if "ntb_user_tabs" in tab_locations: + self.widgets.tbtn_user_tabs.set_sensitive( True ) + else: + self.widgets.tbtn_user_tabs.set_sensitive( False ) + + if "ntb_preview" in tab_locations: + self.widgets.ntb_preview.set_property( "show-tabs", True ) + + # This is normaly only used for the plasma screen layout + if "box_coolant_and_spindle" in tab_locations: + widgetlist = ["box_spindle", "box_cooling"] + for widget in widgetlist: + self.widgets[widget].hide() + + if "box_cooling" in tab_locations: + widgetlist = ["frm_cooling"] + for widget in widgetlist: + self.widgets[widget].hide() + + if "box_spindle" in tab_locations: + widgetlist = ["frm_spindle"] + for widget in widgetlist: + self.widgets[widget].hide() + + if "box_vel_info" in tab_locations: + widgetlist = ["frm_max_vel", "frm_feed_override"] + for widget in widgetlist: + self.widgets[widget].hide() + + if "box_custom_1" in tab_locations: + self.widgets.box_custom_1.show() + + if "box_custom_2" in tab_locations: + self.widgets.box_custom_2.show() + + if "box_custom_3" in tab_locations: + self.widgets.box_custom_3.show() + + if "box_custom_4" in tab_locations: + self.widgets.box_custom_4.show() + + if "box_tool_and_code_info" in tab_locations: + widgetlist = ["frm_tool_info", "active_speed_label", "lbl_speed", "box_vel_info"] + for widget in widgetlist: + self.widgets[widget].hide() + self.widgets.btn_tool.set_sensitive( False ) + +# Dynamic tabs handling End +# ============================================================= + + # and we load the tooltable data + def _init_tooleditor(self): + + # get the path to the tool table + tooltable = self.get_ini_info.get_toolfile() + if not tooltable: + message = _("**** GMOCCAPY ERROR ****\n") + message += _("**** Did not find a toolfile file in [EMCIO] TOOL_TABLE ****") + print(message) + self.dialogs.warning_dialog(self, _("Very critical situation"), message, sound = False) + 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 + self.widgets.tooledit1.set_visible("abcxyzuvwijq", False) + for axis in self.axis_list: + self.widgets.tooledit1.set_visible("{0}".format(axis), True) + + # if it's a lathe config we show lathe related columns + if self.lathe_mode: + self.widgets.tooledit1.set_visible("ijq", True) + if not self.get_ini_info.get_lathe_wear_offsets(): + # hide the wear offset tabs + self.widgets.tooledit1.set_lathe_display(False) + + self.widgets.tooledit1.hide_buttonbox(True) + + def _init_themes(self): + # If there are themes then add them to combo box + model = self.widgets.theme_choice.get_model() + model.clear() + model.append((_("Follow System Theme"),)) + themes = [] + if os.path.exists(USERTHEMEDIR): + names = os.listdir(USERTHEMEDIR) + names.sort() + for dirs in names: + try: + sbdirs = os.listdir(os.path.join(USERTHEMEDIR, dirs)) + if 'gtk-2.0' in sbdirs: + themes.append(dirs) + except: + pass + if os.path.exists(THEMEDIR): + names = os.listdir(THEMEDIR) + names.sort() + for dirs in names: + try: + sbdirs = os.listdir(os.path.join(THEMEDIR, dirs)) + if 'gtk-2.0' in sbdirs: + themes.append(dirs) + except: + pass + temp = 0 + theme_name = self.prefs.getpref("gtk_theme", "Follow System Theme", str) + for index, theme in enumerate(themes): + model.append((theme,)) + if theme == theme_name: + temp = index + 1 + self.widgets.theme_choice.set_active(temp) + settings = gtk.settings_get_default() + if not theme_name == "Follow System Theme": + settings.set_string_property("gtk-theme-name", theme_name, "") + + def _init_audio(self): + # try to add ability for audio feedback to user. + if _AUDIO_AVAILABLE: + print (_("**** GMOCCAPY INFO ****")) + print (_("**** audio available! ****")) + + # the sounds to play if an error or message rises + self.alert_sound = "/usr/share/sounds/freedesktop/stereo/dialog-warning.oga" + self.error_sound = "/usr/share/sounds/freedesktop/stereo/dialog-error.oga" + + self.audio = player.Player() + self.alert_sound = self.prefs.getpref('audio_alert', self.alert_sound, str) + self.error_sound = self.prefs.getpref('audio_error', self.error_sound, str) + self.widgets.audio_alert_chooser.set_filename(self.alert_sound) + self.widgets.audio_error_chooser.set_filename(self.error_sound) + else: + print (_("**** GMOCCAPY INFO ****")) + print (_("**** no audio available! ****")) + print(_("**** PYGST libray not installed? ****")) + print(_("**** is python-gstX.XX installed? ****")) + + self.widgets.audio_alert_chooser.set_sensitive(False) + self.widgets.audio_error_chooser.set_sensitive(False) + + # init the preview + def _init_gremlin( self ): + print (_("**** GMOCCAPY INFO ****")) + print (_("**** Entering init gremlin ****")) + + grid_size = self.prefs.getpref( 'grid_size', 1.0, float ) + self.widgets.grid_size.set_value( grid_size ) + self.widgets.gremlin.grid_size = grid_size + view = view = self.prefs.getpref("view", "p", str ) + self.widgets.gremlin.set_property("view", view ) + self.widgets.gremlin.set_property("metric_units", int( self.stat.linear_units ) ) + self.widgets.gremlin.set_property("mouse_btn_mode", self.prefs.getpref( "mouse_btn_mode", 4, int ) ) + self.widgets.gremlin.set_property("use_commanded", not self.dro_actual) + self.widgets.gremlin.set_property("enable_dro", self.enable_gremlin_dro) + self.widgets.eb_program_label.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0, 0)) + self.widgets.eb_blockheight_label.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0, 0, 0)) + + def _init_kinematics_type (self): + print("Kinematics type changed") + if self.stat.kinematics_type != linuxcnc.KINEMATICS_IDENTITY: + self.widgets.gremlin.set_property("enable_dro", True ) + self.widgets.gremlin.use_joints_mode = True + self.widgets.tbtn_switch_mode.show() + self.widgets.tbtn_switch_mode.set_label(_(" Joint\nmode")) + self.widgets.tbtn_switch_mode.set_sensitive(False) + self.widgets.tbtn_switch_mode.set_active(True) + self.widgets.lbl_replace_mode_btn.hide() + self.widgets.ntb_jog_JA.set_page(1) + else: + self.widgets.gremlin.set_property("enable_dro", self.enable_gremlin_dro ) + self.widgets.gremlin.use_joints_mode = False + self.widgets.tbtn_switch_mode.hide() + self.widgets.lbl_replace_mode_btn.show() + self.widgets.ntb_jog_JA.set_page(0) + + # init the function to hide the cursor + def _init_hide_cursor(self): + hide_cursor = self.prefs.getpref('hide_cursor', False, bool) + self.widgets.chk_hide_cursor.set_active(hide_cursor) + # if hide cursor requested + # we set the graphics to use touchscreen controls + if hide_cursor: + self.widgets.window1.window.set_cursor(INVISABLE) + self.widgets.gremlin.set_property("use_default_controls", False) + else: + self.widgets.window1.window.set_cursor(None) + self.widgets.gremlin.set_property("use_default_controls", True) + +# ============================================================= +# Onboard keybord handling Start + + # shows "Onboard" virtual keyboard if available + # else error message + def _init_keyboard(self, args="", x="", y=""): + self.onboard = False + + # now we check if onboard or matchbox-keyboard is installed + try: + if os.path.isfile("/usr/bin/onboard"): + self.onboard_kb = subprocess.Popen(["onboard", "--xid", args, x, y], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + close_fds=True) + print (_("**** GMOCCAPY INFO ****")) + print (_("**** virtual keyboard program found : ")) + elif os.path.isfile("/usr/bin/matchbox-keyboard"): + self.onboard_kb = subprocess.Popen(["matchbox-keyboard", "--xid"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + close_fds=True) + print (_("**** GMOCCAPY INFO ****")) + print (_("**** virtual keyboard program found : ")) + else: + print (_("**** GMOCCAPY INFO ****")) + print (_("**** No virtual keyboard installed, we checked for and .")) + self._no_virt_keyboard() + return + sid = self.onboard_kb.stdout.readline() + self.socket = gtk.Socket() + self.widgets.key_box.add(self.socket) + self.socket.add_id(long(sid)) + self.socket.show_all() + self.widgets.key_box.show_all() + self.onboard = True + except Exception, e: + print (_("**** GMOCCAPY ERROR ****")) + print (_("**** Error with launching virtual keyboard,")) + print (_("**** is onboard or matchbox-keyboard installed? ****")) + traceback.print_exc() + self._no_virt_keyboard() + + + # get when the keyboard should be shown + # and set the corresponding button active + # only if onbaoard keyboard is ok. + if self.onboard: + self.widgets.chk_use_kb_on_offset.set_active(self.prefs.getpref("show_keyboard_on_offset", + False, bool)) + self.widgets.chk_use_kb_on_tooledit.set_active(self.prefs.getpref("show_keyboard_on_tooledit", + False, bool)) + self.widgets.chk_use_kb_on_edit.set_active(self.prefs.getpref("show_keyboard_on_edit", + True, bool)) + self.widgets.chk_use_kb_on_mdi.set_active(self.prefs.getpref("show_keyboard_on_mdi", + True, bool)) + self.widgets.chk_use_kb_on_file_selection.set_active(self.prefs.getpref("show_keyboard_on_file_selection", + False, bool)) + else: + self._no_virt_keyboard() + + def _no_virt_keyboard(self): + # In this case we will disable the corresponding part on the settings page + self.widgets.chk_use_kb_on_offset.set_active(False) + self.widgets.chk_use_kb_on_tooledit.set_active(False) + self.widgets.chk_use_kb_on_edit.set_active(False) + self.widgets.chk_use_kb_on_mdi.set_active(False) + self.widgets.chk_use_kb_on_file_selection.set_active(False) + self.widgets.frm_keyboard.set_sensitive(False) + self._change_kbd_image("stop") + self.macro_dic["keyboard"].set_sensitive(False) + self.macro_dic["keyboard"].set_property("tooltip-text", _("interrupt running macro")) + self.widgets.btn_keyb.set_sensitive(False) + + def _kill_keyboard(self): + try: + self.onboard_kb.kill() + self.onboard_kb.terminate() + self.onboard_kb = None + except: + try: + self.onboard_kb.kill() + self.onboard_kb.terminate() + self.onboard_kb = None + except: + pass + +# Onboard keybord handling End +# ============================================================= + + def _init_offsetpage(self): + temp = "xyzabcuvw" + self.widgets.offsetpage1.set_col_visible(temp, False) + temp = "" + for axis in self.axis_list: + temp = temp + axis + self.widgets.offsetpage1.set_col_visible(temp, True) + + parameterfile = self.get_ini_info.get_parameter_file() + if not parameterfile: + message = _("**** GMOCCAPY ERROR ****\n") + message += _("**** Did not find a parameter file in [RS274NGC] PARAMETER_FILE ****") + print(message) + self.dialogs.warning_dialog(self, _("Very critical situation"), message, sound = False) + sys.exit() + path = os.path.join(CONFIGPATH, parameterfile) + self.widgets.offsetpage1.set_filename(path) + + self.widgets.offsetpage1.set_display_follows_program_units() + if self.stat.program_units != 1: + self.widgets.offsetpage1.set_to_mm() + self.widgets.offsetpage1.machine_units_mm = _MM + else: + self.widgets.offsetpage1.set_to_inch() + self.widgets.offsetpage1.machine_units_mm = _INCH + self.widgets.offsetpage1.hide_buttonbox(True) + self.widgets.offsetpage1.set_row_visible("1", False) + self.widgets.offsetpage1.set_font("sans 12") + self.widgets.offsetpage1.set_foreground_color("#28D0D9") + self.widgets.offsetpage1.selection_mask = ("Tool", "G5x", "Rot") + systemlist = ["Tool", "G5x", "Rot", "G92", "G54", "G55", "G56", "G57", "G58", "G59", "G59.1", + "G59.2", "G59.3"] + names = [] + for system in systemlist: + system_name = "system_name_{0}".format(system) + name = self.prefs.getpref(system_name, system, str) + names.append([system, name]) + self.widgets.offsetpage1.set_names(names) + + # Icon file selection stuff + def _init_IconFileSelection(self): + self.widgets.IconFileSelection1.set_property("start_dir", self.get_ini_info.get_program_prefix()) + + file_ext = self.get_ini_info.get_file_ext() + filetypes = "" + for ext in file_ext: + filetypes += ext.replace("*.", "") + "," + self.widgets.IconFileSelection1.set_property("filetypes", filetypes) + + jump_to_dir = self.prefs.getpref("jump_to_dir", os.path.expanduser("~"), str) + self.widgets.jump_to_dir_chooser.set_current_folder(jump_to_dir) + self.widgets.IconFileSelection1.set_property("jump_to_dir", jump_to_dir) + + self.widgets.IconFileSelection1.show_buttonbox(False) + self.widgets.IconFileSelection1.show_filelabel(False) + + # now we initialize the button states + self.widgets.btn_home.set_sensitive(self.widgets.IconFileSelection1.btn_home.get_sensitive()) + self.widgets.btn_dir_up.set_sensitive(self.widgets.IconFileSelection1.btn_dir_up.get_sensitive()) + self.widgets.btn_sel_prev.set_sensitive(self.widgets.IconFileSelection1.btn_sel_prev.get_sensitive()) + self.widgets.btn_sel_next.set_sensitive(self.widgets.IconFileSelection1.btn_sel_next.get_sensitive()) + self.widgets.btn_select.set_sensitive(self.widgets.IconFileSelection1.btn_select.get_sensitive()) + self.widgets.btn_jump_to.set_sensitive(self.widgets.IconFileSelection1.btn_jump_to.get_sensitive()) + self.widgets.btn_jump_to.set_sensitive(self.widgets.IconFileSelection1.btn_jump_to.get_sensitive()) + + # init the keyboard shortcut bindings + def _init_keybindings(self): + try: + accel_group = gtk.AccelGroup() + self.widgets.window1.add_accel_group(accel_group) + self.widgets.button_estop.add_accelerator("clicked", accel_group, 65307, 0, gtk.ACCEL_LOCKED) + except: + pass + self.widgets.window1.connect("key_press_event", self.on_key_event, 1) + self.widgets.window1.connect("key_release_event", self.on_key_event, 0) + + # Initialize the file to load dialog, setting an title and the correct + # folder as well as a file filter + def _init_file_to_load(self): + file_dir = self.get_ini_info.get_program_prefix() + self.widgets.file_to_load_chooser.set_current_folder(file_dir) + title = _("Select the file you want to be loaded at program start") + self.widgets.file_to_load_chooser.set_title(title) + self.widgets.ff_file_to_load.set_name("linuxcnc files") + self.widgets.ff_file_to_load.add_pattern("*.ngc") + file_ext = self.get_ini_info.get_file_ext() + for ext in file_ext: + self.widgets.ff_file_to_load.add_pattern(ext) + + # search for and set up user requested message system. + # status displays on the statusbat and requires no acknowledge. + # dialog displays a GTK dialog box with yes or no buttons + # okdialog displays a GTK dialog box with an ok button + # dialogs require an answer before focus is sent back to main screen + def _init_user_messages(self): + user_messages = self.get_ini_info.get_user_messages() + if not user_messages: + return + for message in user_messages: + if message[1] == "status": + pin = hal_glib.GPin(self.halcomp.newpin("messages." + message[2], hal.HAL_BIT, hal.HAL_IN)) + pin.connect("value_changed", self._show_user_message, message) + elif message[1] == "okdialog": + pin = hal_glib.GPin(self.halcomp.newpin("messages." + message[2], hal.HAL_BIT, hal.HAL_IN)) + pin.connect("value_changed", self._show_user_message, message) + pin = hal_glib.GPin( + self.halcomp.newpin("messages." + message[2] + "-waiting", hal.HAL_BIT, hal.HAL_OUT)) + elif message[1] == "yesnodialog": + pin = hal_glib.GPin(self.halcomp.newpin("messages." + message[2], hal.HAL_BIT, hal.HAL_IN)) + pin.connect("value_changed", self._show_user_message, message) + pin = hal_glib.GPin( + self.halcomp.newpin("messages." + message[2] + "-waiting", hal.HAL_BIT, hal.HAL_OUT)) + pin = hal_glib.GPin( + self.halcomp.newpin("messages." + message[2] + "-response", hal.HAL_BIT, hal.HAL_OUT)) + else: + print(_("**** GMOCCAPY ERROR **** /n Message type {0} not supported").format(message[1])) + + def _show_user_message(self, pin, message): + if message[1] == "status": + if pin.get(): + self._show_error((0, message[0])) + elif message[1] == "okdialog": + self.halcomp["messages." + message[2] + "-waiting"] = 0 + if pin.get(): + self.halcomp["messages." + message[2] + "-waiting"] = 1 + title = "Pin " + message[2] + " message" + responce = self.dialogs.show_user_message(self, message[0], title) + self.halcomp["messages." + message[2] + "-waiting"] = 0 + elif message[1] == "yesnodialog": + if pin.get(): + self.halcomp["messages." + message[2] + "-waiting"] = 1 + self.halcomp["messages." + message[2] + "-response"] = 0 + title = "Pin " + message[2] + " message" + responce = self.dialogs.yesno_dialog(self, message[0], title) + self.halcomp["messages." + message[2] + "-waiting"] = 0 + self.halcomp["messages." + message[2] + "-response"] = responce + else: + self.halcomp["messages." + message[2] + "-waiting"] = 0 + else: + print(_("**** GMOCCAPY ERROR **** /n Message type {0} not supported").format(message[1])) + + def _show_offset_tab(self, state): + page = self.widgets.ntb_preview.get_nth_page(1) + if page.get_visible() and state or not page.get_visible() and not state: + return + if state: + page.show() + self.widgets.ntb_preview.set_property("show-tabs", state) + self.widgets.ntb_preview.set_current_page(1) + self.widgets.offsetpage1.mark_active((self.system_list[self.stat.g5x_index]).lower()) + if self.widgets.chk_use_kb_on_offset.get_active(): + self.widgets.ntb_info.set_current_page(1) + else: + names = self.widgets.offsetpage1.get_names() + for system, name in names: + system_name = "system_name_{0}".format(system) + self.prefs.putpref(system_name, name) + page.hide() + + self.touch_button_dic["edit_offsets"].set_active(False) + self.widgets.ntb_preview.set_current_page(0) + self.widgets.ntb_info.set_current_page(0) + if self.widgets.ntb_preview.get_n_pages() <= 4: # else user tabs are available + self.widgets.ntb_preview.set_property("show-tabs", state) + + def _show_tooledit_tab(self, state): + page = self.widgets.ntb_preview.get_nth_page(2) + if page.get_visible() and state or not page.get_visible() and not state: + return + if state: + page.show() + self.widgets.ntb_preview.set_property("show-tabs", not state) + self.widgets.vbx_jog.hide() + self.widgets.ntb_preview.set_current_page(2) + self.widgets.tooledit1.set_selected_tool(self.stat.tool_in_spindle) + if self.widgets.chk_use_kb_on_tooledit.get_active(): + self.widgets.ntb_info.set_current_page(1) + else: + page.hide() + if self.widgets.ntb_preview.get_n_pages() > 4: # user tabs are available + self.widgets.ntb_preview.set_property("show-tabs", not state) + self.widgets.vbx_jog.show() + self.widgets.ntb_preview.set_current_page(0) + self.widgets.ntb_info.set_current_page(0) + + def _show_iconview_tab(self, state): + page = self.widgets.ntb_preview.get_nth_page(3) + if page.get_visible() and state or not page.get_visible() and not state: + return + if state: + page.show() + self.widgets.ntb_preview.set_property("show-tabs", not state) + self.widgets.ntb_preview.set_current_page(3) + if self.widgets.chk_use_kb_on_file_selection.get_active(): + self.widgets.box_info.show() + self.widgets.ntb_info.set_current_page(1) + else: + page.hide() + if self.widgets.ntb_preview.get_n_pages() > 4: # user tabs are available + self.widgets.ntb_preview.set_property("show-tabs", not state) + self.widgets.ntb_preview.set_current_page(0) + self.widgets.ntb_info.set_current_page(0) + + # every 100 milli seconds this gets called + # check linuxcnc for status, error and then update the readout + def _periodic(self): + # we put the poll comand in a try, so if the linuxcnc pid is killed + # from an external command, we also quit the GUI + try: + self.stat.poll() + except: + raise SystemExit, "gmoccapy can not poll linuxcnc status any more" + + error = self.error_channel.poll() + if error: + self._show_error(error) + + if self.gcodes != self.stat.gcodes: + self._update_active_gcodes() + if self.mcodes != self.stat.mcodes: + self._update_active_mcodes() + + if self.lathe_mode: + if "G8" in self.active_gcodes and self.diameter_mode: + self._switch_to_g7(False) + elif "G7" in self.active_gcodes and not self.diameter_mode: + self._switch_to_g7(True) + + self._update_vel() + self._update_coolant() + self._update_spindle() + self._update_halui_pin() + self._update_vc() + + self.widgets.lbl_time.set_label(strftime("%H:%M:%S") + "\n" + strftime("%d.%m.%Y")) + + # keep the timer running + return True + + def _show_error(self, error): + kind, text = error + # print kind,text + if kind in (linuxcnc.NML_ERROR, linuxcnc.OPERATOR_ERROR): + icon = ALERT_ICON + self.halcomp["error"] = True + elif kind in (linuxcnc.NML_TEXT, linuxcnc.OPERATOR_TEXT): + icon = INFO_ICON + elif kind in (linuxcnc.NML_DISPLAY, linuxcnc.OPERATOR_DISPLAY): + icon = INFO_ICON + else: + icon = ALERT_ICON + if text == "" or text == None: + text = _("Unknown error type and no error text given") + self.notification.add_message(text, icon) + + if _AUDIO_AVAILABLE: + if kind == 1 or kind == 11: + self._on_play_sound(None, "error") + else: + self._on_play_sound(None, "alert") + + def on_gremlin_gcode_error(self, widget, errortext): + if self.gcodeerror == errortext: + return + else: + self.gcodeerror = errortext + self.dialogs.warning_dialog(self, _("Important Warning"), errortext) + + +# ========================================================= +# button handlers Start + + # toggle emergency button + def on_tbtn_estop_toggled(self, widget, data=None): + if widget.get_active(): # estop is active, open circuit + self.command.state(linuxcnc.STATE_ESTOP) + self.command.wait_complete() + self.stat.poll() + if self.stat.task_state == linuxcnc.STATE_ESTOP_RESET: + widget.set_active(False) + else: # estop circuit is fine + self.command.state(linuxcnc.STATE_ESTOP_RESET) + self.command.wait_complete() + self.stat.poll() + if self.stat.task_state == linuxcnc.STATE_ESTOP: + widget.set_active(True) + self._show_error((11, _("ERROR : External ESTOP is set, could not change state!"))) + + # toggle machine on / off button + def on_tbtn_on_toggled(self, widget, data=None): + if widget.get_active(): + if self.stat.task_state == linuxcnc.STATE_ESTOP: + widget.set_active(False) + return + self.command.state(linuxcnc.STATE_ON) + self.command.wait_complete() + self.stat.poll() + if self.stat.task_state != linuxcnc.STATE_ON: + widget.set_active(False) + self._show_error((11, _("ERROR : Could not switch the machine on, is limit switch activated?"))) + self._update_widgets(False) + return + self._update_widgets(True) + else: + self.command.state(linuxcnc.STATE_OFF) + self._update_widgets(False) + + # The mode buttons + def on_rbt_manual_pressed(self, widget, data=None): + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + + def on_rbt_mdi_pressed(self, widget, data=None): + self.command.mode(linuxcnc.MODE_MDI) + self.command.wait_complete() + + def on_rbt_auto_pressed(self, widget, data=None): + self.command.mode(linuxcnc.MODE_AUTO) + self.command.wait_complete() + + # If button exit is clicked, press emergency button before closing the application + def on_btn_exit_clicked(self, widget, data=None): + self.widgets.window1.destroy() + +# button handlers End +# ========================================================= + +# ========================================================= +# hal status Start + + # use the hal_status widget to control buttons and + # actions allowed by the user and sensitive widgets + def on_hal_status_all_homed(self, widget): + print("Hal Status all homed") + self.all_homed = True + self.widgets.ntb_button.set_current_page(_BB_MANUAL) + widgetlist = ["rbt_mdi", "rbt_auto", "btn_index_tool", "btn_change_tool", "btn_select_tool_by_no", + "btn_tool_touchoff_x", "btn_tool_touchoff_z", "btn_touch", "tbtn_switch_mode" + ] + self._sensitize_widgets(widgetlist, True) + self._set_motion_mode(1) + if self.widgets.chk_reload_tool.get_active(): + # if there is already a tool in spindle, the user + # homed the second time, unfortunately we will then + # not get out of MDI mode any more + # That happen, because the tool in spindle did not change, so the + # tool info is not updated and we self.change_tool will not be reseted + if self.stat.tool_in_spindle != 0: + return + self.reload_tool() + self.command.mode(linuxcnc.MODE_MANUAL) + + def on_hal_status_not_all_homed(self, widget, joints): + print("Hal Status not all homed", joints) + self.all_homed = False + if self.no_force_homing: + return + widgetlist = ["rbt_mdi", "rbt_auto", "btn_index_tool", "btn_touch", "btn_change_tool", "btn_select_tool_by_no", + "btn_tool_touchoff_x", "btn_tool_touchoff_z", "btn_touch", "tbtn_switch_mode" + ] + self._sensitize_widgets(widgetlist, False) + self._set_motion_mode(0) + + def on_hal_status_file_loaded(self, widget, filename): + widgetlist = ["btn_use_current" ] + # this test is only necessary, because of remap and toolchange, it will emit a file loaded signal + if filename: + fileobject = file(filename, 'r') + lines = fileobject.readlines() + fileobject.close() + self.halcomp["program.length"] = len(lines) + + if len(filename) > 70: + filename = filename[0:10] + "..." + filename[len(filename) - 50:len(filename)] + self.widgets.lbl_program.set_text(filename) + self._sensitize_widgets(widgetlist, True) + else: + self.halcomp["program.length"] = 0 + self._sensitize_widgets(widgetlist, False) + self.widgets.lbl_program.set_text(_("No file loaded")) + + def on_hal_status_line_changed(self, widget, line): + self.halcomp["program.current-line"] = line + # this test is only necessary, because of remap and toolchange, it will emit a file loaded signal + if self.halcomp["program.length"] > 0: + self.halcomp["program.progress"] = 100.00 * line / self.halcomp["program.length"] + else: + self.halcomp["program.progress"] = 0.0 + # print("Progress = {0:.2f} %".format(100.00 * line / self.halcomp["program.length"])) + + def on_hal_status_interp_idle(self, widget): + print("IDLE") + if self.load_tool: + return + + widgetlist = ["rbt_manual", "ntb_jog", "btn_from_line", + "tbtn_flood", "tbtn_mist", "rbt_forward", "rbt_reverse", "rbt_stop", + "btn_load", "btn_edit", "tbtn_optional_blocks" + ] + if not self.widgets.rbt_hal_unlock.get_active() and not self.user_mode: + widgetlist.append("tbtn_setup") + + if self.all_homed or self.no_force_homing: + widgetlist.append("rbt_mdi") + widgetlist.append("rbt_auto") + widgetlist.append("btn_index_tool") + widgetlist.append("btn_change_tool") + widgetlist.append("btn_select_tool_by_no") + widgetlist.append("btn_tool_touchoff_x") + widgetlist.append("btn_tool_touchoff_z") + widgetlist.append("btn_touch") + + # This happen because hal_glib does emmit the signals in the order that idle is emited later that estop + if self.stat.task_state == linuxcnc.STATE_ESTOP or self.stat.task_state == linuxcnc.STATE_OFF: + self._sensitize_widgets(widgetlist, False) + else: + self._sensitize_widgets(widgetlist, True) + + for btn in self.macrobuttons: + btn.set_sensitive(True) + + if self.onboard: + self._change_kbd_image("keyboard") + else: + self._change_kbd_image("stop") + self.macro_dic["keyboard"].set_sensitive(False) + + self.widgets.btn_run.set_sensitive(True) + + if self.tool_change: + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + self.tool_change = False + + self.halcomp["program.current-line"] = 0 + self.halcomp["program.progress"] = 0.0 + + def on_hal_status_interp_run(self, widget): + print("RUN") + + widgetlist = ["rbt_manual", "rbt_mdi", "rbt_auto", "tbtn_setup", "btn_index_tool", + "btn_from_line", "btn_change_tool", "btn_select_tool_by_no", + "btn_load", "btn_edit", "tbtn_optional_blocks", "rbt_reverse", "rbt_stop", "rbt_forward", + "btn_tool_touchoff_x", "btn_tool_touchoff_z", "btn_touch" + ] + # in MDI it should be possible to add more commands, even if the interpreter is running + if self.stat.task_mode != linuxcnc.MODE_MDI: + widgetlist.append("ntb_jog") + + self._sensitize_widgets(widgetlist, False) + self.widgets.btn_run.set_sensitive(False) + + self._change_kbd_image("stop") + self.macro_dic["keyboard"].set_sensitive(True) + + def on_hal_status_tool_in_spindle_changed(self, object, new_tool_no): + # need to save the tool in spindle as preference, to be able to reload it on startup + self.prefs.putpref("tool_in_spindle", new_tool_no, int) + self._update_toolinfo(new_tool_no) + + def on_hal_status_state_estop(self, widget=None): + self.widgets.tbtn_estop.set_active(True) + self.widgets.tbtn_estop.set_image(self.widgets.img_emergency) + self.widgets.tbtn_on.set_image(self.widgets.img_machine_on) + self.widgets.tbtn_on.set_sensitive(False) + self.widgets.chk_ignore_limits.set_sensitive(False) + self.widgets.tbtn_on.set_active(False) + self.command.mode(linuxcnc.MODE_MANUAL) + + def on_hal_status_state_estop_reset(self, widget=None): + self.widgets.tbtn_estop.set_active(False) + self.widgets.tbtn_estop.set_image(self.widgets.img_emergency_off) + self.widgets.tbtn_on.set_image(self.widgets.img_machine_off) + self.widgets.tbtn_on.set_sensitive(True) + self.widgets.ntb_jog.set_sensitive(True) + self.widgets.ntb_jog_JA.set_sensitive(False) + self.widgets.vbtb_jog_incr.set_sensitive(False) + self.widgets.hbox_jog_vel.set_sensitive(False) + self.widgets.chk_ignore_limits.set_sensitive(True) + self._check_limits() + + def on_hal_status_state_off(self, widget): + widgetlist = ["rbt_manual", "rbt_mdi", "rbt_auto", "btn_homing", "btn_touch", "btn_tool", + "hbox_jog_vel", "ntb_jog_JA", "vbtb_jog_incr", "spc_feed", "btn_feed_100", "rbt_forward", "btn_index_tool", + "rbt_reverse", "rbt_stop", "tbtn_flood", "tbtn_mist", "btn_change_tool", "btn_select_tool_by_no", + "btn_spindle_100", "spc_rapid", "spc_spindle", + "btn_tool_touchoff_x", "btn_tool_touchoff_z" + ] + self._sensitize_widgets(widgetlist, False) + if self.widgets.tbtn_on.get_active(): + self.widgets.tbtn_on.set_active(False) + self.widgets.tbtn_on.set_image(self.widgets.img_machine_off) + self.widgets.btn_exit.set_sensitive(True) + self.widgets.chk_ignore_limits.set_sensitive(True) + self.widgets.ntb_main.set_current_page(0) + self.widgets.ntb_button.set_current_page(_BB_MANUAL) + self.widgets.ntb_info.set_current_page(0) + self.widgets.ntb_jog.set_current_page(0) + + def on_hal_status_state_on(self, widget): + widgetlist = ["rbt_manual", "btn_homing", "btn_touch", "btn_tool", + "ntb_jog", "spc_feed", "btn_feed_100", "rbt_forward", + "rbt_reverse", "rbt_stop", "tbtn_flood", "tbtn_mist", + "btn_spindle_100", "spc_rapid", "spc_spindle" + ] + self._sensitize_widgets(widgetlist, True) + if not self.widgets.tbtn_on.get_active(): + self.widgets.tbtn_on.set_active(True) + self.widgets.tbtn_on.set_image(self.widgets.img_machine_on) + self.widgets.btn_exit.set_sensitive(False) + self.widgets.chk_ignore_limits.set_sensitive(False) + if self.widgets.ntb_main.get_current_page() != 0: + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + + def on_hal_status_mode_manual(self, widget): + print ("MANUAL Mode") + self.widgets.rbt_manual.set_active(True) + # if setup page is activated, we must leave here, otherwise the pages will be reset + if self.widgets.tbtn_setup.get_active(): + return + # if we are in user tabs, we must reset the button + if self.widgets.tbtn_user_tabs.get_active(): + self.widgets.tbtn_user_tabs.set_active(False) + self.widgets.ntb_main.set_current_page(0) + self.widgets.ntb_button.set_current_page(_BB_MANUAL) + self.widgets.ntb_info.set_current_page(0) + self.widgets.ntb_jog.set_current_page(0) + self._check_limits() + + # if the status changed, we reset the key event, otherwise the key press + # event will not change, if the user did the last change with keyboard shortcut + # This is caused, because we record the last key event to avoid multiple key + # press events by holding down the key. I.e. One press should only advance one increment + # on incremental jogging. + self.last_key_event = None, 0 + + def on_hal_status_mode_mdi(self, widget): + print ("MDI Mode", self.tool_change) + + # if the edit offsets button is active, we do not want to change + # pages, as the user may want to edit several axis values + if self.touch_button_dic["edit_offsets"].get_active(): + return + + # self.tool_change is set only if the tool change was commanded + # from tooledit widget/page, so we do not want to switch the + # screen layout to MDI, but set the manual widgets + if self.tool_change: + self.widgets.ntb_main.set_current_page(0) + self.widgets.ntb_button.set_current_page(_BB_MANUAL) + self.widgets.ntb_info.set_current_page(0) + self.widgets.ntb_jog.set_current_page(0) + return + + # if MDI button is not sensitive, we are not ready for MDI commands + # so we have to abort external commands and get back to manual mode + # This will happen mostly, if we are in settings mode, as we do disable the mode button + if not self.widgets.rbt_mdi.get_sensitive(): + self.command.abort() + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + self._show_error((13, _("It is not possible to change to MDI Mode at the moment"))) + return + else: + # if we are in user tabs, we must reset the button + if self.widgets.tbtn_user_tabs.get_active(): + self.widgets.tbtn_user_tabs.set_active(False) + if self.widgets.chk_use_kb_on_mdi.get_active(): + self.widgets.ntb_info.set_current_page(1) + else: + self.widgets.ntb_info.set_current_page(0) + self.widgets.ntb_main.set_current_page(0) + self.widgets.ntb_button.set_current_page(_BB_MDI) + self.widgets.ntb_jog.set_current_page(1) + self.widgets.hal_mdihistory.entry.grab_focus() + self.widgets.rbt_mdi.set_active(True) + + # if the status changed, we reset the key event, otherwise the key press + # event will not change, if the user did the last change with keyboard shortcut + # This is caused, because we record the last key event to avoid multiple key + # press events by holding down the key. I.e. One press should only advance one increment + # on incremental jogging. + self.last_key_event = None, 0 + + def on_hal_status_mode_auto(self, widget): + print ("AUTO Mode") + # if Auto button is not sensitive, we are not ready for AUTO commands + # so we have to abort external commands and get back to manual mode + # This will happen mostly, if we are in settings mode, as we do disable the mode button + if not self.widgets.rbt_auto.get_sensitive(): + self.command.abort() + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + self._show_error((13, _("It is not possible to change to Auto Mode at the moment"))) + return + else: + # if we are in user tabs, we must reset the button + if self.widgets.tbtn_user_tabs.get_active(): + self.widgets.tbtn_user_tabs.set_active(False) + self.widgets.ntb_main.set_current_page(0) + self.widgets.ntb_button.set_current_page(_BB_AUTO) + self.widgets.ntb_info.set_current_page(0) + self.widgets.ntb_jog.set_current_page(2) + self.widgets.rbt_auto.set_active(True) + + # if the status changed, we reset the key event, otherwise the key press + # event will not change, if the user did the last change with keyboard shortcut + # This is caused, because we record the last key event to avoid multiple key + # press events by holding down the key. I.e. One press should only advance one increment + # on incremental jogging. + self.last_key_event = None, 0 + + def on_hal_status_motion_mode_changed(self, widget, new_mode): + print("hal status motion mode changed") + # Motion mode change in identity kinematics makes no sense + # so we will not react on the signal and correct the misbehavior + # self.stat.motion_mode return + # Mode 1 = joint ; Mode 2 = MDI ; Mode 3 = teleop + # so in mode 1 we have to show Joints and in Modes 2 and 3 axis values + + widgetlist = ("rbt_mdi", "rbt_auto") + if new_mode == 1 and self.stat.kinematics_type != linuxcnc.KINEMATICS_IDENTITY: + self.widgets.gremlin.set_property("enable_dro", True) + self.widgets.gremlin.use_joints_mode = True + self.widgets.tbtn_switch_mode.set_active(True) + self.widgets.ntb_jog_JA.set_page(1) + state = False + else: + if not self.widgets.tbtn_fullsize_preview0.get_active(): + self.widgets.gremlin.set_property("enable_dro", self.enable_gremlin_dro) + self.widgets.gremlin.use_joints_mode = False + self.widgets.tbtn_switch_mode.set_active(False) + self.widgets.ntb_jog_JA.set_page(0) + state = True + if self.stat.task_state != linuxcnc.STATE_ON: + state = False + self._sensitize_widgets(widgetlist, state) + + def on_hal_status_metric_mode_changed(self, widget, metric_units): + print("hal status metric mode changed") + # set gremlin_units + self.widgets.gremlin.set_property("metric_units", metric_units) + + widgetlist = ["spc_lin_jog_vel"] + + # self.stat.linear_units will return 1.0 for metric and 1/25,4 for imperial + # display units not equal machine units + if metric_units != int(self.stat.linear_units): + # machine units = metric + if self.stat.linear_units == _MM: + self.faktor = (1.0 / 25.4) + # machine units = imperial + else: + self.faktor = 25.4 + self.turtle_jog = self.turtle_jog * self.faktor + self.rabbit_jog = self.rabbit_jog * self.faktor + self._update_slider( widgetlist ) + + else: + # display units equal machine units would be factor = 1, + # but if factor not equal 1.0 than we have to reconvert from previous first + self.turtle_jog = self.turtle_jog / self.faktor + self.rabbit_jog = self.rabbit_jog / self.faktor + if self.faktor != 1.0: + self.faktor = 1 / self.faktor + self._update_slider(widgetlist) + self.faktor = 1.0 + self._update_slider(widgetlist) + + if metric_units: + self.widgets.spc_lin_jog_vel.set_digits(0) + self.widgets.spc_lin_jog_vel.set_property("unit", _("mm/min")) + else: + self.widgets.spc_lin_jog_vel.set_digits(2) + self.widgets.spc_lin_jog_vel.set_property("unit", _("inch/min")) + +# hal status End +# ========================================================= + + # There are some settings we can only do if the window is on the screen already + def on_window1_show(self, widget, data=None): + + # it is time to get the correct estop state and set the button status + self.stat.poll() + if self.stat.task_state == linuxcnc.STATE_ESTOP: + self.widgets.tbtn_estop.set_active(True) + self.widgets.tbtn_estop.set_image(self.widgets.img_emergency) + self.widgets.tbtn_on.set_image(self.widgets.img_machine_off) + self.widgets.tbtn_on.set_sensitive(False) + else: + self.widgets.tbtn_estop.set_active(False) + self.widgets.tbtn_estop.set_image(self.widgets.img_emergency_off) + self.widgets.tbtn_on.set_sensitive(True) + + # if a file should be loaded, we will do so + file = self.prefs.getpref("open_file", "", str) + if file: + self.widgets.file_to_load_chooser.set_filename(file) + self.widgets.hal_action_open.load_file(file) + + # check how to start the GUI + start_as = "rbtn_" + self.prefs.getpref("screen1", "window", str) + self.widgets[start_as].set_active(True) + if start_as == "rbtn_fullscreen": + self.widgets.window1.fullscreen() + elif start_as == "rbtn_maximized": + self.widgets.window1.maximize() + else: + self.xpos = int(self.prefs.getpref("x_pos", 40, float)) + self.ypos = int(self.prefs.getpref("y_pos", 30, float)) + self.width = int(self.prefs.getpref("width", 979, float)) + self.height = int(self.prefs.getpref("height", 750, float)) + + # set the adjustments according to Window position and size + self.widgets.adj_x_pos.set_value(self.xpos) + self.widgets.adj_y_pos.set_value(self.ypos) + self.widgets.adj_width.set_value(self.width) + self.widgets.adj_height.set_value(self.height) + + # move and resize the window + self.widgets.window1.move(self.xpos, self.ypos) + self.widgets.window1.resize(self.width, self.height) + + # set initial state of widgets + self.touch_button_dic["set_active"].set_sensitive(False) + + # set up the hal pin status of tool measurement + # could not be done prior to this, as the hal pin are not created before + # the tool measure check, due to the reason we need tzo know if creation + # of tool measurement button is needed. So we call the toogle action to + # set all up and running + self.on_chk_use_tool_measurement_toggled(self.widgets.chk_use_tool_measurement) + + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + + self.initialized = True + + # kill keyboard and estop machine before closing + def on_window1_destroy(self, widget, data=None): + print "estoping / killing gmoccapy" + if self.onboard: + self._kill_keyboard() + self.command.state(linuxcnc.STATE_OFF) + self.command.state(linuxcnc.STATE_ESTOP) + gtk.main_quit() + + # What to do if a macro button has been pushed + def _on_btn_macro_pressed( self, widget = None, data = None ): + o_codes = data.split() + + command = str( "O<" + o_codes[0] + "> call" ) + + for code in o_codes[1:]: + parameter = self.dialogs.entry_dialog(self, data=None, header=_("Enter value:"), + label=_("Set parameter {0} to:").format(code), integer=False) + if parameter == "ERROR": + print(_("conversion error")) + self.dialogs.warning_dialog(self, _("Conversion error !"), + ("Please enter only numerical values\nValues have not been applied")) + return + elif parameter == "CANCEL": + return + else: + pass + command = command + " [" + str(parameter) + "] " +# TODO: Should not only clear the plot, but also the loaded program? + # self.command.program_open("") + # self.command.reset_interpreter() + self.widgets.gremlin.clear_live_plotter() +# TODO: End + self.command.mdi(command) + for btn in self.macrobuttons: + btn.set_sensitive(False) + # we change the widget_image (doen by hal status) + # and use the button to interrupt running macros + if not self.onboard: + self.macro_dic["keyboard"].set_sensitive(True) + self.widgets.ntb_info.set_current_page(0) + +# helpers functions start +# ========================================================= + + def _change_kbd_image(self, image): + #print("change keyboard image",self.macro_dic) + if image == "stop": + file = "stop.png" + else: + file = "keyboard.png" + filepath = os.path.join(IMAGEDIR, file) + image = self.macro_dic["keyboard"].get_children()[0] + image.set_from_file(filepath) + if self.onboard: + self.macro_dic["keyboard"].set_property("tooltip-text", _("This button will show or hide the keyboard")) + else: + self.macro_dic["keyboard"].set_property("tooltip-text", _("interrupt running macro")) + self.macro_dic["keyboard"].show_all() + + def _update_widgets(self, state): + widgetlist = ["rbt_manual", "btn_homing", "btn_touch", "btn_tool", + "hbox_jog_vel", "ntb_jog_JA", "vbtb_jog_incr", "spc_feed", "btn_feed_100", "rbt_forward", "btn_index_tool", + "rbt_reverse", "rbt_stop", "tbtn_flood", "tbtn_mist", "btn_change_tool", "btn_select_tool_by_no", + "btn_spindle_100", "spc_rapid", "spc_spindle", + "btn_tool_touchoff_x", "btn_tool_touchoff_z" + ] + self._sensitize_widgets(widgetlist, state) + + def _switch_to_g7(self, state): + # we do this only if we have a lathe, the check for lathe is done in gmoccapy + print state + if state: + self.dro_dic["Combi_DRO_0"].set_property("abs_color", gtk.gdk.color_parse("#F2F1F0")) + self.dro_dic["Combi_DRO_0"].set_property("rel_color", gtk.gdk.color_parse("#F2F1F0")) + self.dro_dic["Combi_DRO_0"].set_property("dtg_color", gtk.gdk.color_parse("#F2F1F0")) + self.dro_dic["Combi_DRO_9"].set_property("abs_color", gtk.gdk.color_parse(self.abs_color)) + self.dro_dic["Combi_DRO_9"].set_property("rel_color", gtk.gdk.color_parse(self.rel_color)) + self.dro_dic["Combi_DRO_9"].set_property("dtg_color", gtk.gdk.color_parse(self.dtg_color)) + self.diameter_mode = True + else: + self.dro_dic["Combi_DRO_9"].set_property("abs_color", gtk.gdk.color_parse("#F2F1F0")) + self.dro_dic["Combi_DRO_9"].set_property("rel_color", gtk.gdk.color_parse("#F2F1F0")) + self.dro_dic["Combi_DRO_9"].set_property("dtg_color", gtk.gdk.color_parse("#F2F1F0")) + self.dro_dic["Combi_DRO_0"].set_property("abs_color", gtk.gdk.color_parse(self.abs_color)) + self.dro_dic["Combi_DRO_0"].set_property("rel_color", gtk.gdk.color_parse(self.rel_color)) + self.dro_dic["Combi_DRO_0"].set_property("dtg_color", gtk.gdk.color_parse(self.dtg_color)) + self.diameter_mode = False + + def on_key_event(self, widget, event, signal): + + # get the keyname + keyname = gtk.gdk.keyval_name(event.keyval) + + # estop with F1 shold work every time + # so should also escape abort actions + if keyname == "F1": # will estop the machine, but not reset estop! + self.command.state(linuxcnc.STATE_ESTOP) + return True + if keyname == "Escape": + self.command.abort() + return True + + # change between teleop and world mode + if keyname == "F12" or keyname == "$": + if self.stat.task_mode != linuxcnc.MODE_MANUAL: + return True + # only change mode pressing the key, not releasing it + if signal: + # No mode switch to joints on Identity kinematics + if self.stat.kinematics_type != linuxcnc.KINEMATICS_IDENTITY: + # Mode 1 = joint ; Mode 3 = teleop + if self.stat.motion_mode != 1: + self._set_motion_mode(0) # set joint mode + else: + self._set_motion_mode(1) # set teleop mode + return True + + # This will avoid executing the key press event several times caused by keyboard auto repeat + if self.last_key_event[0] == keyname and self.last_key_event[1] == signal: + return True + + try: + if keyname == "F2" and signal: + # only turn on if no estop! + if self.widgets.tbtn_estop.get_active(): + return True + self.widgets.tbtn_on.emit("clicked") + return True + except: + pass + + if keyname == "space" and signal: + if event.state & gtk.gdk.CONTROL_MASK: # only do it when control is hold down + self.notification.del_message(-1) + self.widgets.window1.grab_focus() + return + + if keyname == "Super_L" and signal: # Left Windows + self.notification.del_last() + self.widgets.window1.grab_focus() + return + + # if the user do not want to use keyboard shortcuts, we leave here + # in this case we do not return true, otherwise entering code in MDI history + # and the integrated editor will not work + if not self.widgets.chk_use_kb_shortcuts.get_active(): + print("Settings say: do not use keyboard shortcuts, abort") + return + + # Only in MDI mode the RETURN key should execute a command + if keyname == "Return" and signal and self.stat.task_mode == linuxcnc.MODE_MDI: + # print("Got enter in MDI") + self.widgets.hal_mdihistory.submit() + self.widgets.hal_mdihistory.entry.grab_focus() + # we need to leave here, otherwise the check for jogging + # only allowed in manual mode will finish the sub + return True + + # mode change are only allowed if the interpreter is idle, like mode switching + if keyname == "F3" or keyname == "F5": + if self.stat.interp_state != linuxcnc.INTERP_IDLE: + if signal: # Otherwise the message will be shown twice + self._show_error((13, _("Mode change is only allowed if the interpreter is idle!"))) + return + else: + # F3 change to manual mode + if keyname == "F3" and signal: + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + # we need to leave here, otherwise the check for jogging + # only allowed in manual mode will finish the sub + self.last_key_event = keyname, signal + return True + + # F5 should change to mdi mode + if keyname == "F5" and signal: + self.command.mode(linuxcnc.MODE_MDI) + self.command.wait_complete() + # we need to leave here, otherwise the check for jogging + # only allowed in manual mode will finish the sub + self.last_key_event = keyname, signal + return True + + # in AUTO Mode we will allow the following key shortcuts + # R = run program + # P = pause program + # S = resume program + if self.stat.task_mode == linuxcnc.MODE_AUTO: + # if we are in edit mode do not start a program! + if self.widgets.ntb_button.get_current_page() == _BB_EDIT: + return + + # all makes only sense, if a program is loaded, + # if so, the button use current is sensitive + if not self.widgets.btn_use_current.get_sensitive(): + return + + if (keyname == "R" or keyname == "r") and self.stat.interp_state == linuxcnc.INTERP_IDLE: + if event.state & gtk.gdk.CONTROL_MASK: + print("R und Control gedrückt") + self.widgets.hal_action_reload.emit("activate") + else: + self.command.auto(linuxcnc.AUTO_RUN,0) + + if (keyname == "p" or keyname == "P") and self.widgets.tbtn_pause.get_sensitive(): + self.command.auto(linuxcnc.AUTO_PAUSE) + + if (keyname == "S" or keyname == "s"): + self.command.auto(linuxcnc.AUTO_RESUME) + if self.widgets.tbtn_pause.get_active(): + self.widgets.tbtn_pause.set_active(False) + + # Only in manual mode jogging with keyboard is allowed + # in this case we do not return true, otherwise entering code in MDI history + # and the integrated editor will not work + # we also check if we are in settings or user page + if self.stat.task_mode != linuxcnc.MODE_MANUAL or not self.widgets.ntb_main.get_current_page() == 0: + return + + # This is just to avoid a terminal message, that this keys are not implemented: + if (keyname == "R" or keyname == "r" or + keyname == "p" or keyname == "P" or + keyname == "S" or keyname == "s"): + return + + # offset page is active, so keys must go through + if self.widgets.ntb_preview.get_current_page() == 1: + return + + # tooledit page is active, so keys must go through + if self.widgets.ntb_preview.get_current_page() == 2: + return + + # take care of different key handling for lathe operation + if self.lathe_mode: + if keyname == "Page_Up" or keyname == "Page_Down" or keyname == "KP_Page_Up" or keyname == "KP_Page_Down": + return + + if event.state & gtk.gdk.SHIFT_MASK: # SHIFT is hold down, fast jogging active + fast = True + else: + fast = False + + if keyname == "Up" or keyname == "KP_Up": + if self.lathe_mode: + if self.backtool_lathe: + button_name = "x+" + else: + button_name = "x-" + else: + button_name = "y+" + if signal: + self._on_btn_jog_pressed(None, button_name, fast) + else: + self._on_btn_jog_released(None, button_name) + elif keyname == "Down" or keyname == "KP_Down": + if self.lathe_mode: + if self.backtool_lathe: + button_name = "x-" + else: + button_name = "x+" + else: + button_name = "y-" + if signal: + self._on_btn_jog_pressed(None, button_name, fast) + else: + self._on_btn_jog_released(None, button_name) + elif keyname == "Left" or keyname == "KP_Left": + if self.lathe_mode: + button_name = "z-" + else: + button_name = "x-" + if signal: + self._on_btn_jog_pressed(None, button_name, fast) + else: + self._on_btn_jog_released(None, button_name) + elif keyname == "Right" or keyname == "KP_Right": + if self.lathe_mode: + button_name = "z+" + else: + button_name = "x+" + if signal: + self._on_btn_jog_pressed(None, button_name, fast) + else: + self._on_btn_jog_released(None, button_name) + elif keyname == "Page_Up" or keyname == "KP_Page_Up": + button_name = "z+" + if signal: + self._on_btn_jog_pressed(None, button_name, fast) + else: + self._on_btn_jog_released(None, button_name) + elif keyname == "Page_Down" or keyname == "KP_Page_Down": + button_name = "z-" + if signal: + self._on_btn_jog_pressed(None, button_name, fast) + else: + self._on_btn_jog_released(None, button_name) + elif keyname == "I" or keyname == "i": + if signal: + if self.stat.state != 1: # still moving + return + # The active button name is hold in self.active_increment + print(self.active_increment) + rbt = int(self.active_increment.split("_")[1]) + if keyname == "I": + # so lets increment it by one + rbt += 1 + # we check if we are still in the allowed limit + if rbt > len(self.jog_increments) - 1: # beginning from zero + rbt = 0 + else: # must be "i" + # so lets reduce it by one + rbt -= 1 + # we check if we are still in the allowed limit + if rbt < 0: + rbt = len(self.jog_increments) - 1 # beginning from zero + # we set the corresponding button active + self.incr_rbt_dic["rbt_{0}".format(rbt)].set_active(True) + # and we have to update all pin and variables + self._jog_increment_changed(self.incr_rbt_dic["rbt_{0}".format(rbt)]) + else: + print("This key has not been implemented yet") + print "Key {0} ({1:d}) was pressed".format(keyname, event.keyval), signal, self.last_key_event + self.last_key_event = keyname, signal + return True + + # Notification stuff. + def _init_notification(self): + start_as = "rbtn_" + self.prefs.getpref("screen1", "window", str) + xpos, ypos = self.widgets.window1.window.get_origin() + self.notification.set_property('x_pos', self.widgets.adj_x_pos_popup.get_value()) + self.notification.set_property('y_pos', self.widgets.adj_y_pos_popup.get_value()) + self.notification.set_property('message_width', self.widgets.adj_width_popup.get_value()) + if int(self.widgets.adj_max_messages.get_value()) != 0: + self.notification.set_property('max_messages', self.widgets.adj_max_messages.get_value()) + self.notification.set_property('use_frames', self.widgets.chk_use_frames.get_active()) + self.notification.set_property('font', self.widgets.fontbutton_popup.get_font_name()) + self.notification.set_property('icon_size', 48) + self.notification.set_property('top_to_bottom', True) + + def _from_internal_linear_unit(self, v, unit=None): + if unit is None: + unit = self.stat.linear_units + lu = (unit or 1) * 25.4 + return v * lu + + def _parse_increment(self, btn_name): + print("parse_jogincrement") + if self.incr_rbt_dic[btn_name] == self.incr_rbt_dic["rbt_0"]: + jogincr = "0" + else: + jogincr = self.incr_rbt_dic[btn_name].get_label() + + if jogincr.endswith("mm"): + scale = self._from_internal_linear_unit(1 / 25.4) + elif jogincr.endswith("cm"): + scale = self._from_internal_linear_unit(10 / 25.4) + elif jogincr.endswith("um"): + scale = self._from_internal_linear_unit(.001 / 25.4) + elif jogincr.endswith("in") or jogincr.endswith("inch"): + scale = self._from_internal_linear_unit(1.) + elif jogincr.endswith("mil"): + scale = self._from_internal_linear_unit(.001) + else: + scale = 1 + jogincr = jogincr.rstrip(" inchmuil°degr") + if "/" in jogincr: + p, q = jogincr.split("/") + jogincr = float(p) / float(q) + else: + jogincr = float(jogincr) + return jogincr * scale + + def show_try_errors(self): + exc_type, exc_value, exc_traceback = sys.exc_info() + formatted_lines = traceback.format_exc().splitlines() + print(_("**** GMOCCAPY ERROR ****")) + print(_("**** {0} ****").format(formatted_lines[0])) + traceback.print_tb(exc_traceback, limit=1, file=sys.stdout) + print (formatted_lines[-1]) + + def _sensitize_widgets(self, widgetlist, value): + for name in widgetlist: + try: + self.widgets[name].set_sensitive(value) + except Exception, e: + print (_("**** GMOCCAPY ERROR ****")) + print _("**** No widget named: {0} to sensitize ****").format(name) + traceback.print_exc() + + def _update_active_gcodes(self): + # active G codes + active_codes = [] + temp = [] + for code in sorted(self.stat.gcodes[1:]): + if code == -1: + continue + if code % 10 == 0: + temp.append("{0}".format(code / 10)) + else: + temp.append("{0}.{1}".format(code / 10, code % 10)) + for num, code in enumerate(temp): + if num == 8: + active_codes.append("\n") + active_codes.append("G" + code) + self.active_gcodes = active_codes + self.gcodes = self.stat.gcodes + self.widgets.active_gcodes_label.set_label(" ".join(self.active_gcodes)) + + def _update_active_mcodes(self): + # M codes + active_codes = [] + temp = [] + for code in sorted(self.stat.mcodes[1:]): + if code == -1: + continue + temp.append("{0}".format(code)) + for code in (temp): + active_codes.append("M" + code) + self.active_mcodes = active_codes + self.mcodes = self.stat.mcodes + self.widgets.active_mcodes_label.set_label(" ".join(self.active_mcodes)) + + # Update the velocity labels + def _update_vel(self): + # self.stat.program_units will return 1 for inch, 2 for mm and 3 for cm + real_feed = float(self.stat.settings[1] * self.stat.feedrate) + if self.stat.program_units != 1: + self.widgets.lbl_current_vel.set_text("{0:d}".format(int(self.stat.current_vel * 60.0 * self.faktor))) + if "G95" in self.active_gcodes: + feed_str = "{0:d}".format(int(self.stat.settings[1])) + real_feed_str = "F {0:.2f}".format(real_feed) + else: + feed_str = "{0:d}".format(int(self.stat.settings[1])) + real_feed_str = "F {0:d}".format(int(real_feed)) + else: + self.widgets.lbl_current_vel.set_text("{0:.2f}".format(self.stat.current_vel * 60.0 * self.faktor)) + if "G95" in self.active_gcodes: + feed_str = "{0:.4f}".format(self.stat.settings[1]) + real_feed_str = "F {0:.4f}".format(real_feed) + else: + feed_str = "{0:.3f}".format(self.stat.settings[1]) + real_feed_str = "F {0:.3f}".format(real_feed) + + # converting 0.0 to string brings nothing, so the string is empty + # happens only on start up + if not real_feed: + real_feed_str = "F 0" + + self.widgets.lbl_active_feed.set_label(feed_str) + self.widgets.lbl_feed_act.set_text(real_feed_str) + + def _update_coolant(self): + if self.stat.flood: + if not self.widgets.tbtn_flood.get_active(): + self.widgets.tbtn_flood.set_active(True) + self.widgets.tbtn_flood.set_image(self.widgets.img_coolant_on) + else: + if self.widgets.tbtn_flood.get_active(): + self.widgets.tbtn_flood.set_active(False) + self.widgets.tbtn_flood.set_image(self.widgets.img_coolant_off) + if self.stat.mist: + if not self.widgets.tbtn_mist.get_active(): + self.widgets.tbtn_mist.set_active(True) + self.widgets.tbtn_mist.set_image(self.widgets.img_mist_on) + else: + if self.widgets.tbtn_mist.get_active(): + self.widgets.tbtn_mist.set_active(False) + self.widgets.tbtn_mist.set_image(self.widgets.img_mist_off) + + def _update_halui_pin(self): + if self.spindle_override != self.stat.spindle[0]['override']: + self.initialized = False + self.widgets.spc_spindle.set_value(self.stat.spindle[0]['override'] * 100) + self.spindle_override = self.stat.spindle[0]['override'] + self.initialized = True + if self.feed_override != self.stat.feedrate: + self.initialized = False + self.widgets.spc_feed.set_value(self.stat.feedrate * 100) + self.feed_override = self.stat.feedrate + self.initialized = True + if self.rapidrate != self.stat.rapidrate: + self.initialized = False + self.widgets.spc_rapid.set_value(self.stat.rapidrate * 100) + self.rapidrate = self.stat.rapidrate + self.initialized = True + + def _update_slider(self, widgetlist): + # update scales and sliders, this must happen if sliders shows units + for widget in widgetlist: + value = self.widgets[widget].get_value() + min = self.widgets[widget].get_property("min") + max = self.widgets[widget].get_property("max") + + self.widgets[widget].set_property("min", min * self.faktor) + self.widgets[widget].set_property("max", max * self.faktor) + self.widgets[widget].set_value(value * self.faktor) + + self.scale_jog_vel = self.scale_jog_vel * self.faktor + + if "spc_lin_jog_vel" in widgetlist: + if self.widgets.tbtn_turtle_jog.get_active(): + self.turtle_jog = self.turtle_jog * self.faktor + else: + self.rabbit_jog = self.rabbit_jog * self.faktor + + def _change_dro_color(self, property, color): + for dro in self.dro_dic: + self.dro_dic[dro].set_property(property, color) + if self.lathe_mode: + # check if G7 or G8 is active + # this is set on purpose wrong, because we want the periodic + # to update the state correctly + if "G7" in self.active_gcodes: + self.diameter_mode = False + else: + self.diameter_mode = True + + def _update_toolinfo(self, tool): + toolinfo = self.widgets.tooledit1.get_toolinfo(tool) + if toolinfo: + # Doku + # toolinfo[0] = cell toggle + # toolinfo[1] = tool number + # toolinfo[2] = pocket number + # toolinfo[3] = X offset + # toolinfo[4] = Y offset + # toolinfo[5] = Z offset + # toolinfo[6] = A offset + # toolinfo[7] = B offset + # toolinfo[8] = C offset + # toolinfo[9] = U offset + # toolinfo[10] = V offset + # toolinfo[11] = W offset + # toolinfo[12] = tool diameter + # toolinfo[13] = frontangle + # toolinfo[14] = backangle + # toolinfo[15] = tool orientation + # toolinfo[16] = tool info + self.widgets.lbl_tool_no.set_text(str(toolinfo[1])) + self.widgets.lbl_tool_dia.set_text(toolinfo[12]) + self.halcomp["tool-diameter"] = float(locale.atof(toolinfo[12])) + self.widgets.lbl_tool_name.set_text(toolinfo[16]) + + # we do not allow touch off with no tool mounted, so we set the + # corresponding widgets unsensitized and set the description accordingly + if tool <= 0: + self.widgets.lbl_tool_no.set_text("0") + self.widgets.lbl_tool_dia.set_text("0") + self.widgets.lbl_tool_name.set_text(_("No tool description available")) + self.widgets.btn_tool_touchoff_x.set_sensitive(False) + self.widgets.btn_tool_touchoff_z.set_sensitive(False) + else: + self.widgets.btn_tool_touchoff_x.set_sensitive(True) + self.widgets.btn_tool_touchoff_z.set_sensitive(True) + + if self.load_tool: + self.load_tool = False + self.on_hal_status_interp_idle(None) + return + + if "G43" in self.active_gcodes and self.stat.task_mode != linuxcnc.MODE_AUTO: + self.command.mode(linuxcnc.MODE_MDI) + self.command.wait_complete() + self.command.mdi("G43") + self.command.wait_complete() + +# helpers functions end +# ========================================================= + + def on_adj_dro_digits_value_changed(self, widget, data=None): + if not self.initialized: + return + self.dro_digits = int(widget.get_value()) + self.prefs.putpref("dro_digits", self.dro_digits, int) + if self.stat.program_units != 1: + format_string_mm = "%" + str(13 - self.dro_digits) + "." + str(self.dro_digits) + "f" + format_string_inch = "%" + str(13 - self.dro_digits - 1) + "." + str(self.dro_digits + 1) + "f" + else: + format_string_inch = "%" + str(13 - self.dro_digits) + "." + str(self.dro_digits) + "f" + format_string_mm = "%" + str(13 - self.dro_digits + 1) + "." + str(self.dro_digits - 1) + "f" + + for dro in self.dro_dic: + self.dro_dic[dro].set_property("mm_text_template", format_string_mm) + self.dro_dic[dro].set_property("imperial_text_template", format_string_inch) + + def on_chk_toggle_readout_toggled(self, widget, data=None): + state = widget.get_active() + self.prefs.putpref("toggle_readout", state) + self.toggle_readout = state + for dro in self.dro_dic: + self.dro_dic[dro].set_property("toggle_readout", state) + + def _on_DRO_clicked(self, widget, joint, order): + for dro in self.dro_dic: + self.dro_dic[dro].set_order(order) + return + + def _on_DRO_axis_clicked(self, widget, axisletter): + print("DRO Axisletter has been clicked", axisletter) + if axisletter == "r" or axisletter == "d": + axisletter = "x" + self._on_btn_set_value_clicked(None, data=axisletter) + + def _offset_changed(self, pin, tooloffset): + joint = None + for axis in ("x", "z"): + if axis in self.axis_list: + joint = self._get_joint_from_joint_axis_dic(axis) + break + else: + continue + + # no X or Z axis in config, so we can not apply offsets + if joint is None: + self.widgets.lbl_tool_offset_z.hide() + self.widgets.lbl_tool_offset_x.hide() + return + + dro = self.dro_dic["Combi_DRO_{0}".format(joint)] + + if dro.machine_units == _MM: + self.widgets.lbl_tool_offset_z.set_text("{0:.3f}".format(self.halcomp["tooloffset-z"])) + self.widgets.lbl_tool_offset_x.set_text("{0:.3f}".format(self.halcomp["tooloffset-x"])) + else: + self.widgets.lbl_tool_offset_z.set_text("{0:.4f}".format(self.halcomp["tooloffset-z"])) + self.widgets.lbl_tool_offset_x.set_text("{0:.4f}".format(self.halcomp["tooloffset-x"])) + + def on_offsetpage1_selection_changed(self, widget, system, name): + if system not in self.system_list[1:] or self.touch_button_dic["edit_offsets"].get_active(): + self.touch_button_dic["set_active"].set_sensitive(False) + else: + self.touch_button_dic["set_active"].set_sensitive(True) + + def on_adj_x_pos_popup_value_changed(self, widget, data=None): + if not self.initialized: + return + self.prefs.putpref("x_pos_popup", widget.get_value(), float) + self._init_notification() + + def on_adj_y_pos_popup_value_changed(self, widget, data=None): + if not self.initialized: + return + self.prefs.putpref("y_pos_popup", widget.get_value(), float) + self._init_notification() + + def on_adj_width_popup_value_changed(self, widget, data=None): + if not self.initialized: + return + self.prefs.putpref("width_popup", widget.get_value(), float) + self._init_notification() + + def on_adj_max_messages_value_changed(self, widget, data=None): + if not self.initialized: + return + self.prefs.putpref("max_messages", widget.get_value(), float) + self._init_notification() + + def on_chk_use_frames_toggled(self, widget, data=None): + if not self.initialized: + return + self.prefs.putpref("use_frames", widget.get_active()) + self._init_notification() + + def on_fontbutton_popup_font_set(self, font): + self.prefs.putpref("message_font", self.widgets.fontbutton_popup.get_font_name()) + self._init_notification() + + def on_btn_launch_test_message_pressed(self, widget=None, data=None): + index = len(self.notification.messages) + text = _("Halo, welcome to the test message {0}").format(index) + self._show_error((13, text)) + + def on_chk_turtle_jog_toggled(self, widget, data=None): + state = widget.get_active() + self.prefs.putpref("hide_turtle_jog_button", state) + self.widgets.tbl_turtle_jog_factor.set_sensitive(not state) + if state: + self.widgets.tbtn_turtle_jog.hide() + else: + self.widgets.tbtn_turtle_jog.show() + self.turtle_jog_factor = self.prefs.getpref('turtle_jog_factor', 20, int) + self.widgets.adj_turtle_jog_factor.configure(self.turtle_jog_factor, 1, + 100, 1, 0, 0) + self.turtle_jog = self.jog_rate_max / self.turtle_jog_factor + + def on_adj_turtle_jog_factor_value_changed(self, widget, data=None): + if not self.initialized: + return + self.turtle_jog_factor = int(widget.get_value()) + self.prefs.putpref("turtle_jog_factor", self.turtle_jog_factor, int) + self.turtle_jog = self.rabbit_jog / self.turtle_jog_factor + if self.widgets.tbtn_turtle_jog.get_active(): + self.widgets.spc_lin_jog_vel.set_property("min", 0) + self.widgets.spc_lin_jog_vel.set_property("max", self.jog_rate_max / self.turtle_jog_factor) + self.widgets.spc_lin_jog_vel.set_value(self.turtle_jog) + + def on_tbtn_turtle_jog_toggled( self, widget, data = None ): + # due to imperial and metric options we have to get first the values of the widget + max = self.widgets.spc_lin_jog_vel.max + min = self.widgets.spc_lin_jog_vel.min + value = self.widgets.spc_lin_jog_vel.get_value() + + if widget.get_active(): + self.rabbit_jog = value + widget.set_image( self.widgets.img_turtle_jog ) + self.widgets.spc_lin_jog_vel.set_property("min", min) + self.widgets.spc_lin_jog_vel.set_property("max", max / self.turtle_jog_factor) + self.widgets.spc_lin_jog_vel.set_value(self.turtle_jog) + else: + self.turtle_jog = value + widget.set_image( self.widgets.img_rabbit_jog ) + self.widgets.spc_lin_jog_vel.set_property("min", min) + self.widgets.spc_lin_jog_vel.set_property("max", max * self.turtle_jog_factor) + self.widgets.spc_lin_jog_vel.set_value(self.rabbit_jog) + + def _on_pin_turtle_jog(self, pin): + self.widgets.tbtn_turtle_jog.set_active(pin.get()) + + # use the current loaded file to be loaded on start up + def on_btn_use_current_clicked(self, widget, data=None): + if self.stat.file: + self.widgets.file_to_load_chooser.set_filename(self.stat.file) + self.prefs.putpref("open_file", self.stat.file) + + # Clear the status to load a file on start up, so there will not be loaded a program + # on the next start of the GUI + def on_btn_none_clicked(self, widget, data=None): + self.widgets.file_to_load_chooser.set_filename(" ") + self.prefs.putpref("open_file", " ") + + def on_ntb_main_switch_page(self, widget, page, page_num, data=None): + if self.widgets.tbtn_setup.get_active(): + if page_num != 1L: # setup page is active, + self.widgets.tbtn_setup.set_active(False) + + def on_tbtn_setup_toggled(self, widget, data=None): + # first we set to manual mode, as we do not allow changing settings in other modes + # otherwise external halui commands could start a program while we are in settings + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + + if widget.get_active(): + # deactivate the mode buttons, so changing modes is not possible while we are in settings mode + self.widgets.rbt_manual.set_sensitive(False) + self.widgets.rbt_mdi.set_sensitive(False) + self.widgets.rbt_auto.set_sensitive(False) + code = False + # here the user don"t want an unlock code + if self.widgets.rbt_no_unlock.get_active(): + code = True + # if hal pin is true, we are allowed to enter settings, this may be + # realized using a key switch + if self.widgets.rbt_hal_unlock.get_active() and self.halcomp["unlock-settings"]: + 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 + # Lets see if the user has the right to enter settings + if code: + 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) + else: + 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) + else: + # check witch button should be sensitive, depending on the state of the machine + if self.stat.task_state == linuxcnc.STATE_ESTOP: + # estopped no mode available + self.widgets.rbt_manual.set_sensitive(False) + self.widgets.rbt_mdi.set_sensitive(False) + self.widgets.rbt_auto.set_sensitive(False) + if (self.stat.task_state == linuxcnc.STATE_ON) and not self.all_homed: + # machine on, but not homed, only manual allowed + self.widgets.rbt_manual.set_sensitive(True) + self.widgets.rbt_mdi.set_sensitive(False) + self.widgets.rbt_auto.set_sensitive(False) + if (self.stat.task_state == linuxcnc.STATE_ON) and (self.all_homed or self.no_force_homing): + # all OK, make all modes available + self.widgets.rbt_manual.set_sensitive(True) + self.widgets.rbt_mdi.set_sensitive(True) + self.widgets.rbt_auto.set_sensitive(True) + # this is needed here, because we do not + # change mode, so on_hal_status_manual will not be called + self.widgets.ntb_main.set_current_page(0) + self.widgets.ntb_button.set_current_page(_BB_MANUAL) + self.widgets.ntb_info.set_current_page(0) + self.widgets.ntb_jog.set_current_page(0) + + # if we are in user tabs, we must reset the button + if self.widgets.tbtn_user_tabs.get_active(): + self.widgets.tbtn_user_tabs.set_active(False) + + # Show or hide the user tabs + def on_tbtn_user_tabs_toggled(self, widget, data=None): + if widget.get_active(): + self.widgets.ntb_main.set_current_page(2) + self.widgets.tbtn_fullsize_preview0.set_sensitive(False) + else: + self.widgets.ntb_main.set_current_page(0) + self.widgets.tbtn_fullsize_preview0.set_sensitive(True) + +# ========================================================= +# The homing functions + def on_btn_homing_clicked(self, widget, data=None): + self.widgets.ntb_button.set_current_page(_BB_HOME) + + def on_btn_home_all_clicked(self, widget, data=None): + self._set_motion_mode(0) + # home -1 means all + self.command.home(-1) + + def _on_btn_unhome_clicked(self, widget): + self._set_motion_mode(0) + # -1 for all + self.command.unhome(-1) + + def _on_btn_home_back_clicked(self, widget): + self.widgets.ntb_button.set_current_page(_BB_MANUAL) + self.widgets.ntb_main.set_current_page(0) + self.widgets.ntb_preview.set_current_page(0) + + def _on_btn_home_clicked(self, widget): + # home axis or joint? + print("on button home clicked = ", widget.name) + if "axis" in widget.name: + value = widget.name[-1] + # now get the joint from directory by the value + joint_or_axis = self._get_joint_from_joint_axis_dic(value) + elif "joint" in widget.name: + joint_or_axis = int(widget.name[-1]) + elif "all" in widget.name: + joint_or_axis = -1 + + self._set_motion_mode(0) + self.command.home(joint_or_axis) + + def _unhome_signal(self, object, joint): + self._set_motion_mode(0) + self.all_homed = False + # -1 for all + self.command.unhome(joint) + + def _set_motion_mode(self, state): + # 1:teleop, 0: joint + self.command.teleop_enable(state) + self.command.wait_complete() + +# The homing functions +# ========================================================= + + def _check_limits(self): + for axis in self.axis_list: + axisnumber = "xyzabcuvw".index(axis) + if self.stat.limit[axisnumber] != 0: + return True + if self.widgets.chk_ignore_limits.get_active(): + self.widgets.chk_ignore_limits.set_active(False) + return False + + def _ignore_limits(self, pin): + self.widgets.chk_ignore_limits.set_active(pin.get()) + + def on_chk_ignore_limits_toggled(self, widget, data=None): + if self.widgets.chk_ignore_limits.get_active(): + if not self._check_limits(): + self._show_error((11, _("ERROR : No limit switch is active, ignore limits will not be set."))) + return + self.command.override_limits() + + def on_tbtn_fullsize_preview_toggled(self, widget, data=None): + name = gtk.Buildable.get_name(widget) + if name[-1] == "0": + name_2 = name[:-1] + "1" + else: + name_2 = name[:-1] + "0" + print("tbtn_fullsize_toggled", name, name_2) + state = widget.get_active() + if state: + self.widgets.box_info.hide() + self.widgets.vbx_jog.hide() + dro = self.dro_dic[self.dro_dic.keys()[0]] + self.widgets.gremlin.set_property("metric_units", dro.metric_units) + self.widgets.gremlin.set_property("enable_dro", True) + if self.lathe_mode: + self.widgets.gremlin.set_property("show_lathe_radius", not self.diameter_mode) + else: + self.widgets.box_info.show() + self.widgets.vbx_jog.show() + if not self.widgets.chk_show_dro.get_active(): + self.widgets.gremlin.set_property("enable_dro", self.enable_gremlin_dro) + self.widgets[name_2].set_active(state) + +# ========================================================= +# this are hal-tools copied from gsreen function + def on_btn_show_hal_clicked(self, widget, data=None): + p = os.popen("tclsh {0}/bin/halshow.tcl &".format(TCLPATH)) + + def on_btn_calibration_clicked(self, widget, data=None): + p = os.popen("tclsh {0}/bin/emccalib.tcl -- -ini {1} > /dev/null &".format(TCLPATH, sys.argv[2]), "w") + + def on_btn_hal_meter_clicked(self, widget, data=None): + p = os.popen("halmeter &") + + def on_btn_status_clicked(self, widget, data=None): + p = os.popen("linuxcnctop > /dev/null &", "w") + + def on_btn_hal_scope_clicked(self, widget, data=None): + p = os.popen("halscope > /dev/null &", "w") + + 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")) + +# ========================================================= +# spindle stuff + + def _update_spindle(self): + if self.stat.spindle[0]['direction'] > 0: + self.widgets.rbt_forward.set_active(True) + elif self.stat.spindle[0]['direction'] < 0: + self.widgets.rbt_reverse.set_active(True) + elif not self.widgets.rbt_stop.get_active(): + self.widgets.rbt_stop.set_active(True) + + # this is needed, because otherwise a command S0 would not set active btn_stop + if not abs(self.stat.spindle[0]['speed']): + self.widgets.rbt_stop.set_active(True) + return + + # set the speed label in active code frame + if self.stat.spindle[0]['speed'] == 0: + speed = self.stat.settings[2] + else: + speed = self.stat.spindle[0]['speed'] + self.widgets.active_speed_label.set_label("{0:.0f}".format(abs(speed))) + self.widgets.lbl_spindle_act.set_text("S {0}".format(int(speed * self.spindle_override))) + + def _update_vc(self): + if self.stat.spindle[0]['direction'] != 0: + if self.stat.spindle[0]['speed'] == 0: + speed = self.stat.settings[2] + else: + speed = self.stat.spindle[0]['speed'] + + if not self.lathe_mode: + diameter = self.halcomp["tool-diameter"] + else: + diameter = int(self.dro_dic["Combi_DRO_0"].get_position()[1] * 2) + speed = self.widgets.spindle_feedback_bar.value + vc = abs(int(speed * self.spindle_override) * diameter * 3.14 / 1000) + else: + vc = 0 + if vc >= 100: + text = "Vc= {0:d}".format(int(vc)) + elif vc >= 10: + text = "Vc= {0:2.1f}".format(vc) + else: + text = "Vc= {0:.2f}".format(vc) + self.widgets.lbl_vc.set_text(text) + + def on_rbt_forward_clicked(self, widget, data=None): + if widget.get_active(): + widget.set_image(self.widgets.img_forward_on) + self._set_spindle("forward") + else: + self.widgets.rbt_forward.set_image(self.widgets.img_forward) + + def on_rbt_reverse_clicked(self, widget, data=None): + if widget.get_active(): + widget.set_image(self.widgets.img_reverse_on) + self._set_spindle("reverse") + else: + widget.set_image(self.widgets.img_reverse) + + def on_rbt_stop_clicked(self, widget, data=None): + if widget.get_active(): + widget.set_image(self.widgets.img_stop_on) + self._set_spindle("stop") + else: + self.widgets.rbt_stop.set_image(self.widgets.img_sstop) + + def _set_spindle(self, command): + # if we are in estop state, we will have to leave here, otherwise + # we get an error, that switching spindle off is not allowed with estop + if self.stat.task_state == linuxcnc.STATE_ESTOP: + return + + # if we do not check this, we will get an error in auto mode and sub + # calls from MDI containing i.e. G96 would not run, as the speed will + # be setted to the commanded value due the next code part + if self.stat.task_mode != linuxcnc.MODE_MANUAL: + if self.stat.interp_state == linuxcnc.INTERP_READING or self.stat.interp_state == linuxcnc.INTERP_WAITING: + if self.stat.spindle[0]['direction'] > 0: + self.widgets.rbt_forward.set_sensitive(True) + self.widgets.rbt_reverse.set_sensitive(False) + self.widgets.rbt_stop.set_sensitive(False) + elif self.stat.spindle[0]['direction'] < 0: + self.widgets.rbt_forward.set_sensitive(False) + self.widgets.rbt_reverse.set_sensitive(True) + self.widgets.rbt_stop.set_sensitive(False) + else: + self.widgets.rbt_forward.set_sensitive(False) + self.widgets.rbt_reverse.set_sensitive(False) + self.widgets.rbt_stop.set_sensitive(True) + return + + rpm = self._check_spindle_range() + # as the commanded value will be multiplied with speed override, + # we take care of that but we have to check for speed override + # to not be zero to avoid division by zero error + try: + rpm_out = rpm / self.stat.spindle[0]['override'] + except: + rpm_out = 0 + self.widgets.lbl_spindle_act.set_label("S {0}".format(int(rpm))) + + if command == "stop": + # documentation of self.command.spindle() + # linuxcnc.spindle(direction, speed, spindle=0) + self.command.spindle(0) + self.widgets.lbl_spindle_act.set_label("S 0") + elif command == "forward": + self.command.spindle(1, rpm_out) + elif command == "reverse": + self.command.spindle(-1, rpm_out) + else: + print(_("Something went wrong, we have an unknown spindle widget {0}").format(command)) + + def _check_spindle_range(self): + rpm = (self.stat.settings[2]) + if rpm == 0: + rpm = self.spindle_start_rpm + + spindle_override = self.widgets.spc_spindle.get_value() / 100 + real_spindle_speed = rpm * spindle_override + + if real_spindle_speed > self.max_spindle_rev: + real_spindle_speed = self.max_spindle_rev + elif real_spindle_speed < self.min_spindle_rev: + real_spindle_speed = self.min_spindle_rev + return real_spindle_speed + + def on_btn_spindle_100_clicked(self, widget, data=None): + self.widgets.spc_spindle.set_value(100) + + def on_spc_spindle_value_changed(self, widget, data=None): + if not self.initialized: + return + # this is in a try except, because on initializing the window the values are still zero + # so we would get an division / zero error + real_spindle_speed = 0 + value = widget.get_value() + try: + if not abs(self.stat.settings[2]): + if self.widgets.rbt_forward.get_active() or self.widgets.rbt_reverse.get_active(): + speed = self.stat.spindle[0]['speed'] + else: + speed = 0 + else: + speed = abs(self.stat.spindle[0]['speed']) + spindle_override = value / 100 + real_spindle_speed = speed * spindle_override + if real_spindle_speed > self.max_spindle_rev: + value_to_set = value / (real_spindle_speed / self.max_spindle_rev) + real_spindle_speed = self.max_spindle_rev + elif real_spindle_speed < self.min_spindle_rev: + value_to_set = value / (real_spindle_speed / self.min_spindle_rev) + real_spindle_speed = self.min_spindle_rev + else: + value_to_set = spindle_override * 100 + widget.set_value(value_to_set) + self.spindle_override = value_to_set / 100 + self.command.spindleoverride(value_to_set / 100) + except: + pass + + def on_adj_start_spindle_RPM_value_changed(self, widget, data=None): + self.spindle_start_rpm = widget.get_value() + self.prefs.putpref("spindle_start_rpm", self.spindle_start_rpm, float) + + def on_adj_spindle_bar_min_value_changed(self, widget, data=None): + self.min_spindle_rev = widget.get_value() + self.prefs.putpref("spindle_bar_min", self.min_spindle_rev, float) + self.widgets.spindle_feedback_bar.set_property("min", self.min_spindle_rev) + + def on_adj_spindle_bar_max_value_changed(self, widget, data=None): + self.max_spindle_rev = widget.get_value() + self.prefs.putpref("spindle_bar_max", self.max_spindle_rev, float) + self.widgets.spindle_feedback_bar.set_property("max", self.max_spindle_rev) + +# ========================================================= +# Coolant an mist coolant button + def on_tbtn_flood_toggled(self, widget, data=None): + if self.stat.flood and self.widgets.tbtn_flood.get_active(): + return + elif not self.stat.flood and not self.widgets.tbtn_flood.get_active(): + return + elif self.widgets.tbtn_flood.get_active(): + self.widgets.tbtn_flood.set_image(self.widgets.img_coolant_on) + self.command.flood(linuxcnc.FLOOD_ON) + else: + self.widgets.tbtn_flood.set_image(self.widgets.img_coolant_off) + self.command.flood(linuxcnc.FLOOD_OFF) + + def on_tbtn_mist_toggled(self, widget, data=None): + if self.stat.mist and self.widgets.tbtn_mist.get_active(): + return + elif not self.stat.mist and not self.widgets.tbtn_mist.get_active(): + return + elif self.widgets.tbtn_mist.get_active(): + self.widgets.tbtn_mist.set_image(self.widgets.img_mist_on) + self.command.mist(linuxcnc.MIST_ON) + else: + self.widgets.tbtn_mist.set_image(self.widgets.img_mist_off) + self.command.mist(linuxcnc.MIST_OFF) + +# ========================================================= +# feed stuff + def on_spc_feed_value_changed(self, widget, data=None): + if not self.initialized: + return + value = widget.get_value() / 100 + self.feed_override = value + self.command.feedrate(value) + + def on_btn_feed_100_clicked(self, widget, data=None): + self.widgets.spc_feed.set_value(100) + + def on_spc_rapid_value_changed(self, widget, data=None): + if not self.initialized: + return + value = widget.get_value() / 100 + self.rapidrate = value + self.command.rapidrate(value) + + # this are the MDI thinks we need + 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") + message += _("delete the listbox entries for this session") + result = self.dialogs.yesno_dialog(self, message, _("Attention!!")) + if result: + self.widgets.hal_mdihistory.model.clear() + + def on_btn_show_kbd_clicked(self, widget): + #print("show Keyboard clicked", self.widgets.key_box.get_children()) + #print(widget) + #print(widget.name) + #print(widget.get_children()[0]) + #print(widget.get_children()[0].get_property("file")) + + # special case if we are in mdi mode + if self.widgets.ntb_button.get_current_page() == _BB_MDI and self.stat.interp_state != linuxcnc.INTERP_IDLE: + self.command.abort() + self.command.wait_complete() + for pos in self.macro_dic: + self.macro_dic[pos].set_sensitive(True) + if self.onboard: + self._change_kbd_image("keyboard") + #self.socket.show_all() # This is needed, because after a rezise the keyboard is not visible for unknown reasons + else: + self.macro_dic["keyboard"].set_sensitive(False) + elif self.widgets.ntb_info.get_current_page() == 1: + self.widgets.ntb_info.set_current_page(0) + else: + self.widgets.ntb_info.set_current_page(1) + + # special case if we are in edit mode + if self.widgets.ntb_button.get_current_page() == _BB_EDIT: + if self.widgets.ntb_info.get_visible(): + self.widgets.box_info.set_size_request(-1, 50) + self.widgets.ntb_info.hide() + else: + self.widgets.box_info.set_size_request(-1, 250) + self.widgets.ntb_info.show() + + def on_ntb_info_switch_page(self, widget, page, page_num, data=None): + if self.stat.task_mode == linuxcnc.MODE_MDI: + self.widgets.hal_mdihistory.entry.grab_focus() + elif self.stat.task_mode == linuxcnc.MODE_AUTO: + self.widgets.gcode_view.grab_focus() + + # Three back buttons to be able to leave notebook pages + # All use the same callback offset + 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 + self.widgets.ntb_button.set_current_page(_BB_AUTO) + if self.widgets.tbtn_fullsize_preview0.get_active(): + self.widgets.vbx_jog.set_visible(False) + elif self.widgets.ntb_button.get_current_page() == _BB_LOAD_FILE: # File selection mode + self.widgets.ntb_button.set_current_page(_BB_AUTO) + else: # else we go to main button on manual + self.widgets.ntb_button.set_current_page(_BB_MANUAL) + self.widgets.ntb_main.set_current_page(0) + self.widgets.ntb_preview.set_current_page(0) + + # The offset settings, set to zero + def on_btn_touch_clicked(self, widget, data=None): + self.widgets.ntb_button.set_current_page(_BB_TOUCH_OFF) + self._show_offset_tab(True) + if self.widgets.rbtn_show_preview.get_active(): + self.widgets.ntb_preview.set_current_page(0) + + def on_tbtn_edit_offsets_toggled(self, widget, data=None): + state = widget.get_active() + self.widgets.offsetpage1.edit_button.set_active(state) + self.widgets.ntb_preview.set_current_page(1) + + if self.widgets.rbtn_show_preview.get_active() and not state: + self.widgets.ntb_preview.set_current_page(0) + + widgetlist = ["ntb_jog", "rbt_mdi","rbt_auto","tbtn_setup"] + + if self.widgets.tbtn_user_tabs.get_sensitive(): + widgetlist.append("tbtn_user_tabs") + self._sensitize_widgets( widgetlist, not state ) + + for element in self.touch_button_dic: + if self.touch_button_dic[element].name in ["edit_offsets", "touch_back"]: + continue + self.touch_button_dic[element].set_sensitive(not state) + + # if no system is selected we will set the button not sensitive + system, name = self.widgets.offsetpage1.get_selected() + if not system: + self.touch_button_dic["set_active"].set_sensitive(False) + + if not state: # we must switch back to manual mode, otherwise jogging is not possible + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + + # show virtual keyboard? + if state and self.widgets.chk_use_kb_on_offset.get_active(): + self.widgets.ntb_info.set_current_page(1) + self.widgets.ntb_preview.set_current_page(1) + + def on_btn_zero_g92_clicked(self, widget, data=None): + self.command.mode(linuxcnc.MODE_MDI) + self.command.wait_complete() + self.command.mdi("G92.1") + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + self.widgets.btn_touch.emit("clicked") + + def _on_btn_set_value_clicked(self, widget, data=None): + if not self.stat.task_state == linuxcnc.STATE_ON or not (self.all_homed or self.no_force_homing): + return + + if widget: + axis = widget.name[-1] + else: + axis = data + + print("touch button clicked ", axis) + + 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"), + 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"), + 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), + label=_("Set axis {0} to:").format(axis), integer=False) + if offset == "CANCEL": + return + elif offset == "ERROR": + print(_("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")) + else: + self.command.mode(linuxcnc.MODE_MDI) + self.command.wait_complete() + command = "G10 L20 P0 {0}{1:f}".format(axis, offset) + self.command.mdi(command) + self.widgets.hal_action_reload.emit("activate") + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + self.prefs.putpref("offset_axis_{0}".format(axis), offset, float) + + def _on_btn_set_selected_clicked(self, widget, data=None): + system, name = self.widgets.offsetpage1.get_selected() + if not system: + message = _("you did not selected a system to be changed to, so nothing will be changed") + self.dialogs.warning_dialog(self, _("Important Warning!"), message) + return + if system == self.system_list[self.stat.g5x_index]: + return + else: + self.command.mode(linuxcnc.MODE_MDI) + self.command.wait_complete() + self.command.mdi(system) + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + + def on_spbtn_probe_height_value_changed(self, widget, data=None): + self.halcomp["probeheight"] = widget.get_value() + self.prefs.putpref("probeheight", widget.get_value(), float) + + def on_spbtn_search_vel_value_changed(self, widget, data=None): + self.halcomp["searchvel"] = widget.get_value() + self.prefs.putpref("searchvel", widget.get_value(), float) + + def on_spbtn_probe_vel_value_changed(self, widget, data=None): + self.halcomp["probevel"] = widget.get_value() + self.prefs.putpref("probevel", widget.get_value(), float) + + def on_chk_use_tool_measurement_toggled(self, widget, data=None): + if widget.get_active(): + self.widgets.eb_blockheight_label.show() + self.widgets.frm_probe_pos.set_sensitive(True) + self.widgets.frm_probe_vel.set_sensitive(True) + self.halcomp["toolmeasurement"] = True + self.halcomp["searchvel"] = self.widgets.spbtn_search_vel.get_value() + self.halcomp["probevel"] = self.widgets.spbtn_probe_vel.get_value() + self.halcomp["probeheight"] = self.widgets.spbtn_probe_height.get_value() + else: + self.widgets.eb_blockheight_label.hide() + self.widgets.frm_probe_pos.set_sensitive(False) + self.widgets.frm_probe_vel.set_sensitive(False) + self.halcomp["toolmeasurement"] = False + self.halcomp["searchvel"] = 0.0 + self.halcomp["probevel"] = 0.0 + self.halcomp["probeheight"] = 0.0 + self.prefs.putpref("use_toolmeasurement", widget.get_active()) + + def on_chk_reload_tool_toggled(self, widget, data=None): + state = widget.get_active() + self.reload_tool_enabled = state + self.prefs.putpref("reload_tool", state) + + 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"), + label=_("Block height measured from base table"), integer=False) + + if blockheight == "CANCEL" or blockheight == "ERROR": + return + if blockheight != False or blockheight == 0: + self.halcomp["blockheight"] = blockheight + self.halcomp["probeheight"] = probeheight + self.prefs.putpref("blockheight", blockheight, float) + self.prefs.putpref("probeheight", probeheight, float) + 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")) + + # set coordinate system to new origin + origin = self.get_ini_info.get_axis_2_min_limit() + blockheight + self.command.mode(linuxcnc.MODE_MDI) + self.command.wait_complete() + self.command.mdi("G10 L2 P0 Z{0}".format(origin)) + self.widgets.hal_action_reload.emit("activate") + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + + # choose a theme to aply + def on_theme_choice_changed(self, widget): + theme = widget.get_active_text() + if theme == None: + return + self.prefs.putpref('gtk_theme', theme) + settings = gtk.settings_get_default() + if theme == "Follow System Theme": + theme = self.default_theme + settings.set_string_property("gtk-theme-name", theme, "") + + def on_rbt_unlock_toggled(self, widget, data=None): + if widget.get_active(): + if widget == self.widgets.rbt_use_unlock: + self.prefs.putpref("unlock_way", "use") + elif widget == self.widgets.rbt_no_unlock: + self.prefs.putpref("unlock_way", "no") + else: + self.prefs.putpref("unlock_way", "hal") + + def on_rbtn_run_from_line_toggled(self, widget, data=None): + if widget.get_active(): + if widget == self.widgets.rbtn_no_run_from_line: + self.prefs.putpref("run_from_line", "no_run") + self.widgets.btn_from_line.set_sensitive(False) + else: # widget == self.widgets.rbtn_run_from_line: + self.prefs.putpref("run_from_line", "run") + self.widgets.btn_from_line.set_sensitive(True) + + def on_chk_use_kb_on_offset_toggled(self, widget, data=None): + self.prefs.putpref("show_keyboard_on_offset", widget.get_active()) + + def on_chk_use_kb_on_tooledit_toggled(self, widget, data=None): + self.prefs.putpref("show_keyboard_on_tooledit", widget.get_active()) + + def on_chk_use_kb_on_edit_toggled(self, widget, data=None): + self.prefs.putpref("show_keyboard_on_edit", widget.get_active()) + + def on_chk_use_kb_on_mdi_toggled(self, widget, data=None): + self.prefs.putpref("show_keyboard_on_mdi", widget.get_active()) + + def on_chk_use_kb_on_file_selection_toggled(self, widget, data=None): + self.prefs.putpref("show_keyboard_on_file_selection", widget.get_active()) + + def on_chk_use_kb_shortcuts_toggled(self, widget, data=None): + self.prefs.putpref("use_keyboard_shortcuts", widget.get_active()) + + def on_rbtn_show_preview_toggled(self, widget, data=None): + self.prefs.putpref("show_preview_on_offset", widget.get_active()) + + def on_adj_scale_jog_vel_value_changed(self, widget, data=None): + self.prefs.putpref("scale_jog_vel", widget.get_value(), float) + self.scale_jog_vel = widget.get_value() + + def on_adj_scale_feed_override_value_changed(self, widget, data=None): + self.prefs.putpref("scale_feed_override", widget.get_value(), float) + self.scale_feed_override = widget.get_value() + + def on_adj_scale_rapid_override_value_changed(self, widget, data=None): + self.prefs.putpref("scale_rapid_override", widget.get_value(), float) + self.scale_rapid_override = widget.get_value() + + def on_adj_scale_spindle_override_value_changed(self, widget, data=None): + self.prefs.putpref("scale_spindle_override", widget.get_value(), float) + self.scale_spindle_override = widget.get_value() + + def on_rbtn_fullscreen_toggled(self, widget): + if widget.get_active(): + self.widgets.window1.fullscreen() + self.prefs.putpref("screen1", "fullscreen") + else: + self.widgets.window1.unfullscreen() + + def on_rbtn_maximized_toggled(self, widget): + if widget.get_active(): + self.widgets.window1.maximize() + self.prefs.putpref("screen1", "maximized") + else: + self.widgets.window1.unmaximize() + + def on_rbtn_window_toggled(self, widget): + self.widgets.spbtn_x_pos.set_sensitive(widget.get_active()) + self.widgets.spbtn_y_pos.set_sensitive(widget.get_active()) + self.widgets.spbtn_width.set_sensitive(widget.get_active()) + self.widgets.spbtn_height.set_sensitive(widget.get_active()) + # we have to check also if the window is active, because the button is toggled the first time + # before the window is shown + if widget.get_active() and self.widgets.window1.is_active(): + self.widgets.window1.move(self.xpos, self.ypos) + self.widgets.window1.resize(self.width, self.height) + self.prefs.putpref("screen1", "window") + + def on_adj_x_pos_value_changed(self, widget, data=None): + if not self.initialized: + return + value = int(widget.get_value()) + self.prefs.putpref("x_pos", value, float) + self.xpos = value + self.widgets.window1.move(value, self.ypos) + + def on_adj_y_pos_value_changed(self, widget, data=None): + if not self.initialized: + return + value = int(widget.get_value()) + self.prefs.putpref("y_pos", value, float) + self.ypos = value + self.widgets.window1.move(self.xpos, value) + + def on_adj_width_value_changed(self, widget, data=None): + if not self.initialized: + return + value = int(widget.get_value()) + self.prefs.putpref("width", value, float) + self.width = value + self.widgets.window1.resize(value, self.height) + + def on_adj_height_value_changed(self, widget, data=None): + if not self.initialized: + return + value = int(widget.get_value()) + self.prefs.putpref("height", value, float) + self.height = value + self.widgets.window1.resize(self.width, value) + + def on_adj_dro_size_value_changed(self, widget, data=None): + value = int(widget.get_value()) + self.prefs.putpref("dro_size", value, int) + self.dro_size = value + + for dro in self.dro_dic: + size = self.dro_size + self.dro_dic[dro].set_property("font_size", size) + + def on_chk_hide_cursor_toggled(self, widget, data=None): + self.prefs.putpref("hide_cursor", widget.get_active()) + self.hide_cursor = widget.get_active() + if widget.get_active(): + self.widgets.window1.window.set_cursor(INVISABLE) + else: + self.widgets.window1.window.set_cursor(None) + self.abs_color = self.prefs.getpref("abs_color", "blue", str) + self.rel_color = self.prefs.getpref("rel_color", "black", str) + self.dtg_color = self.prefs.getpref("dtg_color", "yellow", str) + self.homed_color = self.prefs.getpref("homed_color", "green", str) + self.unhomed_color = self.prefs.getpref("unhomed_color", "red", str) + + def on_rel_colorbutton_color_set(self, widget): + color = widget.get_color() + self.prefs.putpref('rel_color', color) + self._change_dro_color("rel_color", color) + self.rel_color = str(color) + + def on_abs_colorbutton_color_set(self, widget): + color = widget.get_color() + self.prefs.putpref('abs_color', widget.get_color()) + self._change_dro_color("abs_color", color) + self.abs_color = str(color) + + def on_dtg_colorbutton_color_set(self, widget): + color = widget.get_color() + self.prefs.putpref('dtg_color', widget.get_color()) + self._change_dro_color("dtg_color", color) + self.dtg_color = str(color) + + def on_homed_colorbtn_color_set(self, widget): + color = widget.get_color() + self.prefs.putpref('homed_color', widget.get_color()) + self._change_dro_color("homed_color", color) + self.homed_color = str(color) + + def on_unhomed_colorbtn_color_set(self, widget): + color = widget.get_color() + self.prefs.putpref('unhomed_color', widget.get_color()) + self._change_dro_color("unhomed_color", color) + self.unhomed_color = str(color) + + def on_file_to_load_chooser_file_set(self, widget): + self.prefs.putpref("open_file", widget.get_filename()) + + def on_jump_to_dir_chooser_file_set(self, widget, data=None): + path = widget.get_filename() + self.prefs.putpref("jump_to_dir", path) + self.widgets.IconFileSelection1.set_property("jump_to_dir", path) + + def on_grid_size_value_changed(self, widget, data=None): + self.widgets.gremlin.set_property('grid_size', widget.get_value()) + self.prefs.putpref('grid_size', widget.get_value(), float) + + def on_tbtn_log_actions_toggled(self, widget, data=None): + self.prefs.putpref("log_actions", widget.get_active()) + + def on_chk_show_dro_toggled(self, widget, data=None): + state = widget.get_active() + self.widgets.gremlin.set_property("metric_units", self.dro_dic["Combi_DRO_0"].metric_units) + self.widgets.gremlin.set_property("enable_dro", state) + self.prefs.putpref("enable_dro", state) + self.enable_gremlin_dro = state + self.widgets.chk_show_offsets.set_sensitive(state) + self.widgets.chk_show_dtg.set_sensitive(state) + + def on_chk_show_dtg_toggled(self, widget, data=None): + state = widget.get_active() + self.widgets.gremlin.set_property("show_dtg", state) + self.prefs.putpref("show_dtg", state) + + def on_chk_show_offsets_toggled(self, widget, data=None): + state = widget.get_active() + self.widgets.gremlin.show_offsets = state + self.prefs.putpref("show_offsets", state) + + def on_cmb_mouse_button_mode_changed(self, widget): + index = widget.get_active() + self.widgets.gremlin.set_property("mouse_btn_mode", index) + self.prefs.putpref("mouse_btn_mode", index, int) + +# ========================================================= +# tool stuff + # This is used to reload the tool in spindle after starting the GUI + # This is called from the all_homed_signal + def reload_tool(self): + tool_to_load = self.prefs.getpref("tool_in_spindle", 0, int) + if tool_to_load == 0: + return + self.load_tool = True + self.tool_change = True + + self.command.mode(linuxcnc.MODE_MDI) + self.command.wait_complete() + + command = "M61 Q {0} G43".format(tool_to_load) + self.command.mdi(command) + self.command.wait_complete() + + def on_btn_tool_clicked(self, widget, data=None): + if self.widgets.tbtn_fullsize_preview0.get_active(): + self.widgets.tbtn_fullsize_preview0.set_active(False) +# self.widgets.tbtn_fullsize_preview1.set_active(False) + self.widgets.ntb_button.set_current_page(_BB_TOOL) + self._show_tooledit_tab(True) + + # Here we create a manual tool change dialog + def on_tool_change(self, widget): + change = self.halcomp['toolchange-change'] + toolnumber = self.halcomp['toolchange-number'] + if change: + # if toolnumber = 0 we will get an error because we will not be able to get + # any tool description, so we avoid that case + if toolnumber == 0: + message = _("Please remove the mounted tool and press OK when done") + else: + tooldescr = self.widgets.tooledit1.get_toolinfo(toolnumber)[16] + message = _("Please change to tool\n\n# {0:d} {1}\n\n then click OK.").format(toolnumber, tooldescr) + result = self.dialogs.warning_dialog(self, message, title=_("Manual Tool change")) + if result: + self.halcomp["toolchange-changed"] = True + else: + print"toolchange abort", 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) + else: + self.halcomp['toolchange-changed'] = False + + def on_btn_delete_tool_clicked(self, widget, data=None): + act_tool = self.stat.tool_in_spindle + if act_tool == self.widgets.tooledit1.get_selected_tool(): + 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.warning_dialog(self, _("Warning Tool can not be deleted!"), message) + return + + self.widgets.tooledit1.delete(None) + self.widgets.tooledit1.set_selected_tool(act_tool) + + def on_btn_add_tool_clicked(self, widget, data=None): + self.widgets.tooledit1.add(None) + + def on_btn_reload_tooltable_clicked(self, widget, data=None): + self.widgets.tooledit1.reload(None) + self.widgets.tooledit1.set_selected_tool(self.stat.tool_in_spindle) + + def on_btn_apply_tool_changes_clicked(self, widget, data=None): + self.widgets.tooledit1.save(None) + self.widgets.tooledit1.set_selected_tool(self.stat.tool_in_spindle) + + def on_btn_tool_touchoff_clicked(self, widget, data=None): + if not self.widgets.tooledit1.get_selected_tool(): + message = _("No or more than one tool selected in tool table") + message += _("Please select only one tool in the table") + self.dialogs.warning_dialog(self, _("Warning Tool Touch off not possible!"), message) + return + + if self.widgets.tooledit1.get_selected_tool() != self.stat.tool_in_spindle: + message = _("you can not touch of a tool, witch is not mounted in the spindle") + message += _("your selection has been reseted to the tool in spindle") + self.dialogs.warning_dialog(self, _("Warning Tool Touch off not possible!"), message) + self.widgets.tooledit1.reload(self) + self.widgets.tooledit1.set_selected_tool(self.stat.tool_in_spindle) + return + + 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) + return + + if widget == self.widgets.btn_tool_touchoff_x: + axis = "x" + 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")) + return + + value = self.dialogs.entry_dialog(self, 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) + + 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) + return + elif value == "CANCEL": + return + else: + command = "G10 L10 P{0} {1}{2}".format(self.stat.tool_in_spindle, axis, value) + self.command.mode(linuxcnc.MODE_MDI) + self.command.wait_complete() + self.command.mdi(command) + self.command.wait_complete() + if "G43" in self.active_gcodes: + self.command.mdi("G43") + self.command.wait_complete() + self.command.mode(linuxcnc.MODE_MANUAL) + self.command.wait_complete() + + # select a tool entering a number + def on_btn_select_tool_by_no_clicked(self, widget, data=None): + value = self.dialogs.entry_dialog(self, 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.warning_dialog(self, _("Conversion error !"), 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.warning_dialog(self, _("Important Warning!"), message) + return + else: + self.tool_change = True + self.command.mode(linuxcnc.MODE_MDI) + self.command.wait_complete() + command = "T{0} M6".format(int(value)) + self.command.mdi(command) + + # set tool with M61 Q? or with T? M6 + def on_btn_selected_tool_clicked(self, widget, data=None): + tool = self.widgets.tooledit1.get_selected_tool() + 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) + 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) + return + if tool or tool == 0: + self.tool_change = True + tool = int(tool) + self.command.mode(linuxcnc.MODE_MDI) + self.command.wait_complete() + + if widget == self.widgets.btn_change_tool: + command = "T{0} M6".format(tool) + else: + command = "M61 Q{0}".format(tool) + 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) + +# ========================================================= +# gremlin relevant calls + def on_rbt_view_p_toggled(self, widget, data=None): + if self.widgets.rbt_view_p.get_active(): + self.widgets.gremlin.set_property("view", "p") + self.prefs.putpref("view", "p") + + def on_rbt_view_x_toggled(self, widget, data=None): + if self.widgets.rbt_view_x.get_active(): + self.widgets.gremlin.set_property("view", "x") + self.prefs.putpref("view", "x") + + def on_rbt_view_y_toggled(self, widget, data=None): + if self.widgets.rbt_view_y.get_active(): + self.widgets.gremlin.set_property("view", "y") + self.prefs.putpref("view", "y") + + def on_rbt_view_z_toggled(self, widget, data=None): + if self.widgets.rbt_view_z.get_active(): + self.widgets.gremlin.set_property("view", "z") + self.prefs.putpref("view", "z") + + def on_rbt_view_y2_toggled(self, widget, data=None): + if self.widgets.rbt_view_y2.get_active(): + self.widgets.gremlin.set_property("view", "y2") + self.prefs.putpref("view", "y2") + + def on_btn_zoom_in_clicked(self, widget, data=None): + self.widgets.gremlin.zoom_in() + + def on_btn_zoom_out_clicked(self, widget, data=None): + self.widgets.gremlin.zoom_out() + + def on_btn_delete_view_clicked(self, widget, data=None): + self.widgets.gremlin.clear_live_plotter() + + def on_tbtn_view_dimension_toggled(self, widget, data=None): + self.widgets.gremlin.set_property("show_extents_option", widget.get_active()) + self.prefs.putpref("view_dimension", self.widgets.tbtn_view_dimension.get_active()) + + def on_tbtn_view_tool_path_toggled(self, widget, data=None): + self.widgets.gremlin.set_property("show_live_plot", widget.get_active()) + self.prefs.putpref("view_tool_path", self.widgets.tbtn_view_tool_path.get_active()) + + def on_gremlin_line_clicked(self, widget, line): + self.widgets.gcode_view.set_line_number(line) + + def on_btn_load_clicked(self, widget, data=None): + self.widgets.ntb_button.set_current_page(_BB_LOAD_FILE) + self.widgets.ntb_preview.set_current_page(3) + self.widgets.tbtn_fullsize_preview0.set_active(True) +# self.widgets.tbtn_fullsize_preview1.set_active(True) + self._show_iconview_tab(True) + self.widgets.IconFileSelection1.refresh_filelist() + self.widgets.IconFileSelection1.iconView.grab_focus() + self.gcodeerror = "" + + def on_btn_sel_next_clicked(self, widget, data=None): + self.widgets.IconFileSelection1.btn_sel_next.emit("clicked") + + def on_btn_sel_prev_clicked(self, widget, data=None): + self.widgets.IconFileSelection1.btn_sel_prev.emit("clicked") + + def on_btn_home_clicked(self, widget, data=None): + self.widgets.IconFileSelection1.btn_home.emit("clicked") + + def on_btn_jump_to_clicked(self, widget, data=None): + self.widgets.IconFileSelection1.btn_jump_to.emit("clicked") + + def on_btn_dir_up_clicked(self, widget, data=None): + self.widgets.IconFileSelection1.btn_dir_up.emit("clicked") + + def on_btn_select_clicked(self, widget, data=None): + self.widgets.IconFileSelection1.btn_select.emit("clicked") + + def on_IconFileSelection1_selected(self, widget, path=None): + if path: + self.widgets.hal_action_open.load_file(path) + self.widgets.ntb_preview.set_current_page(0) + self.widgets.tbtn_fullsize_preview0.set_active(False) +# self.widgets.tbtn_fullsize_preview1.set_active(False) + self.widgets.ntb_button.set_current_page(_BB_AUTO) + self._show_iconview_tab(False) + + def on_IconFileSelection1_sensitive(self, widget, buttonname, state): + self.widgets[buttonname].set_sensitive(state) + + def on_IconFileSelection1_exit(self, widget): + self.widgets.ntb_preview.set_current_page(0) + self.widgets.tbtn_fullsize_preview0.set_active(False) +# self.widgets.tbtn_fullsize_preview1.set_active(False) + self._show_iconview_tab(False) + + # edit a program or make a new one + def on_btn_edit_clicked(self, widget, data=None): + self.widgets.ntb_button.set_current_page(_BB_EDIT) + self.widgets.ntb_preview.hide() + self.widgets.tbl_DRO.hide() + width = self.widgets.window1.allocation.width + width -= self.widgets.vbtb_main.allocation.width + width -= self.widgets.box_right.allocation.width + width -= self.widgets.box_left.allocation.width + self.widgets.vbx_jog.set_size_request(width, -1) + if not self.widgets.vbx_jog.get_visible(): + self.widgets.vbx_jog.set_visible(True) + self.widgets.gcode_view.set_sensitive(True) + self.widgets.gcode_view.grab_focus() + if self.widgets.chk_use_kb_on_edit.get_active(): + self.widgets.ntb_info.set_current_page(1) + self.widgets.box_info.set_size_request(-1, 250) + else: + self.widgets.ntb_info.hide() + self.widgets.box_info.set_size_request(-1, 50) + self.widgets.tbl_search.show() + self.gcodeerror = "" + + # Search and replace handling in edit mode + # undo changes while in edit mode + def on_btn_undo_clicked(self, widget, data=None): + self.widgets.gcode_view.undo() + + # search backward while in edit mode + def on_btn_search_back_clicked(self, widget, data=None): + self.widgets.gcode_view.text_search(direction=False, + mixed_case=self.widgets.chk_ignore_case.get_active(), + text=self.widgets.search_entry.get_text()) + + # search forward while in edit mode + def on_btn_search_forward_clicked(self, widget, data=None): + self.widgets.gcode_view.text_search(direction=True, + mixed_case=self.widgets.chk_ignore_case.get_active(), + text=self.widgets.search_entry.get_text()) + + # replace text in edit mode + def on_btn_replace_clicked(self, widget, data=None): + self.widgets.gcode_view.replace_text_search(direction=True, + mixed_case=self.widgets.chk_ignore_case.get_active(), + text=self.widgets.search_entry.get_text(), + re_text=self.widgets.replace_entry.get_text(), + replace_all=self.widgets.chk_replace_all.get_active()) + + # redo changes while in edit mode + def on_btn_redo_clicked(self, widget, data=None): + self.widgets.gcode_view.redo() + + # if we leave the edit mode, we will have to show all widgets again + def on_ntb_button_switch_page(self, *args): + if self.widgets.ntb_preview.get_current_page() == 0: # preview tab is active, + # check if offset tab is visible, if so we have to hide it + page = self.widgets.ntb_preview.get_nth_page(1) + if page.get_visible(): + self._show_offset_tab(False) + elif self.widgets.ntb_preview.get_current_page() == 1: + self._show_offset_tab(False) + elif self.widgets.ntb_preview.get_current_page() == 2: + self._show_tooledit_tab(False) + elif self.widgets.ntb_preview.get_current_page() == 3: + self._show_iconview_tab(False) + +# if self.widgets.tbtn_fullsize_preview0.get_active() or self.widgets.tbtn_fullsize_preview1.get_active(): +# self.widgets.tbtn_fullsize_preview0.set_active(False) +# self.widgets.tbtn_fullsize_preview1.set_active(False) + if self.widgets.ntb_button.get_current_page() == _BB_EDIT or self.widgets.ntb_preview.get_current_page() == _BB_HOME: + self.widgets.ntb_preview.show() + self.widgets.tbl_DRO.show() + self.widgets.vbx_jog.set_size_request(360, -1) + self.widgets.gcode_view.set_sensitive(0) + self.widgets.btn_save.set_sensitive(True) + self.widgets.hal_action_reload.emit("activate") + self.widgets.ntb_info.set_current_page(0) + self.widgets.ntb_info.show() + self.widgets.box_info.set_size_request(-1, 200) + self.widgets.tbl_search.hide() + + # make a new file + def on_btn_new_clicked(self, widget, data=None): + tempfilename = os.path.join(_TEMPDIR, "temp.ngc") + content = self.get_ini_info.get_RS274_start_code() + if content == None: + content = " " + content += "\n\n\n\nM2" + gcodefile = open(tempfilename, "w") + gcodefile.write(content) + gcodefile.close() + if self.widgets.lbl_program.get_label() == tempfilename: + self.widgets.hal_action_reload.emit("activate") + else: + self.widgets.hal_action_open.load_file(tempfilename) + # self.command.program_open(tempfilename) + self.widgets.gcode_view.grab_focus() + self.widgets.btn_save.set_sensitive(False) + + def on_tbtn_optional_blocks_toggled(self, widget, data=None): + opt_blocks = widget.get_active() + self.command.set_block_delete(opt_blocks) + self.prefs.putpref("blockdel", opt_blocks) + self.widgets.hal_action_reload.emit("activate") + + #def on_tbtn_optional_stops_toggled(self, widget, data=None): + # opt_stops = widget.get_active() + # self.command.set_optional_stop(opt_stops) + # self.prefs.putpref("opstop", opt_stops) + + # this can not be done with the status widget, + # because it will not emit a RESUME signal + def on_tbtn_pause_toggled(self, widget, data=None): + widgetlist = ["rbt_forward", "rbt_reverse", "rbt_stop"] + self._sensitize_widgets(widgetlist, widget.get_active()) + + def on_btn_stop_clicked(self, widget, data=None): + self.command.abort() + self.start_line = 0 + self.widgets.gcode_view.set_line_number(0) + self.widgets.tbtn_pause.set_active(False) + + def on_btn_run_clicked(self, widget, data=None): + self.command.auto(linuxcnc.AUTO_RUN, self.start_line) + + def on_btn_from_line_clicked(self, widget, data=None): + self.dialogs.restart_dialog(self) + + def on_change_sound(self, widget, sound=None): + file = widget.get_filename() + if file: + if widget == self.widgets.audio_error_chooser: + self.error_sound = file + self.prefs.putpref("audio_error", file) + else: + self.alert_sound = file + self.prefs.putpref("audio_alert", file) + + def on_tbtn_switch_mode_toggled(self, widget, data=None): + if widget.get_active(): + self.widgets.tbtn_switch_mode.set_label(_(" Joint\nmode")) + # Mode 1 = joint ; Mode 2 = MDI ; Mode 3 = teleop + # so in mode 1 we have to show Joints and in Modes 2 and 3 axis values + self._set_motion_mode(0) + else: + self.widgets.tbtn_switch_mode.set_label(_("World\nmode")) + self._set_motion_mode(1) + +# ========================================================= +# Hal Pin Handling Start + + def _on_counts_changed(self, pin, widget): + if not self.initialized: + return + difference = 0 + counts = pin.get() + if self.halcomp["feed.feed-override.count-enable"]: + if widget == "spc_feed": + difference = (counts - self.fo_counts) * self.scale_feed_override + self.fo_counts = counts + self._check_counts(counts) + if self.halcomp["rapid.rapid-override.count-enable"]: + if widget == "spc_rapid": + difference = (counts - self.ro_counts) * self.scale_rapid_override + self.ro_counts = counts + self._check_counts(counts) + if self.halcomp["spindle.spindle-override.count-enable"]: + if widget == "spc_spindle": + difference = (counts - self.so_counts) * self.scale_spindle_override + self.so_counts = counts + self._check_counts(counts) + if self.halcomp["jog.jog-velocity.count-enable"]: + if widget == "spc_lin_jog_vel": + difference = (counts - self.jv_counts) * self.scale_jog_vel + if self.widgets.tbtn_turtle_jog.get_active(): + difference = difference / self.turtle_jog_factor + self.jv_counts = counts + self._check_counts(counts) + if not self.halcomp["feed.feed-override.count-enable"] \ + and not self.halcomp["spindle.spindle-override.count-enable"] \ + and not self.halcomp["jog.jog-velocity.count-enable"] \ + and not self.halcomp["rapid.rapid-override.count-enable"]: + self._check_counts(counts) + + val = self.widgets[widget].get_value() + difference + if val < 0: + val = 0 + if difference != 0: + self.widgets[widget].set_value(val) + + def _check_counts(self, counts): + # as we do not know how the user did connect the jog wheels, we have to check all + # possibilities. Does he use only one jog wheel and a selection switch or do he use + # a mpg for each slider or one for speeds and one for override, or ?? + if self.halcomp["feed.feed-override.counts"] == self.halcomp["spindle.spindle-override.counts"]: + if self.halcomp["feed.feed-override.count-enable"] and self.halcomp["spindle.spindle-override.count-enable"]: + return + self.fo_counts = self.so_counts = counts + if self.halcomp["feed.feed-override.counts"] == self.halcomp["jog.jog-velocity.counts"]: + if self.halcomp["feed.feed-override.count-enable"] and self.halcomp["jog.jog-velocity.count-enable"]: + return + self.fo_counts = self.jv_counts = counts + if self.halcomp["feed.feed-override.counts"] == self.halcomp["rapid.rapid-override.counts"]: + if self.halcomp["feed.feed-override.count-enable"] and self.halcomp["rapid.rapid-override.count-enable"]: + return + self.fo_counts = self.ro_counts = counts + if self.halcomp["spindle.spindle-override.counts"] == self.halcomp["jog.jog-velocity.counts"]: + if self.halcomp["spindle.spindle-override.count-enable"] and self.halcomp["jog.jog-velocity.count-enable"]: + return + self.so_counts = self.jv_counts = counts + if self.halcomp["spindle.spindle-override.counts"] == self.halcomp["rapid.rapid-override.counts"]: + if self.halcomp["spindle.spindle-override.count-enable"] and self.halcomp["rapid.rapid-override.count-enable"]: + return + self.so_counts = self.ro_counts = counts + if self.halcomp["jog.jog-velocity.counts"] == self.halcomp["rapid.rapid-override.counts"]: + if self.halcomp["jog.jog-velocity.count-enable"] and self.halcomp["rapid.rapid-override.count-enable"]: + return + self.jv_counts = self.ro_counts = counts + + def _on_analog_enable_changed(self, pin, widget): + if not self.initialized: + return + if widget == "spc_spindle": + if pin.get(): + self.widgets.btn_spindle_100.hide() + else: + self.widgets.btn_spindle_100.show() + if widget == "spc_feed": + if pin.get(): + self.widgets.btn_feed_100.hide() + else: + self.widgets.btn_feed_100.show() + # widget can also be spc_lin_jog_vel and spc_rapid + self.widgets[widget].hide_button(pin.get()) + + if pin.get(): + # special case of jog_vel, as we have to take care of both modes, + # more details see _on_analog_value_changed + if self.widgets.tbtn_turtle_jog.get_active(): + value = self.rabbit_jog = self.jog_rate_max * self.halcomp["jog.jog-velocity.direct-value"] + elif not self.widgets.tbtn_turtle_jog.get_active(): + value = self.turtle_jog = self.jog_rate_max / self.turtle_jog_factor * self.halcomp["jog.jog-velocity.direct-value"] + self.widgets.spc_lin_jog_vel.set_value(value) + + def _on_analog_value_changed(self, pin, widget): + if not self.initialized: + return + if widget == "spc_lin_jog_vel" and not self.halcomp["jog.jog-velocity.analog-enable"]: + return + if widget == "spc_feed" and not self.halcomp["feed.feed-override.analog-enable"]: + return + if widget == "spc_spindle" and not self.halcomp["spindle.spindle-override.analog-enable"]: + return + if widget == "spc_rapid" and not self.halcomp["rapid.rapid-override.analog-enable"]: + return + percentage = pin.get() + if percentage > 1.0: + percentage = 1.0 + range = self.widgets[widget].get_property("max") - self.widgets[widget].get_property("min") + try: # otherwise a value of 0.0 would give an error + value = self.widgets[widget].get_property("min") + (range * percentage) + except: + value = 0 + self.widgets[widget].set_value(value) + + # special case of jog_vel, as we have to take care of both modes, + # meaning the analog value must be applied to both! If we do not do this, + # it might be that a user has analog in signal set to 0.5 and switch the mode + # but in the other mode he had only 0.3 from its value, so a small change of the + # analog in to 0.51 would result in a jump of 20 %! + if self.widgets.tbtn_turtle_jog.get_active(): + self.rabbit_jog = self.jog_rate_max * pin.get() + elif not self.widgets.tbtn_turtle_jog.get_active(): + self.turtle_jog = self.jog_rate_max / self.turtle_jog_factor * pin.get() + + def _on_unlock_settings_changed(self, pin): + if not self.initialized: + return + if not self.widgets.rbt_hal_unlock.get_active() and not self.user_mode: + return + self.widgets.tbtn_setup.set_sensitive(pin.get()) + + def _on_play_sound(self, widget, sound = None): + print(self,widget,sound) + if _AUDIO_AVAILABLE and sound: + if sound == "error": + self.audio.set_sound(self.error_sound) + elif sound == "alert": + self.audio.set_sound(self.alert_sound) + else: + print("got unknown sound to play") + return + self.audio.run() + + def _on_message_deleted(self, widget, messages): + number = [] + for message in messages: + if message[2] == ALERT_ICON: + number.append(message[0]) + if len(number) == 0: + self.halcomp["error"] = False + + def _del_message_changed(self, pin): + if pin.get(): + if self.halcomp["error"] == True: + number = [] + messages = self.notification.messages + for message in messages: + if message[2] == ALERT_ICON: + number.append(message[0]) + self.notification.del_message(number[0]) + if len(number) == 1: + self.halcomp["error"] = False + else: + self.notification.del_last() + + def _on_pin_incr_changed(self, pin, buttonnumber): + if self.stat.state != 1: + self.command.abort() + self.command.wait_complete() + if not pin.get(): + return + btn_name = "rbt_{0}".format(buttonnumber) + self._jog_increment_changed(self.incr_rbt_dic[btn_name]) + self.incr_rbt_dic[btn_name].set_active(True) + + def _on_pin_jog_changed(self, pin, button_name): + print("Jog Pin Changed") + print(button_name) + if self.stat.kinematics_type != linuxcnc.KINEMATICS_IDENTITY: + if self.stat.motion_mode == 1 and pin.get(): + message = _("Axis jogging is only allowed in world mode, but you are in joint mode!") + print(message) + self._show_error((13, message)) + return + + if pin.get(): + self._on_btn_jog_pressed(None, button_name) + else: + self._on_btn_jog_released(None, button_name) + + def _reset_overide(self, pin, type): + if pin.get(): + if type == "rapid": + self.command.rapidrate(1.0) + return + self.widgets["btn_{0}_100".format(type)].emit("clicked") + + def _on_blockheight_value_changed(self, pin): + self.widgets.lbl_blockheight.set_text("blockheight = {0:.3f}".format(pin.get())) + if self.lathe_mode: + self.widgets.lbl_blockheight.hide() + +# ========================================================= +# The actions of the buttons + def _button_pin_changed(self, pin): + # we check if the button is pressed ore release, + # otherwise a signal will be emitted, if the button is released and + # the signal drob down to zero + if not pin.get(): + return + + if "h-button" in pin.name: + location = "bottom" + elif "v-button" in pin.name: + location = "right" + else: + print(_("Recieved a not clasified signal from pin {0}".format(pin.name))) + return + + number = int(pin.name[-1]) + if number is not number: + print(_("Could not translate {0} to number".format(pin.name))) + return + + button = self._get_child_button(location, number) + if not button: + print(_("no button here")) + return + elif button == -1: + print(_("the button is not sensitive")) + return + + if type(button[0]) == gtk.ToggleButton: + button[0].set_active(not button[0].get_active()) + print(_("Button {0} has been toggled".format(button[1]))) + elif type(button[0]) == gtk.RadioButton: + button[0].set_active(True) + button[0].emit("pressed") + print(_("Button {0} has been pressed".format(button[1]))) + else: + button[0].emit("clicked") + print(_("Button {0} has been clicked".format(button[1]))) + + # this handles the relation between hardware button and the software button + def _get_child_button(self, location, number = None): + # get the position of each button to be able to connect to hardware button + self.child_button_dic = {} + + if location == "bottom": + page = self.widgets.ntb_button.get_current_page() + container = self.widgets.ntb_button.get_children()[page] + elif location == "right": + container = self.widgets.vbtb_main + else: + print(_("got wrong location to locate the childs")) + + children = container.get_children() + hidden = 0 + for child in children: + if not child.get_visible(): + hidden +=1 + else: + if type(child) != gtk.Label: + pos = container.child_get_property(child, "position") + name = child.name + if name == None: + name = gtk.Buildable.get_name(child) + self.child_button_dic[pos - hidden] = (child, name) + + if number is not None: + try: + if self.child_button_dic[number][0].get_sensitive(): + return self.child_button_dic[number] + else: + return -1 + except: + return None + else: + return self.child_button_dic + + +# We need extra HAL pins here is where we do it. +# we make pins for the hardware buttons witch can be placed around the +# screen to activate the corresponding buttons on the GUI + def _make_hal_pins(self): + # generate the horizontal button pins + for h_button in range(0, 10): + pin = self.halcomp.newpin("h-button.button-{0}".format(h_button), hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._button_pin_changed) + + # generate the vertical button pins + for v_button in range(0, 7): + pin = self.halcomp.newpin("v-button.button-{0}".format(v_button), hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._button_pin_changed) + + # buttons for jogging the axis + for jog_button in self.axis_list: + pin = self.halcomp.newpin("jog.axis.jog-{0}-plus".format(jog_button), hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_pin_jog_changed, "{0}+".format(jog_button)) + pin = self.halcomp.newpin("jog.axis.jog-{0}-minus".format(jog_button), hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_pin_jog_changed, "{0}-".format(jog_button)) + + if self.stat.kinematics_type != linuxcnc.KINEMATICS_IDENTITY: + for joint_button in range(0, self.stat.joints): + pin = self.halcomp.newpin("jog.joint.jog-{0}-plus".format(joint_button), hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_pin_jog_changed, "{0}+".format(joint_button)) + pin = self.halcomp.newpin("jog.joint.jog-{0}-minus".format(joint_button), hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_pin_jog_changed, "{0}+".format(joint_button)) + + # jog_increment out pin + self.halcomp.newpin("jog.jog-increment", hal.HAL_FLOAT, hal.HAL_OUT) + + # generate the pins to set the increments + for buttonnumber in range(0, len(self.jog_increments)): + pin = self.halcomp.newpin("jog.jog-inc-{0}".format(buttonnumber), hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_pin_incr_changed, buttonnumber) + + # make the pin for unlocking settings page + pin = self.halcomp.newpin("unlock-settings", hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_unlock_settings_changed) + + # generate the pins to connect encoders to the sliders + pin = self.halcomp.newpin("feed.feed-override.counts", hal.HAL_S32, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_counts_changed, "spc_feed") + pin = self.halcomp.newpin("spindle.spindle-override.counts", hal.HAL_S32, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_counts_changed, "spc_spindle") + pin = self.halcomp.newpin("jog.jog-velocity.counts", hal.HAL_S32, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_counts_changed, "spc_lin_jog_vel") + pin = self.halcomp.newpin("rapid.rapid-override.counts", hal.HAL_S32, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_counts_changed, "spc_rapid") + self.halcomp.newpin("feed.feed-override.count-enable", hal.HAL_BIT, hal.HAL_IN) + self.halcomp.newpin("spindle.spindle-override.count-enable", hal.HAL_BIT, hal.HAL_IN) + self.halcomp.newpin("jog.jog-velocity.count-enable", hal.HAL_BIT, hal.HAL_IN) + self.halcomp.newpin("rapid.rapid-override.count-enable", hal.HAL_BIT, hal.HAL_IN) + + # generate the pins to connect analog inputs for sliders + pin = self.halcomp.newpin("feed.feed-override.analog-enable", hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_analog_enable_changed, "spc_feed") + pin = self.halcomp.newpin("spindle.spindle-override.analog-enable", hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_analog_enable_changed, "spc_spindle") + pin = self.halcomp.newpin("jog.jog-velocity.analog-enable", hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_analog_enable_changed, "spc_lin_jog_vel") + pin = self.halcomp.newpin("rapid.rapid-override.analog-enable", hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_analog_enable_changed, "spc_rapid") + pin = self.halcomp.newpin("feed.feed-override.direct-value", hal.HAL_FLOAT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_analog_value_changed, "spc_feed") + pin = self.halcomp.newpin("spindle.spindle-override.direct-value", hal.HAL_FLOAT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_analog_value_changed, "spc_spindle") + pin = self.halcomp.newpin("jog.jog-velocity.direct-value", hal.HAL_FLOAT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_analog_value_changed, "spc_lin_jog_vel") + pin = self.halcomp.newpin("rapid.rapid-override.direct-value", hal.HAL_FLOAT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_analog_value_changed, "spc_rapid") + + # make a pin to set turtle jog vel + pin = self.halcomp.newpin("jog.turtle-jog", hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._on_pin_turtle_jog) + + # make the pins for tool measurement + self.halcomp.newpin("probeheight", hal.HAL_FLOAT, hal.HAL_OUT) + pin = self.halcomp.newpin("blockheight", hal.HAL_FLOAT, hal.HAL_OUT) + hal_glib.GPin(pin).connect("value_changed", self._on_blockheight_value_changed) + preset = self.prefs.getpref("blockheight", 0.0, float) + self.halcomp["blockheight"] = preset + self.halcomp.newpin("toolmeasurement", hal.HAL_BIT, hal.HAL_OUT) + self.halcomp.newpin("searchvel", hal.HAL_FLOAT, hal.HAL_OUT) + self.halcomp.newpin("probevel", hal.HAL_FLOAT, hal.HAL_OUT) + + # make pins to react to tool_offset changes + pin = self.halcomp.newpin("tooloffset-x", hal.HAL_FLOAT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._offset_changed, "tooloffset-x") + pin = self.halcomp.newpin("tooloffset-z", hal.HAL_FLOAT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._offset_changed, "tooloffset-z") + self.halcomp.newpin("tool-diameter", hal.HAL_FLOAT, hal.HAL_OUT) + + # make a pin to delete a notification message + pin = self.halcomp.newpin("delete-message", hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._del_message_changed) + + # for manual tool change dialog + self.halcomp.newpin("toolchange-number", hal.HAL_S32, hal.HAL_IN) + self.halcomp.newpin("toolchange-changed", hal.HAL_BIT, hal.HAL_OUT) + pin = self.halcomp.newpin('toolchange-change', hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect('value_changed', self.on_tool_change) + + # make a pin to reset feed override to 100 % + pin = self.halcomp.newpin("feed.reset-feed-override", hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._reset_overide, "feed") + + # make a pin to reset rapid override to 100 % + pin = self.halcomp.newpin("rapid.reset-rapid-override", hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._reset_overide, "rapid") + + # make a pin to reset spindle override to 100 % + pin = self.halcomp.newpin("spindle.reset-spindle-override", hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._reset_overide, "spindle") + + # make an error pin to indicate a error to hardware + self.halcomp.newpin("error", hal.HAL_BIT, hal.HAL_OUT) + + # make pins to indicate program progress information + self.halcomp.newpin("program.length", hal.HAL_S32, hal.HAL_OUT) + self.halcomp.newpin("program.current-line", hal.HAL_S32, hal.HAL_OUT) + self.halcomp.newpin("program.progress", hal.HAL_FLOAT, hal.HAL_OUT) + + # make a pin to set ignore limits + pin = self.halcomp.newpin("ignore-limits", hal.HAL_BIT, hal.HAL_IN) + hal_glib.GPin(pin).connect("value_changed", self._ignore_limits) + +# Hal Pin Handling End +# ========================================================= + +if __name__ == "__main__": + app = gmoccapy(sys.argv) + + inifile = sys.argv[2] + print ("**** GMOCCAPY INFO : inifile = {0} ****:".format(sys.argv[2])) + postgui_halfile = app.get_ini_info.get_postgui_halfile() + print ("**** GMOCCAPY INFO : postgui halfile = {0} ****:".format(postgui_halfile)) + + if postgui_halfile: + if postgui_halfile.lower().endswith('.tcl'): + res = os.spawnvp(os.P_WAIT, "haltcl", ["haltcl", "-i", inifile, postgui_halfile]) + else: + res = os.spawnvp(os.P_WAIT, "halcmd", ["halcmd", "-i", inifile, "-f", postgui_halfile]) + if res: + raise SystemExit, res + + gtk.main() + -- GitLab