def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.current_mode = (None, None) self._last_count = 0 self.STYLEEDITOR = SSE(widgets, paths)
def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATH = paths.CONFIGPATH self.IMAGE_PATH = paths.IMAGEDIR self._big_view = -1 self.STYLEEDITOR = SSE(widgets, paths)
def __init__(self, halcomp,widgets,paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets,paths) self.picked_line = None STATUS.connect('general',self.return_value) STATUS.connect('graphics-line-selected', self.set_picked_line)
def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets, paths) global TOOLBAR TOOLBAR = ToolBarActions(path=paths) STATUS.connect('general', self.return_value)
def __init__(self, halcomp,widgets,paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets,paths) self.picked_line = None STATUS.connect('general',self.return_value) STATUS.connect('graphics-line-selected', self.set_picked_line)
def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets, paths) global TOOLBAR STATUS.connect('general', self.return_value) STATUS.connect('motion-mode-changed', self.motion_mode) STATUS.connect('user-system-changed', self._set_user_system_text) STATUS.connect('actual-spindle-speed-changed', self.update_spindle)
def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets, paths) global TOOLBAR TOOLBAR = ToolBarActions(path=paths) STATUS.connect('general', self.return_value) STATUS.connect('motion-mode-changed', self.motion_mode) STATUS.connect('user-system-changed', self._set_user_system_text)
def __init__(self, halcomp,widgets,paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets,paths) global TOOLBAR TOOLBAR = ToolBarActions(path=paths) STATUS.connect('general',self.return_value) STATUS.connect('motion-mode-changed',self.motion_mode) STATUS.connect('user-system-changed', self._set_user_system_text)
def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.stat = linuxcnc.stat() self.cmnd = linuxcnc.command() self.error = linuxcnc.error_channel() self.PATHS = paths self.IMAGE_PATH = paths.IMAGEDIR self.STYLEEDITOR = SSE(widgets, paths) global TOOLBAR TOOLBAR = ToolBarActions(path=paths)
def __init__(self, halcomp, widgets, paths): self.h = halcomp self.w = widgets self.gcodes = GCodes(widgets) self.valid = QtGui.QDoubleValidator(-999.999, 999.999, 3) self.styleeditor = SSE(widgets, paths) KEYBIND.add_call('Key_F4', 'on_keycall_F4') KEYBIND.add_call('Key_F12','on_keycall_F12') KEYBIND.add_call('Key_Pause', 'on_keycall_PAUSE') KEYBIND.add_call('Key_Space', 'on_keycall_PAUSE') # some global variables self.factor = 1.0 self.probe = None self.default_setup = os.path.join(PATH.CONFIGPATH, "default_setup.html") self.start_line = 0 self.run_time = 0 self.time_tenths = 0 self.timer_on = False self.home_all = False self.min_spindle_rpm = INFO.MIN_SPINDLE_SPEED self.max_spindle_rpm = INFO.MAX_SPINDLE_SPEED self.max_spindle_power = INFO.get_error_safe_setting('DISPLAY', 'MAX_SPINDLE_POWER',"0") self.max_linear_velocity = INFO.MAX_TRAJ_VELOCITY self.system_list = ["G54","G55","G56","G57","G58","G59","G59.1","G59.2","G59.3"] self.slow_jog_factor = 10 self.reload_tool = 0 self.last_loaded_program = "" self.first_turnon = True self.unit_label_list = ["ts_height", "tp_height", "zoffset_units", "max_probe_units"] self.lineedit_list = ["work_height", "touch_height", "sensor_height", "laser_x", "laser_y", "sensor_x", "sensor_y", "camera_x", "camera_y", "search_vel", "probe_vel", "max_probe", "eoffset_count"] self.onoff_list = ["frame_program", "frame_tool", "frame_offsets", "frame_dro", "frame_override"] self.axis_a_list = ["label_axis_a", "dro_axis_a", "action_zero_a", "axistoolbutton_a", "action_home_a", "widget_jog_angular", "widget_increments_angular", "a_plus_jogbutton", "a_minus_jogbutton"] STATUS.connect('general', self.dialog_return) STATUS.connect('state-on', lambda w: self.enable_onoff(True)) STATUS.connect('state-off', lambda w: self.enable_onoff(False)) STATUS.connect('mode-auto', lambda w: self.enable_auto(True)) STATUS.connect('gcode-line-selected', lambda w, line: self.set_start_line(line)) STATUS.connect('hard-limits-tripped', self.hard_limit_tripped) STATUS.connect('program-pause-changed', lambda w, state: self.w.btn_pause_spindle.setEnabled(state)) STATUS.connect('user-system-changed', lambda w, data: self.user_system_changed(data)) STATUS.connect('metric-mode-changed', lambda w, mode: self.metric_mode_changed(mode)) STATUS.connect('file-loaded', self.file_loaded) STATUS.connect('all-homed', self.all_homed) STATUS.connect('not-all-homed', self.not_all_homed) STATUS.connect('periodic', lambda w: self.update_runtimer()) STATUS.connect('interp-idle', lambda w: self.stop_timer()) STATUS.connect('actual-spindle-speed-changed',self.update_spindle) STATUS.connect('requested-spindle-speed-changed',self.update_spindle_requested)
def __init__(self, halcomp, widgets, paths): # some global variables self.hal = halcomp self.w = widgets self.PATH = paths self._big_view = -1 self.flag = 0 self.activeStyle = ''' { background-color: white;}''' self.defaultStyle = ''' { background-color: light blue;}''' self.activeWidgetDict = { 'programPage': False, 'userPage': False, 'machinePage': False, 'tooloffsetsPage': False, 'loadPage': False, 'mdiPage': False, 'workoffsetsPage': False, 'setupPage': False } self.current_mode = (None, None) self._last_count = 0 self.run_time = 0 self.time_tenths = 0 self.timerOn = False self.slow_jog_factor = 10 self.STYLEEDITOR = SSE(widgets, paths) self.GCODES = GCodes() STATUS.connect('periodic', lambda w: self.update_runtimer()) STATUS.connect('command-running', lambda w: self.start_timer()) STATUS.connect('command-stopped', lambda w: self.stop_timer()) STATUS.connect("metric-mode-changed", lambda w, d: self.mode_changed(d)) STATUS.connect('state-off', lambda w: self.w.pushbutton_metric.setEnabled(False)) STATUS.connect('state-estop', lambda w: self.w.pushbutton_metric.setEnabled(False)) STATUS.connect( 'interp-idle', lambda w: self.w.pushbutton_metric.setEnabled( self.homed_on_test())) STATUS.connect('interp-run', lambda w: self.w.pushbutton_metric.setEnabled(False)) STATUS.connect('all-homed', lambda w: self.w.pushbutton_metric.setEnabled(True)) STATUS.connect( 'not-all-homed', lambda w, data: self.w.pushbutton_metric.setEnabled(False))
def __init__(self, halcomp, widgets, paths): self.h = halcomp self.w = widgets self.PATHS = paths INIPATH = os.environ.get('INI_FILE_NAME', '/dev/null') self.inifile = linuxcnc.ini(INIPATH) self.STYLEEDITOR = SSE(widgets, paths) self.GCODES = GCodes(widgets) self.valid = QtGui.QDoubleValidator(0.0, 999.999, 3) KEYBIND.add_call('Key_F12', 'on_keycall_F12') KEYBIND.add_call('Key_Pause', 'on_keycall_pause') KEYBIND.add_call('Key_Plus', 'on_keycall_plus') KEYBIND.add_call('Key_Minus', 'on_keycall_minus') STAT.connect('general', self.dialog_return) STAT.connect('state-on', lambda w: self.enable_onoff(True)) STAT.connect('state-off', lambda w: self.enable_onoff(False)) STAT.connect('gcode-line-selected', lambda w, line: self.set_start_line(line)) STAT.connect('hard-limits-tripped', self.hard_limit_tripped) STAT.connect('interp-idle', lambda w: self.set_start_line(0)) STAT.connect('user-system-changed', self.user_system_changed) STAT.connect('file-loaded', self.file_loaded) STAT.connect('all-homed', self.all_homed) STAT.connect('not-all-homed', lambda w, list: self.set_dro_homed(False)) # some global variables self.axis_list = INFO.AVAILABLE_AXES self.max_linear_velocity = INFO.MAX_LINEAR_VELOCITY self.max_spindle_rpm = INFO.MAX_SPINDLE_SPEED self.system_list = [ "G53", "G54", "G55", "G56", "G57", "G58", "G59", "G59.1", "G59.2", "G59.3" ] self.slow_jog_factor = 10 self.reload_tool = 0 self.last_loaded_program = "" self.first_turnon = True self.onoff_list = ["frame_program", "frame_tool", "frame_dro"] self.axis_a_list = [ "label_axis_a", "dro_axis_a", "action_zero_a", "axistoolbutton_a", "action_home_a", "widget_jog_angular", "widget_increments_angular", "a_plus_jogbutton", "a_minus_jogbutton" ]
def __init__(self, halcomp, widgets, paths): self.w = widgets self.PATHS = paths self.hal = halcomp INIPATH = os.environ.get('INI_FILE_NAME', '/dev/null') self.inifile = linuxcnc.ini(INIPATH) self.STYLEEDITOR = SSE(widgets, paths) KEYBIND.add_call('Key_F12', 'on_keycall_F12') # STAT.connect('general',self.return_value) STAT.connect('state-on', self.machine_on) STAT.connect('state-off', self.machine_off) STAT.connect('gcode-line-selected', self.gcode_line_selected) STAT.connect('hard-limits-tripped', self.hard_limit_tripped) STAT.connect("interp-idle", self.interp_idle_changed) STAT.connect("user-system-changed", self.user_system_changed) STAT.connect("tool-in-spindle-changed", self.tool_in_spindle_changed) STAT.connect("file-loaded", self.file_loaded) STAT.connect("all-homed", self.all_homed) STAT.connect("not-all-homed", self.not_homed) # some global variables self.axis_list = INFO.AVAILABLE_AXES self.joint_list = INFO.AVAILABLE_JOINTS self.max_velocity = INFO.MAX_LINEAR_VELOCITY self.system_list = [ "G53", "G54", "G55", "G56", "G57", "G58", "G59", "G59.1", "G59.2", "G59.3" ] self.home_location_x = self.inifile.find('JOINT_0', 'HOME') self.home_location_y = self.inifile.find('JOINT_1', 'HOME') self.home_location_z = self.inifile.find('JOINT_2', 'HOME') self.tool_sensor_x = self.inifile.find('TOOLSENSOR', 'X') self.tool_sensor_y = self.inifile.find('TOOLSENSOR', 'Y') self.laser_offset_x = self.inifile.find('LASER', 'X') self.laser_offset_y = self.inifile.find('LASER', 'Y') self.homed = False self.start_line = 0 self.program_length = 0 self.slow_jog_factor = 10 self.tool_in_spindle = 0 self.reload_tool = 0 self.last_loaded_program = "" self.onoff_list = ["widget_controls", "Program_frame", "DRO_frame"]
def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATH = paths self._big_view = -1 self.STYLEEDITOR = SSE(widgets, paths) self.flag = 0 self.activeStyle = ''' { background-color: white;}''' self.defaultStyle = ''' { background-color: light blue;}''' self.activeWidgetDict = { 'programPage': False, 'userPage': False, 'machinePage': False, 'tooloffsetsPage': False, 'loadPage': False, 'mdiPage': False, 'workoffsetsPage': False, 'setupPage': False } self.current_mode = (None, None) self._last_count = 0
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets, paths) global TOOLBAR TOOLBAR = ToolBarActions(path=paths) STATUS.connect('general', self.return_value) ########################################## # Special Functions called from QTSCREEN ########################################## # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): KEYBIND.add_call('Key_F12', 'on_keycall_F12') TOOLBAR.configure_submenu(self.w.menuRecent, 'recent_submenu') TOOLBAR.configure_submenu(self.w.menuHoming, 'home_submenu') TOOLBAR.configure_submenu(self.w.menuUnhome, 'unhome_submenu') TOOLBAR.configure_submenu(self.w.menuZeroCoordinateSystem, 'zero_systems_submenu') TOOLBAR.configure_action(self.w.actionEstop, 'estop') TOOLBAR.configure_action(self.w.actionMachineOn, 'power') TOOLBAR.configure_action(self.w.actionOpen, 'load') TOOLBAR.configure_action(self.w.actionReload, 'Reload') TOOLBAR.configure_action(self.w.actionRun, 'run') TOOLBAR.configure_action(self.w.actionPause, 'pause') TOOLBAR.configure_action(self.w.actionStop, 'abort') TOOLBAR.configure_action(self.w.actionSkip, 'block_delete') TOOLBAR.configure_action(self.w.actionOptionalStop, 'optional_stop') TOOLBAR.configure_action(self.w.actionZoomIn, 'zoom_in') TOOLBAR.configure_action(self.w.actionZoomOut, 'zoom_out') TOOLBAR.configure_action(self.w.actionFrontView, 'view_x') TOOLBAR.configure_action(self.w.actionSideView, 'view_y') TOOLBAR.configure_action(self.w.actionRotatedView, 'view_z2') TOOLBAR.configure_action(self.w.actionTopView, 'view_z') TOOLBAR.configure_action(self.w.actionPerspectiveView, 'view_p') TOOLBAR.configure_action(self.w.actionClearPlot, 'view_clear') TOOLBAR.configure_action(self.w.actionQuit, 'Quit', lambda d: self.w.close()) TOOLBAR.configure_action(self.w.actionProperties, 'gcode_properties') TOOLBAR.configure_action(self.w.actionCalibration, 'load_calibration') TOOLBAR.configure_action(self.w.actionStatus, 'load_status') TOOLBAR.configure_action(self.w.actionHalshow, 'load_halshow') TOOLBAR.configure_action(self.w.actionHalmeter, 'load_halmeter') TOOLBAR.configure_action(self.w.actionHalscope, 'load_halscope') TOOLBAR.configure_action(self.w.actionAbout, 'about') TOOLBAR.configure_action(self.w.actionTouchoffWorkplace, 'touchoffworkplace') TOOLBAR.configure_action(self.w.actionEdit, 'edit', self.edit) TOOLBAR.configure_action(self.w.actionTouchoffFixture, 'touchofffixture') TOOLBAR.configure_action(self.w.actionRunFromLine, 'runfromline') self.w.actionQuickRef.triggered.connect(self.quick_reference) def processed_key_event__(self, receiver, event, is_pressed, key, code, shift, cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in (QtCore.Qt.Key_Escape, QtCore.Qt.Key_F1, QtCore.Qt.Key_F2, QtCore.Qt.Key_F3, QtCore.Qt.Key_F5, QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = False break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STATUS.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True # ok if we got here then try keybindings try: return KEYBIND.call(self, event, is_pressed, shift, cntrl) except NameError as e: LOG.debug('Exception in KEYBINDING: {}'.format(e)) except Exception as e: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s' % ( KEYBIND.convert(event), key) return False ######################## # callbacks from STATUS # ######################## ####################### # callbacks from form # ####################### def tool_offset_clicked(self): conversion = { 0: "X", 1: "Y", 2: "Z", 3: "A", 4: "B", 5: "C", 6: "U", 7: "V", 8: "W" } axis = STATUS.get_selected_axis() mess = { 'NAME': 'ENTRY', 'ID': 'FORM__', 'AXIS': conversion[axis], 'FIXTURE': self.w.actionTouchoffWorkplace.isChecked(), 'TITLE': 'Set Tool Offset' } STATUS.emit('dialog-request', mess) LOG.debug('message sent:{}'.format(mess)) ##################### # general functions # ##################### # process the STATUS return message def return_value(self, w, message): num = message['RETURN'] code = bool(message['ID'] == 'FORM__') name = bool(message['NAME'] == 'ENTRY') if num and code and name: LOG.debug('message return:{}'.format(message)) axis = message['AXIS'] fixture = message['FIXTURE'] ACTION.SET_TOOL_OFFSET(axis, num, fixture) STATUS.emit('update-machine-log', 'Set tool offset of Axis %s to %f' % (axis, num), 'TIME') def edit(self, widget, state): if state: self.w.gcode_editor.editMode() else: self.w.gcode_editor.readOnlyMode() def quick_reference(self): help1 = [ ("F1", _("Emergency stop")), ("F2", _("Turn machine on")), ("", ""), ("X", _("Activate first axis")), ("Y", _("Activate second axis")), ("Z", _("Activate third axis")), ("A", _("Activate fourth axis")), ("` or 0,1..8", _("Activate first through ninth joint <br>if joints radiobuttons visible" )), ("", _("")), ("`,1..9,0", _("Set Feed Override from 0% to 100%")), ("", _("if axes radiobuttons visible")), (_(", and ."), _("Select jog speed")), (_("< and >"), _("Select angular jog speed")), (_("I, Shift-I"), _("Select jog increment")), ("C", _("Continuous jog")), (_("Home"), _("Send active joint home")), (_("Ctrl-Home"), _("Home all joints")), (_("Shift-Home"), _("Zero G54 offset for active axis")), (_("End"), _("Set G54 offset for active axis")), (_("Ctrl-End"), _("Set tool offset for loaded tool")), ("-, =", _("Jog active axis or joint")), (";, '", _("Select Max velocity")), ("", ""), (_("Left, Right"), _("Jog first axis or joint")), (_("Up, Down"), _("Jog second axis or joint")), (_("Pg Up, Pg Dn"), _("Jog third axis or joint")), (_("Shift+above jogs"), _("Jog at traverse speed")), ("[, ]", _("Jog fourth axis or joint")), ("", ""), ("D", _("Toggle between Drag and Rotate mode")), (_("Left Button"), _("Pan, rotate or select line")), (_("Shift+Left Button"), _("Rotate or pan")), (_("Right Button"), _("Zoom view")), (_("Wheel Button"), _("Rotate view")), (_("Rotate Wheel"), _("Zoom view")), (_("Control+Left Button"), _("Zoom view")), ] help2 = [ ("F3", _("Manual control")), ("F5", _("Code entry (MDI)")), (_("Control-M"), _("Clear MDI history")), (_("Control-H"), _("Copy selected MDI history elements")), ("", _("to clipboard")), (_("Control-Shift-H"), _("Paste clipboard to MDI history")), ("L", _("Override Limits")), ("", ""), ("O", _("Open program")), (_("Control-R"), _("Reload program")), (_("Control-S"), _("Save g-code as")), ("R", _("Run program")), ("T", _("Step program")), ("P", _("Pause program")), ("S", _("Resume program")), ("ESC", _("Stop running program, or")), ("", _("stop loading program preview")), ("", ""), ("F7", _("Toggle mist")), ("F8", _("Toggle flood")), ("B", _("Spindle brake off")), (_("Shift-B"), _("Spindle brake on")), ("F9", _("Turn spindle clockwise")), ("F10", _("Turn spindle counterclockwise")), ("F11", _("Turn spindle more slowly")), ("F12", _("Turn spindle more quickly")), (_("Control-K"), _("Clear live plot")), ("V", _("Cycle among preset views")), ("F4", _("Cycle among preview, DRO, and user tabs")), ("@", _("toggle Actual/Commanded")), ("#", _("toggle Relative/Machine")), (_("Ctrl-Space"), _("Clear notifications")), (_("Alt-F, M, V"), _("Open a Menu")), ] help = zip(help1, help2) msg = QtWidgets.QDialog() msg.setWindowTitle("Quick Reference") button = QtWidgets.QPushButton("Ok") button.clicked.connect(lambda: msg.close()) edit = QtWidgets.QTextEdit() edit.setLineWrapMode(0) mess = '''<TABLE border="1"><COLGROUP> <COL><COL align="char" char="."><THEAD> <TR><TH>Key <TH>Command<TH>Key <TH>Command <TBODY>''' for i, j in help: m = '<TR><TD><b>%s</b> <TD>%s<TD><b>%s</b> <TD>%s' % ( i[0], i[1], j[0], j[1]) mess += m mess += '</TABLE' edit.setText(mess) edit.setReadOnly(True) layout = QtWidgets.QVBoxLayout() layout.addWidget(edit) layout.addWidget(button) msg.setLayout(layout) msg.setMinimumSize(700, 800) msg.show() retval = msg.exec_() # keyboard jogging from key binding calls # double the rate if fast is true def kb_jog(self, state, joint, direction, fast=False, linear=True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte() / 60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular() / 60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self, event, state, shift, cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self, event, state, shift, cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self, event, state, shift, cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self, event, state, shift, cntrl): if state: if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: self.cmnd.abort() # Linear Jogging def on_keycall_XPOS(self, event, state, shift, cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self, event, state, shift, cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self, event, state, shift, cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self, event, state, shift, cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self, event, state, shift, cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self, event, state, shift, cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) def on_keycall_F12(self, event, state, shift, cntrl): if state: self.STYLEEDITOR.load_dialog() ########################### # **** closing event **** # ########################### ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.current_mode = (None, None) self._last_count = 0 self.rted = RichTextEditorDialog() self.STYLEEDITOR = SSE(widgets, paths) ########################################## # Special Functions called from QTSCREEN ########################################## # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): KEYBIND.add_call('Key_F12', 'on_keycall_F12') self.pin_mpg_in = self.hal.newpin('mpg-in', hal.HAL_S32, hal.HAL_IN) self.pin_mpg_in.value_changed.connect(lambda s: self.external_mpg(s)) self.pin_cycle_start_in = self.hal.newpin('cycle-start-in', hal.HAL_BIT, hal.HAL_IN) self.pin_cycle_start_in.value_changed.connect( lambda s: self.cycleStart(s)) self.pin_abort = self.hal.newpin('abort', hal.HAL_BIT, hal.HAL_IN) self.pin_abort.value_changed.connect(lambda s: self.abort(s)) self.wheel_x = self.hal.newpin('jog.wheel.x', hal.HAL_BIT, hal.HAL_OUT) self.wheel_y = self.hal.newpin('jog.wheel.y', hal.HAL_BIT, hal.HAL_OUT) self.wheel_z = self.hal.newpin('jog.wheel.z', hal.HAL_BIT, hal.HAL_OUT) self.jog_increment = self.hal.newpin('jog.wheel.incement', hal.HAL_FLOAT, hal.HAL_OUT) STATUS.connect( 'feed-override-changed', lambda w, data: self.w.pushbutton_fo. setText('FO {0:.0f}%'.format(data))) STATUS.connect( 'rapid-override-changed', lambda w, data: self.w.pushbutton_ro. setText('RO {0:.0f}%'.format(data))) STATUS.connect( 'spindle-override-changed', lambda w, data: self.w.pushbutton_so. setText('SO {0:.0f}%'.format(data))) STATUS.connect('jogincrement-changed', lambda w, incr, label: self.updateIncrementPin(incr)) if self.w.PREFS_: try: value = self.w.PREFS_.getpref('DRO_Font', '', str, 'CUSTOM_FORM_ENTRIES') if value != '': font = QtGui.QFont() font.fromString(value) self.setDROFont(font) except: pass try: value = self.w.PREFS_.getpref('DRO_Color', '', str, 'CUSTOM_FORM_ENTRIES') if value != '': self.setDROColor(value) except: pass def processed_key_event__(self, receiver, event, is_pressed, key, code, shift, cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in (QtCore.Qt.Key_Escape, QtCore.Qt.Key_F1, QtCore.Qt.Key_F2, QtCore.Qt.Key_F3, QtCore.Qt.Key_F5, QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = True break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STATUS.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True # ok if we got here then try keybindings try: return KEYBIND.call(self, event, is_pressed, shift, cntrl) except NameError as e: LOG.debug('Exception in KEYBINDING: {}'.format(e)) except Exception as e: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s' % ( KEYBIND.convert(event), key) return False ######################## # callbacks from STATUS # ######################## def updateIncrementPin(self, incr): self.jog_increment.set(incr) ####################### # callbacks from form # ####################### def updateJogState(self): state = self.w.pushbutton_jog.isChecked() if state: ACTION.SET_MANUAL_MODE() selected = STATUS.get_selected_axis() for temp in INFO.AVAILABLE_AXES: if selected == temp: self['wheel_{}'.format(temp.lower())].set(state) else: self['wheel_{}'.format(temp.lower())].set(False) def colorDialog(self): color = QtWidgets.QColorDialog.getColor() self.setDROColor(color.name()) def fontDialog(self): font, ok = QtWidgets.QFontDialog.getFont(self.w.dro_label_1.font()) if ok: self.setDROFont(font) def togglePointer(self, data): if data: QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.BlankCursor) else: QtWidgets.QApplication.restoreOverrideCursor() ##################### # general functions # ##################### def setDROFont(self, font): for i in range(1, 10): self.w['dro_label_{}'.format(i)].setFont(font) def setDROColor(self, color): for i in range(1, 10): self.w['dro_label_{}'.format(i)].setStyleSheet( "QWidget { color: %s}" % color) def abort(self, state): if not state: return if STATUS.stat.interp_state == labvcnc.INTERP_IDLE: self.w.close() else: ACTION.ABORT() def cycleStart(self, state): print state, self.current_mode if state: tab = self.w.mainTab.currentWidget() if tab in (self.w.tab_auto, self.w.tab_graphics): print 'start cycle!', self.w.gcode_editor.get_line() ACTION.RUN(line=0) elif tab == self.w.tab_files: print 'load program' self.w.filemanager.load() elif tab == self.w.tab_mdi: print 'run MDI' self.w.mditouchy.run_command() # MPG scrolling of program or MDI history def external_mpg(self, count): diff = count - self._last_count if self.w.pushbutton_scroll.isChecked(): if self.w.mainTab.currentWidget() == self.w.tab_auto: self.w.gcode_editor.jump_line(diff) elif self.w.mainTab.currentWidget() == self.w.tab_files: if diff < 0: self.w.filemanager.down() else: self.w.filemanager.up() elif self.w.pushbutton_fo.isChecked(): scaled = (STATUS.stat.feedrate * 100 + diff) if scaled < 0: scaled = 0 elif scaled > INFO.MAX_FEED_OVERRIDE: scaled = INFO.MAX_FEED_OVERRIDE ACTION.SET_FEED_RATE(scaled) elif self.w.pushbutton_ro.isChecked(): scaled = (STATUS.stat.rapidrate * 100 + diff) if scaled < 0: scaled = 0 elif scaled > 100: scaled = 100 ACTION.SET_RAPID_RATE(scaled) elif self.w.pushbutton_so.isChecked(): scaled = (STATUS.stat.spindle[0]['override'] * 100 + diff) if scaled < INFO.MIN_SPINDLE_OVERRIDE: scaled = INFO.MIN_SPINDLE_OVERRIDE elif scaled > INFO.MAX_SPINDLE_OVERRIDE: scaled = INFO.MAX_SPINDLE_OVERRIDE ACTION.SET_SPINDLE_RATE(scaled) self._last_count = count # keyboard jogging from key binding calls # double the rate if fast is true def kb_jog(self, state, joint, direction, fast=False, linear=True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte() / 60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular() / 60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self, event, state, shift, cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self, event, state, shift, cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self, event, state, shift, cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self, event, state, shift, cntrl): if state: if STATUS.stat.interp_state == labvcnc.INTERP_IDLE: self.w.close() else: self.cmnd.abort() # Function keys def on_keycall_F12(self, event, state, shift, cntrl): if state: self.STYLEEDITOR.load_dialog() # Linear Jogging def on_keycall_XPOS(self, event, state, shift, cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self, event, state, shift, cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self, event, state, shift, cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self, event, state, shift, cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self, event, state, shift, cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self, event, state, shift, cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self, event, state, shift, cntrl): if 'A' in INFO.AVAILABLE_AXES: self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self, event, state, shift, cntrl): if 'A' in INFO.AVAILABLE_AXES: self.kb_jog(state, 3, -1, shift, linear=False) ########################### # **** closing event **** # ########################### def closing_cleanup__(self): if self.w.PREFS_: self.w.PREFS_.putpref('DRO_Font', self.w.dro_label_1.font().toString(), str, 'CUSTOM_FORM_ENTRIES') color = self.w.dro_label_1.palette().color( QtGui.QPalette.Foreground).name() self.w.PREFS_.putpref('DRO_Color', color, str, 'CUSTOM_FORM_ENTRIES') ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: def __init__(self, halcomp, widgets, paths): self.h = halcomp self.w = widgets self.PATHS = paths INIPATH = os.environ.get('INI_FILE_NAME', '/dev/null') self.inifile = linuxcnc.ini(INIPATH) self.STYLEEDITOR = SSE(widgets,paths) self.GCODES = GCodes(widgets) self.valid = QtGui.QDoubleValidator(0.0, 999.999, 3) KEYBIND.add_call('Key_F12','on_keycall_F12') KEYBIND.add_call('Key_Pause', 'on_keycall_pause') KEYBIND.add_call('Key_Plus', 'on_keycall_plus') KEYBIND.add_call('Key_Minus', 'on_keycall_minus') STAT.connect('general', self.dialog_return) STAT.connect('state-on', lambda w: self.enable_onoff(True)) STAT.connect('state-off', lambda w: self.enable_onoff(False)) STAT.connect('gcode-line-selected', lambda w, line: self.set_start_line(line)) STAT.connect('hard-limits-tripped', self.hard_limit_tripped) STAT.connect('interp-idle', lambda w: self.set_start_line(0)) STAT.connect('user-system-changed', self.user_system_changed) STAT.connect('file-loaded', self.file_loaded) STAT.connect('all-homed', self.all_homed) STAT.connect('not-all-homed', lambda w, list: self.set_dro_homed(False)) # some global variables self.axis_list = INFO.AVAILABLE_AXES self.max_linear_velocity = INFO.MAX_LINEAR_VELOCITY self.max_spindle_rpm = INFO.MAX_SPINDLE_SPEED self.system_list = ["G53","G54","G55","G56","G57","G58","G59","G59.1","G59.2","G59.3"] self.slow_jog_factor = 10 self.reload_tool = 0 self.last_loaded_program = "" self.first_turnon = True self.onoff_list = ["frame_program", "frame_tool", "frame_dro"] self.axis_a_list = ["label_axis_a", "dro_axis_a", "action_zero_a", "axistoolbutton_a", "action_home_a", "widget_jog_angular", "widget_increments_angular", "a_plus_jogbutton", "a_minus_jogbutton"] def initialized__(self): self.init_pins() self.init_preferences() self.init_widgets() self.GCODES.setup_list() self.w.stackedWidget_log.setCurrentIndex(0) if "A" not in self.axis_list: for i in self.axis_a_list: self.w[i].hide() self.w.lbl_increments_linear.setText("INCREMENTS") ############################# # SPECIAL FUNCTIONS SECTION # ############################# def init_pins(self): pin = self.h.newpin("spindle_speed_fb", hal.HAL_FLOAT, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.spindle_fb_changed) pin = self.h.newpin("spindle_amps", hal.HAL_FLOAT, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.spindle_amps_changed) pin = self.h.newpin("spindle_power", hal.HAL_FLOAT, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.spindle_power_changed) pin = self.h.newpin("spindle_fault", hal.HAL_U32, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.spindle_fault_changed) pin = self.h.newpin("modbus-errors", hal.HAL_U32, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self.mb_errors_changed) def init_preferences(self): if not self.w.PREFS_: self.add_alarm("CRITICAL - no preference file found, enable preferences in screenoptions widget") return self.last_loaded_program = self.w.PREFS_.getpref('last_file_path', None, str,'BOOK_KEEPING') self.reload_tool = self.w.PREFS_.getpref('Tool to load', 0, int,'CUSTOM_FORM_ENTRIES') self.w.chk_reload_program.setChecked(self.w.PREFS_.getpref('Reload program', False, bool,'CUSTOM_FORM_ENTRIES')) self.w.chk_reload_tool.setChecked(self.w.PREFS_.getpref('Reload tool', False, bool,'CUSTOM_FORM_ENTRIES')) self.w.chk_use_keyboard.setChecked(self.w.PREFS_.getpref('Use keyboard', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_run_from_line.setChecked(self.w.PREFS_.getpref('Run from line', False, bool, 'CUSTOM_FORM_ENTRIES')) def closing_cleanup__(self): if not self.w.PREFS_: return self.w.PREFS_.putpref('Tool to load', self.w.statuslabel_tool.text().encode('utf-8'), int, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Reload program', self.w.chk_reload_program.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Reload tool', self.w.chk_reload_tool.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use keyboard', self.w.chk_use_keyboard.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Run from line', self.w.chk_run_from_line.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') def init_widgets(self): self.w.main_tab_widget.setCurrentIndex(0) self.w.slider_jog_linear.setMaximum(self.max_linear_velocity * 60) self.w.slider_jog_linear.setValue(INFO.DEFAULT_LINEAR_JOG_VEL) self.w.slider_jog_angular.setMaximum(INFO.MAX_ANGULAR_JOG_VEL) self.w.slider_jog_angular.setValue(INFO.DEFAULT_ANGULAR_JOG_VEL) self.w.slider_feed_ovr.setMaximum(INFO.MAX_FEED_OVERRIDE) self.w.slider_feed_ovr.setValue(100) self.w.slider_rapid_ovr.setMaximum(100) self.w.slider_rapid_ovr.setValue(100) self.w.slider_spindle_ovr.setMinimum(INFO.MIN_SPINDLE_OVERRIDE) self.w.slider_spindle_ovr.setMaximum(INFO.MAX_SPINDLE_OVERRIDE) self.w.slider_spindle_ovr.setValue(100) self.w.chk_override_limits.setChecked(False) self.w.chk_override_limits.setEnabled(False) self.w.filemanager.show() self.w.gcode_editor.hide() self.w.lbl_max_rapid.setText(str(self.max_linear_velocity * 60)) self.w.lbl_search_vel.setText(self.inifile.find('TOOLSENSOR', 'SEARCH_VEL') or "200") self.w.lbl_probe_vel.setText(self.inifile.find('TOOLSENSOR', 'PROBE_VEL') or "50") self.w.lbl_max_probe.setText(self.inifile.find('TOOLSENSOR', 'MAXPROBE') or "10") self.w.lbl_touch_height.setText(self.inifile.find('TOOLSENSOR', 'TOUCH') or "50") self.w.lbl_laser_x.setText(self.inifile.find('LASER', 'X') or "100") self.w.lbl_laser_y.setText(self.inifile.find('LASER', 'Y') or "-10") self.w.lbl_home_x.setText(self.inifile.find('JOINT_0', 'HOME') or "50") self.w.lbl_home_y.setText(self.inifile.find('JOINT_1', 'HOME') or "50") #set up gcode list self.w.gcode_list.currentRowChanged.connect(self.list_row_changed) titles = mdiText.gcode_titles() for key in sorted(titles.iterkeys()): self.w.gcode_list.addItem(key + ' ' + titles[key]) self.w.gcode_description.setReadOnly(True) def processed_key_event__(self,receiver,event,is_pressed,key,code,shift,cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in(QtCore.Qt.Key_Escape,QtCore.Qt.Key_F1 ,QtCore.Qt.Key_F2): # QtCore.Qt.Key_F3,QtCore.Qt.Key_F4,QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, QtWidgets.QLineEdit): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = True break if isinstance(receiver2, TOOL_TABLE): flag = True break if isinstance(receiver2, OFFSET_VIEW): flag = True break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STAT.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True # ok if we got here then try keybindings try: return KEYBIND.call(self,event,is_pressed,shift,cntrl) except NameError as e: self.add_alarm('Exception in KEYBINDING: {}'.format (e)) except Exception as e: LOG.error('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s'%(KEYBIND.convert(event),key) return False ######################### # CALLBACKS FROM STATUS # ######################### def spindle_fb_changed(self, data): rpm = int(self.h['spindle_speed_fb']) self.w.lbl_spindle_rpm.setText(str(rpm)) if float(self.w.label_spindle_set.text()) > self.max_spindle_rpm: self.w.label_spindle_set.setProperty('overspeed', True) else: self.w.label_spindle_set.setProperty('overspeed', False) self.w.label_spindle_set.setStyle(self.w.label_spindle_set.style()) def spindle_amps_changed(self, data): amps = "{:1.1f}".format(self.h['spindle_amps']) self.w.lbl_spindle_amps.setText(amps) def spindle_power_changed(self, data): power = "{:4.1f}".format(self.h['spindle_power']) self.w.lbl_spindle_power.setText(power) def spindle_fault_changed(self, data): fault = hex(self.h['spindle_fault']) self.w.lbl_spindle_fault.setText(fault) def mb_errors_changed(self, data): errors = self.h['modbus-errors'] self.w.lbl_mb_errors.setText(str(errors)) def dialog_return(self, w, message): rtn = message.get('RETURN') name = message.get('NAME') jog_code = bool(message.get('ID') == '_touchoff_') if jog_code and name == 'MESSAGE' and rtn is True: z_offset = float(self.w.lbl_touch_height.text()) max_probe = self.w.lbl_max_probe.text() search_vel = self.w.lbl_search_vel.text() probe_vel = self.w.lbl_probe_vel.text() ACTION.CALL_MDI("G21 G49") ACTION.CALL_MDI("G10 L20 P0 Z0") ACTION.CALL_MDI("G91") command = "G38.2 Z-{} F{}".format(max_probe, search_vel) if ACTION.CALL_MDI_WAIT(command, 10) == -1: return if ACTION.CALL_MDI_WAIT("G1 Z4.0") == -1: return ACTION.CALL_MDI("G4 P0.5") command = "G38.2 Z-4.4 F{}".format(probe_vel) if ACTION.CALL_MDI_WAIT(command, 10) == -1: return command = "G10 L20 P0 Z{}".format(z_offset) ACTION.CALL_MDI(command) command = "G1 Z10.0 F{}".format(search_vel) ACTION.CALL_MDI_WAIT(command, 10) ACTION.CALL_MDI("G90") def user_system_changed(self, obj, data): sys = self.system_list[int(data)] self.w.actionbutton_rel.setText(sys) def file_loaded(self, obj, filename): if filename is not None: self.w.progressBar.setValue(0) self.last_loaded_program = filename else: self.add_alarm("Filename not valid") def all_homed(self, obj): self.set_dro_homed(True) if self.first_turnon is True: self.first_turnon = False if self.w.chk_reload_tool.isChecked(): command = "M61 Q{}".format(self.reload_tool) ACTION.CALL_MDI(command) if self.last_loaded_program and self.w.chk_reload_program.isChecked(): ACTION.OPEN_PROGRAM(self.last_loaded_program) self.w.filemanager.updateDirectoryView(self.last_loaded_program) def hard_limit_tripped(self, obj, tripped, list_of_tripped): self.w.chk_override_limits.setEnabled(tripped) if not tripped: self.w.chk_override_limits.setChecked(False) ####################### # CALLBACKS FROM FORM # ####################### # program frame def btn_start_clicked(self): if self.w.main_tab_widget.currentIndex() != 0: return if not STAT.is_auto_mode(): self.add_alarm("Must be in AUTO mode to run a program") return self.w.btn_start.setProperty('running', True) self.w.btn_start.setStyle(self.w.btn_start.style()) start_line = int(self.w.lbl_start_line.text().encode('utf-8')) self.add_alarm("Started program from line {}".format(start_line)) ACTION.RUN(start_line) def btn_reload_file_clicked(self): if self.last_loaded_program: self.w.progressBar.setValue(0) ACTION.OPEN_PROGRAM(self.last_loaded_program) # tool frame def btn_go_home_clicked(self): ACTION.CALL_MDI_WAIT("G53 G0 Z0") command = "G53 G0 X{} Y{}".format(self.w.lbl_home_x.text(), self.w.lbl_home_y.text()) ACTION.CALL_MDI_WAIT(command, 10) def btn_ref_laser_clicked(self): command = "G10 L20 P0 X{} Y{}".format(self.w.lbl_laser_x.text(), self.w.lbl_laser_y.text()) ACTION.CALL_MDI(command) def btn_touchoff_clicked(self): if self.w.statuslabel_tool.text() == "0": self.add_alarm("Cannot touchoff with no tool loaded") return if not STAT.is_all_homed(): self.add_alarm("Must be homed to perform tool touchoff") return max_probe = self.w.lbl_max_probe.text() self.add_alarm("Tool touchoff started") # instantiate dialog box info = "Jog to within {} mm of touch plate and click OK".format(max_probe) mess = {'NAME':'MESSAGE', 'ID':'_touchoff_', 'MESSAGE':'TOOL TOUCHOFF', 'MORE':info, 'TYPE':'OKCANCEL'} STAT.emit('dialog-request', mess) # override frame def slow_button_clicked(self, state): slider = self.w.sender().property('slider') current = self.w[slider].value() max = self.w[slider].maximum() if state: self.w.sender().setText("SLOW") self.w[slider].setMaximum(max / self.slow_jog_factor) self.w[slider].setValue(current / self.slow_jog_factor) self.w[slider].setPageStep(10) else: self.w.sender().setText("FAST") self.w[slider].setMaximum(max * self.slow_jog_factor) self.w[slider].setValue(current * self.slow_jog_factor) self.w[slider].setPageStep(100) # file tab def btn_gcode_edit_clicked(self, state): if not STAT.is_on_and_idle(): return for x in ["load", "next", "prev"]: self.w["btn_file_{}".format(x)].setEnabled(not state) if state: self.w.filemanager.hide() self.w.gcode_editor.show() self.w.gcode_editor.editMode() else: self.w.filemanager.show() self.w.gcode_editor.hide() self.w.gcode_editor.readOnlyMode() # tool tab def btn_m61_clicked(self): checked = self.w.tooloffsetview.get_checked_list() if len(checked) > 1: self.add_alarm("Select only 1 tool to load") elif checked: ACTION.CALL_MDI("M61 Q{}".format(checked[0])) else: self.add_alarm("No tool selected") # alarm tab def btn_clear_alarms_clicked(self): STAT.emit('update-machine-log', None, 'DELETE') def btn_save_alarms_clicked(self): text = self.w.machinelog.toPlainText() filename = self.w.lbl_clock.text().encode('utf-8') filename = 'alarms_' + filename.replace(' ','_') + '.txt' with open(filename, 'w') as f: f.write(text) # gcode tab def list_row_changed(self, row): line = self.w.gcode_list.currentItem().text().encode('utf-8') text = line.split(' ')[0] desc = mdiText.gcode_descriptions(text) or 'No Match' self.w.gcode_description.clear() self.w.gcode_description.insertPlainText(desc) if text != 'null': words = mdiText.gcode_words() if text in words: parm = text + ' ' for index, value in enumerate(words[text], start=0): parm += value self.w.gcode_parameters.setText(parm) else: self.w.gcode_parameters.setText('') # settings tab def chk_override_limits_checked(self, state): if state: print("Override limits set") ACTION.SET_LIMITS_OVERRIDE() else: print("Override limits not set") def chk_run_from_line_checked(self, state): if not state: self.w.lbl_start_line.setText('1') ##################### # GENERAL FUNCTIONS # ##################### def kb_jog(self, state, joint, direction, fast = False, linear = True): if not STAT.is_man_mode() or not STAT.machine_is_on(): self.add_alarm('Machine must be ON and in Manual mode to jog') return if linear: distance = STAT.get_jog_increment() rate = STAT.get_jograte()/60 else: distance = STAT.get_jog_increment_angular() rate = STAT.get_jograte_angular()/60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) def add_alarm(self, message): STAT.emit('update-machine-log', message, 'TIME') def alarm_added(self): self.w.led_alarm.setState(True) def tab_changed(self, index): self.w.btn_gcode_edit.setChecked(False) self.btn_gcode_edit_clicked(False) if index == 4: self.w.led_alarm.setState(False) def set_dro_homed(self, state): self.w.action_home_all.setProperty('homed', state) self.w.action_home_all.setStyle(self.w.action_home_all.style()) for i in map(str.lower, self.axis_list): self.w["dro_axis_{}".format(i)].setProperty('homed', state) self.w["dro_axis_{}".format(i)].setStyle(self.w["dro_axis_{}".format(i)].style()) def enable_onoff(self, state): for widget in self.onoff_list: self.w[widget].setEnabled(state) def set_start_line(self, line): if self.w.chk_run_from_line.isChecked(): self.w.lbl_start_line.setText(str(line)) else: self.w.lbl_start_line.setText('1') self.add_alarm('Run from line is disabled') def use_keyboard(self): if self.w.chk_use_keyboard.isChecked(): return True else: self.add_alarm('Keyboard shortcuts are disabled') return False ##################### # KEY BINDING CALLS # ##################### def on_keycall_ESTOP(self,event,state,shift,cntrl): if state: ACTION.SET_ESTOP_STATE(True) def on_keycall_POWER(self,event,state,shift,cntrl): if state: ACTION.SET_MACHINE_STATE(False) def on_keycall_ABORT(self,event,state,shift,cntrl): if state: ACTION.ABORT() def on_keycall_HOME(self,event,state,shift,cntrl): if state and not STAT.is_all_homed() and self.use_keyboard(): ACTION.SET_MACHINE_HOMING(-1) def on_keycall_pause(self,event,state,shift,cntrl): if state and STAT.is_auto_mode() and self.use_keyboard(): ACTION.PAUSE() def on_keycall_XPOS(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 2, -1, shift) def on_keycall_plus(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 3, 1, shift, False) def on_keycall_minus(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 3, -1, shift, False) def on_keycall_F12(self,event,state,shift,cntrl): if state: self.STYLEEDITOR.load_dialog() ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATH = paths self._big_view = -1 self.STYLEEDITOR = SSE(widgets, paths) self.flag = 0 self.activeStyle = ''' { background-color: white;}''' self.defaultStyle = ''' { background-color: light blue;}''' self.activeWidgetDict = { 'programPage': False, 'userPage': False, 'machinePage': False, 'tooloffsetsPage': False, 'loadPage': False, 'mdiPage': False, 'workoffsetsPage': False, 'setupPage': False } self.current_mode = (None, None) self._last_count = 0 ########################################## # Special Functions called from QTVCP ########################################## # For changing functions in widgets we can 'class patch'. # class patching must be done before the class is instantiated. # def class_patch__(self): GCODE.exitCall = self.editor_exit # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): STATUS.emit('play-sound', 'SPEAK This is a test screen for Haas styled QT lathe') KEYBIND.add_call('Key_F3', 'on_keycall_F3') KEYBIND.add_call('Key_F4', 'on_keycall_F4') KEYBIND.add_call('Key_F5', 'on_keycall_F5') KEYBIND.add_call('Key_F6', 'on_keycall_F6') KEYBIND.add_call('Key_F7', 'on_keycall_F7') KEYBIND.add_call('Key_F12', 'on_keycall_F12') self.pin_mpg_in = self.hal.newpin('mpg-in', hal.HAL_S32, hal.HAL_IN) self.pin_mpg_in.value_changed.connect(lambda s: self.external_mpg(s)) self.pin_cycle_start_in = self.hal.newpin('cycle-start-in', hal.HAL_BIT, hal.HAL_IN) self.pin_cycle_start_in.value_changed.connect( lambda s: self.cycleStart(s)) self.pin_abort = self.hal.newpin('abort', hal.HAL_BIT, hal.HAL_IN) self.pin_abort.value_changed.connect(lambda s: self.abort(s)) self.pin_select_scroll = self.hal.newpin('select-scroll', hal.HAL_BIT, hal.HAL_IN) self.pin_select_fo = self.hal.newpin('select-feedoverride', hal.HAL_BIT, hal.HAL_IN) self.pin_pin_select_ro = self.hal.newpin('select-rapidoverride', hal.HAL_BIT, hal.HAL_IN) self.pin_select_so = self.hal.newpin('select-spindleoverride', hal.HAL_BIT, hal.HAL_IN) def before_loop__(self): STATUS.connect('state-estop', self.showclose) self.w.close() # This is because STATUS actual sends two of every message def showclose(self, w): self.flag += 1 if self.flag == 2: self.w.close() self.flag = 0 def processed_key_event__(self, receiver, event, is_pressed, key, code, shift, cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in (QtCore.Qt.Key_Escape, QtCore.Qt.Key_F1, QtCore.Qt.Key_F2, QtCore.Qt.Key_F3, QtCore.Qt.Key_F5, QtCore.Qt.Key_F5): raise # ok if we got here then try keybindings try: return KEYBIND.call(self, event, is_pressed, shift, cntrl) except NameError as e: LOG.debug('Exception in KEYBINDING: {}'.format(e)) except Exception as e: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s' % ( KEYBIND.convert(event), key) return False ######################## # callbacks from STATUS # ######################## ####################### # callbacks from form # ####################### def toggle_prog(self): cur = self.w.mainPaneStack.currentIndex() if self.current_mode == ('program', 'run'): self.w.mainPaneStack.setCurrentIndex(1) self.set_active_mode('program', 'load') else: self.w.mainPaneStack.setCurrentIndex(0) self.set_active_mode('program', 'run') def toggle_MDI(self): self.w.mainPaneStack.setCurrentIndex(0) cur = self.w.widgetswitcher.currentIndex() if cur == 4: next = self.w.mdi_tab.currentIndex() + 1 if next > self.w.mdi_tab.count() - 1: next = 0 self.w.mdi_tab.setCurrentIndex(next) else: self.w.widgetswitcher.setCurrentIndex(4) self.w.mdi_tab.setCurrentIndex(0) self.set_active_mode('mdi', cur) def toggle_setup(self): self.w.widgetswitcher.setCurrentIndex(3) self.set_active_mode('setup', None) def toggle_dro(self): cur = self.w.droPaneStack.currentIndex() if cur == 0: self.w.droPaneStack.setCurrentIndex(cur + 1) else: self.w.droPaneStack.setCurrentIndex(0) def toggle_offsets(self): self.w.mainPaneStack.setCurrentIndex(0) cur = self.w.widgetswitcher.currentIndex() if cur == 2: self.set_active_mode('offsetPage', 'tool') self.w.widgetswitcher.setCurrentIndex(0) else: self.w.widgetswitcher.setCurrentIndex(2) self.set_active_mode('offsetPage', 'work') def set_edit_mode(self, num): if num == 2: self.w.gcodeeditor.editMode() else: self.w.gcodeeditor.readOnlyMode() def toggle_graphics(self): cur = self.w.mainLeftStack.currentIndex() if cur == 0: if self.w.widgetswitcher.get_current_number() == 0: self.w.widgetswitcher.show_default() self.w.mainLeftStack.setCurrentIndex(1) elif self.w.widgetswitcher.get_current_number() == 1: self.w.widgetswitcher.show_default() self.w.mainLeftStack.setCurrentIndex(0) elif cur == 1: self.w.mainLeftStack.setCurrentIndex(0) self.w.widgetswitcher.show_id_widget(1) ##################### # general functions # ##################### def kb_jog(self, state, joint, direction, fast=False, linear=True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte() / 60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular() / 60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) def editor_exit(self): self.w.gcodeeditor.exit() def set_active_mode(self, mode, index): #print mode,index def update(widget): for key, value in self.activeWidgetDict.iteritems(): #print mode,key,value if key == widget: print widget self.w[key].setStyleSheet('#%s%s' % (key, self.activeStyle)) self.activeWidgetDict[key] = True elif value == True: print 'switch off', key self.w[key].setStyleSheet('#%s%s' % (key, self.defaultStyle)) self.activeWidgetDict[key] = False if mode == 'program': if index == 'run': update('programPage') self.w.label_mode.setText('Operation-Run Program') else: update('loadPage') self.w.label_mode.setText('Operation-Load Program') elif mode == 'setup': update('setupPage') self.w.label_mode.setText('Operation- Manual Setup') elif mode == 'mdi': update('mdiPage') self.w.label_mode.setText('Operation- MDI Control') elif mode == 'offsetPage': if index == 'tool': update('tooloffsetsPage') elif index == 'work': update('workoffsetsPage') else: print('mode/index not recognized') return self.current_mode = (mode, index) def abort(self, state): if not state: return if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: ACTION.ABORT() def cycleStart(self, state): print state, self.current_mode if state: if self.current_mode[0] == 'program': if self.current_mode[1] == 'run': print 'start cycle!', self.w.gcode_editor.get_line() ACTION.RUN(line=0) elif self.current_mode[1] == 'load': print 'load program' self.w.filemanager.load() elif self.current_mode[0] == 'mdi': self.w.mdihistory.run_command() # MPG scolling of program or MDI history def external_mpg(self, count): diff = count - self._last_count if self.pin_select_scroll.get(): if self.current_mode[0] == 'program': if self.current_mode[1] == 'run': self.w.gcode_editor.jump_line(diff) elif self.current_mode[1] == 'load': if diff < 0: self.w.filemanager.down() else: self.w.filemanager.up() elif self.current_mode[0] == 'mdi': if diff < 0: self.w.mdihistory.line_down() else: self.w.mdihistory.line_up() elif self.pin_select_fo.get(): scaled = (STATUS.stat.feedrate * 100 + diff) if scaled < 0: scaled = 0 elif scaled > INFO.MAX_FEED_OVERRIDE: scaled = INFO.MAX_FEED_OVERRIDE ACTION.SET_FEED_RATE(scaled) elif self.pin_pin_select_ro.get(): scaled = (STATUS.stat.rapidrate * 100 + diff) if scaled < 0: scaled = 0 elif scaled > 100: scaled = 100 ACTION.SET_RAPID_RATE(scaled) elif self.pin_select_so.get(): scaled = (STATUS.stat.spindle[0]['override'] * 100 + diff) if scaled < INFO.MIN_SPINDLE_OVERRIDE: scaled = INFO.MIN_SPINDLE_OVERRIDE elif scaled > INFO.MAX_SPINDLE_OVERRIDE: scaled = INFO.MAX_SPINDLE_OVERRIDE ACTION.SET_SPINDLE_RATE(scaled) self._last_count = count ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self, event, state, shift, cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self, event, state, shift, cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self, event, state, shift, cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self, event, state, shift, cntrl): if state: self.abort(state) # dialogs def on_keycall_F3(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'ORIGINOFFSET'}) def on_keycall_F4(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'CAMVIEW'}) def on_keycall_F5(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'MACROTAB'}) def on_keycall_F6(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'TOOLOFFSET'}) def on_keycall_F7(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'VERSAPROBE'}) def on_keycall_F12(self, event, state, shift, cntrl): if state: self.STYLEEDITOR.load_dialog() # Linear Jogging def on_keycall_XPOS(self, event, state, shift, cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self, event, state, shift, cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self, event, state, shift, cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self, event, state, shift, cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self, event, state, shift, cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self, event, state, shift, cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) ########################### # **** closing event **** # ########################### def closing_cleanup__(self): pass ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
def __init__(self, halcomp,widgets,paths): self.hal = halcomp self.w = widgets self.PATH = paths self._big_view = -1 self.STYLEEDITOR = SSE(widgets,paths)
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets, paths) ########################################## # Special Functions called from QTSCREEN ########################################## # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): KEYBIND.add_call('Key_F12', 'on_keycall_F12') def processed_key_event__(self, receiver, event, is_pressed, key, code, shift, cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in (QtCore.Qt.Key_Escape, QtCore.Qt.Key_F1, QtCore.Qt.Key_F2, QtCore.Qt.Key_F3, QtCore.Qt.Key_F5, QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = False break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STATUS.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True # ok if we got here then try keybindings try: return KEYBIND.call(self, event, is_pressed, shift, cntrl) except NameError as e: LOG.debug('Exception in KEYBINDING: {}'.format(e)) except Exception as e: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s' % ( KEYBIND.convert(event), key) return False ######################## # callbacks from STATUS # ######################## ####################### # callbacks from form # ####################### ##################### # general functions # ##################### # keyboard jogging from key binding calls # double the rate if fast is true def kb_jog(self, state, joint, direction, fast=False, linear=True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte() / 60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular() / 60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self, event, state, shift, cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self, event, state, shift, cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self, event, state, shift, cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self, event, state, shift, cntrl): if state: if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: self.cmnd.abort() # Linear Jogging def on_keycall_XPOS(self, event, state, shift, cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self, event, state, shift, cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self, event, state, shift, cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self, event, state, shift, cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self, event, state, shift, cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self, event, state, shift, cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) def on_keycall_F12(self, event, state, shift, cntrl): if state: self.STYLEEDITOR.load_dialog() ########################### # **** closing event **** # ########################### ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp,widgets,paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets,paths) global TOOLBAR TOOLBAR = ToolBarActions(widgets) STATUS.connect('general',self.return_value) STATUS.connect('motion-mode-changed',self.motion_mode) STATUS.connect('user-system-changed', self._set_user_system_text) STATUS.connect('actual-spindle-speed-changed',self.update_spindle) ########################################## # Special Functions called from QTSCREEN ########################################## def class_patch__(self): GCODE.exitCall = self.editor_exit # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): KEYBIND.add_call('Key_F12','on_keycall_F12') KEYBIND.add_call('Key_QuoteLeft','on_keycall_feedoverride',0) KEYBIND.add_call('Key_1','on_keycall_feedoverride',10) KEYBIND.add_call('Key_2','on_keycall_feedoverride',20) KEYBIND.add_call('Key_3','on_keycall_feedoverride',30) KEYBIND.add_call('Key_4','on_keycall_feedoverride',40) KEYBIND.add_call('Key_5','on_keycall_feedoverride',50) KEYBIND.add_call('Key_6','on_keycall_feedoverride',60) KEYBIND.add_call('Key_7','on_keycall_feedoverride',70) KEYBIND.add_call('Key_8','on_keycall_feedoverride',80) KEYBIND.add_call('Key_9','on_keycall_feedoverride',90) KEYBIND.add_call('Key_0','on_keycall_feedoverride',100) #KEYBIND.add_call('Key_AsciiTilde','on_keycall_spindleoverride',0) #KEYBIND.add_call('Key_Exclam','on_keycall_spindleoverride',10) #KEYBIND.add_call('Key_At','on_keycall_spindleoverride',20) #KEYBIND.add_call('Key_NumberSign','on_keycall_spindleoverride',30) #KEYBIND.add_call('Key_DollarSign','on_keycall_spindleoverride',40) #KEYBIND.add_call('Key_Percent','on_keycall_spindleoverride',50) KEYBIND.add_call('Key_AsciiCircum','on_keycall_spindleoverride',60) KEYBIND.add_call('Key_Ampersand','on_keycall_spindleoverride',70) KEYBIND.add_call('Key_Asterisk','on_keycall_spindleoverride',80) KEYBIND.add_call('Key_Parenleft','on_keycall_spindleoverride',90) KEYBIND.add_call('Key_ParenRight','on_keycall_spindleoverride',100) KEYBIND.add_call('Key_Underscore','on_keycall_spindleoverride',110) TOOLBAR.configure_submenu(self.w.menuRecent, 'recent_submenu') TOOLBAR.configure_submenu(self.w.menuHoming, 'home_submenu') TOOLBAR.configure_submenu(self.w.menuUnhome, 'unhome_submenu') TOOLBAR.configure_submenu(self.w.menuZeroCoordinateSystem, 'zero_systems_submenu') TOOLBAR.configure_action(self.w.actionEstop, 'estop') TOOLBAR.configure_action(self.w.actionMachineOn, 'power') TOOLBAR.configure_action(self.w.actionOpen, 'load') TOOLBAR.configure_action(self.w.actionReload, 'Reload') TOOLBAR.configure_action(self.w.actionRun, 'run') TOOLBAR.configure_action(self.w.actionPause, 'pause') TOOLBAR.configure_action(self.w.actionStop, 'abort') TOOLBAR.configure_action(self.w.actionSkip, 'block_delete') TOOLBAR.configure_action(self.w.actionOptionalStop, 'optional_stop') TOOLBAR.configure_action(self.w.actionZoomIn, 'zoom_in') TOOLBAR.configure_action(self.w.actionZoomOut, 'zoom_out') TOOLBAR.configure_action(self.w.actionFrontView, 'view_x') TOOLBAR.configure_action(self.w.actionSideView, 'view_y') TOOLBAR.configure_action(self.w.actionRotatedView, 'view_z2') TOOLBAR.configure_action(self.w.actionTopView, 'view_z') TOOLBAR.configure_action(self.w.actionPerspectiveView, 'view_p') TOOLBAR.configure_action(self.w.actionClearPlot, 'view_clear') TOOLBAR.configure_action(self.w.actionShowOffsets, 'show_offsets') TOOLBAR.configure_action(self.w.actionQuit, 'Quit', lambda d:self.w.close()) TOOLBAR.configure_action(self.w.actionShutdown, 'system_shutdown') TOOLBAR.configure_action(self.w.actionProperties, 'gcode_properties') TOOLBAR.configure_action(self.w.actionCalibration, 'load_calibration') TOOLBAR.configure_action(self.w.actionStatus, 'load_status') TOOLBAR.configure_action(self.w.actionHalshow, 'load_halshow') TOOLBAR.configure_action(self.w.actionHalmeter, 'load_halmeter') TOOLBAR.configure_action(self.w.actionHalscope, 'load_halscope') TOOLBAR.configure_action(self.w.actionAbout, 'about') TOOLBAR.configure_action(self.w.actionTouchoffWorkplace, 'touchoffworkplace') TOOLBAR.configure_action(self.w.actionEdit, 'edit', self.edit) TOOLBAR.configure_action(self.w.actionTouchoffFixture, 'touchofffixture') TOOLBAR.configure_action(self.w.actionRunFromLine, 'runfromline') TOOLBAR.configure_action(self.w.actionToolOffsetDialog, 'tooloffsetdialog') TOOLBAR.configure_action(self.w.actionOriginOffsetDialog, 'originoffsetdialog') self.w.actionQuickRef.triggered.connect(self.quick_reference) self.w.actionMachineLog.triggered.connect(self.launch_log_dialog) if not INFO.HOME_ALL_FLAG: self.w.actionButton_home.setText("Home Selected") self.w.actionButton_home.set_home_select(True) self.w.rpm_bar = QtWidgets.QProgressBar() self.w.rpm_bar.setRange(0, INFO.MAX_SPINDLE_SPEED) self.w.rightTab.setCornerWidget(self.w.rpm_bar) def processed_key_event__(self,receiver,event,is_pressed,key,code,shift,cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in(QtCore.Qt.Key_Escape,QtCore.Qt.Key_F1 ,QtCore.Qt.Key_F2, QtCore.Qt.Key_F3,QtCore.Qt.Key_F5,QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = True break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # send events to gcode widget if in edit mode # else do our keybindings if self.w.actionEdit.isChecked() == True: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True # ok if we got here then try keybindings try: b = KEYBIND.call(self,event,is_pressed,shift,cntrl) event.accept() return True except NameError as e: LOG.debug('Exception in KEYBINDING: {}'.format (e)) except Exception as e: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s'%(KEYBIND.convert(event),key) return False def closing_cleanup__(self): TOOLBAR.saveRecentPaths() ######################## # callbacks from STATUS # ######################## # process the STATUS return message from set-tool-offset def return_value(self, w, message): num = message['RETURN'] code = bool(message['ID'] == 'FORM__') name = bool(message['NAME'] == 'ENTRY') if num is not None and code and name: LOG.debug('message return:{}'.format (message)) axis = message['AXIS'] fixture = message['FIXTURE'] ACTION.SET_TOOL_OFFSET(axis,num,fixture) STATUS.emit('update-machine-log', 'Set tool offset of Axis %s to %f' %(axis, num), 'TIME') def motion_mode(self, w, mode): #print STATUS.stat.joints #print STATUS.stat.kinematics_type #print INFO.AVAILABLE_AXES #print INFO.GET_NAME_FROM_JOINT if mode == linuxcnc.TRAJ_MODE_COORD: pass # Joint mode elif mode == linuxcnc.TRAJ_MODE_FREE: if STATUS.stat.kinematics_type == linuxcnc.KINEMATICS_IDENTITY: self.show_axes() else: self.show_joints() elif mode == linuxcnc.TRAJ_MODE_TELEOP: self.show_axes() def update_spindle(self,w,data): self.w.rpm_bar.setInvertedAppearance(bool(data<0)) self.w.rpm_bar.setFormat('{0:d} RPM'.format(int(data))) self.w.rpm_bar.setValue(abs(data)) ####################### # callbacks from form # ####################### def leftTabChanged(self, num): if num == 0: ACTION.SET_MANUAL_MODE() ##################### # general functions # ##################### def show_joints(self): for i in range(0,9): if i in INFO.AVAILABLE_JOINTS: self.w['ras_label_%s'%i].show() self.w['ras_%s'%i].show() self.w['ras_label_%s'%i].setText('J%d'%i) try: self.w['machine_label_j%d'%i].setText('<html><head/><body><p><span style=" font-size:20pt; font-weight:600;">Joint %d:</span></p></body></html>'%i) except: pass continue self.w['ras_label_%s'%i].hide() self.w['ras_%s'%i].hide() def show_axes(self): for i in range(0,9): j = INFO.GET_NAME_FROM_JOINT.get(i) if j and len(j) == 1: self.w['ras_label_%s'%i].show() self.w['ras_%s'%i].show() self.w['ras_label_%s'%i].setText('%s'%j) try: self.w['machine_label_j%d'%i].setText('<html><head/><body><p><span style=" font-size:20pt; font-weight:600;">Machine %s:</span></p></body></html>' %j) except: pass continue self.w['ras_label_%s'%i].hide() self.w['ras_%s'%i].hide() def _set_user_system_text(self, w, data): convert = { 1:"G54 ", 2:"G55 ", 3:"G56 ", 4:"G57 ", 5:"G58 ", 6:"G59 ", 7:"G59.1 ", 8:"G59.2 ", 9:"G59.3 "} unit = convert[int(data)] for i in ('x','y','z'): self.w['dro_label_g5x_%s'%i].imperial_template = unit + i.upper() + '%9.4f' self.w['dro_label_g5x_%s'%i].metric_template = unit + i.upper() + '%10.3f' self.w['dro_label_g5x_%s'%i].update_units() self.w.dro_label_g5x_r.angular_template = unit + 'R %3.2f' self.w.dro_label_g5x_r.update_units() self.w.dro_label_g5x_r.update_rotation(None, STATUS.stat.rotation_xy) def editor_exit(self): self.w.gcode_editor.exit() self.w.actionEdit.setChecked(False) self.edit(None,False) def edit(self, widget, state): if state: self.w.gcode_editor.editMode() self.w.gcode_editor.setMaximumHeight(1000) self.w.frame.hide() self.w.rightTab.hide() else: self.w.gcode_editor.readOnlyMode() self.w.gcode_editor.setMaximumHeight(500) self.w.frame.show() self.w.rightTab.show() def quick_reference(self): help1 = [ ("F1", _("Emergency stop")), ("F2", _("Turn machine on")), ("", ""), ("X", _("Activate first axis")), ("Y", _("Activate second axis")), ("Z", _("Activate third axis")), ("A", _("Activate fourth axis")), ("` or 0,1..8", _("Activate first through ninth joint <br>if joints radiobuttons visible")), ("", _("")), ("`,1..9,0", _("Set Feed Override from 0% to 100%")), ("", _("if axes radiobuttons visible")), (_(", and ."), _("Select jog speed")), (_("< and >"), _("Select angular jog speed")), (_("I, Shift-I"), _("Select jog increment")), ("C", _("Continuous jog")), (_("Home"), _("Send active joint home")), (_("Ctrl-Home"), _("Home all joints")), (_("Shift-Home"), _("Zero G54 offset for active axis")), (_("End"), _("Set G54 offset for active axis")), (_("Ctrl-End"), _("Set tool offset for loaded tool")), ("-, =", _("Jog active axis or joint")), (";, '", _("Select Max velocity")), ("", ""), (_("Left, Right"), _("Jog first axis or joint")), (_("Up, Down"), _("Jog second axis or joint")), (_("Pg Up, Pg Dn"), _("Jog third axis or joint")), (_("Shift+above jogs"), _("Jog at traverse speed")), ("[, ]", _("Jog fourth axis or joint")), ("", ""), ("D", _("Toggle between Drag and Rotate mode")), (_("Left Button"), _("Pan, rotate or select line")), (_("Shift+Left Button"), _("Rotate or pan")), (_("Right Button"), _("Zoom view")), (_("Wheel Button"), _("Rotate view")), (_("Rotate Wheel"), _("Zoom view")), (_("Control+Left Button"), _("Zoom view")), ] help2 = [ ("F3", _("Manual control")), ("F5", _("Code entry (MDI)")), (_("Control-M"), _("Clear MDI history")), (_("Control-H"), _("Copy selected MDI history elements")), ("", _("to clipboard")), (_("Control-Shift-H"), _("Paste clipboard to MDI history")), ("L", _("Override Limits")), ("", ""), ("O", _("Open program")), (_("Control-R"), _("Reload program")), (_("Control-S"), _("Save g-code as")), ("R", _("Run program")), ("T", _("Step program")), ("P", _("Pause program")), ("S", _("Resume program")), ("ESC", _("Stop running program, or")), ("", _("stop loading program preview")), ("", ""), ("F7", _("Toggle mist")), ("F8", _("Toggle flood")), ("B", _("Spindle brake off")), (_("Shift-B"), _("Spindle brake on")), ("F9", _("Turn spindle clockwise")), ("F10", _("Turn spindle counterclockwise")), ("F11", _("Turn spindle more slowly")), ("F12", _("Turn spindle more quickly")), (_("Control-K"), _("Clear live plot")), ("V", _("Cycle among preset views")), ("F4", _("Cycle among preview, DRO, and user tabs")), ("@", _("toggle Actual/Commanded")), ("#", _("toggle Relative/Machine")), (_("Ctrl-Space"), _("Clear notifications")), (_("Alt-F, M, V"), _("Open a Menu")), ] help = zip(help1,help2) msg = QtWidgets.QDialog() msg.setWindowTitle("Quick Reference") button = QtWidgets.QPushButton("Ok") button.clicked.connect(lambda: msg.close()) edit = QtWidgets.QTextEdit() edit.setLineWrapMode(0) mess = '''<TABLE border="1"><COLGROUP> <COL><COL align="char" char="."><THEAD> <TR><TH>Key <TH>Command<TH>Key <TH>Command <TBODY>''' for i,j in help: m='<TR><TD><b>%s</b> <TD>%s<TD><b>%s</b> <TD>%s'%(i[0],i[1],j[0],j[1]) mess += m mess += '</TABLE' edit.setText(mess) edit.setReadOnly(True) layout = QtWidgets.QVBoxLayout() layout.addWidget(edit) layout.addWidget(button) msg.setLayout(layout) msg.setMinimumSize(700,800) msg.show() retval = msg.exec_() def launch_log_dialog(self): STATUS.emit('dialog-request',{'NAME':'MACHINELOG', 'ID':'_qtaxis_handler_'}) # keyboard jogging from key binding calls # double the rate if fast is true def kb_jog(self, state, joint, direction, fast = False, linear = True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte()/60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular()/60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self,event,state,shift,cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self,event,state,shift,cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self,event,state,shift,cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self,event,state,shift,cntrl): if state: if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: ACTION.ABORT() # Linear Jogging def on_keycall_XPOS(self,event,state,shift,cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self,event,state,shift,cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self,event,state,shift,cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self,event,state,shift,cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self,event,state,shift,cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self,event,state,shift,cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self,event,state,shift,cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self,event,state,shift,cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) def on_keycall_F12(self,event,state,shift,cntrl): if state: self.STYLEEDITOR.load_dialog() def on_keycall_feedoverride(self,event,state,shift,cntrl,value): if state: ACTION.SET_FEED_RATE(value) def on_keycall_spindleoverride(self,event,state,shift,cntrl,value): if state: ACTION.SET_SPINDLE_RATE(value) ########################### # **** closing event **** # ########################### ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATH = paths.CONFIGPATH self.IMAGE_PATH = paths.IMAGEDIR self._big_view = -1 self.STYLEEDITOR = SSE(widgets, paths) ########################################## # Special Functions called from QTVCP ########################################## # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): STATUS.emit('play-alert', 'SPEAK This is a test screen for Qt V C P') KEYBIND.add_call('Key_F3', 'on_keycall_F3') KEYBIND.add_call('Key_F4', 'on_keycall_F4') KEYBIND.add_call('Key_F5', 'on_keycall_F5') KEYBIND.add_call('Key_F6', 'on_keycall_F6') KEYBIND.add_call('Key_F7', 'on_keycall_F7') KEYBIND.add_call('Key_F12', 'on_keycall_F12') self.w.tooloffsetdialog._geometry_string = '0 0 600 400 onwindow ' def processed_key_event__(self, receiver, event, is_pressed, key, code, shift, cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in (QtCore.Qt.Key_Escape, QtCore.Qt.Key_F1, QtCore.Qt.Key_F2, QtCore.Qt.Key_F3, QtCore.Qt.Key_F5, QtCore.Qt.Key_F5): if isinstance(receiver, OFFVIEW_WIDGET) or \ isinstance(receiver, MDI_WIDGET) or isinstance(receiver, TOOLVIEW_WIDGET): if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif isinstance(receiver, GCODE) and STATUS.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif isinstance(receiver, QDialog): print 'dialog' return True try: return KEYBIND.call(self, event, is_pressed, shift, cntrl) except NameError as e: LOG.debug('Exception in KEYBINDING: {}'.format(e)) except Exception as e: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s' % ( KEYBIND.convert(event), key) return False ######################## # callbacks from STATUS # ######################## ####################### # callbacks from form # ####################### def widget_switch(self, data): self.w.widgetswitcher.show_next() def set_edit_mode(self, num): if num == 1: self.w.gcodeeditor.editMode() else: self.w.gcodeeditor.readOnlyMode() ##################### # general functions # ##################### def kb_jog(self, state, joint, direction, fast=False, linear=True): if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte() / 60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular() / 60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) # called from 'machine on' button's python command in designer # to test that function def test_function(self, text=None): print text ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self, event, state, shift, cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self, event, state, shift, cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self, event, state, shift, cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self, event, state, shift, cntrl): if state: if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: ACTION.ABORT() # dialogs def on_keycall_F3(self, event, state, shift, cntrl): if state: self.w.originoffsetdialog.load_dialog() def on_keycall_F4(self, event, state, shift, cntrl): if state: self.w.camviewdialog.load_dialog() def on_keycall_F5(self, event, state, shift, cntrl): if state: self.w.macrotabdialog.load_dialog() def on_keycall_F6(self, event, state, shift, cntrl): if state: self.w.tooloffsetdialog.load_dialog() def on_keycall_F7(self, event, state, shift, cntrl): if state: self.w.versaprobedialog.load_dialog() def on_keycall_F12(self, event, state, shift, cntrl): if state: self.STYLEEDITOR.load_dialog() # Linear Jogging def on_keycall_XPOS(self, event, state, shift, cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self, event, state, shift, cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self, event, state, shift, cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self, event, state, shift, cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self, event, state, shift, cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self, event, state, shift, cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) ########################### # **** closing event **** # ########################### def closing_cleanup__(self): pass ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp,widgets,paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets,paths) self.picked_line = None STATUS.connect('general',self.return_value) STATUS.connect('graphics-line-selected', self.set_picked_line) ########################################## # Special Functions called from QTSCREEN ########################################## # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): KEYBIND.add_call('Key_F12','on_keycall_F12') def processed_key_event__(self,receiver,event,is_pressed,key,code,shift,cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in(QtCore.Qt.Key_Escape,QtCore.Qt.Key_F1 ,QtCore.Qt.Key_F2, QtCore.Qt.Key_F3,QtCore.Qt.Key_F5,QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = False break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STATUS.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True if event.isAutoRepeat():return True # ok if we got here then try keybindings function calls # KEYBINDING will call functions from handler file as # registered by KEYBIND.add_call(KEY,FUNCTION) above return KEYBIND.manage_function_calls(self,event,is_pressed,key,shift,cntrl) ######################## # callbacks from STATUS # ######################## # process the STATUS return message for run-from-line def return_value(self, obj, message): num = message['RETURN'] code = bool(message['ID'] == '_RunFromLine_') name = bool(message['NAME'] == 'CALCULATOR') if num is not None and code and name: ACTION.RUN(int(num)) def set_picked_line(self, obj, line): self.picked_line = line ####################### # callbacks from form # ####################### def full_screen(self, state): if state: self.w.stackedWidget_0.setCurrentIndex(1) self.w.widgetswitcher.show_id_widget(1) else: self.w.stackedWidget_0.setCurrentIndex(0) self.w.widgetswitcher.show_id_widget(0) def slow_jog_slider_changed(self, rate): if self.w.pbtn_jog_rate_slow.isChecked(): ACTION.SET_JOG_RATE(rate) def fast_jog_slider_changed(self, rate): if self.w.pbtn_jog_rate_fast.isChecked(): ACTION.SET_JOG_RATE(rate) def set_slow_rate(self, state): if state: ACTION.SET_JOG_RATE(self.w.scrb_jog_linear_slow.value()) def set_fast_rate(self, state): if state: ACTION.SET_JOG_RATE(self.w.scrb_jog_linear_fast.value()) def run_from_line_clicked(self): mess = {'NAME':'CALCULATOR','ID':'_RunFromLine_', 'PRELOAD':self.picked_line, 'TITLE':'Run From Line Dialog'} ACTION.CALL_DIALOG('dialog-request', mess) ##################### # general functions # ##################### # keyboard jogging from key binding calls # double the rate if fast is true def kb_jog(self, state, joint, direction, fast = False, linear = True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte()/60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular()/60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self,event,state,shift,cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self,event,state,shift,cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self,event,state,shift,cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self,event,state,shift,cntrl): if state: if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: ACTION.ABORT() # Linear Jogging def on_keycall_XPOS(self,event,state,shift,cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self,event,state,shift,cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self,event,state,shift,cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self,event,state,shift,cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self,event,state,shift,cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self,event,state,shift,cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self,event,state,shift,cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self,event,state,shift,cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) def on_keycall_F12(self,event,state,shift,cntrl): if state: self.STYLEEDITOR.load_dialog() ########################### # **** closing event **** # ########################### ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp, widgets, paths): # some global variables self.hal = halcomp self.w = widgets self.PATH = paths self._big_view = -1 self.flag = 0 self.activeStyle = ''' { background-color: white;}''' self.defaultStyle = ''' { background-color: light blue;}''' self.activeWidgetDict = { 'programPage': False, 'userPage': False, 'machinePage': False, 'tooloffsetsPage': False, 'loadPage': False, 'mdiPage': False, 'workoffsetsPage': False, 'setupPage': False } self.current_mode = (None, None) self._last_count = 0 self.run_time = 0 self.time_tenths = 0 self.timerOn = False self.slow_jog_factor = 10 self.STYLEEDITOR = SSE(widgets, paths) self.GCODES = GCodes() STATUS.connect('periodic', lambda w: self.update_runtimer()) STATUS.connect('command-running', lambda w: self.start_timer()) STATUS.connect('command-stopped', lambda w: self.stop_timer()) STATUS.connect("metric-mode-changed", lambda w, d: self.mode_changed(d)) STATUS.connect('state-off', lambda w: self.w.pushbutton_metric.setEnabled(False)) STATUS.connect('state-estop', lambda w: self.w.pushbutton_metric.setEnabled(False)) STATUS.connect( 'interp-idle', lambda w: self.w.pushbutton_metric.setEnabled( self.homed_on_test())) STATUS.connect('interp-run', lambda w: self.w.pushbutton_metric.setEnabled(False)) STATUS.connect('all-homed', lambda w: self.w.pushbutton_metric.setEnabled(True)) STATUS.connect( 'not-all-homed', lambda w, data: self.w.pushbutton_metric.setEnabled(False)) ########################################## # Special Functions called from QTVCP ########################################## # For changing functions in widgets we can 'class patch'. # class patching must be done before the class is instantiated. # def class_patch__(self): GCODE.exitCall = self.editor_exit # patch filemanager so we can trap single click loading # (doesn't work well on a touchscreen) # you can only load using a button defined in designer now # keep a reference to the original function as 'superLoad' FILEMGR.superLoad = FILEMGR.load FILEMGR.load = self.FMGRnoop # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): STATUS.emit('play-sound', 'SPEAK This is a test screen for Haas styled QT lathe') KEYBIND.add_call('Key_F3', 'on_keycall_F3') KEYBIND.add_call('Key_F4', 'on_keycall_F4') KEYBIND.add_call('Key_F5', 'on_keycall_F5') KEYBIND.add_call('Key_F6', 'on_keycall_F6') KEYBIND.add_call('Key_F7', 'on_keycall_F7') KEYBIND.add_call('Key_F9', 'on_keycall_F9') KEYBIND.add_call('Key_F11', 'on_keycall_F11') KEYBIND.add_call('Key_F12', 'on_keycall_F12') TOOLBAR.configure_action(self.w.actionCalculatorDialog, 'calculatordialog') TOOLBAR.configure_submenu(self.w.menuGridSize, 'grid_size_submenu') TOOLBAR.configure_action(self.w.actionToolOffsetDialog, 'tooloffsetdialog') TOOLBAR.configure_action(self.w.actionReload, 'Reload') TOOLBAR.configure_statusbar(self.w.statusbar, 'message_controls') self.w.pushbutton_metric.clicked[bool].connect(self.change_mode) # web view widget for SETUP SHEET page self.web_view = QWebView() self.w.verticalLayout_setup.addWidget(self.web_view) self.set_default_html() self.GCODES.setup_list() self.w.gcode_editor.hide() def before_loop__(self): STATUS.connect('state-estop', lambda q: self.w.close()) def processed_key_event__(self, receiver, event, is_pressed, key, code, shift, cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in (QtCore.Qt.Key_Escape, QtCore.Qt.Key_F1, QtCore.Qt.Key_F2, QtCore.Qt.Key_F3, QtCore.Qt.Key_F5, QtCore.Qt.Key_F5, QtCore.Qt.Key_F6, QtCore.Qt.Key_F7, QtCore.Qt.Key_F11, QtCore.Qt.Key_F12): raise if event.isAutoRepeat(): return True # ok if we got here then try keybindings try: return KEYBIND.call(self, event, is_pressed, shift, cntrl) except NameError as e: LOG.debug('Exception in KEYBINDING: {}'.format(e)) except Exception as e: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s' % ( KEYBIND.convert(event), key) return False ######################## # callbacks from STATUS # ######################## def runtime_sec_changed(self, data): text = "{:02d}:{:02d}:{:02d}".format(self.h['runtime_hrs'], self.h['runtime_min'], self.h['runtime_sec']) self.w.lbl_runtime.setText(text) def file_loaded(self, obj, filename): if filename is not None: self.w.progressBar.setValue(0) self.last_loaded_program = filename else: self.add_alarm("Filename not valid") def all_homed(self, obj): self.set_dro_homed(True) if self.first_turnon is True: self.first_turnon = False if self.w.chk_reload_tool.isChecked(): STATUS.emit('update-machine-log', 'PreLoad Tool #{}: '.format(self.reload_tool), 'TIME') command = "M61 Q{}".format(self.reload_tool) ACTION.CALL_MDI(command) if self.last_loaded_program is not None and self.w.chk_reload_program.isChecked( ): STATUS.emit('update-machine-log', 'PreLoading NGC: ' + self.last_loaded_program, 'TIME') ACTION.OPEN_PROGRAM(self.last_loaded_program) self.w.filemanager.updateDirectoryView( self.last_loaded_program) def not_all_homed(self, obj, list): self.home_all = False self.w.lbl_home_all.setText("HOME\nALL") for i in INFO.AVAILABLE_JOINTS: if str(i) in list: axis = INFO.GET_NAME_FROM_JOINT.get(i).lower() try: self.w["dro_axis_{}".format(axis)].setProperty( 'homed', False) self.w["dro_axis_{}".format(axis)].setStyle( self.w["dro_axis_{}".format(axis)].style()) except: pass ####################### # callbacks from form # ####################### def percentLoaded(self, fraction): if fraction < 1: self.w.progressBar.setValue(0) self.w.progressBar.setFormat('') else: self.w.progressBar.setValue(fraction) self.w.progressBar.setFormat('Loading: {}%'.format(fraction)) def percentCompleted(self, fraction): self.w.progressBar.setValue(fraction) if fraction < 1: self.w.progressBar.setFormat('') else: self.w.progressBar.setFormat('Completed: {}%'.format(fraction)) def toggle_prog(self): if self.current_mode == ('program', 'run'): self.set_active_mode('program', 'load') else: self.set_active_mode('program', 'run') def toggle_MDI(self): self.set_active_mode('mdi', None) def toggle_setup(self): self.set_active_mode('setup', None) def toggle_dro(self): next = self.w.droPaneStack.currentIndex() + 1 if next == self.w.droPaneStack.count(): self.w.droPaneStack.setCurrentIndex(0) else: self.w.droPaneStack.setCurrentIndex(next) def toggle_offsets(self): self.set_active_mode('offsetPage', None) def set_edit_mode(self, num): if num == 2: self.w.gcodeeditor.editMode() else: self.w.gcodeeditor.readOnlyMode() def toggle_graphics(self): self.set_active_mode('graphics', None) # tool tab def btn_m61_clicked(self): checked = self.w.tooloffsetview.get_checked_list() if len(checked) > 1: self.add_alarm("Select only 1 tool to load") elif checked: ACTION.CALL_MDI("M61 Q{}".format(checked[0])) else: self.add_alarm("No tool selected") # alarm tab def btn_clear_alarms_clicked(self): ACTION.UPDATE_MACHINE_LOG('', 'DELETE') def btn_save_alarms_clicked(self): text = self.w.machinelog.toPlainText() filename = self.w.lbl_clock.text().encode('utf-8') filename = 'alarms_' + filename.replace(' ', '_') + '.txt' with open(filename, 'w') as f: f.write(text) def btn_reload_file_clicked(self): if self.last_loaded_program: self.w.progressBar.setValue(0) ACTION.OPEN_PROGRAM(self.last_loaded_program) def slow_jog_clicked(self, state): slider = self.w.sender().property('slider') current = self.w[slider].value() max = self.w[slider].maximum() if state: self.w.sender().setText("SLOW") self.w[slider].setMaximum(max / self.slow_jog_factor) self.w[slider].setValue(current / self.slow_jog_factor) self.w[slider].setPageStep(1) else: self.w.sender().setText("FAST") self.w[slider].setMaximum(max * self.slow_jog_factor) self.w[slider].setValue(current * self.slow_jog_factor) self.w[slider].setPageStep(1) ##################### # general functions # ##################### def kb_jog(self, state, joint, direction, fast=False, linear=True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte() / 60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular() / 60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) def editor_exit(self): self.btn_gcode_edit_clicked(False) def set_active_mode(self, mode, index): #print mode,index def update(widget): for key, value in self.activeWidgetDict.iteritems(): #print mode,key,value if key == widget: print widget self.w[key].setStyleSheet('#%s%s' % (key, self.activeStyle)) self.activeWidgetDict[key] = True elif value == True: print 'switch off', key self.w[key].setStyleSheet('#%s%s' % (key, self.defaultStyle)) self.activeWidgetDict[key] = False if mode == 'program': if index == 'run': self.w.mainLeftStack.setCurrentIndex(0) # gcode self.w.mainPaneStack.setCurrentIndex(0) # normal update('programPage') self.w.label_mode.setText('Operation-Run Program') else: self.w.mainLeftStack.setCurrentIndex(0) # gcode self.w.mainPaneStack.setCurrentIndex(1) # load update('loadPage') self.w.label_mode.setText('Operation-Load Program') elif mode == 'setup': self.w.mainPaneStack.setCurrentIndex(0) # normal self.w.widgetswitcher.setCurrentIndex(3) # setup manual self.w.mainLeftStack.setCurrentIndex(1) # setup html update('setupPage') self.w.label_mode.setText('Operation- Manual Setup') elif mode == 'mdi': self.w.mainPaneStack.setCurrentIndex(0) cur = self.w.widgetswitcher.currentIndex() if cur == 4: next = self.w.mdi_tab.currentIndex() + 1 if next > self.w.mdi_tab.count() - 1: next = 0 self.w.mdi_tab.setCurrentIndex(next) else: self.w.widgetswitcher.setCurrentIndex(4) self.w.mdi_tab.setCurrentIndex(0) update('mdiPage') self.w.label_mode.setText('Operation- MDI Control') elif mode == 'offsetPage': self.w.mainPaneStack.setCurrentIndex(0) cur = self.w.widgetswitcher.currentIndex() if cur == 2: self.w.widgetswitcher.setCurrentIndex(0) update('tooloffsetsPage') else: self.w.widgetswitcher.setCurrentIndex(2) update('workoffsetsPage') elif mode == 'graphics': cur = self.w.mainLeftStack.currentIndex() if cur == 0: # gcode if self.w.widgetswitcher.get_current_number() == 0: self.w.widgetswitcher.show_default() self.w.mainLeftStack.setCurrentIndex(2) elif self.w.widgetswitcher.get_current_number() == 1: self.w.widgetswitcher.show_default() self.w.mainLeftStack.setCurrentIndex(0) elif cur == 2: self.w.mainLeftStack.setCurrentIndex(0) self.w.widgetswitcher.show_id_widget(1) else: print('mode/index not recognized') return self.current_mode = (mode, index) def btn_start_macro_clicked(self): self.w.label_mode.setText('Operation- MDI Control') self.w.mditouchy.run_command() return def abort(self, state): if not state: return if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: ACTION.ABORT() def make_progressbar(self): self.w.progressbBar = QtWidgets.QProgressBar() self.w.progressBar.setRange(0, 100) self.w.statusBar.addWidget(self.w.progressBar) def update_runtimer(self): if self.timerOn is False or STATUS.is_auto_paused(): return self.time_tenths += 1 if self.time_tenths == 10: self.time_tenths = 0 self.run_time += 1 hours, remainder = divmod(self.run_time, 3600) minutes, seconds = divmod(remainder, 60) self.w.lbl_runtime.setText("{:02d}:{:02d}:{:02d}".format( hours, minutes, seconds)) def start_timer(self): self.run_time = 0 self.timerOn = True def stop_timer(self): self.timerOn = False def mode_changed(self, data): self._block_signal = True self.w.pushbutton_metric.setChecked(data) # if using state labels option update the labels if self.w.pushbutton_metric._state_text: self.w.pushbutton_metric.setText(None) self._block_signal = False def change_mode(self, data): if self._block_signal: return if data: ACTION.CALL_MDI('G21') else: ACTION.CALL_MDI('G20') def homed_on_test(self): return (STATUS.machine_is_on() and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED)) # file tab def btn_gcode_edit_clicked(self, state): if not STATUS.is_on_and_idle(): return for x in ["load", "next", "prev"]: self.w["btn_file_{}".format(x)].setEnabled(not state) if state: self.w.filemanager.hide() self.w.gcode_editor.show() self.w.gcode_editor.editMode() else: self.w.filemanager.show() self.w.gcode_editor.hide() self.w.gcode_editor.readOnlyMode() def btn_load_file_clicked(self): fname = self.w.filemanager.getCurrentSelected() if fname[1] is True: self.load_code(fname[0]) # class patched filemanager to trap single click loading # this makes the original function do nothing def FMGRnoop(self, fname): pass def load_code(self, fname): if fname is None: return if fname.endswith(".ngc") or fname.endswith(".py"): # call original filemanager load function to load program self.w.filemanager.superLoad(fname) # change filepath extension to autoload an html setup page fname = os.path.splitext(fname)[0] + '.html' if fname.endswith(".html"): if os.path.exists(fname): self.web_view.load(QtCore.QUrl.fromLocalFile(fname)) return self.set_default_html(fname) def set_default_html(self, filename=None): if filename is None: filename = 'No program Loaded' print filename self.html = """<html> <head> <title>Test page for the download:// scheme</title> </head> <body> <h1>Setup Tab</h1> <p> tried loading::%s</p> <p>If there was a HTML setup file , it would auto load and be shown here..</p> <img src="file://%s" alt="lcnc_swoop" /> <hr /> <a href="http://linuxcnc.org/docs/html/lathe/lathe-user.html">Lathe User Information link</a> </body> </html> """ % (filename, os.path.join(self.PATH.IMAGEDIR, 'lcnc_swoop.png')) self.web_view.setHtml(self.html) ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self, event, state, shift, cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self, event, state, shift, cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self, event, state, shift, cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self, event, state, shift, cntrl): if state: self.abort(state) def on_keycall_pause(self, event, state, shift, cntrl): if state and STATUS.is_auto_mode() and self.use_keyboard(): ACTION.PAUSE() # dialogs def on_keycall_F3(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'ORIGINOFFSET'}) def on_keycall_F4(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'CAMVIEW'}) def on_keycall_F6(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'TOOLOFFSET'}) def on_keycall_F7(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'VERSAPROBE'}) def on_keycall_F9(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'Calculator'}) def on_keycall_F11(self, event, state, shift, cntrl): if state: pass def on_keycall_F12(self, event, state, shift, cntrl): if state: self.STYLEEDITOR.load_dialog() # Linear Jogging def on_keycall_XPOS(self, event, state, shift, cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self, event, state, shift, cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self, event, state, shift, cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self, event, state, shift, cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self, event, state, shift, cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self, event, state, shift, cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) ########################### # **** closing event **** # ########################### def closing_cleanup__(self): pass ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: def __init__(self, halcomp, widgets, paths): self.w = widgets self.PATHS = paths self.hal = halcomp INIPATH = os.environ.get('INI_FILE_NAME', '/dev/null') self.inifile = linuxcnc.ini(INIPATH) self.STYLEEDITOR = SSE(widgets,paths) KEYBIND.add_call('Key_F12','on_keycall_F12') # STAT.connect('general',self.return_value) STAT.connect('state-on', self.machine_on) STAT.connect('state-off', self.machine_off) STAT.connect('gcode-line-selected', self.gcode_line_selected) STAT.connect('hard-limits-tripped', self.hard_limit_tripped) STAT.connect("interp-idle", self.interp_idle_changed) STAT.connect("user-system-changed", self.user_system_changed) STAT.connect("tool-in-spindle-changed", self.tool_in_spindle_changed) STAT.connect("file-loaded", self.file_loaded) STAT.connect("all-homed", self.all_homed) STAT.connect("not-all-homed", self.not_homed) # some global variables self.axis_list = INFO.AVAILABLE_AXES self.joint_list = INFO.AVAILABLE_JOINTS self.max_velocity = INFO.MAX_LINEAR_VELOCITY self.system_list = ["G53","G54","G55","G56","G57","G58","G59","G59.1","G59.2","G59.3"] self.home_location_x = self.inifile.find('JOINT_0', 'HOME') self.home_location_y = self.inifile.find('JOINT_1', 'HOME') self.home_location_z = self.inifile.find('JOINT_2', 'HOME') self.tool_sensor_x = self.inifile.find('TOOLSENSOR', 'X') self.tool_sensor_y = self.inifile.find('TOOLSENSOR', 'Y') self.laser_offset_x = self.inifile.find('LASER', 'X') self.laser_offset_y = self.inifile.find('LASER', 'Y') self.homed = False self.start_line = 0 self.program_length = 0 self.slow_jog_factor = 10 self.tool_in_spindle = 0 self.reload_tool = 0 self.last_loaded_program = "" self.onoff_list = ["widget_controls", "Program_frame", "DRO_frame"] def initialized__(self): self.init_pins() self.init_preferences() self.init_widgets() self.init_locations() # initialize DRO style for i in map(str.lower, self.axis_list): self.w["dro_axis_{}".format(i)].setStyle(self.w["dro_axis_{}".format(i)].style()) ############################# # SPECIAL FUNCTIONS SECTION # ############################# def init_pins(self): # these pins are needed so that the touchoff subroutine can read the variables self.hal.newpin("touch_height", hal.HAL_FLOAT, hal.HAL_OUT) self.hal.newpin("sensor_height", hal.HAL_FLOAT, hal.HAL_OUT) self.hal.newpin("zero_height", hal.HAL_FLOAT, hal.HAL_OUT) self.hal.newpin("search_vel", hal.HAL_FLOAT, hal.HAL_OUT) self.hal.newpin("probe_vel", hal.HAL_FLOAT, hal.HAL_OUT) self.hal.newpin("max_probe", hal.HAL_FLOAT, hal.HAL_OUT) def init_preferences(self): if self.w.PREFS_: self.reload_tool = self.w.PREFS_.getpref('Tool to load', 0, int,'CUSTOM_FORM_ENTRIES') self.last_loaded_program = self.w.PREFS_.getpref('last_file_path', None, str,'BOOK_KEEPING') temp1 = self.w.PREFS_.getpref('Reload program', False, bool,'CUSTOM_FORM_ENTRIES') temp2 = self.w.PREFS_.getpref('Reload tool', False, bool,'CUSTOM_FORM_ENTRIES') temp3 = self.w.PREFS_.getpref('Tool sensor', False, bool,'CUSTOM_FORM_ENTRIES') else: temp1 = temp2 = temp3 = False self.add_alarm("No preference file found") self.w.checkBox_reload_program.setChecked(temp1) self.w.checkBox_reload_tool.setChecked(temp2) self.w.checkBox_tool_sensor.setChecked(temp3) self.chk_tool_sensor(temp3) def init_widgets(self): self.w.main_tab_widget.setCurrentIndex(0) self.w.slider_jog.setMaximum(self.max_velocity * 60) self.w.slider_jog.setValue(INFO.DEFAULT_LINEAR_JOG_VEL) self.w.slider_maxv.setMaximum(self.max_velocity * 60) self.w.slider_maxv.setValue(self.max_velocity * 60) self.w.slider_feed.setMaximum(INFO.MAX_FEED_OVERRIDE) self.w.slider_feed.setValue(100) self.w.slider_rapid.setMaximum(100) self.w.slider_rapid.setValue(100) self.w.slider_spindle.setMinimum(INFO.MIN_SPINDLE_OVERRIDE) self.w.slider_spindle.setMaximum(INFO.MAX_SPINDLE_OVERRIDE) self.w.slider_spindle.setValue(100) self.w.checkBox_override_limits.setChecked(False) self.w.checkBox_override_limits.setEnabled(False) self.w.filemanager.show() self.w.gcode_editor.hide() self.w.btn_from_line.setEnabled(False) def init_locations(self): touch_height = self.inifile.find('TOOLSENSOR', 'TOUCH_HEIGHT') or "50" sensor_height = self.inifile.find('TOOLSENSOR', 'SENSOR_HEIGHT') or "50" max_probe = self.inifile.find('TOOLSENSOR', 'MAXPROBE') or "40" search_vel = self.inifile.find('TOOLSENSOR', 'SEARCH_VEL') or "200" probe_vel = self.inifile.find('TOOLSENSOR', 'PROBE_VEL') or "50" self.w.lbl_maxv.setText(str(self.max_velocity * 60)) self.w.lbl_touch_height.setText(touch_height) self.w.lbl_sensor_height.setText(sensor_height) self.w.lbl_maxprobe.setText(max_probe) self.w.lbl_search_vel.setText(search_vel) self.w.lbl_probe_vel.setText(probe_vel) self.hal['touch_height'] = touch_height self.hal['sensor_height'] = sensor_height self.hal['max_probe'] = max_probe self.hal['search_vel'] = search_vel self.hal['probe_vel'] = probe_vel # home location if not self.home_location_x or not self.home_location_y or not self.home_location_z: self.w.btn_go_home.setEnabled(False) self.w.groupBox_home.hide() self.w.lbl_no_home.show() self.add_alarm("No valid home location found") else: self.w.lbl_home_x.setText(self.home_location_x) self.w.lbl_home_y.setText(self.home_location_y) self.w.lbl_no_home.hide() # laser offsets if not self.laser_offset_x or not self.laser_offset_y: self.w.btn_ref_laser.setEnabled(False) self.w.btn_laser_on.setEnabled(False) self.w.groupBox_laser.hide() self.w.lbl_no_laser_offsets.show() self.add_alarm("No valid laser offsets found") else: self.w.lbl_laser_x.setText(self.laser_offset_x) self.w.lbl_laser_y.setText(self.laser_offset_y) self.w.lbl_no_laser_offsets.hide() # tool sensor location if not self.tool_sensor_x or not self.tool_sensor_y: self.w.chk_tool_sensor.hide() self.w.btn_go_g30.setEnabled(False) self.w.groupBox_sensor.hide() self.w.lbl_no_toolsensor.show() self.add_alarm("No valid tool sensor location found") else: self.w.lbl_sensor_x.setText(self.tool_sensor_x) self.w.lbl_sensor_y.setText(self.tool_sensor_y) self.w.lbl_no_toolsensor.hide() def processed_key_event__(self,receiver,event,is_pressed,key,code,shift,cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in(QtCore.Qt.Key_Escape,QtCore.Qt.Key_F1 ,QtCore.Qt.Key_F2): # QtCore.Qt.Key_F3,QtCore.Qt.Key_F4,QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, QtWidgets.QLineEdit): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = True break if isinstance(receiver2, TOOL_TABLE): flag = True break if isinstance(receiver2, OFFSET_VIEW): flag = True break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STAT.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True # ok if we got here then try keybindings try: return KEYBIND.call(self,event,is_pressed,shift,cntrl) except NameError as e: self.add_alarm('Exception in KEYBINDING: {}'.format (e)) except Exception as e: LOG.error('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s'%(KEYBIND.convert(event),key) return False ######################### # CALLBACKS FROM STATUS # ######################### def machine_on(self, obj): for widget in self.onoff_list: self.w[widget].setEnabled(True) def machine_off(self, obj): for widget in self.onoff_list: self.w[widget].setEnabled(False) def gcode_line_selected(self, obj, data): if self.w.btn_from_line.isChecked(): self.start_line = data self.w.btn_start.setText("START FROM {}".format(data)) def interp_idle_changed(self, obj): self.start_line = 0 self.w.btn_start.setText("START FROM 0") def user_system_changed(self, obj, data): sys = self.system_list[int(data)] self.w.actionbutton_rel.setText(sys) def tool_in_spindle_changed(self, obj, data): self.tool_in_spindle = data def file_loaded(self, obj, filename): if filename is not None: self.w.progressBar.setValue(0) self.last_loaded_program = filename fileobject = file(filename, 'r') lines = fileobject.readlines() fileobject.close() self.program_length = len(lines) self.start_line = 0 self.w.btn_from_line.setEnabled(True) self.w.btn_start.setText("START FROM 0") else: self.w.btn_from_line.setEnabled(False) self.add_alarm("Filename not valid") def all_homed(self, obj): self.homed = True self.w.actionbutton_view_p.click() for i in map(str.lower, self.axis_list): self.w["dro_axis_{}".format(i)].setProperty('homed', True) self.w["dro_axis_{}".format(i)].setStyle(self.w["dro_axis_{}".format(i)].style()) if self.tool_in_spindle == 0 and self.w.checkBox_reload_tool.checkState(): command = "M61 Q{}".format(self.reload_tool) ACTION.CALL_MDI_WAIT(command) if self.last_loaded_program and self.w.checkBox_reload_program.checkState(): ACTION.OPEN_PROGRAM(self.last_loaded_program) self.w.filemanager.updateDirectoryView(self.last_loaded_program) def not_homed(self, obj, data): self.homed = False for i in map(str.lower, self.axis_list): self.w["dro_axis_{}".format(i)].setProperty('homed', False) self.w["dro_axis_{}".format(i)].setStyle(self.w["dro_axis_{}".format(i)].style()) def hard_limit_tripped(self, obj, tripped, list_of_tripped): self.w.checkBox_override_limits.setEnabled(tripped) if not tripped: self.w.checkBox_override_limits.setChecked(False) ####################### # CALLBACKS FROM FORM # ####################### # program frame def btn_start_clicked(self): if not STAT.is_auto_mode(): return self.w.btn_from_line.setChecked(False) self.add_alarm("Started program from line {}".format(self.start_line)) ACTION.RUN(self.start_line) def btn_reload_file_clicked(self): if self.last_loaded_program: self.w.progressBar.setValue(0) ACTION.OPEN_PROGRAM(self.last_loaded_program) # tool frame def btn_go_home_clicked(self): ACTION.CALL_MDI_WAIT("G53 G0 Z0") command = "G53 G0 X{} Y{}".format(self.w.lbl_home_x.text(), self.w.lbl_home_y.text()) ACTION.CALL_MDI_WAIT(command) def btn_go_g30_clicked(self): ACTION.CALL_MDI_WAIT("G53 G0 Z0") command = "G53 G0 X{} Y{}".format(self.w.lbl_sensor_x.text(), self.w.lbl_sensor_y.text()) ACTION.CALL_MDI_WAIT(command) def btn_ref_laser_clicked(self): command = "G10 L20 P0 X{} Y{}".format(self.w.lbl_laser_x.text(), self.w.lbl_laser_y.text()) ACTION.CALL_MDI_WAIT(command) def btn_from_line_clicked(self, state): if state is False: self.start_line = 0 self.w.btn_start.setText("START FROM 0") def btn_touchoff_clicked(self): code = None if self.tool_in_spindle == 0: self.add_alarm("Cannot probe with no tool loaded") return if self.w.btn_touchoff.text() == "TOOL\nSENSOR": self.hal['zero_height'] = self.w.lineEdit_zero_height.text().encode('utf-8') code = "o< tool_sensor > call" elif self.w.btn_touchoff.text() == "TOUCH\nPLATE": code = "o< touch_plate > call" if not code is None: ACTION.CALL_OWORD(code) STAT.emit('forced-update') # override frame def btn_slow_clicked(self, state): if state: current = self.w.slider_jog.value() self.w.btn_slow.setText("SLOW") self.w.slider_jog.setMaximum(self.max_velocity * 60 / self.slow_jog_factor) self.w.slider_jog.setValue(current / self.slow_jog_factor) else: current = self.w.slider_jog.value() self.w.btn_slow.setText("FAST") self.w.slider_jog.setMaximum(self.max_velocity * 60) self.w.slider_jog.setValue(current * self.slow_jog_factor) def btn_maxv_max_clicked(self): self.w.slider_maxv.setValue(self.max_velocity * 60) # file tab def btn_gcode_edit_clicked(self, state): if not STAT.is_on_and_idle(): return for x in ["load", "next", "prev"]: self.w["btn_file_{}".format(x)].setEnabled(not state) if state: self.w.filemanager.hide() self.w.gcode_editor.show() self.w.gcode_editor.editMode() else: self.w.filemanager.show() self.w.gcode_editor.hide() self.w.gcode_editor.readOnlyMode() # tool tab def btn_m61_clicked(self): checked = self.w.tooloffsetview.get_checked_list() if len(checked) > 1: self.add_alarm("Select only 1 tool to load") elif checked: ACTION.CALL_MDI_WAIT("M61 Q{}".format(checked[0])) else: self.add_alarm("No tool selected") # alarm tab def btn_clear_alarms_clicked(self): STAT.emit('update-machine-log', None, 'DELETE') def btn_save_alarms_clicked(self): text = self.w.machinelog.toPlainText() filename = self.w.lbl_clock.text().encode('utf-8') filename = 'alarms_' + filename.replace(' ','_') + '.txt' with open(filename, 'w') as f: f.write(text) # settings tab def chk_tool_sensor(self, state): if state: self.w.btn_touchoff.setText("TOOL\nSENSOR") else: self.w.btn_touchoff.setText("TOUCH\nPLATE") def chk_override_limits(self, state): if state: print("Override limits set") ACTION.SET_LIMITS_OVERRIDE() else: print("Override limits not set") ##################### # GENERAL FUNCTIONS # ##################### def kb_jog(self, state, joint, direction, fast = False, linear = True): if not STAT.is_man_mode() or not STAT.machine_is_on(): return if linear: distance = STAT.get_jog_increment() rate = STAT.get_jograte()/60 else: distance = STAT.get_jog_increment_angular() rate = STAT.get_jograte_angular()/60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) def add_alarm(self, message): STAT.emit('update-machine-log', message, 'TIME') def alarm_added(self): self.w.led_alarm.setState(True) def tab_changed(self, index): self.w.btn_gcode_edit.setChecked(False) self.btn_gcode_edit_clicked(False) if index == 4: self.w.led_alarm.setState(False) ##################### # KEY BINDING CALLS # ##################### def on_keycall_ESTOP(self,event,state,shift,cntrl): if state: ACTION.SET_ESTOP_STATE(True) def on_keycall_POWER(self,event,state,shift,cntrl): if state: ACTION.SET_MACHINE_STATE(False) def on_keycall_ABORT(self,event,state,shift,cntrl): if state: ACTION.ABORT() def on_keycall_HOME(self,event,state,shift,cntrl): if state and self.homed is False: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_XPOS(self,event,state,shift,cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self,event,state,shift,cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self,event,state,shift,cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self,event,state,shift,cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self,event,state,shift,cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self,event,state,shift,cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_F12(self,event,state,shift,cntrl): if state: self.STYLEEDITOR.load_dialog() ########################### # **** closing event **** # ########################### # items to save in preference file def closing_cleanup__(self): if self.w.PREFS_: self.w.PREFS_.putpref('Reload program', self.w.checkBox_reload_program.checkState(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Tool to load', self.tool_in_spindle, int, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Reload tool', self.w.checkBox_reload_tool.checkState(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Tool sensor', self.w.checkBox_tool_sensor.checkState(), bool, 'CUSTOM_FORM_ENTRIES') ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp,widgets,paths): self.hal = halcomp self.w = widgets self.PATH = paths self._big_view = -1 self.STYLEEDITOR = SSE(widgets,paths) ########################################## # Special Functions called from QTVCP ########################################## # For changing functions in widgets we can 'class patch'. # class patching must be done before the class is instantiated. # def class_patch__(self): GCODE.exitCall = self.editor_exit # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): STATUS.emit('play-alert','SPEAK This is a test screen for Qt V C P') KEYBIND.add_call('Key_F3','on_keycall_F3') KEYBIND.add_call('Key_F4','on_keycall_F4') KEYBIND.add_call('Key_F5','on_keycall_F5') KEYBIND.add_call('Key_F6','on_keycall_F6') KEYBIND.add_call('Key_F7','on_keycall_F7') KEYBIND.add_call('Key_F12','on_keycall_F12') self.w.toolOffsetDialog_._geometry_string='0 0 600 400 onwindow ' def processed_key_event__(self,receiver,event,is_pressed,key,code,shift,cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in(QtCore.Qt.Key_Escape,QtCore.Qt.Key_F1 ,QtCore.Qt.Key_F2, QtCore.Qt.Key_F3,QtCore.Qt.Key_F5,QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = True break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STATUS.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True # ok if we got here then try keybindings try: return KEYBIND.call(self,event,is_pressed,shift,cntrl) except NameError as e: LOG.debug('Exception in KEYBINDING: {}'.format (e)) except Exception as e: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s'%(KEYBIND.convert(event),key) return False ######################## # callbacks from STATUS # ######################## ####################### # callbacks from form # ####################### def widget_switch(self,data): self.w.widgetswitcher.show_next() def set_edit_mode(self, num): if num == 2: self.w.gcodeeditor.editMode() else: self.w.gcodeeditor.readOnlyMode() ##################### # general functions # ##################### def kb_jog(self, state, joint, direction, fast = False, linear = True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte()/60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular()/60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) # called from 'machine on' button's python command in designer # to test that function def test_function(self, text=None): print text def editor_exit(self): self.w.gcodeeditor.exit() ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self,event,state,shift,cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self,event,state,shift,cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self,event,state,shift,cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self,event,state,shift,cntrl): if state: if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: ACTION.ABORT() # dialogs def on_keycall_F3(self,event,state,shift,cntrl): if state: STATUS.emit('dialog-request',{'NAME':'ORIGINOFFSET'}) def on_keycall_F4(self,event,state,shift,cntrl): if state: STATUS.emit('dialog-request',{'NAME':'CAMVIEW'}) def on_keycall_F5(self,event,state,shift,cntrl): if state: STATUS.emit('dialog-request',{'NAME':'MACROTAB'}) def on_keycall_F6(self,event,state,shift,cntrl): if state: STATUS.emit('dialog-request',{'NAME':'TOOLOFFSET'}) def on_keycall_F7(self,event,state,shift,cntrl): if state: STATUS.emit('dialog-request',{'NAME':'VERSAPROBE'}) def on_keycall_F12(self,event,state,shift,cntrl): if state: self.STYLEEDITOR.load_dialog() # Linear Jogging def on_keycall_XPOS(self,event,state,shift,cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self,event,state,shift,cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self,event,state,shift,cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self,event,state,shift,cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self,event,state,shift,cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self,event,state,shift,cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self,event,state,shift,cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self,event,state,shift,cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) ########################### # **** closing event **** # ########################### def closing_cleanup__(self): pass ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATH = paths self._big_view = -1 self.STYLEEDITOR = SSE(widgets, paths) ########################################## # Special Functions called from QTVCP ########################################## # For changing functions in widgets we can 'class patch'. # class patching must be done before the class is instantiated. # def class_patch__(self): GCODE.exitCall = self.editor_exit # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): STATUS.emit('play-sound', 'SPEAK This is a test screen for Qt V C P') KEYBIND.add_call('Key_F3', 'on_keycall_F3') KEYBIND.add_call('Key_F4', 'on_keycall_F4') KEYBIND.add_call('Key_F5', 'on_keycall_F5') KEYBIND.add_call('Key_F6', 'on_keycall_F6') KEYBIND.add_call('Key_F7', 'on_keycall_F7') KEYBIND.add_call('Key_F8', 'on_keycall_F8') KEYBIND.add_call('Key_F9', 'on_keycall_custom', 'f9 pressed tesst') KEYBIND.add_call('Key_F10', 'on_keycall_custom', 'f10 pressed tesst') KEYBIND.add_call('Key_F11', 'on_keycall_custom', 'f11 pressed test') KEYBIND.add_call('Key_F12', 'on_keycall_F12') self.w.toolOffsetDialog_._geometry_string = '0 0 600 400 onwindow ' def processed_key_event__(self, receiver, event, is_pressed, key, code, shift, cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in (QtCore.Qt.Key_Escape, QtCore.Qt.Key_F1, QtCore.Qt.Key_F2, QtCore.Qt.Key_F3, QtCore.Qt.Key_F5, QtCore.Qt.Key_F6, QtCore.Qt.Key_F7, QtCore.Qt.Key_F8, QtCore.Qt.Key_F12): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = True break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STATUS.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True # ok if we got here then try keybindings try: return KEYBIND.call(self, event, is_pressed, shift, cntrl) except NameError as e: LOG.debug('Exception in KEYBINDING: {}'.format(e)) except Exception as e: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s' % ( KEYBIND.convert(event), key) return False ######################## # callbacks from STATUS # ######################## ####################### # callbacks from form # ####################### def widget_switch(self, data): self.w.widgetswitcher.show_next() def set_edit_mode(self, num): if num == 2: self.w.gcodeeditor.editMode() else: self.w.gcodeeditor.readOnlyMode() ##################### # general functions # ##################### def kb_jog(self, state, joint, direction, fast=False, linear=True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte() / 60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular() / 60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) # called from 'machine on' button's python command in designer # to test that function def test_function(self, text=None): print text def editor_exit(self): self.w.gcodeeditor.exit() ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self, event, state, shift, cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self, event, state, shift, cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self, event, state, shift, cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self, event, state, shift, cntrl): if state: if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: ACTION.ABORT() # dialogs def on_keycall_F3(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'ORIGINOFFSET'}) def on_keycall_F4(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', { 'NAME': 'CAMVIEW', 'NONBLOCKING': True }) def on_keycall_F5(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'MACROTAB'}) def on_keycall_F6(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'TOOLOFFSET'}) def on_keycall_F7(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', {'NAME': 'VERSAPROBE'}) def on_keycall_F8(self, event, state, shift, cntrl): if state: STATUS.emit('dialog-request', { 'NAME': 'MACHINELOG', 'NONBLOCKING': True }) def on_keycall_F12(self, event, state, shift, cntrl): if state: self.STYLEEDITOR.load_dialog() # Linear Jogging def on_keycall_XPOS(self, event, state, shift, cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self, event, state, shift, cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self, event, state, shift, cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self, event, state, shift, cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self, event, state, shift, cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self, event, state, shift, cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) # f9, f10 and f11 call this function with different values def on_keycall_custom(self, event, state, shift, cntrl, value): if state: print 'custom keycall function value: ', value ########################### # **** closing event **** # ########################### def closing_cleanup__(self): pass ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: def __init__(self, halcomp, widgets, paths): self.h = halcomp self.w = widgets self.gcodes = GCodes(widgets) self.valid = QtGui.QDoubleValidator(-999.999, 999.999, 3) self.styleeditor = SSE(widgets, paths) KEYBIND.add_call('Key_F4', 'on_keycall_F4') KEYBIND.add_call('Key_F12','on_keycall_F12') KEYBIND.add_call('Key_Pause', 'on_keycall_PAUSE') KEYBIND.add_call('Key_Space', 'on_keycall_PAUSE') # some global variables self.factor = 1.0 self.probe = None self.default_setup = os.path.join(PATH.CONFIGPATH, "default_setup.html") self.docs = os.path.join(PATH.SCREENDIR, PATH.BASEPATH,'docs/getting_started.html') self.start_line = 0 self.run_time = 0 self.time_tenths = 0 self.timer_on = False self.home_all = False self.min_spindle_rpm = INFO.MIN_SPINDLE_SPEED self.max_spindle_rpm = INFO.MAX_SPINDLE_SPEED self.max_spindle_power = INFO.get_error_safe_setting('DISPLAY', 'MAX_SPINDLE_POWER',"0") self.max_linear_velocity = INFO.MAX_TRAJ_VELOCITY self.system_list = ["G54","G55","G56","G57","G58","G59","G59.1","G59.2","G59.3"] self.slow_jog_factor = 10 self.reload_tool = 0 self.last_loaded_program = "" self.first_turnon = True self.unit_label_list = ["ts_height", "tp_height", "zoffset_units", "max_probe_units"] self.lineedit_list = ["work_height", "touch_height", "sensor_height", "laser_x", "laser_y", "sensor_x", "sensor_y", "camera_x", "camera_y", "search_vel", "probe_vel", "max_probe", "eoffset_count"] self.onoff_list = ["frame_program", "frame_tool", "frame_offsets", "frame_dro", "frame_override"] self.axis_a_list = ["label_axis_a", "dro_axis_a", "action_zero_a", "axistoolbutton_a", "action_home_a", "widget_jog_angular", "widget_increments_angular", "a_plus_jogbutton", "a_minus_jogbutton"] STATUS.connect('general', self.dialog_return) STATUS.connect('state-on', lambda w: self.enable_onoff(True)) STATUS.connect('state-off', lambda w: self.enable_onoff(False)) STATUS.connect('mode-auto', lambda w: self.enable_auto(True)) STATUS.connect('gcode-line-selected', lambda w, line: self.set_start_line(line)) STATUS.connect('hard-limits-tripped', self.hard_limit_tripped) STATUS.connect('program-pause-changed', lambda w, state: self.w.btn_pause_spindle.setEnabled(state)) STATUS.connect('user-system-changed', lambda w, data: self.user_system_changed(data)) STATUS.connect('metric-mode-changed', lambda w, mode: self.metric_mode_changed(mode)) STATUS.connect('file-loaded', self.file_loaded) STATUS.connect('all-homed', self.all_homed) STATUS.connect('not-all-homed', self.not_all_homed) STATUS.connect('periodic', lambda w: self.update_runtimer()) STATUS.connect('interp-idle', lambda w: self.stop_timer()) STATUS.connect('actual-spindle-speed-changed',self.update_spindle) STATUS.connect('requested-spindle-speed-changed',self.update_spindle_requested) STATUS.connect('override-limits-changed', lambda w, state, data: self._check_override_limits(state, data)) self.html = """<html> <head> <title>Test page for the download:// scheme</title> </head> <body> <h1>Setup Tab</h1> <p>If you select a file with .html as a file ending, it will be shown here..</p> <img src="file://%s" alt="lcnc_swoop" /> <hr /> </body> </html> """ %(os.path.join(paths.IMAGEDIR,'lcnc_swoop.png')) def class_patch__(self): self.old_fman = FM.load FM.load = self.load_code def initialized__(self): self.init_pins() self.init_preferences() self.init_widgets() self.init_probe() self.init_utils() self.w.stackedWidget_log.setCurrentIndex(0) self.w.stackedWidget_dro.setCurrentIndex(0) if self.probe is not None: self.probe.hide() self.w.btn_pause_spindle.setEnabled(False) self.w.btn_dimensions.setChecked(True) self.w.page_buttonGroup.buttonClicked.connect(self.main_tab_changed) self.w.filemanager.onUserClicked() self.w.filemanager_usb.onMediaClicked() # hide widgets for A axis if not present if "A" not in INFO.AVAILABLE_AXES: for i in self.axis_a_list: self.w[i].hide() self.w.lbl_increments_linear.setText("INCREMENTS") # set validators for lineEdit widgets for val in self.lineedit_list: self.w['lineEdit_' + val].setValidator(self.valid) # set unit labels according to machine mode unit = "MM" if INFO.MACHINE_IS_METRIC else "IN" for i in self.unit_label_list: self.w['lbl_' + i].setText(unit) unit = "MM/MIN" if INFO.MACHINE_IS_METRIC else "IN/MIN" for i in ["search_vel_units", "probe_vel_units", "jog_linear"]: self.w['lbl_' + i].setText(unit) self.w.setWindowFlags(QtCore.Qt.FramelessWindowHint) ############################# # SPECIAL FUNCTIONS SECTION # ############################# def init_pins(self): # spindle control pins pin = self.h.newpin("spindle_amps", hal.HAL_FLOAT, hal.HAL_IN) pin.value_changed.connect(self.spindle_pwr_changed) pin = self.h.newpin("spindle_volts", hal.HAL_FLOAT, hal.HAL_IN) pin.value_changed.connect(self.spindle_pwr_changed) pin = self.h.newpin("spindle_fault", hal.HAL_U32, hal.HAL_IN) pin.value_changed.connect(self.spindle_fault_changed) # self.h.newpin("spindle_at_speed", hal.HAL_BIT, hal.HAL_IN) pin = self.h.newpin("modbus-errors", hal.HAL_U32, hal.HAL_IN) pin.value_changed.connect(self.mb_errors_changed) self.h.newpin("spindle_inhibit", hal.HAL_BIT, hal.HAL_OUT) # external offset control pins self.h.newpin("eoffset_enable", hal.HAL_BIT, hal.HAL_OUT) self.h.newpin("eoffset_clear", hal.HAL_BIT, hal.HAL_OUT) self.h.newpin("eoffset_count", hal.HAL_S32, hal.HAL_OUT) pin = self.h.newpin("eoffset_value", hal.HAL_FLOAT, hal.HAL_IN) pin.value_changed.connect(self.eoffset_changed) def init_preferences(self): if not self.w.PREFS_: self.add_status("CRITICAL - no preference file found, enable preferences in screenoptions widget") return self.last_loaded_program = self.w.PREFS_.getpref('last_loaded_file', None, str,'BOOK_KEEPING') self.reload_tool = self.w.PREFS_.getpref('Tool to load', 0, int,'CUSTOM_FORM_ENTRIES') self.w.lineEdit_laser_x.setText(str(self.w.PREFS_.getpref('Laser X', 100, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_laser_y.setText(str(self.w.PREFS_.getpref('Laser Y', -20, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_sensor_x.setText(str(self.w.PREFS_.getpref('Sensor X', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_sensor_y.setText(str(self.w.PREFS_.getpref('Sensor Y', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_camera_x.setText(str(self.w.PREFS_.getpref('Camera X', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_camera_y.setText(str(self.w.PREFS_.getpref('Camera Y', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_work_height.setText(str(self.w.PREFS_.getpref('Work Height', 20, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_touch_height.setText(str(self.w.PREFS_.getpref('Touch Height', 40, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_sensor_height.setText(str(self.w.PREFS_.getpref('Sensor Height', 40, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_search_vel.setText(str(self.w.PREFS_.getpref('Search Velocity', 40, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_probe_vel.setText(str(self.w.PREFS_.getpref('Probe Velocity', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_max_probe.setText(str(self.w.PREFS_.getpref('Max Probe', 10, float, 'CUSTOM_FORM_ENTRIES'))) self.w.lineEdit_eoffset_count.setText(str(self.w.PREFS_.getpref('Eoffset count', 0, int, 'CUSTOM_FORM_ENTRIES'))) self.w.chk_eoffsets.setChecked(self.w.PREFS_.getpref('External offsets', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_reload_program.setChecked(self.w.PREFS_.getpref('Reload program', False, bool,'CUSTOM_FORM_ENTRIES')) self.w.chk_reload_tool.setChecked(self.w.PREFS_.getpref('Reload tool', False, bool,'CUSTOM_FORM_ENTRIES')) self.w.chk_use_keyboard.setChecked(self.w.PREFS_.getpref('Use keyboard', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_use_tool_sensor.setChecked(self.w.PREFS_.getpref('Use tool sensor', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_use_touchplate.setChecked(self.w.PREFS_.getpref('Use tool touchplate', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_run_from_line.setChecked(self.w.PREFS_.getpref('Run from line', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_use_virtual.setChecked(self.w.PREFS_.getpref('Use virtual keyboard', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_use_camera.setChecked(self.w.PREFS_.getpref('Use camera', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_alpha_mode.setChecked(self.w.PREFS_.getpref('Use alpha display mode', False, bool, 'CUSTOM_FORM_ENTRIES')) self.w.chk_inhibit_selection.setChecked(self.w.PREFS_.getpref('Inhibit display mouse selection', True, bool, 'CUSTOM_FORM_ENTRIES')) def closing_cleanup__(self): if not self.w.PREFS_: return if self.last_loaded_program is not None: self.w.PREFS_.putpref('last_loaded_directory', os.path.dirname(self.last_loaded_program), str, 'BOOK_KEEPING') self.w.PREFS_.putpref('last_loaded_file', self.last_loaded_program, str, 'BOOK_KEEPING') self.w.PREFS_.putpref('Tool to load', STATUS.get_current_tool(), int, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Laser X', self.w.lineEdit_laser_x.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Laser Y', self.w.lineEdit_laser_y.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Sensor X', self.w.lineEdit_sensor_x.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Sensor Y', self.w.lineEdit_sensor_y.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Camera X', self.w.lineEdit_camera_x.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Camera Y', self.w.lineEdit_camera_y.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Work Height', self.w.lineEdit_work_height.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Touch Height', self.w.lineEdit_touch_height.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Sensor Height', self.w.lineEdit_sensor_height.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Search Velocity', self.w.lineEdit_search_vel.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Probe Velocity', self.w.lineEdit_probe_vel.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Max Probe', self.w.lineEdit_max_probe.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Eoffset count', self.w.lineEdit_eoffset_count.text().encode('utf-8'), int, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('External offsets', self.w.chk_eoffsets.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Reload program', self.w.chk_reload_program.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Reload tool', self.w.chk_reload_tool.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use keyboard', self.w.chk_use_keyboard.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use tool sensor', self.w.chk_use_tool_sensor.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use tool touchplate', self.w.chk_use_touchplate.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Run from line', self.w.chk_run_from_line.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use virtual keyboard', self.w.chk_use_virtual.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use camera', self.w.chk_use_camera.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Use alpha display mode', self.w.chk_alpha_mode.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') self.w.PREFS_.putpref('Inhibit display mouse selection', self.w.chk_inhibit_selection.isChecked(), bool, 'CUSTOM_FORM_ENTRIES') if self.probe is not None: self.probe.closing_cleanup__() def init_widgets(self): self.w.main_tab_widget.setCurrentIndex(0) self.w.slider_jog_linear.setMaximum(INFO.MAX_LINEAR_JOG_VEL) self.w.slider_jog_linear.setValue(INFO.DEFAULT_LINEAR_JOG_VEL) self.w.slider_jog_angular.setMaximum(INFO.MAX_ANGULAR_JOG_VEL) self.w.slider_jog_angular.setValue(INFO.DEFAULT_ANGULAR_JOG_VEL) self.w.slider_feed_ovr.setMaximum(INFO.MAX_FEED_OVERRIDE) self.w.slider_feed_ovr.setValue(100) self.w.slider_spindle_ovr.setMinimum(INFO.MIN_SPINDLE_OVERRIDE) self.w.slider_spindle_ovr.setMaximum(INFO.MAX_SPINDLE_OVERRIDE) self.w.slider_spindle_ovr.setValue(100) self.w.chk_override_limits.setChecked(False) self.w.chk_override_limits.setEnabled(False) self.w.lbl_maxv_percent.setText("100 %") self.w.lbl_max_rapid.setText(str(self.max_linear_velocity)) self.w.lbl_home_x.setText(INFO.get_error_safe_setting('JOINT_0', 'HOME',"50")) self.w.lbl_home_y.setText(INFO.get_error_safe_setting('JOINT_1', 'HOME',"50")) self.w.cmb_gcode_history.addItem("No File Loaded") self.w.cmb_gcode_history.wheelEvent = lambda event: None self.w.jogincrements_linear.wheelEvent = lambda event: None self.w.jogincrements_angular.wheelEvent = lambda event: None self.w.gcode_editor.hide() self.w.filemanager.table.setShowGrid(False) self.w.filemanager_usb.table.setShowGrid(False) self.w.tooloffsetview.setShowGrid(False) self.w.offset_table.setShowGrid(False) self.w.divider_line.hide() # move clock to statusbar self.w.statusbar.addPermanentWidget(self.w.lbl_clock) #set up gcode list self.gcodes.setup_list() # check for default setup html file try: # web view widget for SETUP page if self.w.web_view: self.toolBar = QtWidgets.QToolBar(self.w) self.w.layout_setup.addWidget(self.toolBar) self.backBtn = QtWidgets.QPushButton(self.w) self.backBtn.setEnabled(True) self.backBtn.setIcon(QtGui.QIcon(':/qt-project.org/styles/commonstyle/images/left-32.png')) self.backBtn.clicked.connect(self.back) self.toolBar.addWidget(self.backBtn) self.forBtn = QtWidgets.QPushButton(self.w) self.forBtn.setEnabled(True) self.forBtn.setIcon(QtGui.QIcon(':/qt-project.org/styles/commonstyle/images/right-32.png')) self.forBtn.clicked.connect(self.forward) self.toolBar.addWidget(self.forBtn) self.writeBtn = QtWidgets.QPushButton('SetUp\n Writer',self.w) self.writeBtn.setEnabled(True) self.writeBtn.clicked.connect(self.writer) self.toolBar.addWidget(self.writeBtn) self.w.layout_setup.addWidget(self.w.web_view) if os.path.exists(self.default_setup): self.w.web_view.load(QtCore.QUrl.fromLocalFile(self.default_setup)) else: self.w.web_view.setHtml(self.html) except Exception as e: print("No default setup file found - {}".format(e)) # set up spindle gauge self.w.gauge_spindle.set_max_value(self.max_spindle_rpm) self.w.gauge_spindle.set_max_reading(self.max_spindle_rpm / 1000) self.w.gauge_spindle.set_threshold(self.min_spindle_rpm) self.w.gauge_spindle.set_label("RPM") def init_probe(self): probe = INFO.get_error_safe_setting('PROBE', 'USE_PROBE', 'none').lower() if probe == 'versaprobe': LOG.info("Using Versa Probe") from qtvcp.widgets.versa_probe import VersaProbe self.probe = VersaProbe() self.probe.setObjectName('versaprobe') elif probe == 'basicprobe': LOG.info("Using Basic Probe") from qtvcp.widgets.basic_probe import BasicProbe self.probe = BasicProbe() self.probe.setObjectName('basicprobe') else: LOG.info("No valid probe widget specified") self.w.btn_probe.hide() return self.w.probe_layout.addWidget(self.probe) self.probe.hal_init() def init_utils(self): from qtvcp.lib.gcode_utility.facing import Facing self.facing = Facing() self.w.layout_facing.addWidget(self.facing) from qtvcp.lib.gcode_utility.hole_circle import Hole_Circle self.hole_circle = Hole_Circle() self.w.layout_hole_circle.addWidget(self.hole_circle) def processed_focus_event__(self, receiver, event): if not self.w.chk_use_virtual.isChecked() or STATUS.is_auto_mode(): return if isinstance(receiver, QtWidgets.QLineEdit): if not receiver.isReadOnly(): self.w.stackedWidget_dro.setCurrentIndex(1) elif isinstance(receiver, QtWidgets.QTableView): self.w.stackedWidget_dro.setCurrentIndex(1) elif isinstance(receiver, QtWidgets.QCommonStyle): return def processed_key_event__(self,receiver,event,is_pressed,key,code,shift,cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in(QtCore.Qt.Key_Escape,QtCore.Qt.Key_F1 ,QtCore.Qt.Key_F2): # QtCore.Qt.Key_F3,QtCore.Qt.Key_F4,QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, QtWidgets.QLineEdit): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = True break if isinstance(receiver2, TOOL_TABLE): flag = True break if isinstance(receiver2, OFFSET_VIEW): flag = True break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual or in readonly mode do our keybindings - otherwise # send events to gcode widget if STATUS.is_man_mode() == False or not receiver2.isReadOnly(): if is_pressed: receiver.keyPressEvent(event) event.accept() return True if is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True if event.isAutoRepeat():return True # ok if we got here then try keybindings function calls # KEYBINDING will call functions from handler file as # registered by KEYBIND.add_call(KEY,FUNCTION) above return KEYBIND.manage_function_calls(self,event,is_pressed,key,shift,cntrl) ######################### # CALLBACKS FROM STATUS # ######################### def update_spindle(self,w,data): self.w.gauge_spindle.update_value(abs(data)) def update_spindle_requested(self,w,data): self.w.gauge_spindle.set_setpoint(abs(data)) def spindle_pwr_changed(self, data): # this calculation assumes the voltage is line to neutral # that the current reported by the VFD is total current for all 3 phases # and that the synchronous motor spindle has a power factor of 0.9 try: power = float(self.h['spindle_volts'] * self.h['spindle_amps'] * 0.9) # V x I x PF pc_power = (power / float(self.max_spindle_power)) * 100 if pc_power > 100: pc_power = 100 self.w.spindle_power.setValue(int(pc_power)) except: self.w.spindle_power.setValue(0) def spindle_fault_changed(self, data): fault = hex(self.h['spindle_fault']) self.w.lbl_spindle_fault.setText(fault) def mb_errors_changed(self, data): errors = self.h['modbus-errors'] self.w.lbl_mb_errors.setText(str(errors)) def eoffset_changed(self, data): eoffset = "{:2.3f}".format(self.h['eoffset_value']) self.w.lbl_eoffset_value.setText(eoffset) def dialog_return(self, w, message): rtn = message.get('RETURN') name = message.get('NAME') plate_code = bool(message.get('ID') == '_touchplate_') sensor_code = bool(message.get('ID') == '_toolsensor_') wait_code = bool(message.get('ID') == '_wait_resume_') unhome_code = bool(message.get('ID') == '_unhome_') if plate_code and name == 'MESSAGE' and rtn is True: self.touchoff('touchplate') elif sensor_code and name == 'MESSAGE' and rtn is True: self.touchoff('sensor') elif wait_code and name == 'MESSAGE': self.h['eoffset_clear'] = False elif unhome_code and name == 'MESSAGE' and rtn is True: ACTION.SET_MACHINE_UNHOMED(-1) def user_system_changed(self, data): sys = self.system_list[int(data) - 1] self.w.offset_table.selectRow(int(data) + 3) self.w.actionbutton_rel.setText(sys) def metric_mode_changed(self, mode): unit = "MM" if mode else "IN" self.w.lbl_jog_linear.setText(unit + "/MIN") if INFO.MACHINE_IS_METRIC == mode: self.factor = 1.0 rapid = (float(self.w.slider_rapid_ovr.value()) / 100) * self.max_linear_velocity elif mode: self.factor = 25.4 rapid = (float(self.w.slider_rapid_ovr.value()) / 100) * self.max_linear_velocity * 25.4 else: self.factor = 1/25.4 rapid = (float(self.w.slider_rapid_ovr.value()) / 100) * self.max_linear_velocity / 25.4 self.w.lbl_max_rapid.setText("{:4.0f}".format(rapid)) def file_loaded(self, obj, filename): if os.path.basename(filename).count('.') > 1: self.last_loaded_program = "" return if filename is not None: self.add_status("Loaded file {}".format(filename)) self.w.progressBar.reset() self.last_loaded_program = filename self.w.lbl_runtime.setText("00:00:00") else: self.add_status("Filename not valid") def percent_loaded_changed(self, fraction): if fraction < 0: self.w.progressBar.setValue(0) self.w.progressBar.setFormat('PROGRESS') else: self.w.progressBar.setValue(fraction) self.w.progressBar.setFormat('LOADING: {}%'.format(fraction)) def percent_done_changed(self, fraction): self.w.progressBar.setValue(fraction) if fraction < 0: self.w.progressBar.setValue(0) self.w.progressBar.setFormat('PROGRESS') else: self.w.progressBar.setFormat('COMPLETE: {}%'.format(fraction)) def all_homed(self, obj): self.home_all = True self.w.btn_home_all.setText("ALL HOMED") if self.first_turnon is True: self.first_turnon = False if self.w.chk_reload_tool.isChecked(): command = "M61 Q{}".format(self.reload_tool) ACTION.CALL_MDI(command) if self.last_loaded_program is not None and self.w.chk_reload_program.isChecked(): if os.path.isfile(self.last_loaded_program): self.w.cmb_gcode_history.addItem(self.last_loaded_program) self.w.cmb_gcode_history.setCurrentIndex(self.w.cmb_gcode_history.count() - 1) self.w.cmb_gcode_history.setToolTip(fname) ACTION.OPEN_PROGRAM(self.last_loaded_program) ACTION.SET_MANUAL_MODE() self.w.manual_mode_button.setChecked(True) def not_all_homed(self, obj, list): self.home_all = False self.w.btn_home_all.setText("HOME ALL") def hard_limit_tripped(self, obj, tripped, list_of_tripped): self.add_status("Hard limits tripped") self.w.chk_override_limits.setEnabled(tripped) if not tripped: self.w.chk_override_limits.setChecked(False) # keep check button in synch of external changes def _check_override_limits(self,state,data): if 0 in data: self.w.chk_override_limits.setChecked(False) else: self.w.chk_override_limits.setChecked(True) ####################### # CALLBACKS FROM FORM # ####################### # main button bar def main_tab_changed(self, btn): index = btn.property("index") if index is None: return # if in automode still allow settings to show so override linits can be used if STATUS.is_auto_mode() and index != 8: self.add_status("Cannot switch pages while in AUTO mode") # make sure main page is showing self.w.main_tab_widget.setCurrentIndex(0) self.w.btn_main.setChecked(True) return if index == TAB_MAIN: self.w.stackedWidget_dro.setCurrentIndex(0) elif index == TAB_FILE and self.w.btn_gcode_edit.isChecked(): self.w.btn_gcode_edit.setChecked(False) self.w.btn_gcode_edit_clicked(False) if btn == self.w.btn_probe: self.probe.show() self.w.divider_line.show() elif self.probe is not None: self.probe.hide() self.w.divider_line.hide() self.w.main_tab_widget.setCurrentIndex(index) # gcode frame def cmb_gcode_history_clicked(self): if self.w.cmb_gcode_history.currentIndex() == 0: return filename = self.w.cmb_gcode_history.currentText().encode('utf-8') if filename == self.last_loaded_program: self.add_status("Selected program is already loaded") else: ACTION.OPEN_PROGRAM(filename) # program frame def btn_start_clicked(self, obj): if self.w.main_tab_widget.currentIndex() != 0: return if not STATUS.is_auto_mode(): self.add_status("Must be in AUTO mode to run a program") return if STATUS.is_auto_running(): self.add_status("Program is already running") return self.run_time = 0 self.w.lbl_runtime.setText("00:00:00") if self.start_line <= 1: ACTION.RUN(self.start_line) else: # instantiate run from line preset dialog info = '<b>Running From Line: {} <\b>'.format(self.start_line) mess = {'NAME':'RUNFROMLINE', 'TITLE':'Preset Dialog', 'ID':'_RUNFROMLINE', 'MESSAGE':info, 'LINE':self.start_line} ACTION.CALL_DIALOG(mess) self.add_status("Started program from line {}".format(self.start_line)) self.timer_on = True def btn_stop_clicked(self): if not self.w.btn_pause_spindle.isChecked(): return self.w.btn_pause_spindle.setChecked(False) self.btn_pause_spindle_clicked(False) def btn_reload_file_clicked(self): if self.last_loaded_program: self.w.progressBar.reset() self.add_status("Loaded program file {}".format(self.last_loaded_program)) ACTION.OPEN_PROGRAM(self.last_loaded_program) def btn_pause_spindle_clicked(self, state): self.w.action_pause.setEnabled(not state) self.w.action_step.setEnabled(not state) if state: # set external offsets to lift spindle self.h['eoffset_enable'] = self.w.chk_eoffsets.isChecked() fval = float(self.w.lineEdit_eoffset_count.text()) self.h['eoffset_count'] = int(fval) self.h['spindle_inhibit'] = True else: self.h['eoffset_count'] = 0 self.h['eoffset_clear'] = True self.h['spindle_inhibit'] = False if STATUS.is_auto_running(): # instantiate warning box info = "Wait for spindle at speed signal before resuming" mess = {'NAME':'MESSAGE', 'ICON':'WARNING', 'ID':'_wait_resume_', 'MESSAGE':'CAUTION', 'MORE':info, 'TYPE':'OK'} ACTION.CALL_DIALOG(mess) # offsets frame def btn_goto_location_clicked(self): dest = self.w.sender().property('location') if dest == 'home': x = float(self.w.lbl_home_x.text()) y = float(self.w.lbl_home_y.text()) elif dest == 'sensor': x = float(self.w.lineEdit_sensor_x.text()) y = float(self.w.lineEdit_sensor_y.text()) else: return if not STATUS.is_metric_mode(): x = x / 25.4 y = y / 25.4 ACTION.CALL_MDI("G90") ACTION.CALL_MDI_WAIT("G53 G0 Z0") command = "G53 G0 X{:3.4f} Y{:3.4f}".format(x, y) ACTION.CALL_MDI_WAIT(command, 10) def btn_ref_laser_clicked(self): x = float(self.w.lineEdit_laser_x.text()) y = float(self.w.lineEdit_laser_y.text()) if not STATUS.is_metric_mode(): x = x / 25.4 y = y / 25.4 self.add_status("Laser offsets set") command = "G10 L20 P0 X{:3.4f} Y{:3.4f}".format(x, y) ACTION.CALL_MDI(command) def btn_ref_camera_clicked(self): x = float(self.w.lineEdit_camera_x.text()) y = float(self.w.lineEdit_camera_y.text()) if not STATUS.is_metric_mode(): x = x / 25.4 y = y / 25.4 self.add_status("Camera offsets set") command = "G10 L20 P0 X{:3.4f} Y{:3.4f}".format(x, y) ACTION.CALL_MDI(command) def btn_touchoff_clicked(self): if STATUS.get_current_tool() == 0: self.add_status("Cannot touchoff with no tool loaded") return if not STATUS.is_all_homed(): self.add_status("Must be homed to perform tool touchoff") return # instantiate dialog box sensor = self.w.sender().property('sensor') info = "Ensure tooltip is within {} mm of tool sensor and click OK".format(self.w.lineEdit_max_probe.text()) mess = {'NAME':'MESSAGE', 'ID':sensor, 'MESSAGE':'TOOL TOUCHOFF', 'MORE':info, 'TYPE':'OKCANCEL'} ACTION.CALL_DIALOG(mess) def chk_lock_wph_changed(self, state): self.w.lineEdit_work_height.setReadOnly(not state) # DRO frame def btn_home_all_clicked(self, obj): if self.home_all is False: ACTION.SET_MACHINE_HOMING(-1) else: # instantiate dialog box info = "Unhome All Axes?" mess = {'NAME':'MESSAGE', 'ID':'_unhome_', 'MESSAGE':'UNHOME ALL', 'MORE':info, 'TYPE':'OKCANCEL'} ACTION.CALL_DIALOG(mess) def btn_home_clicked(self): joint = self.w.sender().property('joint') axis = INFO.GET_NAME_FROM_JOINT.get(joint).lower() if self.w["dro_axis_{}".format(axis)].property('isHomed') is True: ACTION.SET_MACHINE_UNHOMED(joint) else: ACTION.SET_MACHINE_HOMING(joint) # override frame def slow_button_clicked(self, state): slider = self.w.sender().property('slider') current = self.w[slider].value() max = self.w[slider].maximum() if state: self.w.sender().setText("SLOW") self.w[slider].setMaximum(max / self.slow_jog_factor) self.w[slider].setValue(current / self.slow_jog_factor) self.w[slider].setPageStep(10) else: self.w.sender().setText("FAST") self.w[slider].setMaximum(max * self.slow_jog_factor) self.w[slider].setValue(current * self.slow_jog_factor) self.w[slider].setPageStep(100) def slider_maxv_changed(self, value): maxpc = (float(value) / self.max_linear_velocity) * 100 self.w.lbl_maxv_percent.setText("{:3.0f} %".format(maxpc)) def slider_rapid_changed(self, value): rapid = (float(value) / 100) * self.max_linear_velocity * self.factor self.w.lbl_max_rapid.setText("{:4.0f}".format(rapid)) def btn_maxv_100_clicked(self): self.w.slider_maxv_ovr.setValue(self.max_linear_velocity) def btn_maxv_50_clicked(self): self.w.slider_maxv_ovr.setValue(self.max_linear_velocity / 2) # file tab def btn_gcode_edit_clicked(self, state): if not STATUS.is_on_and_idle(): return if state: self.w.filemanager.hide() self.w.gcode_editor.show() self.w.gcode_editor.editMode() else: self.w.filemanager.show() self.w.gcode_editor.hide() self.w.gcode_editor.readOnlyMode() def btn_load_file_clicked(self): if self.w.btn_gcode_edit.isChecked(): return fname = self.w.filemanager.getCurrentSelected() if fname[1] is True: self.load_code(fname[0]) def btn_copy_file_clicked(self): if self.w.btn_gcode_edit.isChecked(): return if self.w.sender() == self.w.btn_copy_right: source = self.w.filemanager_usb.getCurrentSelected() target = self.w.filemanager.getCurrentSelected() elif self.w.sender() == self.w.btn_copy_left: source = self.w.filemanager.getCurrentSelected() target = self.w.filemanager_usb.getCurrentSelected() else: return if source[1] is False: self.add_status("Specified source is not a file") return if target[1] is True: destination = os.path.join(os.path.dirname(target[0]), os.path.basename(source[0])) else: destination = os.path.join(target[0], os.path.basename(source[0])) try: copyfile(source[0], destination) self.add_status("Copied file from {} to {}".format(source[0], destination)) except Exception as e: self.add_status("Unable to copy file. %s" %e) # tool tab def btn_m61_clicked(self): checked = self.w.tooloffsetview.get_checked_list() if len(checked) > 1: self.add_status("Select only 1 tool to load") elif checked: self.add_status("Loaded tool {}".format(checked[0])) ACTION.CALL_MDI("M61 Q{}".format(checked[0])) else: self.add_status("No tool selected") # status tab def btn_clear_status_clicked(self): STATUS.emit('update-machine-log', None, 'DELETE') def btn_save_status_clicked(self): text = self.w.machinelog.toPlainText() filename = self.w.lbl_clock.text().encode('utf-8') filename = 'status_' + filename.replace(' ','_') + '.txt' self.add_status("Saving Status file to {}".format(filename)) with open(filename, 'w') as f: f.write(text) def btn_dimensions_clicked(self, state): self.w.gcodegraphics.show_extents_option = state self.w.gcodegraphics.clear_live_plotter() # camview tab def cam_zoom_changed(self, value): self.w.camview.scale = float(value) / 10 def cam_dia_changed(self, value): self.w.camview.diameter = value def cam_rot_changed(self, value): self.w.camview.rotation = float(value) / 10 # settings tab def chk_override_limits_checked(self, state): # only toggle override if it's not in synch with the button if state and not STATUS.is_limits_override_set(): self.add_status("Override limits set") ACTION.TOGGLE_LIMITS_OVERRIDE() elif not state and STATUS.is_limits_override_set(): error = ACTION.TOGGLE_LIMITS_OVERRIDE() # if override can't be released set the check button to reflect this if error == False: self.w.chk_override_limits.blockSignals(True) self.w.chk_override_limits.setChecked(True) self.w.chk_override_limits.blockSignals(False) else: self.add_status("Override limits not set") def chk_run_from_line_changed(self, state): if not state: self.w.btn_cycle_start.setText('CYCLE START') def chk_alpha_mode_changed(self, state): self.w.gcodegraphics.set_alpha_mode(state) def chk_inhibit_selection_changed(self, state): self.w.gcodegraphics.set_inhibit_selection(state) def chk_use_camera_changed(self, state): self.w.btn_ref_camera.setEnabled(state) if state : self.w.btn_camera.show() else: self.w.btn_camera.hide() def chk_use_sensor_changed(self, state): self.w.btn_tool_sensor.setEnabled(state) def chk_use_touchplate_changed(self, state): self.w.btn_touchplate.setEnabled(state) def chk_use_virtual_changed(self, state): codestring = "CALCULATOR" if state else "ENTRY" for i in ("x", "y", "z", "a"): self.w["axistoolbutton_" + i].set_dialog_code(codestring) if self.probe: self.probe.dialog_code = codestring if not state: self.w.stackedWidget_dro.setCurrentIndex(0) ##################### # GENERAL FUNCTIONS # ##################### def load_code(self, fname): if fname is None: return filename, file_extension = os.path.splitext(fname) if not file_extension in (".html", '.pdf'): if not (INFO.program_extension_valid(fname)): self.add_status("Unknown or invalid filename extension {}".format(file_extension)) return self.w.cmb_gcode_history.addItem(fname) self.w.cmb_gcode_history.setCurrentIndex(self.w.cmb_gcode_history.count() - 1) self.w.cmb_gcode_history.setToolTip(fname) ACTION.OPEN_PROGRAM(fname) self.add_status("Loaded program file : {}".format(fname)) self.w.main_tab_widget.setCurrentIndex(TAB_MAIN) self.w.filemanager.recordBookKeeping() # adjust ending to check for related HTML setup files fname = filename+'.html' if os.path.exists(fname): self.w.web_view.load(QtCore.QUrl.fromLocalFile(fname)) self.add_status("Loaded HTML file : {}".format(fname)) else: self.w.web_view.setHtml(self.html) # look for PDF setup files # load it with system program fname = filename+'.pdf' if os.path.exists(fname): url = QtCore.QUrl.fromLocalFile(fname) QtGui.QDesktopServices.openUrl(url) self.add_status("Loaded PDF file : {}".format(fname)) return if file_extension == ".html": try: self.w.web_view.load(QtCore.QUrl.fromLocalFile(fname)) self.add_status("Loaded HTML file : {}".format(fname)) self.w.main_tab_widget.setCurrentIndex(TAB_SETUP) self.w.stackedWidget.setCurrentIndex(0) self.w.btn_setup.setChecked(True) self.w.jogging_frame.hide() except Exception as e: print("Error loading HTML file : {}".format(e)) else: # load PDF with system program if os.path.exists(fname): url = QtCore.QUrl.fromLocalFile(fname) QtGui.QDesktopServices.openUrl(url) self.add_status("Loaded PDF file : {}".format(fname)) def disable_spindle_pause(self): self.h['eoffset_count'] = 0 self.h['spindle_inhibit'] = False if self.w.btn_pause_spindle.isChecked(): self.w.btn_pause_spindle.setChecked(False) def touchoff(self, selector): if selector == 'touchplate': z_offset = float(self.w.lineEdit_touch_height.text()) elif selector == 'sensor': z_offset = float(self.w.lineEdit_sensor_height.text()) - float(self.w.lineEdit_work_height.text()) else: self.add_alarm("Unknown touchoff routine specified") return self.add_status("Touchoff to {} started".format(selector)) max_probe = self.w.lineEdit_max_probe.text() search_vel = self.w.lineEdit_search_vel.text() probe_vel = self.w.lineEdit_probe_vel.text() ACTION.CALL_MDI("G21 G49") ACTION.CALL_MDI("G10 L20 P0 Z0") ACTION.CALL_MDI("G91") command = "G38.2 Z-{} F{}".format(max_probe, search_vel) if ACTION.CALL_MDI_WAIT(command, 10) == -1: ACTION.CALL_MDI("G90") return if ACTION.CALL_MDI_WAIT("G1 Z4.0"): ACTION.CALL_MDI("G90") return ACTION.CALL_MDI("G4 P0.5") command = "G38.2 Z-4.4 F{}".format(probe_vel) if ACTION.CALL_MDI_WAIT(command, 10) == -1: ACTION.CALL_MDI("G90") return command = "G10 L20 P0 Z{}".format(z_offset) ACTION.CALL_MDI_WAIT(command) command = "G1 Z10 F{}".format(search_vel) ACTION.CALL_MDI_WAIT(command) ACTION.CALL_MDI("G90") def kb_jog(self, state, joint, direction, fast = False, linear = True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): self.add_status('Machine must be ON and in Manual mode to jog') return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte()/60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular()/60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) def add_status(self, message): self.w.statusbar.showMessage(message) STATUS.emit('update-machine-log', message, 'TIME') def enable_auto(self, state): if state is True: self.w.btn_main.setChecked(True) self.w.main_tab_widget.setCurrentIndex(TAB_MAIN) if self.probe is not None: self.probe.hide() self.w.stackedWidget_dro.setCurrentIndex(0) def enable_onoff(self, state): if state: self.add_status("Machine ON") else: self.add_status("Machine OFF") self.w.btn_pause_spindle.setChecked(False) self.h['eoffset_count'] = 0 for widget in self.onoff_list: self.w[widget].setEnabled(state) def set_start_line(self, line): if self.w.chk_run_from_line.isChecked(): self.start_line = line self.w.btn_cycle_start.setText("CYCLE START\nLINE {}".format(self.start_line)) else: self.start_line = 1 def use_keyboard(self): if self.w.chk_use_keyboard.isChecked(): return True else: self.add_status('Keyboard shortcuts are disabled') return False def update_rpm(self, speed): rpm = int(speed) if rpm == 0: in_range = True at_speed = False else: in_range = (self.min_spindle_rpm <= int(speed) <= self.max_spindle_rpm) at_speed = self.h['spindle_at_speed'] widget = self.w.lbl_spindle_set widget.setProperty('in_range', in_range) widget.style().unpolish(widget) widget.style().polish(widget) if at_speed is True: self.w.lbl_spindle_atspeed.setText('AT SPEED') else: self.w.lbl_spindle_atspeed.setText('') def update_runtimer(self): if self.timer_on is False or STATUS.is_auto_paused(): return self.time_tenths += 1 if self.time_tenths == 10: self.time_tenths = 0 self.run_time += 1 hours, remainder = divmod(self.run_time, 3600) minutes, seconds = divmod(remainder, 60) self.w.lbl_runtime.setText("{:02d}:{:02d}:{:02d}".format(hours, minutes, seconds)) def stop_timer(self): if self.timer_on: self.timer_on = False self.add_status("Run timer stopped at {}".format(self.w.lbl_runtime.text())) def back(self): if os.path.exists(self.default_setup): self.w.web_view.load(QtCore.QUrl.fromLocalFile(self.default_setup)) else: self.w.web_view.setHtml(self.html) #self.w.web_view.page().triggerAction(QWebEnginePage.Back) def forward(self): self.w.web_view.load(QtCore.QUrl.fromLocalFile(self.docs)) #self.w.web_view.page().triggerAction(QWebEnginePage.Forward) def writer(self): WRITER.show() ##################### # KEY BINDING CALLS # ##################### def on_keycall_ESTOP(self,event,state,shift,cntrl): if state: ACTION.SET_ESTOP_STATE(True) def on_keycall_POWER(self,event,state,shift,cntrl): if state: ACTION.SET_MACHINE_STATE(False) def on_keycall_ABORT(self,event,state,shift,cntrl): if state: ACTION.ABORT() def on_keycall_HOME(self,event,state,shift,cntrl): if state and not STATUS.is_all_homed() and self.use_keyboard(): ACTION.SET_MACHINE_HOMING(-1) def on_keycall_PAUSE(self,event,state,shift,cntrl): if state and STATUS.is_auto_mode() and self.use_keyboard(): ACTION.PAUSE() def on_keycall_XPOS(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self,event,state,shift,cntrl): if self.use_keyboard(): self.kb_jog(state, 3, -1, shift, False) def on_keycall_F4(self,event,state,shift,cntrl): if state: mess = {'NAME':'CALCULATOR', 'TITLE':'Calculator', 'ID':'_calculator_'} ACTION.CALL_DIALOG(mess) def on_keycall_F12(self,event,state,shift,cntrl): if state: self.styleeditor.load_dialog() ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.stat = linuxcnc.stat() self.cmnd = linuxcnc.command() self.error = linuxcnc.error_channel() self.PATHS = paths self.IMAGE_PATH = paths.IMAGEDIR self.STYLEEDITOR = SSE(widgets, paths) global TOOLBAR TOOLBAR = ToolBarActions(path=paths) ########################################## # Special Functions called from QTSCREEN ########################################## # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): KEYBIND.add_call('Key_F12', 'on_keycall_F12') TOOLBAR.configure_action(self.w.actionEstop, 'estop') TOOLBAR.configure_action(self.w.actionMachineOn, 'power') TOOLBAR.configure_action(self.w.actionOpen, 'load') TOOLBAR.configure_action(self.w.actionReload, 'Reload') TOOLBAR.configure_action(self.w.actionRun, 'run') TOOLBAR.configure_action(self.w.actionPause, 'pause') TOOLBAR.configure_action(self.w.actionStop, 'abort') TOOLBAR.configure_action(self.w.actionSkip, 'block_delete') TOOLBAR.configure_action(self.w.actionOptionalStop, 'optional_stop') TOOLBAR.configure_action(self.w.actionZoomIn, 'zoom_in') TOOLBAR.configure_action(self.w.actionZoomOut, 'zoom_out') TOOLBAR.configure_action(self.w.actionFrontView, 'view_x') TOOLBAR.configure_action(self.w.actionSideView, 'view_y') TOOLBAR.configure_action(self.w.actionRotatedView, 'view_z2') TOOLBAR.configure_action(self.w.actionTopView, 'view_z') TOOLBAR.configure_action(self.w.actionPerspectiveView, 'view_p') TOOLBAR.configure_action(self.w.actionClearPlot, 'view_clear') TOOLBAR.configure_action(self.w.actionQuit, 'Quit', lambda d: self.w.close()) TOOLBAR.configure_action(self.w.menuRecent, 'recent_submenu') TOOLBAR.configure_action(self.w.menuHoming, 'home_submenu') TOOLBAR.configure_action(self.w.actionProperties, 'gcode_properties') TOOLBAR.configure_action(self.w.actionCalibration, 'load_calibration') TOOLBAR.configure_action(self.w.actionStatus, 'load_status') TOOLBAR.configure_action(self.w.actionHalshow, 'load_halshow') TOOLBAR.configure_action(self.w.actionHalmeter, 'load_halmeter') TOOLBAR.configure_action(self.w.actionHalscope, 'load_halscope') TOOLBAR.configure_action(self.w.actionAbout, 'about') self.w.actionQuickRef.triggered.connect(self.quick_reference) def processed_key_event__(self, receiver, event, is_pressed, key, code, shift, cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in (QtCore.Qt.Key_Escape, QtCore.Qt.Key_F1, QtCore.Qt.Key_F2, QtCore.Qt.Key_F3, QtCore.Qt.Key_F5, QtCore.Qt.Key_F5): if isinstance(receiver, OFFVIEW_WIDGET) or isinstance( receiver, MDI_WIDGET): if is_pressed: receiver.keyPressEvent(event) event.accept() return True if isinstance(receiver, QtWidgets.QDialog): print 'dialog' return True try: KEYBIND.call(self, event, is_pressed, shift, cntrl) return True except Exception as e: #log.debug('Exception loading Macros:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s' % ( KEYBIND.convert(event), key) if e: print e #print 'from %s'% receiver return False ######################## # callbacks from STATUS # ######################## ####################### # callbacks from form # ####################### ##################### # general functions # ##################### def quick_reference(self): help1 = [ ("F1", _("Emergency stop")), ("F2", _("Turn machine on")), ("", ""), ("X", _("Activate first axis")), ("Y", _("Activate second axis")), ("Z", _("Activate third axis")), ("A", _("Activate fourth axis")), ("` or 0,1..8", _("Activate first through ninth joint <br>if joints radiobuttons visible" )), ("", _("")), ("`,1..9,0", _("Set Feed Override from 0% to 100%")), ("", _("if axes radiobuttons visible")), (_(", and ."), _("Select jog speed")), (_("< and >"), _("Select angular jog speed")), (_("I, Shift-I"), _("Select jog increment")), ("C", _("Continuous jog")), (_("Home"), _("Send active joint home")), (_("Ctrl-Home"), _("Home all joints")), (_("Shift-Home"), _("Zero G54 offset for active axis")), (_("End"), _("Set G54 offset for active axis")), (_("Ctrl-End"), _("Set tool offset for loaded tool")), ("-, =", _("Jog active axis or joint")), (";, '", _("Select Max velocity")), ("", ""), (_("Left, Right"), _("Jog first axis or joint")), (_("Up, Down"), _("Jog second axis or joint")), (_("Pg Up, Pg Dn"), _("Jog third axis or joint")), (_("Shift+above jogs"), _("Jog at traverse speed")), ("[, ]", _("Jog fourth axis or joint")), ("", ""), ("D", _("Toggle between Drag and Rotate mode")), (_("Left Button"), _("Pan, rotate or select line")), (_("Shift+Left Button"), _("Rotate or pan")), (_("Right Button"), _("Zoom view")), (_("Wheel Button"), _("Rotate view")), (_("Rotate Wheel"), _("Zoom view")), (_("Control+Left Button"), _("Zoom view")), ] help2 = [ ("F3", _("Manual control")), ("F5", _("Code entry (MDI)")), (_("Control-M"), _("Clear MDI history")), (_("Control-H"), _("Copy selected MDI history elements")), ("", _("to clipboard")), (_("Control-Shift-H"), _("Paste clipboard to MDI history")), ("L", _("Override Limits")), ("", ""), ("O", _("Open program")), (_("Control-R"), _("Reload program")), (_("Control-S"), _("Save g-code as")), ("R", _("Run program")), ("T", _("Step program")), ("P", _("Pause program")), ("S", _("Resume program")), ("ESC", _("Stop running program, or")), ("", _("stop loading program preview")), ("", ""), ("F7", _("Toggle mist")), ("F8", _("Toggle flood")), ("B", _("Spindle brake off")), (_("Shift-B"), _("Spindle brake on")), ("F9", _("Turn spindle clockwise")), ("F10", _("Turn spindle counterclockwise")), ("F11", _("Turn spindle more slowly")), ("F12", _("Turn spindle more quickly")), (_("Control-K"), _("Clear live plot")), ("V", _("Cycle among preset views")), ("F4", _("Cycle among preview, DRO, and user tabs")), ("@", _("toggle Actual/Commanded")), ("#", _("toggle Relative/Machine")), (_("Ctrl-Space"), _("Clear notifications")), (_("Alt-F, M, V"), _("Open a Menu")), ] help = zip(help1, help2) msg = QtWidgets.QDialog() msg.setWindowTitle("Quick Reference") button = QtWidgets.QPushButton("Ok") button.clicked.connect(lambda: msg.close()) edit = QtWidgets.QTextEdit() edit.setLineWrapMode(0) mess = '''<TABLE border="1"><COLGROUP> <COL><COL align="char" char="."><THEAD> <TR><TH>Key <TH>Command<TH>Key <TH>Command <TBODY>''' for i, j in help: m = '<TR><TD><b>%s</b> <TD>%s<TD><b>%s</b> <TD>%s' % ( i[0], i[1], j[0], j[1]) mess += m mess += '</TABLE' edit.setText(mess) edit.setReadOnly(True) layout = QtWidgets.QVBoxLayout() layout.addWidget(edit) layout.addWidget(button) msg.setLayout(layout) msg.setMinimumSize(700, 800) msg.show() retval = msg.exec_() # keyboard jogging from key binding calls # double the rate if fast is true def kb_jog(self, state, joint, direction, fast=False, linear=True): if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte() / 60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular() / 60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self, event, state, shift, cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self, event, state, shift, cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self, event, state, shift, cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self, event, state, shift, cntrl): if state: if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: self.cmnd.abort() # Linear Jogging def on_keycall_XPOS(self, event, state, shift, cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self, event, state, shift, cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self, event, state, shift, cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self, event, state, shift, cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self, event, state, shift, cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self, event, state, shift, cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self, event, state, shift, cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) def on_keycall_F12(self, event, state, shift, cntrl): if state: self.STYLEEDITOR.load_dialog() ########################### # **** closing event **** # ########################### ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
def __init__(self, halcomp, widgets, paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets, paths)
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp,widgets,paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets,paths) self.picked_line = None STATUS.connect('general',self.return_value) STATUS.connect('graphics-line-selected', self.set_picked_line) ########################################## # Special Functions called from QTSCREEN ########################################## # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): KEYBIND.add_call('Key_F12','on_keycall_F12') def processed_key_event__(self,receiver,event,is_pressed,key,code,shift,cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in(QtCore.Qt.Key_Escape,QtCore.Qt.Key_F1 ,QtCore.Qt.Key_F2, QtCore.Qt.Key_F3,QtCore.Qt.Key_F5,QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = False break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STATUS.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True # ok if we got here then try keybindings try: return KEYBIND.call(self,event,is_pressed,shift,cntrl) except NameError as e: LOG.debug('Exception in KEYBINDING: {}'.format (e)) except Exception as e: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s'%(KEYBIND.convert(event),key) return False ######################## # callbacks from STATUS # ######################## # process the STATUS return message for run-from-line def return_value(self, obj, message): num = message['RETURN'] code = bool(message['ID'] == '_RunFromLine_') name = bool(message['NAME'] == 'CALCULATOR') if num is not None and code and name: ACTION.RUN(int(num)) def set_picked_line(self, obj, line): self.picked_line = line ####################### # callbacks from form # ####################### def full_screen(self, state): if state: self.w.stackedWidget_0.setCurrentIndex(1) self.w.widgetswitcher.show_id_widget(1) else: self.w.stackedWidget_0.setCurrentIndex(0) self.w.widgetswitcher.show_id_widget(0) def slow_jog_slider_changed(self, rate): if self.w.pbtn_jog_rate_slow.isChecked(): ACTION.SET_JOG_RATE(rate) def fast_jog_slider_changed(self, rate): if self.w.pbtn_jog_rate_fast.isChecked(): ACTION.SET_JOG_RATE(rate) def set_slow_rate(self, state): if state: ACTION.SET_JOG_RATE(self.w.scrb_jog_linear_slow.value()) def set_fast_rate(self, state): if state: ACTION.SET_JOG_RATE(self.w.scrb_jog_linear_fast.value()) def run_from_line_clicked(self): mess = {'NAME':'CALCULATOR','ID':'_RunFromLine_', 'PRELOAD':self.picked_line, 'TITLE':'Run From Line Dialog'} STATUS.emit('dialog-request', mess) ##################### # general functions # ##################### # keyboard jogging from key binding calls # double the rate if fast is true def kb_jog(self, state, joint, direction, fast = False, linear = True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte()/60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular()/60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self,event,state,shift,cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self,event,state,shift,cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self,event,state,shift,cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self,event,state,shift,cntrl): if state: if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: self.cmnd.abort() # Linear Jogging def on_keycall_XPOS(self,event,state,shift,cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self,event,state,shift,cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self,event,state,shift,cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self,event,state,shift,cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self,event,state,shift,cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self,event,state,shift,cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self,event,state,shift,cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self,event,state,shift,cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) def on_keycall_F12(self,event,state,shift,cntrl): if state: self.STYLEEDITOR.load_dialog() ########################### # **** closing event **** # ########################### ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: ######################## # **** INITIALIZE **** # ######################## # widgets allows access to widgets from the qtvcp files # at this point the widgets and hal pins are not instantiated def __init__(self, halcomp,widgets,paths): self.hal = halcomp self.w = widgets self.PATHS = paths self.STYLEEDITOR = SSE(widgets,paths) global TOOLBAR TOOLBAR = ToolBarActions(path=paths) STATUS.connect('general',self.return_value) STATUS.connect('motion-mode-changed',self.motion_mode) STATUS.connect('user-system-changed', self._set_user_system_text) ########################################## # Special Functions called from QTSCREEN ########################################## # at this point: # the widgets are instantiated. # the HAL pins are built but HAL is not set ready def initialized__(self): KEYBIND.add_call('Key_F12','on_keycall_F12') TOOLBAR.configure_submenu(self.w.menuRecent, 'recent_submenu') TOOLBAR.configure_submenu(self.w.menuHoming, 'home_submenu') TOOLBAR.configure_submenu(self.w.menuUnhome, 'unhome_submenu') TOOLBAR.configure_submenu(self.w.menuZeroCoordinateSystem, 'zero_systems_submenu') TOOLBAR.configure_action(self.w.actionEstop, 'estop') TOOLBAR.configure_action(self.w.actionMachineOn, 'power') TOOLBAR.configure_action(self.w.actionOpen, 'load') TOOLBAR.configure_action(self.w.actionReload, 'Reload') TOOLBAR.configure_action(self.w.actionRun, 'run') TOOLBAR.configure_action(self.w.actionPause, 'pause') TOOLBAR.configure_action(self.w.actionStop, 'abort') TOOLBAR.configure_action(self.w.actionSkip, 'block_delete') TOOLBAR.configure_action(self.w.actionOptionalStop, 'optional_stop') TOOLBAR.configure_action(self.w.actionZoomIn, 'zoom_in') TOOLBAR.configure_action(self.w.actionZoomOut, 'zoom_out') TOOLBAR.configure_action(self.w.actionFrontView, 'view_x') TOOLBAR.configure_action(self.w.actionSideView, 'view_y') TOOLBAR.configure_action(self.w.actionRotatedView, 'view_z2') TOOLBAR.configure_action(self.w.actionTopView, 'view_z') TOOLBAR.configure_action(self.w.actionPerspectiveView, 'view_p') TOOLBAR.configure_action(self.w.actionClearPlot, 'view_clear') TOOLBAR.configure_action(self.w.actionQuit, 'Quit', lambda d:self.w.close()) TOOLBAR.configure_action(self.w.actionProperties, 'gcode_properties') TOOLBAR.configure_action(self.w.actionCalibration, 'load_calibration') TOOLBAR.configure_action(self.w.actionStatus, 'load_status') TOOLBAR.configure_action(self.w.actionHalshow, 'load_halshow') TOOLBAR.configure_action(self.w.actionHalmeter, 'load_halmeter') TOOLBAR.configure_action(self.w.actionHalscope, 'load_halscope') TOOLBAR.configure_action(self.w.actionAbout, 'about') TOOLBAR.configure_action(self.w.actionTouchoffWorkplace, 'touchoffworkplace') TOOLBAR.configure_action(self.w.actionEdit, 'edit', self.edit) TOOLBAR.configure_action(self.w.actionTouchoffFixture, 'touchofffixture') TOOLBAR.configure_action(self.w.actionRunFromLine, 'runfromline') self.w.actionQuickRef.triggered.connect(self.quick_reference) def processed_key_event__(self,receiver,event,is_pressed,key,code,shift,cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in(QtCore.Qt.Key_Escape,QtCore.Qt.Key_F1 ,QtCore.Qt.Key_F2, QtCore.Qt.Key_F3,QtCore.Qt.Key_F5,QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = False break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STATUS.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True # ok if we got here then try keybindings try: return KEYBIND.call(self,event,is_pressed,shift,cntrl) except NameError as e: LOG.debug('Exception in KEYBINDING: {}'.format (e)) except Exception as e: LOG.debug('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s'%(KEYBIND.convert(event),key) return False ######################## # callbacks from STATUS # ######################## # process the STATUS return message from set-tool-offset def return_value(self, w, message): num = message['RETURN'] code = bool(message['ID'] == 'FORM__') name = bool(message['NAME'] == 'ENTRY') if num is not None and code and name: LOG.debug('message return:{}'.format (message)) axis = message['AXIS'] fixture = message['FIXTURE'] ACTION.SET_TOOL_OFFSET(axis,num,fixture) STATUS.emit('update-machine-log', 'Set tool offset of Axis %s to %f' %(axis, num), 'TIME') def motion_mode(self, w, mode): #print STATUS.stat.joints #print STATUS.stat.kinematics_type #print INFO.AVAILABLE_AXES #print INFO.GET_NAME_FROM_JOINT if mode == linuxcnc.TRAJ_MODE_COORD: print 'Mode Coordinate' # Joint mode elif mode == linuxcnc.TRAJ_MODE_FREE: if STATUS.stat.kinematics_type == linuxcnc.KINEMATICS_IDENTITY: self.show_axes() else: print 'Mode Free' self.show_joints() elif mode == linuxcnc.TRAJ_MODE_TELEOP: print 'Mode Teleop' self.show_axes() ####################### # callbacks from form # ####################### def tool_offset_clicked(self): conversion = {0:"X", 1:"Y", 2:"Z", 3:"A", 4:"B", 5:"C", 6:"U", 7:"V", 8:"W"} axis = STATUS.get_selected_axis() mess = {'NAME':'ENTRY','ID':'FORM__', 'AXIS':conversion[axis], 'FIXTURE':self.w.actionTouchoffWorkplace.isChecked(), 'TITLE':'Set Tool Offset'} STATUS.emit('dialog-request', mess) LOG.debug('message sent:{}'.format (mess)) ##################### # general functions # ##################### def show_joints(self): for i in range(0,9): if i in INFO.AVAILABLE_JOINTS: self.w['ras_label_%s'%i].show() self.w['ras_%s'%i].show() self.w['ras_label_%s'%i].setText('J%d'%i) try: self.w['machine_label_j%d'%i].setText('<html><head/><body><p><span style=" font-size:20pt; font-weight:600;">Joint %d:</span></p></body></html>'%i) except: pass continue self.w['ras_label_%s'%i].hide() self.w['ras_%s'%i].hide() def show_axes(self): for i in range(0,9): j = INFO.GET_NAME_FROM_JOINT.get(i) if j and len(j) == 1: self.w['ras_label_%s'%i].show() self.w['ras_%s'%i].show() self.w['ras_label_%s'%i].setText('%s'%j) try: self.w['machine_label_j%d'%i].setText('<html><head/><body><p><span style=" font-size:20pt; font-weight:600;">Machine %s:</span></p></body></html>' %j) except: pass continue self.w['ras_label_%s'%i].hide() self.w['ras_%s'%i].hide() def _set_user_system_text(self, w, data): print data convert = { 1:"G54 ", 2:"G55 ", 3:"G56 ", 4:"G57 ", 5:"G58 ", 6:"G59 ", 7:"G59.1 ", 8:"G59.2 ", 9:"G59.3 "} unit = convert[int(data)] for i in ('x','y','z'): self.w['dro_label_g5x_%s'%i].imperial_template = unit + i.upper() + '%9.4f' self.w['dro_label_g5x_%s'%i].metric_template = unit + i.upper() + '%10.3f' self.w['dro_label_g5x_%s'%i].update_units() self.w.dro_label_g5x_r.angular_template = unit + 'R %3.2f' self.w.dro_label_g5x_r.update_units() self.w.dro_label_g5x_r.update_rotation(None, STATUS.stat.rotation_xy) def edit(self, widget, state): if state: self.w.gcode_editor.editMode() else: self.w.gcode_editor.readOnlyMode() def quick_reference(self): help1 = [ ("F1", _("Emergency stop")), ("F2", _("Turn machine on")), ("", ""), ("X", _("Activate first axis")), ("Y", _("Activate second axis")), ("Z", _("Activate third axis")), ("A", _("Activate fourth axis")), ("` or 0,1..8", _("Activate first through ninth joint <br>if joints radiobuttons visible")), ("", _("")), ("`,1..9,0", _("Set Feed Override from 0% to 100%")), ("", _("if axes radiobuttons visible")), (_(", and ."), _("Select jog speed")), (_("< and >"), _("Select angular jog speed")), (_("I, Shift-I"), _("Select jog increment")), ("C", _("Continuous jog")), (_("Home"), _("Send active joint home")), (_("Ctrl-Home"), _("Home all joints")), (_("Shift-Home"), _("Zero G54 offset for active axis")), (_("End"), _("Set G54 offset for active axis")), (_("Ctrl-End"), _("Set tool offset for loaded tool")), ("-, =", _("Jog active axis or joint")), (";, '", _("Select Max velocity")), ("", ""), (_("Left, Right"), _("Jog first axis or joint")), (_("Up, Down"), _("Jog second axis or joint")), (_("Pg Up, Pg Dn"), _("Jog third axis or joint")), (_("Shift+above jogs"), _("Jog at traverse speed")), ("[, ]", _("Jog fourth axis or joint")), ("", ""), ("D", _("Toggle between Drag and Rotate mode")), (_("Left Button"), _("Pan, rotate or select line")), (_("Shift+Left Button"), _("Rotate or pan")), (_("Right Button"), _("Zoom view")), (_("Wheel Button"), _("Rotate view")), (_("Rotate Wheel"), _("Zoom view")), (_("Control+Left Button"), _("Zoom view")), ] help2 = [ ("F3", _("Manual control")), ("F5", _("Code entry (MDI)")), (_("Control-M"), _("Clear MDI history")), (_("Control-H"), _("Copy selected MDI history elements")), ("", _("to clipboard")), (_("Control-Shift-H"), _("Paste clipboard to MDI history")), ("L", _("Override Limits")), ("", ""), ("O", _("Open program")), (_("Control-R"), _("Reload program")), (_("Control-S"), _("Save g-code as")), ("R", _("Run program")), ("T", _("Step program")), ("P", _("Pause program")), ("S", _("Resume program")), ("ESC", _("Stop running program, or")), ("", _("stop loading program preview")), ("", ""), ("F7", _("Toggle mist")), ("F8", _("Toggle flood")), ("B", _("Spindle brake off")), (_("Shift-B"), _("Spindle brake on")), ("F9", _("Turn spindle clockwise")), ("F10", _("Turn spindle counterclockwise")), ("F11", _("Turn spindle more slowly")), ("F12", _("Turn spindle more quickly")), (_("Control-K"), _("Clear live plot")), ("V", _("Cycle among preset views")), ("F4", _("Cycle among preview, DRO, and user tabs")), ("@", _("toggle Actual/Commanded")), ("#", _("toggle Relative/Machine")), (_("Ctrl-Space"), _("Clear notifications")), (_("Alt-F, M, V"), _("Open a Menu")), ] help = zip(help1,help2) msg = QtWidgets.QDialog() msg.setWindowTitle("Quick Reference") button = QtWidgets.QPushButton("Ok") button.clicked.connect(lambda: msg.close()) edit = QtWidgets.QTextEdit() edit.setLineWrapMode(0) mess = '''<TABLE border="1"><COLGROUP> <COL><COL align="char" char="."><THEAD> <TR><TH>Key <TH>Command<TH>Key <TH>Command <TBODY>''' for i,j in help: m='<TR><TD><b>%s</b> <TD>%s<TD><b>%s</b> <TD>%s'%(i[0],i[1],j[0],j[1]) mess += m mess += '</TABLE' edit.setText(mess) edit.setReadOnly(True) layout = QtWidgets.QVBoxLayout() layout.addWidget(edit) layout.addWidget(button) msg.setLayout(layout) msg.setMinimumSize(700,800) msg.show() retval = msg.exec_() # keyboard jogging from key binding calls # double the rate if fast is true def kb_jog(self, state, joint, direction, fast = False, linear = True): if not STATUS.is_man_mode() or not STATUS.machine_is_on(): return if linear: distance = STATUS.get_jog_increment() rate = STATUS.get_jograte()/60 else: distance = STATUS.get_jog_increment_angular() rate = STATUS.get_jograte_angular()/60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) ##################### # KEY BINDING CALLS # ##################### # Machine control def on_keycall_ESTOP(self,event,state,shift,cntrl): if state: ACTION.SET_ESTOP_STATE(STATUS.estop_is_clear()) def on_keycall_POWER(self,event,state,shift,cntrl): if state: ACTION.SET_MACHINE_STATE(not STATUS.machine_is_on()) def on_keycall_HOME(self,event,state,shift,cntrl): if state: if STATUS.is_all_homed(): ACTION.SET_MACHINE_UNHOMED(-1) else: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_ABORT(self,event,state,shift,cntrl): if state: if STATUS.stat.interp_state == linuxcnc.INTERP_IDLE: self.w.close() else: self.cmnd.abort() # Linear Jogging def on_keycall_XPOS(self,event,state,shift,cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self,event,state,shift,cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self,event,state,shift,cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self,event,state,shift,cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self,event,state,shift,cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self,event,state,shift,cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_APOS(self,event,state,shift,cntrl): pass #self.kb_jog(state, 3, 1, shift, False) def on_keycall_ANEG(self,event,state,shift,cntrl): pass #self.kb_jog(state, 3, -1, shift, linear=False) def on_keycall_F12(self,event,state,shift,cntrl): if state: self.STYLEEDITOR.load_dialog() ########################### # **** closing event **** # ########################### ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
class HandlerClass: def __init__(self, halcomp, widgets, paths): self.w = widgets self.PATHS = paths self.hal = halcomp INIPATH = os.environ.get('INI_FILE_NAME', '/dev/null') self.inifile = linuxcnc.ini(INIPATH) self.STYLEEDITOR = SSE(widgets, paths) KEYBIND.add_call('Key_F12', 'on_keycall_F12') STAT.connect('state-on', self.machine_on) STAT.connect('state-off', self.machine_off) STAT.connect('hard-limits-tripped', self.hard_limit_tripped) STAT.connect("interp-idle", self.interp_idle_changed) STAT.connect("user-system-changed", self.user_system_changed) STAT.connect("file-loaded", self.file_loaded) STAT.connect("all-homed", self.all_homed) STAT.connect("not-all-homed", self.not_homed) # some global variables self.axis_list = INFO.AVAILABLE_AXES self.joint_list = INFO.AVAILABLE_JOINTS self.max_velocity = INFO.MAX_LINEAR_VELOCITY self.system_list = [ "G53", "G54", "G55", "G56", "G57", "G58", "G59", "G59.1", "G59.2", "G59.3" ] self.home_location_x = self.inifile.find('JOINT_0', 'HOME') self.home_location_y = self.inifile.find('JOINT_1', 'HOME') self.home_location_z = self.inifile.find('JOINT_2', 'HOME') self.homed = False self.start_line = 0 self.program_length = 0 self.slow_jog_factor = 10 self.reload_tool = 0 self.last_loaded_program = "" self.onoff_list = ["widget_controls", "Program_frame", "DRO_frame"] def initialized__(self): #self.init_pins() self.init_preferences() self.init_widgets() self.init_locations() # initialize DRO style for i in map(str.lower, self.axis_list): self.w["dro_axis_{}".format(i)].setStyle( self.w["dro_axis_{}".format(i)].style()) ############################# # SPECIAL FUNCTIONS SECTION # ############################# #def init_pins(self): # these pins are needed so that the touchoff subroutine can read the variables #self.hal.newpin("touch_height", hal.HAL_FLOAT, hal.HAL_OUT) def init_preferences(self): if self.w.PREFS_: self.last_loaded_program = self.w.PREFS_.getpref( 'last_file_path', None, str, 'BOOK_KEEPING') temp1 = self.w.PREFS_.getpref('Reload program', False, bool, 'CUSTOM_FORM_ENTRIES') else: temp1 = False self.add_alarm("No preference file found") self.w.checkBox_reload_program.setChecked(temp1) def init_widgets(self): #laser power set MAX_POWER = int(self.inifile.find('LASER', 'POWER')) or 500 FIX_VEL_1 = int(self.inifile.find('LASER', 'FIX_VEL_1')) or 100 FIX_VEL_2 = int(self.inifile.find('LASER', 'FIX_VEL_2')) or 200 FIX_VEL_3 = int(self.inifile.find('LASER', 'FIX_VEL_3')) or 400 self.w.label_max_power.setText(str(MAX_POWER)) self.w.slider_power.setMaximum(MAX_POWER) self.w.slider_power.setValue(MAX_POWER / 2) compare = lambda v, min, max: (v > min) and (v < max) if compare(FIX_VEL_1, 0, MAX_POWER) and compare( FIX_VEL_2, 0, MAX_POWER) and compare(FIX_VEL_3, 0, MAX_POWER): self.w.btn_power_1.setText(str(FIX_VEL_1)) self.w.btn_power_2.setText(str(FIX_VEL_2)) self.w.btn_power_3.setText(str(FIX_VEL_3)) else: self.w.btn_power_1.setText(str(0)) self.w.btn_power_2.setText(str(MAX_POWER / 2)) self.w.btn_power_3.setText(str(MAX_POWER)) #powder vel set MAX_POWER = int(self.inifile.find('POWDER', 'MAX_VEL')) or 29 FIX_VEL_1 = float(self.inifile.find('POWDER', 'FIX_VEL_1')) or 1 FIX_VEL_2 = float(self.inifile.find('POWDER', 'FIX_VEL_2')) or 5 FIX_VEL_3 = float(self.inifile.find('POWDER', 'FIX_VEL_3')) or 10 POWDER_NUM = int(self.inifile.find('POWDER', 'NUM')) or 1 if POWDER_NUM > 26: POWDER_NUM = 26 self.w.label_max_powder_vel.setText(str(MAX_POWER)) self.w.slider_powder_vel.setMaximum(MAX_POWER * 10) self.w.slider_powder_vel.setValue(MAX_POWER / 2) if compare(FIX_VEL_1, 0, MAX_POWER) and compare( FIX_VEL_2, 0, MAX_POWER) and compare(FIX_VEL_3, 0, MAX_POWER): self.w.btn_powder_vel_1.setText(str(FIX_VEL_1)) self.w.btn_powder_vel_2.setText(str(FIX_VEL_2)) self.w.btn_powder_vel_3.setText(str(FIX_VEL_3)) else: self.w.btn_powder_vel_1.setText(str(0)) self.w.btn_powder_vel_2.setText(str(MAX_POWER / 2)) self.w.btn_powder_vel_3.setText(str(MAX_POWER)) for index in range(POWDER_NUM): self.w.comboBox_powder_num.addItem("PDR " + str(index + 1)) #other set self.w.main_tab_widget.setCurrentIndex(0) self.w.slider_jog.setMaximum(self.max_velocity * 60) self.w.slider_jog.setValue(INFO.DEFAULT_LINEAR_JOG_VEL) self.w.slider_maxv.setMaximum(self.max_velocity * 60) self.w.slider_maxv.setValue(self.max_velocity * 60) self.w.slider_feed.setMaximum(INFO.MAX_FEED_OVERRIDE) self.w.slider_feed.setValue(100) self.w.slider_rapid.setMaximum(100) self.w.slider_rapid.setValue(100) self.w.checkBox_override_limits.setChecked(False) self.w.checkBox_override_limits.setEnabled(False) self.w.filemanager.show() self.w.gcode_editor.hide() self.w.btn_from_line.setEnabled(False) def init_locations(self): self.w.lbl_maxv.setText(str(self.max_velocity * 60)) # home location if not self.home_location_x or not self.home_location_y or not self.home_location_z: self.w.btn_go_home.setEnabled(False) self.w.groupBox_home.hide() self.w.lbl_no_home.show() self.add_alarm("No valid home location found") else: self.w.lbl_home_x.setText(self.home_location_x) self.w.lbl_home_y.setText(self.home_location_y) self.w.lbl_no_home.hide() def processed_key_event__(self, receiver, event, is_pressed, key, code, shift, cntrl): # when typing in MDI, we don't want keybinding to call functions # so we catch and process the events directly. # We do want ESC, F1 and F2 to call keybinding functions though if code not in (QtCore.Qt.Key_Escape, QtCore.Qt.Key_F1, QtCore.Qt.Key_F2): # QtCore.Qt.Key_F3,QtCore.Qt.Key_F4,QtCore.Qt.Key_F5): # search for the top widget of whatever widget received the event # then check if it's one we want the keypress events to go to flag = False receiver2 = receiver while receiver2 is not None and not flag: if isinstance(receiver2, QtWidgets.QDialog): flag = True break if isinstance(receiver2, QtWidgets.QLineEdit): flag = True break if isinstance(receiver2, MDI_WIDGET): flag = True break if isinstance(receiver2, GCODE): flag = True break if isinstance(receiver2, TOOL_TABLE): flag = True break if isinstance(receiver2, OFFSET_VIEW): flag = True break receiver2 = receiver2.parent() if flag: if isinstance(receiver2, GCODE): # if in manual do our keybindings - otherwise # send events to gcode widget if STAT.is_man_mode() == False: if is_pressed: receiver.keyPressEvent(event) event.accept() return True elif is_pressed: receiver.keyPressEvent(event) event.accept() return True else: event.accept() return True # ok if we got here then try keybindings try: return KEYBIND.call(self, event, is_pressed, shift, cntrl) except NameError as e: self.add_alarm('Exception in KEYBINDING: {}'.format(e)) except Exception as e: LOG.error('Exception in KEYBINDING:', exc_info=e) print 'Error in, or no function for: %s in handler file for-%s' % ( KEYBIND.convert(event), key) return False ######################### # CALLBACKS FROM STATUS # ######################### def machine_on(self, obj): for widget in self.onoff_list: self.w[widget].setEnabled(True) def machine_off(self, obj): for widget in self.onoff_list: self.w[widget].setEnabled(False) def gcode_line_selected(self, obj, data): if self.w.btn_from_line.isChecked(): self.start_line = data self.w.btn_start.setText("START FROM {}".format(data)) def interp_idle_changed(self, obj): self.start_line = 0 self.w.btn_start.setText("START FROM 0") def user_system_changed(self, obj, data): sys = self.system_list[int(data)] self.w.actionbutton_rel.setText(sys) def file_loaded(self, obj, filename): if filename is not None: self.w.progressBar.setValue(0) self.last_loaded_program = filename fileobject = file(filename, 'r') lines = fileobject.readlines() fileobject.close() self.program_length = len(lines) self.start_line = 0 self.w.btn_from_line.setEnabled(True) self.w.btn_start.setText("START FROM 0") else: self.w.btn_from_line.setEnabled(False) self.add_alarm("Filename not valid") def all_homed(self, obj): self.homed = True self.w.actionbutton_view_p.click() for i in map(str.lower, self.axis_list): self.w["dro_axis_{}".format(i)].setProperty('homed', True) self.w["dro_axis_{}".format(i)].setStyle( self.w["dro_axis_{}".format(i)].style()) if self.last_loaded_program and self.w.checkBox_reload_program.checkState( ): ACTION.OPEN_PROGRAM(self.last_loaded_program) self.w.filemanager.updateDirectoryView(self.last_loaded_program) def not_homed(self, obj, data): self.homed = False for i in map(str.lower, self.axis_list): self.w["dro_axis_{}".format(i)].setProperty('homed', False) self.w["dro_axis_{}".format(i)].setStyle( self.w["dro_axis_{}".format(i)].style()) def hard_limit_tripped(self, obj, tripped, list_of_tripped): self.w.checkBox_override_limits.setEnabled(tripped) if not tripped: self.w.checkBox_override_limits.setChecked(False) ####################### # CALLBACKS FROM FORM # ####################### def tool_widgets_enable(self, state): self.w.btn_laser_on.setEnabled(state) self.w.btn_gas_on.setEnabled(state) self.w.btn_laser_en.setEnabled(state) self.w.btn_light_on.setEnabled(state) self.w.btn_powder_on.setEnabled(state) self.w.comboBox_powder_num.setEnabled(state) # program frame def btn_start_clicked(self): if not STAT.is_auto_mode(): return self.w.btn_from_line.setChecked(False) self.tool_widgets_enable(False) self.add_alarm("Started program from line {}".format(self.start_line)) ACTION.RUN(self.start_line) def btn_abort_clicked(self): self.tool_widgets_enable(True) def btn_reload_file_clicked(self): if self.last_loaded_program: self.w.progressBar.setValue(0) ACTION.OPEN_PROGRAM(self.last_loaded_program) def btn_from_line_clicked(self, state): if state is False: self.start_line = 0 self.w.btn_start.setText("START FROM 0") # tool slot def btn_laser_on_clicked(self, state): if not STAT.is_mdi_mode(): return if state is False: ACTION.CALL_MDI_WAIT("M115") else: ACTION.CALL_MDI_WAIT("M105 P{}".format( self.w.slider_power.value())) def btn_laser_enable_clicked(self, state): if state is False: ACTION.CALL_MDI_WAIT("M114") else: ACTION.CALL_MDI_WAIT("M104") def btn_gas_on_clicked(self, state): if state is False: ACTION.CALL_MDI_WAIT("M116") else: ACTION.CALL_MDI_WAIT("M106") def btn_light_on_clicked(self, state): if state is False: ACTION.CALL_MDI_WAIT("M117") else: ACTION.CALL_MDI_WAIT("M107") def btn_powder_on_clicked(self, state): if state is False: ACTION.CALL_MDI_WAIT( "M118 P{}".format(self.w.comboBox_powder_num.currentIndex() + 1)) else: ACTION.CALL_MDI_WAIT("M108 P{} Q{}".format( self.w.comboBox_powder_num.currentIndex() + 1, float(self.w.slider_powder_vel.value()) / 10)) # override frame def btn_slow_clicked(self, state): if state: current = self.w.slider_jog.value() self.w.btn_slow.setText("SLOW") self.w.slider_jog.setMaximum(self.max_velocity * 60 / self.slow_jog_factor) self.w.slider_jog.setValue(current / self.slow_jog_factor) else: current = self.w.slider_jog.value() self.w.btn_slow.setText("FAST") self.w.slider_jog.setMaximum(self.max_velocity * 60) self.w.slider_jog.setValue(current * self.slow_jog_factor) def btn_maxv_max_clicked(self): self.w.slider_maxv.setValue(self.max_velocity * 60) def btn_power_fix_1(self): self.w.slider_power.setValue(int(self.w.btn_power_1.text())) def btn_power_fix_2(self): self.w.slider_power.setValue(int(self.w.btn_power_2.text())) def btn_power_fix_3(self): self.w.slider_power.setValue(int(self.w.btn_power_3.text())) def btn_powder_vel_fix_1(self): self.w.slider_powder_vel.setValue( float(self.w.btn_powder_vel_1.text()) * 10) def btn_powder_vel_fix_2(self): self.w.slider_powder_vel.setValue( float(self.w.btn_powder_vel_2.text()) * 10) def btn_powder_vel_fix_3(self): self.w.slider_powder_vel.setValue( float(self.w.btn_powder_vel_3.text()) * 10) def sld_powder_vel_changed(self, vel): self.w.label_powder_vel.setText(str(float(vel) / 10)) # file tab def btn_gcode_edit_clicked(self, state): if not STAT.is_on_and_idle(): return for x in ["load", "next", "prev"]: self.w["btn_file_{}".format(x)].setEnabled(not state) if state: self.w.filemanager.hide() self.w.gcode_editor.show() self.w.gcode_editor.editMode() else: self.w.filemanager.show() self.w.gcode_editor.hide() self.w.gcode_editor.readOnlyMode() # alarm tab def btn_clear_alarms_clicked(self): STAT.emit('update-machine-log', None, 'DELETE') def btn_save_alarms_clicked(self): text = self.w.machinelog.toPlainText() filename = self.w.lbl_clock.text().encode('utf-8') filename = 'alarms_' + filename.replace(' ', '_') + '.txt' with open(filename, 'w') as f: f.write(text) def chk_override_limits(self, state): if state: print("Override limits set") ACTION.SET_LIMITS_OVERRIDE() else: print("Override limits not set") ##################### # GENERAL FUNCTIONS # ##################### def kb_jog(self, state, joint, direction, fast=False, linear=True): if not STAT.is_man_mode() or not STAT.machine_is_on(): return if linear: distance = STAT.get_jog_increment() rate = STAT.get_jograte() / 60 else: distance = STAT.get_jog_increment_angular() rate = STAT.get_jograte_angular() / 60 if state: if fast: rate = rate * 2 ACTION.JOG(joint, direction, rate, distance) else: ACTION.JOG(joint, 0, 0, 0) def add_alarm(self, message): STAT.emit('update-machine-log', message, 'TIME') def alarm_added(self): self.w.led_alarm.setState(True) def tab_changed(self, index): self.w.btn_gcode_edit.setChecked(False) self.btn_gcode_edit_clicked(False) if index == 4: self.w.led_alarm.setState(False) ##################### # KEY BINDING CALLS # ##################### def on_keycall_ESTOP(self, event, state, shift, cntrl): if state: ACTION.SET_ESTOP_STATE(True) def on_keycall_POWER(self, event, state, shift, cntrl): if state: ACTION.SET_MACHINE_STATE(False) def on_keycall_ABORT(self, event, state, shift, cntrl): if state: ACTION.ABORT() def on_keycall_HOME(self, event, state, shift, cntrl): if state and self.homed is False: ACTION.SET_MACHINE_HOMING(-1) def on_keycall_XPOS(self, event, state, shift, cntrl): self.kb_jog(state, 0, 1, shift) def on_keycall_XNEG(self, event, state, shift, cntrl): self.kb_jog(state, 0, -1, shift) def on_keycall_YPOS(self, event, state, shift, cntrl): self.kb_jog(state, 1, 1, shift) def on_keycall_YNEG(self, event, state, shift, cntrl): self.kb_jog(state, 1, -1, shift) def on_keycall_ZPOS(self, event, state, shift, cntrl): self.kb_jog(state, 2, 1, shift) def on_keycall_ZNEG(self, event, state, shift, cntrl): self.kb_jog(state, 2, -1, shift) def on_keycall_F12(self, event, state, shift, cntrl): if state: self.STYLEEDITOR.load_dialog() ########################### # **** closing event **** # ########################### # items to save in preference file def closing_cleanup__(self): if self.w.PREFS_: self.w.PREFS_.putpref('Reload program', self.w.checkBox_reload_program.checkState(), bool, 'CUSTOM_FORM_ENTRIES') ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)