2431 lines
107 KiB
Python
2431 lines
107 KiB
Python
import os, time
|
|
from qtpy import QtCore, QtWidgets, QtGui
|
|
from qtpy.QtWidgets import QAction, QMenu
|
|
from qtpy.QtGui import QIcon
|
|
from qtpy.QtCore import QCoreApplication
|
|
from qtvcp.widgets.gcode_editor import GcodeEditor as GCODE
|
|
from qtvcp.widgets.gcode_graphics import GCodeGraphics as GRAPHICS
|
|
from qtvcp.widgets.mdi_line import MDILine as MDI_WIDGET
|
|
from qtvcp.widgets.tool_offsetview import ToolOffsetView as TOOL_TABLE
|
|
from qtvcp.widgets.origin_offsetview import OriginOffsetView as OFFSET_VIEW
|
|
from qtvcp.widgets.stylesheeteditor import StyleSheetEditor as SSE
|
|
from qtvcp.widgets.file_manager import FileManager as FM
|
|
from qtvcp.widgets.status_slider import StatusSlider as SLIDER
|
|
from qtvcp.lib.qt_ngcgui.ngcgui import NgcGui
|
|
from qtvcp.lib.writer import writer
|
|
from qtvcp.lib.keybindings import Keylookup
|
|
from qtvcp.lib.gcodes import GCodes
|
|
from qtvcp.lib.qt_pdf import PDFViewer
|
|
from qtvcp.core import Status, Action, Info, Path, Qhal
|
|
from qtvcp import logger
|
|
from shutil import copyfile
|
|
|
|
LOG = logger.getLogger(__name__)
|
|
KEYBIND = Keylookup()
|
|
STATUS = Status()
|
|
INFO = Info()
|
|
ACTION = Action()
|
|
PATH = Path()
|
|
STYLEEDITOR = SSE()
|
|
WRITER = writer.Main()
|
|
QHAL = Qhal()
|
|
|
|
try:
|
|
from qtpy.QtWebEngineWidgets import QWebEnginePage
|
|
except:
|
|
LOG.warning('QtDragon Warning with loading QtWebEngineWidget - is python3-pyqt5.qtwebengine installed?')
|
|
|
|
_translate = QCoreApplication.translate
|
|
|
|
# constants for tab pages
|
|
TAB_MAIN = 0
|
|
TAB_FILE = 1
|
|
TAB_OFFSETS = 2
|
|
TAB_TOOL = 3
|
|
TAB_STATUS = 4
|
|
TAB_PROBE = 5
|
|
TAB_CAMERA = 6
|
|
TAB_GCODES = 7
|
|
TAB_SETUP = 8
|
|
TAB_SETTINGS = 9
|
|
TAB_UTILITIES = 10
|
|
TAB_USER = 11
|
|
|
|
# constants for (left side) stacked widget
|
|
PAGE_UNCHANGED = -1
|
|
PAGE_GCODE = 0
|
|
PAGE_FILE = 1
|
|
PAGE_OFFSET = 2
|
|
PAGE_TOOL = 3
|
|
PAGE_NGCGUI = 4
|
|
|
|
# message constants
|
|
DEFAULT = 0
|
|
WARNING = 1
|
|
CRITICAL = 2
|
|
DEBUG = 3
|
|
SUCCESS = 4
|
|
ERROR = 5
|
|
|
|
VERSION ='1.4'
|
|
|
|
class HandlerClass:
|
|
def __init__(self, halcomp, widgets, paths):
|
|
self.p = paths
|
|
self.h = halcomp
|
|
self.w = widgets
|
|
self.gcodes = GCodes(widgets)
|
|
# This validator precludes using comma as a decimal
|
|
self.valid = QtGui.QRegularExpressionValidator(QtCore.QRegularExpression('-?[0-9]{0,6}[.][0-9]{0,3}'))
|
|
self.KEYBIND = KEYBIND
|
|
KEYBIND.add_call('Key_F11','on_keycall_F11')
|
|
KEYBIND.add_call('Key_F12','on_keycall_F12')
|
|
KEYBIND.add_call('Key_Pause', 'on_keycall_pause')
|
|
KEYBIND.add_call('Key_Any', 'on_keycall_pause')
|
|
|
|
KEYBIND.add_call('Key_Period','on_keycall_jograte',1)
|
|
KEYBIND.add_call('Key_Comma','on_keycall_jograte',0)
|
|
KEYBIND.add_call('Key_Greater','on_keycall_angular_jograte',1)
|
|
KEYBIND.add_call('Key_Less','on_keycall_angular_jograte',0)
|
|
|
|
# some global variables
|
|
self.factor = 1.0
|
|
self._dialog_message = None
|
|
self._spindle_wait = False
|
|
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._last_count = 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.spindle_lift_pins_present = False
|
|
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._maintab_cycle = 1
|
|
self._lastSelectButton = None
|
|
self.MPGFocusWidget = None
|
|
self.CycleFocusWidget = None
|
|
self.auto_mode_switch = False
|
|
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_dro", "frame_override", "frame_status"]
|
|
self.auto_list = ["chk_eoffsets", "cmb_gcode_history","lineEdit_eoffset_count"]
|
|
self.axis_list = ["axis_select_", "dro_axis_", "action_zero_",
|
|
"dro_button_stack_", "plus_jogbutton_", "minus_jogbutton_"]
|
|
self.button_response_list = ["btn_start", "btn_home_all", "btn_home_0", "btn_home_1",
|
|
"btn_home_2", "btn_home_3","btn_home_4", "btn_reload_file", "macrobutton0", "macrobutton1",
|
|
"macrobutton2", "macrobutton3", "macrobutton4", "macrobutton5", "macrobutton6",
|
|
"macrobutton7", "macrobutton8", "macrobutton9"]
|
|
self.statusbar_reset_time = 10000 # ten seconds
|
|
|
|
STATUS.connect('general', self.dialog_return)
|
|
STATUS.connect('dialog-request', self.dialog_request)
|
|
STATUS.connect('state-on', lambda w: self.enable_onoff(True))
|
|
STATUS.connect('state-off', lambda w: self.enable_onoff(False))
|
|
STATUS.connect('mode-manual', lambda w: self.enable_auto(True))
|
|
STATUS.connect('mode-mdi', lambda w: self.enable_auto(True))
|
|
STATUS.connect('mode-auto', lambda w: self.enable_auto(False))
|
|
STATUS.connect('interp-run', lambda w: self.set_button_response_state(True))
|
|
STATUS.connect('interp-idle', lambda w: self.set_button_response_state(False))
|
|
STATUS.connect('gcode-line-selected', lambda w, line: self.set_start_line(line))
|
|
STATUS.connect('graphics-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.update_pause_button(state))
|
|
STATUS.connect('actual-spindle-speed-changed', lambda w, speed: self.update_rpm(speed))
|
|
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.periodic_update())
|
|
STATUS.connect('command-stopped', lambda w: self.stop_timer())
|
|
STATUS.connect('progress', lambda w,p,t: self.updateProgress(p,t))
|
|
STATUS.connect('override-limits-changed', lambda w, state, data: self._check_override_limits(state, data))
|
|
STATUS.connect('graphics-gcode-properties', lambda w, d: self.update_gcode_properties(d))
|
|
STATUS.connect('status-message', lambda w, d, o: self.add_external_status(d,o))
|
|
STATUS.connect('runstop-line-changed', lambda w, l :self.lastRunLine(l))
|
|
STATUS.connect('cycle-start-request', lambda w, state :self.btn_start_clicked(state))
|
|
STATUS.connect('cycle-pause-request', lambda w, state: self.btn_pause_clicked(state))
|
|
STATUS.connect('macro-call-request', lambda w, name: self.request_macro_call(name))
|
|
STATUS.connect('ok-request', lambda w, state: self.dialog_ext_control(w,1,1))
|
|
STATUS.connect('cancel-request', lambda w, state: self.dialog_ext_control(w,1,0))
|
|
|
|
self.swoopPath = os.path.join(paths.IMAGEDIR,'lcnc_swoop.png')
|
|
self.swoopURL = QtCore.QUrl.fromLocalFile(self.swoopPath)
|
|
|
|
txt1 = _translate("HandlerClass","Setup Tab")
|
|
txt2 = _translate("HandlerClass","If you select a file with .html as a file ending, it will be shown here.")
|
|
txt3 = _translate("HandlerClass","Documents online")
|
|
txt4 = _translate("HandlerClass","QtDragon online")
|
|
txt5 = _translate("HandlerClass","Local files")
|
|
self.html = """<html>
|
|
<head>
|
|
<title>Test page for the download:// scheme</title>
|
|
</head>
|
|
<body>
|
|
<h1>%s</h1>
|
|
<p>%s.</p>
|
|
<li><a href="http://linuxcnc.org/docs/devel/html/">%s</a></li>
|
|
<li><a href="http://linuxcnc.org/docs/2.9/html/gui/qtdragon.html">%s</a></li>
|
|
<li><a href="file://%s">%s</a></li>
|
|
<img src="file://%s" alt="lcnc_swoop" />
|
|
<hr />
|
|
</body>
|
|
</html>
|
|
"""%( txt1, txt2, txt3, txt4, os.path.expanduser('~/linuxcnc'), txt5,
|
|
self.swoopPath)
|
|
|
|
def class_patch__(self):
|
|
# override file manager load button
|
|
self.old_fman = FM.load
|
|
FM.load = self.load_code
|
|
|
|
# override NGCGui path check
|
|
NgcGui.check_linuxcnc_paths_fail = self.check_linuxcnc_paths_fail_override
|
|
|
|
# override action class macro run function
|
|
ACTION.RUN_MACRO = self.run_macro
|
|
|
|
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.setCurrentIndex(0)
|
|
self.w.stackedWidget_dro.setCurrentIndex(0)
|
|
self.w.btn_spindle_pause.setEnabled(False)
|
|
self.w.page_buttonGroup.buttonClicked.connect(self.main_tab_changed)
|
|
self.w.selectButtonGroup.buttonClicked.connect(self.MPG_select_changed)
|
|
self.w.filemanager_usb.showMediaDir(quiet = True)
|
|
|
|
# hide or initiate 4th/5th AXIS dro/jog
|
|
flag = False
|
|
for num in range(0,5):
|
|
if num < len(INFO.AVAILABLE_AXES):
|
|
temp = INFO.AVAILABLE_AXES[num]
|
|
if temp in ('A','B','C'):
|
|
flag = True
|
|
self.initiate_axis_dro(num,temp)
|
|
else:
|
|
try:
|
|
for i in self.axis_list:
|
|
self.w[i+str(num)].hide()
|
|
except:
|
|
pass
|
|
|
|
# angular increment controls
|
|
if flag:
|
|
self.w.lbl_increments_linear.setText(_translate('HandlerClass',"INCREMENTS"))
|
|
else:
|
|
self.w.widget_jog_angular.hide()
|
|
self.w.widget_increments_angular.hide()
|
|
|
|
# set validators for lineEdit widgets
|
|
for val in self.lineedit_list:
|
|
self.w['lineEdit_' + val].setValidator(self.valid)
|
|
self.w.lineEdit_eoffset_count.setValidator(QtGui.QIntValidator(0,100))
|
|
# check for default setup html file
|
|
try:
|
|
# web view widget for SETUP page
|
|
if self.w.webwidget:
|
|
self.toolBar = QtWidgets.QToolBar(self.w)
|
|
self.w.tabWidget_setup.setCornerWidget(self.toolBar)
|
|
|
|
self.zoomBtn = QtWidgets.QPushButton(self.w)
|
|
self.zoomBtn.setEnabled(True)
|
|
self.zoomBtn.setMinimumSize(64, 40)
|
|
self.zoomBtn.setIconSize(QtCore.QSize(38, 38))
|
|
self.zoomBtn.setIcon(QtGui.QIcon(QtGui.QPixmap(':/buttons/images/zoom.png')))
|
|
self.zoomBtn.clicked.connect(self.zoomWeb)
|
|
self.toolBar.addWidget(self.zoomBtn)
|
|
|
|
self.homeBtn = QtWidgets.QPushButton(self.w)
|
|
self.homeBtn.setEnabled(True)
|
|
self.homeBtn.setMinimumSize(64, 40)
|
|
self.homeBtn.setIconSize(QtCore.QSize(38, 38))
|
|
self.homeBtn.setIcon(QtGui.QIcon(':/qt-project.org/styles/commonstyle/images/up-32.png'))
|
|
self.homeBtn.clicked.connect(self.homeWeb)
|
|
self.toolBar.addWidget(self.homeBtn)
|
|
|
|
self.backBtn = QtWidgets.QPushButton(self.w)
|
|
self.backBtn.setEnabled(True)
|
|
self.backBtn.setMinimumSize(64, 40)
|
|
self.backBtn.setIconSize(QtCore.QSize(38, 38))
|
|
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.setMinimumSize(64, 40)
|
|
self.forBtn.setIconSize(QtCore.QSize(38, 38))
|
|
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.setMinimumSize(64, 40)
|
|
self.writeBtn.setEnabled(True)
|
|
self.writeBtn.clicked.connect(self.writer)
|
|
self.toolBar.addWidget(self.writeBtn)
|
|
|
|
if os.path.exists(self.default_setup):
|
|
self.w.webwidget.load(QtCore.QUrl.fromLocalFile(self.default_setup))
|
|
else:
|
|
self.w.webwidget.setHtml(self.html, self.swoopURL)
|
|
self.w.webwidget.page().urlChanged.connect(self.onLoadFinished)
|
|
|
|
except Exception as e:
|
|
text = _translate("HandlerClass","No default setup file found -")
|
|
print(f"{text} {e}")
|
|
|
|
# PDF setup page
|
|
self.PDFView = PDFViewer.PDFView()
|
|
self.w.layout_PDF.addWidget(self.PDFView)
|
|
self.PDFView.loadSample('setup_tab')
|
|
|
|
self.configureMacroButtons()
|
|
|
|
self.log_version()
|
|
STATUS.emit('update-machine-log', '', 'OFF')
|
|
|
|
# preset MPG focus object
|
|
self.MPGFocusWidget = self.w.gcodegraphics
|
|
self.MPGFocusWidgetBorder= 'stackedWidget_mainTabPage1'
|
|
|
|
def init_utils(self):
|
|
from qtvcp.lib.gcode_utility.facing import Facing
|
|
self.facing = Facing()
|
|
self.w.layout_facing.addWidget(self.facing)
|
|
|
|
try:
|
|
from qtvcp.lib.gcode_utility.hole_enlarge import Hole_Enlarge
|
|
self.hole_enlarge = Hole_Enlarge()
|
|
ACTION.ADD_WIDGET_TO_TAB(self.w.tabWidget_utilities,self.hole_enlarge, 'Hole Enlarge')
|
|
except Exception as e:
|
|
LOG.info("Utility hole enlarge unavailable: {}".format(e))
|
|
|
|
from qtvcp.lib.gcode_utility.hole_circle import Hole_Circle
|
|
self.hole_circle = Hole_Circle()
|
|
self.w.layout_hole_circle.addWidget(self.hole_circle)
|
|
|
|
# load the NgcGui widget into the utilities tab
|
|
# then move (warp) the info tab from it to the left tab widget
|
|
self.ngcgui = NgcGui()
|
|
self.w.layout_ngcgui.addWidget(self.ngcgui)
|
|
self.ngcgui.warp_info_frame(self.w.ngcGuiLeftLayout)
|
|
|
|
#############################
|
|
# SPECIAL FUNCTIONS SECTION #
|
|
#############################
|
|
def init_pins(self):
|
|
# spindle control pins
|
|
pin = QHAL.newPin("spindle-amps", QHAL.HAL_FLOAT, QHAL.HAL_IN)
|
|
pin.pinValueChanged.connect(lambda p,v: self.spindle_pwr_changed(v))
|
|
|
|
pin = QHAL.newPin("spindle-volts", QHAL.HAL_FLOAT, QHAL.HAL_IN)
|
|
pin.pinValueChanged.connect(lambda p,v: self.spindle_pwr_changed(v))
|
|
|
|
pin = QHAL.newPin("spindle-fault-u32", QHAL.HAL_U32, QHAL.HAL_IN)
|
|
pin.pinValueChanged.connect(lambda p,v: self.spindle_fault_changed(v))
|
|
pin = QHAL.newPin("spindle-fault", QHAL.HAL_S32, QHAL.HAL_IN)
|
|
pin.pinValueChanged.connect(lambda p,v: self.spindle_fault_changed(v))
|
|
|
|
pin = QHAL.newPin("spindle-modbus-errors-u32", QHAL.HAL_U32, QHAL.HAL_IN)
|
|
pin.pinValueChanged.connect(lambda p,v: self.mb_errors_changed(v))
|
|
pin = QHAL.newPin("spindle-modbus-errors", QHAL.HAL_S32, QHAL.HAL_IN)
|
|
pin.pinValueChanged.connect(lambda p,v: self.mb_errors_changed(v))
|
|
|
|
pin = QHAL.newPin("spindle-modbus-connection", QHAL.HAL_BIT, QHAL.HAL_IN)
|
|
pin.pinValueChanged.connect(lambda p,v: self.mb_connection_changed(v))
|
|
|
|
QHAL.newPin("spindle-inhibit", QHAL.HAL_BIT, QHAL.HAL_OUT)
|
|
|
|
pin = QHAL.newPin("external-pause", QHAL.HAL_BIT, QHAL.HAL_IN)
|
|
pin.pinValueChanged.connect(lambda p,v: self.btn_pause_clicked(v))
|
|
|
|
# external offset control pins
|
|
QHAL.newPin("eoffset-enable", QHAL.HAL_BIT, QHAL.HAL_OUT)
|
|
QHAL.newPin("eoffset-clear", QHAL.HAL_BIT, QHAL.HAL_OUT)
|
|
self.h['eoffset-clear'] = True
|
|
QHAL.newPin("eoffset-spindle-count", QHAL.HAL_S32, QHAL.HAL_OUT)
|
|
pin = QHAL.newPin("eoffset-is-active", QHAL.HAL_BIT, QHAL.HAL_IN)
|
|
pin.pinValueChanged.connect(lambda p,v: self.external_offset_state_changed(v))
|
|
|
|
# total external offset
|
|
pin = QHAL.newPin("eoffset-value", QHAL.HAL_FLOAT, QHAL.HAL_IN)
|
|
|
|
# MPG scrolling pin
|
|
self.pin_mpg_in = QHAL.newPin('mpg-in',QHAL.HAL_S32, QHAL.HAL_IN)
|
|
self.pin_mpg_in.pinValueChanged.connect(lambda p,v: self.external_mpg(v))
|
|
|
|
# dialog answer pins
|
|
pin = QHAL.newPin("dialog-ok", QHAL.HAL_BIT, QHAL.HAL_IN)
|
|
pin.pinValueChanged.connect(lambda p,v: self.dialog_ext_control(p,v,1))
|
|
pin = QHAL.newPin("dialog-no", QHAL.HAL_BIT, QHAL.HAL_IN)
|
|
pin.pinValueChanged.connect(lambda p,v: self.dialog_ext_control(p,v,2))
|
|
pin = QHAL.newPin("dialog-cancel", QHAL.HAL_BIT, QHAL.HAL_IN)
|
|
pin.pinValueChanged.connect(lambda p,v: self.dialog_ext_control(p,v,0))
|
|
|
|
def init_preferences(self):
|
|
if not self.w.PREFS_:
|
|
txt = _translate("HandlerClass","No preference file found, enable preferences in screenoptions widget")
|
|
self.add_status(txt, CRITICAL)
|
|
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_retract_distance.setText(str(self.w.PREFS_.getpref('Retract Distance', 10, float, 'CUSTOM_FORM_ENTRIES')))
|
|
self.w.lineEdit_z_safe_travel.setText(str(self.w.PREFS_.getpref('Z Safe Travel', 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_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_tool_sensor.setChecked(self.w.PREFS_.getpref('Use tool sensor', 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_auto_mode_ext_macro.setChecked(self.w.PREFS_.getpref('Auto mode external macro', True, bool, 'CUSTOM_FORM_ENTRIES'))
|
|
self.w.chk_auto_mode_macro_buttons.setChecked(self.w.PREFS_.getpref('Auto mode macro buttons', True, bool, 'CUSTOM_FORM_ENTRIES'))
|
|
# make sure the button's property are current
|
|
self.chk_auto_mode_macro_changed(self.w.chk_auto_mode_macro_buttons.isChecked())
|
|
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'))
|
|
self.cam_xscale_changed(self.w.PREFS_.getpref('Camview xscale', 100, int, 'CUSTOM_FORM_ENTRIES'))
|
|
self.cam_yscale_changed(self.w.PREFS_.getpref('Camview yscale', 100, int, 'CUSTOM_FORM_ENTRIES'))
|
|
self.w.camview._camNum = self.w.PREFS_.getpref('Camview cam number', 0, int, 'CUSTOM_FORM_ENTRIES')
|
|
self.w.camview.setAPI(self.w.PREFS_.getpref('Camview cam api', 'ANY', str, 'CUSTOM_FORM_ENTRIES'))
|
|
self.w.camview.setResolution(self.w.PREFS_.getpref('Camview cam resolution', 'DEFAULT', str, 'CUSTOM_FORM_ENTRIES'))
|
|
self.auto_mode_switch = self.w.PREFS_.getpref('auto mode switching', False, bool, 'CUSTOM_FORM_ENTRIES')
|
|
|
|
def closing_cleanup__(self):
|
|
if not self.w.PREFS_: return
|
|
LOG.debug("Saving Preferences")
|
|
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('Retract Distance', self.w.lineEdit_retract_distance.text().encode('utf-8'), float, 'CUSTOM_FORM_ENTRIES')
|
|
self.w.PREFS_.putpref('Z Safe Travel', self.w.lineEdit_z_safe_travel.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('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 tool sensor', self.w.chk_use_tool_sensor.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('Auto mode external macro', self.w.chk_auto_mode_ext_macro.isChecked(), bool, 'CUSTOM_FORM_ENTRIES')
|
|
self.w.PREFS_.putpref('Auto mode macro buttons', self.w.chk_auto_mode_macro_buttons.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')
|
|
self.w.PREFS_.putpref('Camview xscale', self.cam_xscale_percent(), int, 'CUSTOM_FORM_ENTRIES')
|
|
self.w.PREFS_.putpref('Camview yscale', self.cam_yscale_percent(), int, 'CUSTOM_FORM_ENTRIES')
|
|
self.w.PREFS_.putpref('Camview cam number', self.w.camview._camNum, int, 'CUSTOM_FORM_ENTRIES')
|
|
self.w.PREFS_.putpref('Camview cam api', self.w.camview.getAPIName(self.w.camview._camNum), str, 'CUSTOM_FORM_ENTRIES')
|
|
self.w.PREFS_.putpref('Camview cam resolution', self.w.camview.resolution, str, 'CUSTOM_FORM_ENTRIES')
|
|
|
|
def init_widgets(self):
|
|
self.adjust_stacked_widgets(TAB_MAIN)
|
|
self.w.chk_override_limits.setChecked(False)
|
|
self.w.chk_override_limits.setEnabled(False)
|
|
self.w.lbl_maxv_percent.setText("100 %")
|
|
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"))
|
|
txt = _translate("HandlerClass","No File Loaded")
|
|
self.w.cmb_gcode_history.addItem(txt)
|
|
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.list.setAlternatingRowColors(False)
|
|
self.w.filemanager_usb.list.setAlternatingRowColors(False)
|
|
self.w.filemanager_usb.showList()
|
|
|
|
if not INFO.MACHINE_IS_METRIC:
|
|
self.w.lbl_tool_sensor_B2W.setText('INCH')
|
|
self.w.lbl_tool_sensor_B2S.setText('INCH')
|
|
self.w.lbl_touchheight_units.setText('INCH')
|
|
self.w.lbl_max_probe_units.setText('INCH')
|
|
self.w.lbl_search_vel_units.setText('INCH/<sup> MIN</sup>')
|
|
self.w.lbl_probe_vel_units.setText('INCH/<sup> MIN</sup>')
|
|
self.w.lbl_z_ext_offset.setText('INCH')
|
|
self.w.lbl_tool_sensor_loc.setText('INCH')
|
|
self.w.lbl_laser_offset.setText('INCH')
|
|
self.w.lbl_camera_offset.setText('INCH')
|
|
self.w.lbl_touchheight_units.setText('INCH')
|
|
self.w.lbl_retract_dist_units.setText('INCH')
|
|
self.w.lbl_z_safe_travel_units.setText('INCH')
|
|
|
|
#set up gcode list
|
|
self.gcodes.setup_list()
|
|
|
|
# hide user tab button if no user tabs
|
|
if self.w.stackedWidget_mainTab.count() == 11:
|
|
self.w.btn_user.hide()
|
|
|
|
# see if a popup window panels is required
|
|
self.Btn = None
|
|
if not INFO.ZIPPED_TABS is None:
|
|
for name, loc, cmd in INFO.ZIPPED_TABS:
|
|
if loc =='WINDOW':
|
|
|
|
# add panel to a dialog window
|
|
self.w['popup'] = d = QtWidgets.QDialog(self.w)
|
|
temp = self.w[name.replace(' ','_')]
|
|
# set to apropriate size for panel
|
|
d.setMinimumSize(600,400)
|
|
d.setWindowTitle(name)
|
|
d.setWindowFlags(d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
|
|
d.finished.connect(self.onClosePopup)
|
|
d._lastgeometry = None
|
|
|
|
layout = QtWidgets.QGridLayout(d)
|
|
layout.setContentsMargins(0,0,0,0)
|
|
layout.addWidget(temp, 0, 0)
|
|
|
|
# add launch button to screen
|
|
self.btn = QtWidgets.QPushButton(self.w)
|
|
self.btn.setEnabled(True)
|
|
self.btn.setMinimumSize(64, 40)
|
|
self.btn.setIconSize(QtCore.QSize(38, 38))
|
|
self.btn.setIcon(QtGui.QIcon(':/qt-project.org/styles/commonstyle/images/up-32.png'))
|
|
self.btn.clicked.connect(self.togglePopup)
|
|
self.w.layout_buttonbar.insertWidget(len(self.w.layout_buttonbar) - 4, self.btn)
|
|
|
|
# only one allowed
|
|
break
|
|
|
|
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 processed_focus_event__(self, receiver, event):
|
|
#print(receiver.parent(), receiver)
|
|
|
|
if isinstance(receiver.parent(), GCODE):
|
|
#print ('Gcode editor focus',receiver.parent().parent().objectName())
|
|
self.removeMPGFocusBorder()
|
|
|
|
name = receiver.parent().parent().objectName()
|
|
color = self.w.screen_options.property('user4Color').name()
|
|
self.colorMPGFocusBorder(name, receiver.parent(), color)
|
|
|
|
elif isinstance(receiver, GRAPHICS):
|
|
#print ('Gcode graphics focus',receiver,receiver.parent().objectName())
|
|
self.removeMPGFocusBorder()
|
|
|
|
name = receiver.parent().objectName()
|
|
color = self.w.screen_options.property('user4Color').name()
|
|
self.colorMPGFocusBorder(name, receiver, color)
|
|
|
|
elif isinstance(receiver.parent(), FM):
|
|
self.removeMPGFocusBorder()
|
|
|
|
#print ('File Manager focus',receiver.parent().parent().objectName())
|
|
name = receiver.parent().parent().objectName()
|
|
color = self.w.screen_options.property('user4Color').name()
|
|
self.colorMPGFocusBorder(name, receiver.parent(), color)
|
|
|
|
elif isinstance(receiver, SLIDER):
|
|
#print('Slider',receiver.objectName(),receiver.parent())
|
|
if not receiver in(self.w.slider_jog_linear,self.w.slider_jog_angular):
|
|
self.removeMPGFocusBorder()
|
|
name = receiver.objectName()
|
|
color = self.w.screen_options.property('user4Color').name()
|
|
self.colorMPGFocusBorder(name, receiver, color)
|
|
|
|
elif isinstance(receiver, TOOL_TABLE):
|
|
self.removeMPGFocusBorder()
|
|
|
|
#print ('Tool Offset focus',receiver.parent().objectName())
|
|
name = receiver.parent().objectName()
|
|
color = self.w.screen_options.property('user4Color').name()
|
|
self.colorMPGFocusBorder(name, receiver, color)
|
|
|
|
elif isinstance(receiver, OFFSET_VIEW):
|
|
self.removeMPGFocusBorder()
|
|
|
|
#print ('origon Offset focus',receiver.parent().objectName())
|
|
name = receiver.parent().objectName()
|
|
color = self.w.screen_options.property('user4Color').name()
|
|
self.colorMPGFocusBorder(name, receiver, color)
|
|
|
|
elif isinstance(receiver, MDI_WIDGET):
|
|
self.removeMPGFocusBorder()
|
|
self.removeCycleFocusBorder()
|
|
|
|
#print ('MDI line focus',receiver.parent().objectName())
|
|
name = receiver.parent().objectName()
|
|
color = self.w.screen_options.property('user5Color').name()
|
|
self.colorCycleFocusBorder(name, receiver, color)
|
|
|
|
elif isinstance(receiver, self._probeLibrary):
|
|
self.removeCycleFocusBorder()
|
|
|
|
#print ('Versa/Basic Probe focus',receiver.parent().objectName())
|
|
name = receiver.parent().objectName()
|
|
color = self.w.screen_options.property('user5Color').name()
|
|
self.colorCycleFocusBorder(name, receiver, color)
|
|
|
|
# show virtual keyboard
|
|
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)
|
|
|
|
def removeMPGFocusBorder(self):
|
|
try:
|
|
self.MPGFocusWidget.setStyleSheet( '')
|
|
name = self.MPGFocusWidgetBorder
|
|
self.w[name].setStyleSheet('')
|
|
except:
|
|
pass
|
|
|
|
def removeCycleFocusBorder(self):
|
|
return
|
|
try:
|
|
self.CycleFocusWidget.setStyleSheet( '')
|
|
name = self.CycleFocusWidgetBorder
|
|
self.w[name].setStyleSheet('')
|
|
except:
|
|
pass
|
|
|
|
def recolorMPGFocusBorder(self):
|
|
try:
|
|
colorName = self.w.screen_options.property('user4Color').name()
|
|
name = self.MPGFocusWidgetBorder
|
|
self.w[name].setStyleSheet('#%s {border: 3px solid %s;}'%(name,colorName))
|
|
except:
|
|
pass
|
|
|
|
def colorMPGFocusBorder(self, name, receiver, colorName):
|
|
if self.w.btn_mpg_scroll.isChecked():
|
|
self.MPGFocusWidgetBorder = name
|
|
self.MPGFocusWidget = receiver
|
|
self.w[name].setStyleSheet('#%s {border: 3px solid %s;}'%(name,colorName))
|
|
|
|
def colorCycleFocusBorder(self, name, receiver, colorName):
|
|
return
|
|
self.CycleFocusWidgetBorder = name
|
|
self.CycleFocusWidget = receiver
|
|
self.w[name].setStyleSheet('#%s {border: 3px solid %s;}'%(name,colorName))
|
|
|
|
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
|
|
if isinstance(receiver2, writer.Main):
|
|
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
|
|
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)
|
|
|
|
def before_loop__(self):
|
|
# Should be initialized and ready to log good info
|
|
STATUS.emit('update-machine-log', '', 'ON')
|
|
|
|
# no spindle lift without pins connected
|
|
self.spindle_lift_pins_present = True
|
|
for i in ('qtdragon.eoffset-is-active','qtdragon.spindle-inhibit','qtdragon.eoffset-clear',
|
|
'qtdragon.eoffset-spindle-count','qtdragon.eoffset-value'):
|
|
# no driving pin cennected?
|
|
if not self.h.hal.pin_has_writer(i):
|
|
self.spindle_lift_pins_present = False
|
|
#self.add_status('{} pin not connected in a HAL FILE'.format(i))
|
|
LOG.warning("{} {}".format(_translate("HandlerClass","not connected"), i))
|
|
|
|
# halui pins could cause resume without the spindle running
|
|
# and would not honour spindle lift. Should use qtdragon pin provided
|
|
for i in ('halui.program.resume','halui.program.pause'):
|
|
if self.h.hal.pin_has_writer(i):
|
|
self.spindle_lift_pins_present = False
|
|
LOG.warning(_translate("HandlerClass","HALUI pause/resume pin(s) connected - spindle lift not available"))
|
|
txt1 = _translate("HandlerClass","Warning:")
|
|
txt2 = _translate("HandlerClass", "pin connected in a HAL file, spindle lift disabled")
|
|
self.add_status(f"{txt2} {i} {txt2}", WARNING)
|
|
break
|
|
|
|
#########################
|
|
# CALLBACKS FROM STATUS #
|
|
#########################
|
|
|
|
def spindle_pwr_changed(self, data):
|
|
# this calculation assumes the voltage is line to neutral
|
|
# and that the synchronous motor spindle has a power factor of 0.9
|
|
power = self.h['spindle-volts'] * self.h['spindle-amps'] * 0.9 # Watts = V x I x PF
|
|
amps = "{:1.1f}".format(self.h['spindle-amps'])
|
|
pwr = "{:1.1f}".format(power)
|
|
self.w.lbl_spindle_amps.setText(amps)
|
|
self.w.lbl_spindle_power.setText(pwr)
|
|
|
|
def spindle_fault_changed(self, data):
|
|
fault = hex(data)
|
|
self.w.lbl_spindle_fault.setText(fault)
|
|
|
|
def mb_errors_changed(self, data):
|
|
self.w.lbl_mb_errors.setText(str(data))
|
|
|
|
def mb_connection_changed(self, data):
|
|
if data:
|
|
self.w.lbl_mb_errors.setStyleSheet('')
|
|
else:
|
|
self.w.lbl_mb_errors.setStyleSheet('background-color:{};'.format(
|
|
self.w.screen_options.property('user5Color').name()))
|
|
|
|
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_')
|
|
lower_code = bool(message.get('ID') == '_wait_to_lower_')
|
|
unhome_code = bool(message.get('ID') == '_unhome_')
|
|
overwrite = bool(message.get('ID') == '_overwrite_')
|
|
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.lowerSpindle()
|
|
elif unhome_code and name == 'MESSAGE' and rtn is True:
|
|
ACTION.SET_MACHINE_UNHOMED(-1)
|
|
elif overwrite and name == 'MESSAGE':
|
|
if rtn is True:
|
|
self.do_file_copy()
|
|
else:
|
|
self.add_status(_translate("HandlerClass","File not copied"), CRITICAL)
|
|
elif lower_code and name == 'MESSAGE':
|
|
self.h['eoffset-spindle-count'] = 0
|
|
self.h['spindle-inhibit'] = False
|
|
self.add_status(_translate("HandlerClass",'Spindle lowered after machine stopped'))
|
|
|
|
self._dialog_message = None
|
|
|
|
def dialog_request(self,w, message):
|
|
self._dialog_message = message
|
|
|
|
def user_system_changed(self, data):
|
|
sys = self.system_list[int(data) - 1]
|
|
self.w.offset_table.selectRow(int(data) + 3)
|
|
self.w.systemtoolbutton.setText(sys)
|
|
|
|
def metric_mode_changed(self, mode):
|
|
rate = (float(self.w.slider_rapid_ovr.value()) / 100)
|
|
if mode is False:
|
|
self.w.lbl_jog_linear.setText('INCH/<sup>MIN</sup>')
|
|
self.factor = INFO.convert_machine_to_imperial(INFO.MAX_TRAJ_VELOCITY)
|
|
else:
|
|
self.w.lbl_jog_linear.setText('MM/<sup>MIN</sup>')
|
|
self.factor = INFO.convert_machine_to_metric(INFO.MAX_TRAJ_VELOCITY)
|
|
self.w.lbl_max_rapid.setText("{:4.0f}".format(rate * self.factor))
|
|
|
|
def file_loaded(self, obj, filename):
|
|
if os.path.basename(filename).count('.') > 1:
|
|
if INFO.INI.find("DISPLAY", "NO_MULTIPLE_DOT_FILENAME"):
|
|
self.last_loaded_program = ""
|
|
return
|
|
if filename is not None:
|
|
txt = _translate("HandlerClass","Loaded file")
|
|
self.add_status(f"{txt} {filename}")
|
|
self.w.progressBar.setValue(0)
|
|
self.last_loaded_program = filename
|
|
self.w.lbl_runtime.setText("00:00:00")
|
|
else:
|
|
self.add_status(_translate("HandlerClass","Filename not valid"), CRITICAL)
|
|
|
|
def updateProgress(self, p,text):
|
|
if p <0:
|
|
self.w.progressBar.setValue(0)
|
|
self.w.progressBar.setFormat(_translate("HandlerClass",'PROGRESS'))
|
|
else:
|
|
self.w.progressBar.setValue(p)
|
|
self.w.progressBar.setFormat('{}: {}%'.format(text, p))
|
|
|
|
def percent_loaded_changed(self, fraction):
|
|
if fraction <0:
|
|
self.w.progressBar.setValue(0)
|
|
self.w.progressBar.setFormat(_translate("HandlerClass",'PROGRESS'))
|
|
else:
|
|
self.w.progressBar.setValue(fraction)
|
|
txt = _translate("HandlerClass","LOADING")
|
|
self.w.progressBar.setFormat('{}: {}%'.format(txt, fraction))
|
|
|
|
def percent_done_changed(self, fraction):
|
|
self.w.progressBar.setValue(fraction)
|
|
if fraction <0:
|
|
self.w.progressBar.setValue(0)
|
|
self.w.progressBar.setFormat(_translate("HandlerClass",'PROGRESS'))
|
|
else:
|
|
txt = _translate("HandlerClass","COMPLETE")
|
|
self.w.progressBar.setFormat('{}: {}%'.format(txt, fraction))
|
|
|
|
def all_homed(self, obj):
|
|
self.home_all = True
|
|
self.w.btn_home_all.setText(_translate("HandlerClass","ALL\nHOMED"))
|
|
self.add_status(_translate("HandlerClass","All homed"))
|
|
if self.first_turnon is True:
|
|
self.first_turnon = False
|
|
if self.w.chk_reload_tool.isChecked():
|
|
command = "M61 Q{} G43".format(self.reload_tool)
|
|
ACTION.CALL_MDI(command, mode_return=True)
|
|
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(self.last_loaded_program)
|
|
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(_translate("HandlerClass","HOME\nALL"))
|
|
|
|
def hard_limit_tripped(self, obj, tripped, list_of_tripped):
|
|
if tripped:
|
|
self.add_status(_translate("HandlerClass","Hard limits tripped"), CRITICAL)
|
|
self.w.chk_override_limits.setEnabled(tripped)
|
|
else:
|
|
self.w.chk_override_limits.setChecked(False)
|
|
self.add_status(_translate("HandlerClass","Hard Limits Clear"),)
|
|
|
|
# keep check button in sync 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)
|
|
|
|
def set_button_response_state(self, state):
|
|
for i in (self.button_response_list):
|
|
self.w[i].setEnabled(not state)
|
|
|
|
def add_external_status(self, message, option):
|
|
level = option.get('LEVEL', STATUS.DEFAULT)
|
|
log = option.get("LOG", True)
|
|
title = message.get('TITLE', '')
|
|
mess = message.get('SHORTTEXT', '')
|
|
logtext = message.get('DETAILS', '')
|
|
|
|
self.add_status(mess, level, noLog=True)
|
|
if log:
|
|
STATUS.emit('update-machine-log', "{}\n{}".format(title, logtext), 'TIME')
|
|
|
|
# Log the last run line (in auto mode) if stopped
|
|
def lastRunLine(self, line):
|
|
if line >0:
|
|
self.add_status(_translate("HandlerClass",'last running line before stoppage: {}'.format(line)))
|
|
|
|
# called from hal_glib to run macros from external event
|
|
def request_macro_call(self, data):
|
|
if not self.w.chk_auto_mode_ext_macro.isChecked() and not STATUS.is_mdi_mode():
|
|
self.add_status(_translate("HandlerClass",'Machine must be in MDI mode to run macros'), WARNING)
|
|
return
|
|
|
|
if 'ini-macro-cmd' in data:
|
|
data = data.replace('ini-macro-cmd-','')
|
|
try:
|
|
temp = INFO.MACRO_COMMAND_DICT.get(data).get('cmd')
|
|
self.run_macro(data=temp)
|
|
return
|
|
except:
|
|
self.add_status(_translate(f"HandlerClass",'External requested INI macro data not recognized:{data}'), CRITICAL)
|
|
|
|
elif 'ini-mdi-cmd' in data:
|
|
for b in range(0,10):
|
|
button = self.w['macrobutton{}'.format(b)]
|
|
# prefer named INI MDI commands
|
|
key = button.property('ini_mdi_key')
|
|
code = INFO.get_ini_mdi_command(key)
|
|
#print(data,key,code)
|
|
if key == '' or code is None:
|
|
# fallback to legacy nth line
|
|
key = button.property('ini_mdi_number')
|
|
code = INFO.get_ini_mdi_command(key)
|
|
if code is None:
|
|
continue
|
|
if str(key) in data:
|
|
#print('match',button.objectName())
|
|
text = button.text().replace('\n',' ')
|
|
self.add_status(_translate("HandlerClass",'Running macro: {} {}'.format(key, text)))
|
|
try:
|
|
button.click()
|
|
except Exception as e:
|
|
self.add_status(_translate("HandlerClass",'Error running macro: {} {}\n{}'.format(key, text, e)))
|
|
break
|
|
else:
|
|
self.add_status(_translate(f"HandlerClass",'External requested INI mdi {data} does not match button name/number'), CRITICAL)
|
|
else:
|
|
self.add_status(_translate(f"HandlerClass",'External requested INI macro data not recognized:{data}'), CRITICAL)
|
|
|
|
#######################
|
|
# CALLBACKS FROM FORM #
|
|
#######################
|
|
|
|
# main button bar
|
|
def main_tab_changed(self, btn):
|
|
index = btn.property("index")
|
|
|
|
if index == TAB_USER:
|
|
pass
|
|
# if you select the tab showing, force the DRO to show
|
|
elif index == self.w.stackedWidget_mainTab.currentIndex():
|
|
if index == TAB_MAIN and STATUS.is_auto_mode():
|
|
self._maintab_cycle +=1
|
|
if index is None: return
|
|
|
|
# adjust the stack widgets depending on modes
|
|
self.adjust_stacked_widgets(index)
|
|
|
|
def hideVirtualKeyboard(self):
|
|
self.w.stackedWidget_dro.setCurrentIndex(0)
|
|
|
|
# gcode frame
|
|
def cmb_gcode_history_clicked(self):
|
|
if self.w.cmb_gcode_history.currentIndex() == 0: return
|
|
filename = self.w.cmb_gcode_history.currentText()
|
|
if filename == self.last_loaded_program:
|
|
self.add_status(_translate("HandlerClass","Selected program is already loaded"))
|
|
else:
|
|
ACTION.OPEN_PROGRAM(filename)
|
|
|
|
# program frame
|
|
def btn_start_clicked(self, obj):
|
|
if not STATUS.is_all_homed():
|
|
self.add_status(_translate("HandlerClass","Machine must be is homed"), CRITICAL)
|
|
return
|
|
if not os.path.exists(self.last_loaded_program):
|
|
self.add_status(_translate("HandlerClass","No program to execute"), WARNING)
|
|
return
|
|
if not STATUS.is_auto_mode() and not self.auto_mode_switch:
|
|
self.add_status(_translate("HandlerClass","Must be in AUTO mode to run a program"), WARNING)
|
|
return
|
|
if self.w.stackedWidget_mainTab.currentIndex() != 0:
|
|
self.add_status(_translate("HandlerClass","Switch view mode to MAIN"), WARNING)
|
|
return
|
|
if STATUS.is_auto_running():
|
|
self.add_status(_translate("HandlerClass","Program is already running"), WARNING)
|
|
return
|
|
if self.start_line <= 1:
|
|
ACTION.RUN(self.start_line)
|
|
else:
|
|
# instantiate run from line preset dialog
|
|
info = '<b>{}: {} <\b>'.format(_translate("HandlerClass","Running From Line"),self.start_line)
|
|
mess = {'NAME':'RUNFROMLINE', 'TITLE':'Preset Dialog', 'ID':'_RUNFROMLINE', 'MESSAGE':info, 'LINE':self.start_line}
|
|
ACTION.CALL_DIALOG(mess)
|
|
|
|
self.start_timer()
|
|
self.add_status("{} {}".format(_translate("HandlerClass","Started program from line"),self.start_line))
|
|
|
|
def btn_reload_file_clicked(self):
|
|
if self.last_loaded_program:
|
|
self.w.progressBar.setValue(0)
|
|
self.add_status("{} {}".format(_translate("HandlerClass","Loaded program file"), self.last_loaded_program))
|
|
ACTION.OPEN_PROGRAM(self.last_loaded_program)
|
|
|
|
# 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 = _translate("HandlerClass","Unhome All Axes?")
|
|
mess = {'NAME':'MESSAGE', 'ID':'_unhome_', 'MESSAGE':'UNHOME ALL', 'MORE':info, 'TYPE':'OKCANCEL'}
|
|
ACTION.CALL_DIALOG(mess)
|
|
|
|
def btn_home_clicked(self):
|
|
axisnum = self.w.sender().property('joint')
|
|
joint = INFO.get_jnum_from_axisnum(axisnum)
|
|
if STATUS.is_joint_homed(joint) == 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(_translate("HandlerClass","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(_translate("HandlerClass","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) / INFO.MAX_LINEAR_JOG_VEL) * 100
|
|
self.w.lbl_maxv_percent.setText("{:3.0f} %".format(maxpc))
|
|
|
|
def slider_rapid_changed(self, value):
|
|
rapid = (float(value) / 100) * self.factor
|
|
self.w.lbl_max_rapid.setText("{:4.0f}".format(rapid))
|
|
|
|
def btn_maxv_100_clicked(self):
|
|
self.w.slider_maxv_ovr.setValue(INFO.MAX_LINEAR_JOG_VEL)
|
|
|
|
def btn_maxv_50_clicked(self):
|
|
self.w.slider_maxv_ovr.setValue(INFO.MAX_LINEAR_JOG_VEL / 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.widget_file_copy.hide()
|
|
self.w.gcode_editor.show()
|
|
self.w.gcode_editor.editMode()
|
|
else:
|
|
self.w.filemanager.show()
|
|
self.w.widget_file_copy.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])
|
|
|
|
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(_translate("HandlerClass","Specified source is not a file"), WARNING)
|
|
return
|
|
self.source_file = source[0]
|
|
if target[1] is True:
|
|
self.destination_file = os.path.join(os.path.dirname(target[0]), os.path.basename(source[0]))
|
|
else:
|
|
self.destination_file = os.path.join(target[0], os.path.basename(source[0]))
|
|
if os.path.isfile(self.destination_file):
|
|
info = "{} {}".format(_translate("HandlerClass","already exists in destination directory"), self.destination_file)
|
|
mess = {'NAME':'MESSAGE', 'ICON':'WARNING', 'ID':'_overwrite_', 'MESSAGE':'OVERWRITE FILE?', 'MORE':info, 'TYPE':'YESNO','NONBLOCKING':True}
|
|
ACTION.CALL_DIALOG(mess)
|
|
else:
|
|
self.do_file_copy()
|
|
|
|
# offsets tab
|
|
def btn_goto_sensor_clicked(self):
|
|
x = float(self.w.lineEdit_sensor_x.text())
|
|
y = float(self.w.lineEdit_sensor_y.text())
|
|
|
|
if STATUS.is_metric_mode():
|
|
x = INFO.convert_machine_to_metric(x)
|
|
y = INFO.convert_machine_to_metric(y)
|
|
else:
|
|
x = INFO.convert_machine_to_imperial(x)
|
|
y = INFO.convert_machine_to_imperial(y)
|
|
|
|
# call mdi in a background process so screen stay responsive.
|
|
cmd =["G90","G53 G0 Z0","G53 G0 X{:3.4f} Y{:3.4f}".format(x, y)]
|
|
ACTION.CALL_BACKGROUND_MDI(cmd, label='Goto Sensor Button', timeout=30)
|
|
|
|
def btn_ref_laser_clicked(self):
|
|
x = float(self.w.lineEdit_laser_x.text())
|
|
y = float(self.w.lineEdit_laser_y.text())
|
|
|
|
if STATUS.is_metric_mode():
|
|
x = INFO.convert_machine_to_metric(x)
|
|
y = INFO.convert_machine_to_metric(y)
|
|
else:
|
|
x = INFO.convert_machine_to_imperial(x)
|
|
y = INFO.convert_machine_to_imperial(y)
|
|
|
|
self.add_status(_translate("HandlerClass","Laser offsets set"))
|
|
command = "G10 L20 P0 X{:3.4f} Y{:3.4f}".format(x, y)
|
|
ACTION.CALL_MDI(command, mode_return=True)
|
|
|
|
def btn_ref_camera_clicked(self):
|
|
x = float(self.w.lineEdit_camera_x.text())
|
|
y = float(self.w.lineEdit_camera_y.text())
|
|
|
|
if STATUS.is_metric_mode():
|
|
x = INFO.convert_machine_to_metric(x)
|
|
y = INFO.convert_machine_to_metric(y)
|
|
else:
|
|
x = INFO.convert_machine_to_imperial(x)
|
|
y = INFO.convert_machine_to_imperial(y)
|
|
|
|
self.add_status(_translate("HandlerClass","Camera offsets set"))
|
|
command = "G10 L20 P0 X{:3.4f} Y{:3.4f}".format(x, y)
|
|
ACTION.CALL_MDI(command, mode_return=True)
|
|
|
|
# tool tab
|
|
def btn_m61_clicked(self):
|
|
checked = self.w.tooloffsetview.get_checked_list()
|
|
if len(checked) > 1:
|
|
self.add_status(_translate("HandlerClass","Select only 1 tool to load"), WARNING)
|
|
elif checked:
|
|
self.add_status("{} {}".format(_translate("HandlerClass","Loaded tool"), checked[0]))
|
|
ACTION.CALL_MDI("M61 Q{} G43".format(checked[0]), mode_return=True)
|
|
else:
|
|
self.add_status(_translate("HandlerClass","No tool selected"), CRITICAL)
|
|
|
|
def btn_touchoff_clicked(self):
|
|
if STATUS.get_current_tool() == 0:
|
|
self.add_status(_translate("HandlerClass","Cannot touchoff with no tool loaded"), CRITICAL)
|
|
return
|
|
if not STATUS.is_all_homed():
|
|
self.add_status(_translate("HandlerClass","Must be homed to perform tool touchoff"), CRITICAL)
|
|
return
|
|
# instantiate dialog box
|
|
sensor = self.w.sender().property('sensor')
|
|
unit = "mm" if INFO.MACHINE_IS_METRIC else "in"
|
|
info = "{} {} {} {}".format(
|
|
_translate("HandlerClass","Ensure tooltip is within"),
|
|
_translate("HandlerClass","of tool sensor and click OK"),
|
|
self.w.lineEdit_max_probe.text(),unit)
|
|
mess = {'NAME':'MESSAGE', 'ID':sensor, 'MESSAGE':'TOOL TOUCHOFF', 'MORE':info, 'TYPE':'OKCANCEL'}
|
|
ACTION.CALL_DIALOG(mess)
|
|
|
|
# status tab
|
|
def btn_clear_status_clicked(self):
|
|
STATUS.emit('update-machine-log', None, 'DELETE')
|
|
|
|
def btn_save_status_clicked(self):
|
|
if self.w.stackedWidget_log.currentIndex():
|
|
text = self.w.integrator_log.getLogText()
|
|
name = 'sysLog_'
|
|
else:
|
|
text = self.w.machinelog.getLogText()
|
|
name = 'mchnLog_'
|
|
filename = self.w.lbl_clock.text()
|
|
filename = name + filename.replace(' ','_') + '.txt'
|
|
self.add_status("{} {}".format(_translate("HandlerClass","Saving Log file to"), 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
|
|
|
|
# scaling of the camera image for size aspect corrections
|
|
# set from preference file
|
|
def cam_xscale_changed(self, value):
|
|
self.w.camview.scaleX = float(value/100)
|
|
def cam_xscale_percent(self):
|
|
return self.w.camview.scaleX * 100
|
|
def cam_yscale_changed(self, value):
|
|
self.w.camview.scaleY = float(value/100)
|
|
def cam_yscale_percent(self):
|
|
return self.w.camview.scaleY * 100
|
|
|
|
# settings tab
|
|
def chk_override_limits_checked(self, state):
|
|
# only toggle override if it's not in sync with the button
|
|
if state and not STATUS.is_limits_override_set():
|
|
self.add_status(_translate("HandlerClass","Override limits set"), WARNING)
|
|
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(_translate("HandlerClass","Override limits cleared"))
|
|
|
|
def chk_run_from_line_checked(self, state):
|
|
self.w.btn_start.setText(_translate("HandlerClass","START\n1")) if state else self.w.btn_start.setText(_translate("HandlerClass","CYCLE\nSTART"))
|
|
|
|
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)
|
|
self.w.btn_camera.show() if state else self.w.btn_camera.hide()
|
|
|
|
def chk_auto_mode_macro_changed(self, state):
|
|
for b in range(0,10):
|
|
button = self.w['macrobutton{}'.format(b)]
|
|
button.setProperty('mdi_mode_check_action',not state)
|
|
|
|
def chk_auto_mode_external_macro_changed(self, state):
|
|
pass
|
|
|
|
def chk_use_sensor_changed(self, state):
|
|
self.w.btn_touch_sensor.setEnabled(state)
|
|
|
|
def chk_use_virtual_changed(self, state):
|
|
if not state:
|
|
self.w.stackedWidget_dro.setCurrentIndex(0)
|
|
|
|
# show ngcgui info tab (in the stackedWidget) if ngcgui utilites
|
|
# tab is selected
|
|
def tab_utilities_changed(self, num):
|
|
if 'ngc' in self.w.tabWidget_utilities.tabText(num).lower():
|
|
self.w.stackedWidget.setCurrentIndex(PAGE_NGCGUI)
|
|
else:
|
|
self.w.stackedWidget.setCurrentIndex(PAGE_GCODE)
|
|
|
|
def btn_about_clicked(self):
|
|
self.log_version()
|
|
|
|
info = ACTION.GET_ABOUT_INFO()
|
|
self.w.aboutDialog_.showdialog()
|
|
|
|
def btn_zoomin_clicked(self):
|
|
self.w.gcode_viewer.editor.zoomIn()
|
|
def btn_zoomout_clicked(self):
|
|
self.w.gcode_viewer.editor.zoomOut()
|
|
|
|
def btn_spindle_z_up_clicked(self):
|
|
fval = int(self.w.lineEdit_eoffset_count.text())
|
|
if INFO.MACHINE_IS_METRIC:
|
|
fval += 5
|
|
else:
|
|
fval += 1
|
|
self.w.lineEdit_eoffset_count.setText(str(fval))
|
|
if self.h['eoffset-clear'] != True:
|
|
self.h['eoffset-spindle-count'] = int(fval)
|
|
|
|
def btn_spindle_z_down_clicked(self):
|
|
fval = int(self.w.lineEdit_eoffset_count.text())
|
|
if INFO.MACHINE_IS_METRIC:
|
|
fval -= 5
|
|
else:
|
|
fval -= 1
|
|
if fval <0: fval = 0
|
|
self.w.lineEdit_eoffset_count.setText(str(fval))
|
|
if self.h['eoffset-clear'] != True:
|
|
self.h['eoffset-spindle-count'] = int(fval)
|
|
|
|
def btn_pause_clicked(self, data):
|
|
|
|
# pause request
|
|
if data:
|
|
ACTION.PAUSE_MACHINE()
|
|
self.w.btn_spindle_pause.setEnabled(False)
|
|
self.liftSpindle()
|
|
|
|
# no lowering required - resume
|
|
elif int(self.w.lineEdit_eoffset_count.text()) == 0 or \
|
|
not self.w.btn_spindle_pause.isChecked() or \
|
|
self.h['eoffset-clear']:
|
|
ACTION.RESUME()
|
|
# only enable the lift selection button, if pins are connected
|
|
if self.spindle_lift_pins_present:
|
|
self.w.btn_spindle_pause.setEnabled(True)
|
|
|
|
# lowering required before resuming
|
|
elif STATUS.is_auto_paused():
|
|
self.h['spindle-inhibit'] = False
|
|
self.add_status(_translate("HandlerClass",'Request Spindle Speed Wait'))
|
|
# If spindle at speed is connected to a pin, use it lower spindle
|
|
if self.h.hal.pin_has_writer('spindle.0.at-speed'):
|
|
self._spindle_wait = True
|
|
return
|
|
else:
|
|
# or wait for dialog to close before lowering spindle
|
|
# instantiate warning box
|
|
info = _translate("HandlerClass","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)
|
|
|
|
|
|
# from HAL pin 'eoffset-is-active'
|
|
# unpause machine if external offsets state is false
|
|
def external_offset_state_changed(self, data):
|
|
# only if running a program and only if machine in on
|
|
if not STATUS.is_auto_running() or not STATUS.machine_is_on():
|
|
return
|
|
|
|
# if GUI setting is at zero lift - ignore
|
|
if int(self.w.lineEdit_eoffset_count.text()) == 0:return
|
|
|
|
# only if pin is false (external offsets are off)
|
|
if not data:
|
|
# spindle better be un-inhibitted
|
|
if self.h['spindle-inhibit'] == True:
|
|
self.add_status(_translate("HandlerClass",'Spindle not running - keeping paused'),CRITICAL)
|
|
return
|
|
ACTION.RESUME()
|
|
|
|
# only enable the lift selection button if pins are connected
|
|
if self.spindle_lift_pins_present and self.w.chk_eoffsets.isChecked():
|
|
self.w.btn_spindle_pause.setEnabled(True)
|
|
else:
|
|
self.w.btn_spindle_pause.setEnabled(False)
|
|
self.w.btn_spindle_pause.setChecked(False)
|
|
|
|
# make step program button active again
|
|
self.w.action_step.setEnabled(True)
|
|
|
|
def liftSpindle(self):
|
|
|
|
# if we were waiting for restart dialog to return
|
|
# cancel it - we want to relift
|
|
mess = {'NAME':'MESSAGE', 'ID':'_wait_resume_', 'CLOSE':True}
|
|
ACTION.CALL_DIALOG(mess)
|
|
# cancel auto lowering when spindle at speed
|
|
self._spindle_wait = False
|
|
|
|
# only enable the lift selection button if pins are connected
|
|
if self.spindle_lift_pins_present and self.w.chk_eoffsets.isChecked():
|
|
pass
|
|
else:
|
|
if not self.w.chk_eoffsets.isChecked() and self.w.btn_spindle_pause.isChecked():
|
|
self.add_status(_translate("HandlerClass",'Eoffsets Setting Unchecked - Can Not Raise Spindle'),CRITICAL)
|
|
self.w.btn_spindle_pause.setEnabled(False)
|
|
self.w.btn_spindle_pause.setChecked(False)
|
|
return
|
|
# do we need to lift spindle?
|
|
if int(self.w.lineEdit_eoffset_count.text()) == 0: return
|
|
if not self.w.btn_spindle_pause.isChecked(): return
|
|
|
|
# can't step when lifted
|
|
self.w.action_step.setEnabled(False)
|
|
|
|
# set external offsets to lift spindle
|
|
self.h['eoffset-clear'] = False
|
|
self.h['eoffset-enable'] = True
|
|
fval = int(self.w.lineEdit_eoffset_count.text())
|
|
self.h['eoffset-spindle-count'] = int(fval)
|
|
|
|
# stop the spindle
|
|
self.h['spindle-inhibit'] = True
|
|
self.add_status("{} {}".format(_translate("HandlerClass","Spindle stopped and raising to"), fval))
|
|
|
|
def lowerSpindle(self):
|
|
# spindle better be un-inhibitted
|
|
if self.h['spindle-inhibit'] == True:
|
|
self.add_status(_translate("HandlerClass",'Spindle not running - keeping raised and paused'),CRITICAL)
|
|
return
|
|
self.h['eoffset-spindle-count'] = 0
|
|
self.h['eoffset-clear'] = True
|
|
self.add_status('Spindle lowering')
|
|
|
|
# from abort button
|
|
def btn_stop_clicked(self,data):
|
|
if not self.w.btn_spindle_pause.isChecked(): return
|
|
self.w.btn_spindle_pause.setChecked(False)
|
|
if not self.h['eoffset-spindle-count'] == 0:
|
|
# or wait for dialog to close before lowering spindle
|
|
# instantiate warning box
|
|
info = _translate("HandlerClass","Spindle lift still active. press OK to lower spindle")
|
|
mess = {'NAME':'MESSAGE', 'ICON':'WARNING', 'ID':'_wait_to_lower_',
|
|
'MESSAGE':'CAUTION', 'MORE':info, 'TYPE':'OK'}
|
|
ACTION.CALL_DIALOG(mess)
|
|
|
|
|
|
# make sure the pause button follows STATUS pause messages
|
|
# as well as physical button pushes
|
|
def update_pause_button(self,data):
|
|
self.w.action_pause._blockSignals(True)
|
|
self.w.action_pause.setChecked(data)
|
|
self.pause_timer(data)
|
|
# only enable the lift selection button if pins are connected
|
|
if self.spindle_lift_pins_present and self.w.chk_eoffsets.isChecked():
|
|
self.w.btn_spindle_pause.setEnabled(not data)
|
|
self.w.action_pause._blockSignals(False)
|
|
|
|
def btn_systemtool_toggled(self, state):
|
|
if state:
|
|
STATUS.emit('dro-reference-change-request', 1)
|
|
|
|
def MPG_select_changed(self, button):
|
|
#print(button)
|
|
# Auto exclusive doesn't allow unchecking all buttons
|
|
# We force it here
|
|
if button == self._lastSelectButton:
|
|
button.group().setExclusive(False)
|
|
button.setChecked(False)
|
|
button.group().setExclusive(True)
|
|
self._lastSelectButton = None
|
|
|
|
if button == self.w.btn_mpg_scroll:
|
|
self.removeMPGFocusBorder()
|
|
return
|
|
if button == self.w.btn_mpg_scroll:
|
|
if self.w.btn_mpg_scroll.isChecked():
|
|
self.recolorMPGFocusBorder()
|
|
else:
|
|
self.removeMPGFocusBorder()
|
|
|
|
#self.set_statusbar('MPG output Selected: {}'.format(cmd.toolTip()),DEFAULT,noLog=True)
|
|
self._lastSelectButton = button
|
|
|
|
# if a macro has been requested, do path checks and run it
|
|
def run_macro( self, data = None ):
|
|
o_codes = data.split()
|
|
command = str( "O<" + o_codes[0] + "> call" )
|
|
|
|
# confirm oword path exists
|
|
rtn = ACTION.check_macro_path(command)
|
|
if not rtn is True:
|
|
for i in rtn:
|
|
self.add_status(i, WARNING)
|
|
return
|
|
|
|
for code in o_codes[1:]:
|
|
# wait but don't block:
|
|
parameter, ok = self.w.calculatorDialog_.getValue(f"Enter a value for: {code}:")
|
|
if not ok:
|
|
self.add_status('Macro cancelled')
|
|
return
|
|
command = command + " [" + str(parameter) + "] "
|
|
|
|
# pop a dialog of the properties
|
|
msg = QtWidgets.QMessageBox()
|
|
msg.setIcon(QtWidgets.QMessageBox.Information)
|
|
msg.setText(_translate("HandlerClass",f"Run Macro Command: {command}"))
|
|
msg.setWindowTitle(_translate("HandlerClass","Confirm To Run Macro Command"))
|
|
msg.setStandardButtons(QtWidgets.QMessageBox.Ok|QtWidgets.QMessageBox.Cancel)
|
|
msg.show()
|
|
retval = msg.exec_()
|
|
if retval == QtWidgets.QMessageBox.Ok:
|
|
self.add_status(f'Run Macro Command:{command}')
|
|
ACTION.SET_GRAPHICS_VIEW('clear')
|
|
ACTION.CALL_MDI(command, mode_return=True)
|
|
return
|
|
|
|
self.add_status('Macro cancelled')
|
|
|
|
#####################
|
|
# GENERAL FUNCTIONS #
|
|
#####################
|
|
|
|
# file manager widget overridden function
|
|
def load_code(self, fname):
|
|
if fname is None: return
|
|
filename, file_extension = os.path.splitext(fname)
|
|
|
|
# loading ngc then HTML/PDF
|
|
|
|
if not file_extension in (".html", '.pdf'):
|
|
if not (INFO.program_extension_valid(fname)):
|
|
self.add_status("{} {}".format(_translate("HandlerClass","Unknown or invalid filename extension"),file_extension), CRITICAL)
|
|
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("{} : {}".format(_translate("HandlerClass","Loaded program file"), fname))
|
|
self.adjust_stacked_widgets(TAB_MAIN)
|
|
self.w.filemanager.recordBookKeeping()
|
|
|
|
# adjust ending to check for related HTML setup files
|
|
fname = filename+'.html'
|
|
try:
|
|
if os.path.exists(fname):
|
|
self.w.webwidget.loadFile(fname)
|
|
self.add_status("{} : {}".format(_translate("HandlerClass","Loaded HTML file"), fname), CRITICAL)
|
|
else:
|
|
self.w.webwidget.setHtml(self.html, self.swoopURL)
|
|
except Exception as e:
|
|
self.add_status("{} {} : {}".format(_translate("HandlerClass","Error loading HTML file"), fname,e))
|
|
# look for PDF setup files
|
|
# load it with system program
|
|
fname = filename+'.pdf'
|
|
if os.path.exists(fname):
|
|
self.PDFView.loadView(fname)
|
|
self.add_status("{} : {}".format(_translate("HandlerClass","Loaded PDF file"), fname))
|
|
else:
|
|
self.PDFView.loadSample('setup_tab')
|
|
|
|
return
|
|
|
|
# loading HTML/PDF directly
|
|
|
|
if file_extension == ".html":
|
|
try:
|
|
self.w.webwidget.loadFile(fname)
|
|
self.add_status("{} : {}".format(_translate("HandlerClass","Loaded HTML file"),fname))
|
|
self.w.stackedWidget_mainTab.setCurrentIndex(TAB_SETUP)
|
|
self.w.stackedWidget.setCurrentIndex(0)
|
|
self.w.tabWidget_setup.setCurrentIndex(1)
|
|
self.w.btn_setup.setChecked(True)
|
|
self.w.jogging_frame.hide()
|
|
except Exception as e:
|
|
self.add_status("{} {} : {}".format(_translate("HandlerClass","Error loading HTML file"), fname,e))
|
|
else:
|
|
if os.path.exists(fname):
|
|
self.PDFView.loadView(fname)
|
|
self.add_status("{} : {}".format(_translate("HandlerClass","Loaded PDF file"), fname))
|
|
self.w.stackedWidget_mainTab.setCurrentIndex(TAB_SETUP)
|
|
self.w.stackedWidget.setCurrentIndex(0)
|
|
self.w.tabWidget_setup.setCurrentIndex(1)
|
|
self.w.btn_setup.setChecked(True)
|
|
self.w.jogging_frame.hide()
|
|
|
|
# NGCGui library overridden function
|
|
# adds an error message to status
|
|
def check_linuxcnc_paths_fail_override(self, fname):
|
|
self.add_status("{} {} {}".format(_translate("HandlerClass","NGCGUI Path"), _translate("HandlerClass","not in linuxcnc's SUBROUTINE_PATH INI entry"), fname), CRITICAL)
|
|
return ''
|
|
|
|
def update_gcode_properties(self, props ):
|
|
# substitute nice looking text:
|
|
property_names = {
|
|
'name': "Name:", 'size': "Size:",
|
|
' tools': "Tool order:", 'g0': "Rapid distance:",
|
|
'g1': "Feed distance:", 'g': "Total distance:",
|
|
'run': "Run time:",'machine_unit_sys':"Machine Unit System:",
|
|
'x': "X bounds:",'x_zero_rxy':'X @ Zero Rotation:',
|
|
'y': "Y bounds:",'y_zero_rxy':'Y @ Zero Rotation:',
|
|
'z': "Z bounds:",'z_zero_rxy':'Z @ Zero Rotation:',
|
|
'a': "A bounds:", 'b': "B bounds:",
|
|
'c': "C bounds:",'toollist':'Tool Change List:',
|
|
'gcode_units':"Gcode Units:"
|
|
}
|
|
|
|
smallmess = mess = ''
|
|
if props:
|
|
for i in props:
|
|
smallmess += '<b>%s</b>: %s<br>' % (property_names.get(i), props[i])
|
|
mess += '<span style=" font-size:18pt; font-weight:600; color:black;">%s </span>\
|
|
<span style=" font-size:18pt; font-weight:600; color:#aa0000;">%s</span>\
|
|
<br>'% (property_names.get(i), props[i])
|
|
|
|
# put the details into the properties page
|
|
self.w.textedit_properties.setText(mess)
|
|
return
|
|
# pop a dialog of the properties
|
|
msg = QtWidgets.QMessageBox()
|
|
msg.setIcon(QtWidgets.QMessageBox.Information)
|
|
msg.setText(smallmess)
|
|
msg.setWindowTitle(_translate("HandlerClass","Gcode Properties"))
|
|
msg.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
|
msg.show()
|
|
retval = msg.exec_()
|
|
|
|
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_status(_translate("HandlerClass","Unknown touchoff routine specified"), CRITICAL)
|
|
return
|
|
|
|
max_probe = self.w.lineEdit_max_probe.text()
|
|
search_vel = self.w.lineEdit_search_vel.text()
|
|
probe_vel = self.w.lineEdit_probe_vel.text()
|
|
retract = self.w.lineEdit_retract_distance.text()
|
|
safe_z = self.w.lineEdit_z_safe_travel.text()
|
|
self.add_status("{} {} {} {} {} {} {} {} {}".format(
|
|
_translate("HandlerClass","Touchoff to"),_translate("HandlerClass","started with"), selector,
|
|
search_vel, probe_vel, max_probe,
|
|
z_offset, retract, safe_z))
|
|
rtn = ACTION.TOUCHPLATE_TOUCHOFF(search_vel, probe_vel, max_probe,
|
|
z_offset, retract, safe_z, self.touchoff_return,self.touchoff_error)
|
|
if not rtn == 1:
|
|
self.add_status(rtn, CRITICAL)
|
|
|
|
def touchoff_return(self, data):
|
|
self.add_status(_translate("HandlerClass","Touchplate touchoff routine returned successfully"))
|
|
self.add_status(_translate("HandlerClass","Touchplate returned: ")+data, CRITICAL)
|
|
|
|
def touchoff_error(self, data):
|
|
ACTION.SET_ERROR_MESSAGE(data)
|
|
self.add_status(data, CRITICAL)
|
|
|
|
def kb_jog(self, state, joint, direction, fast = False, linear = True):
|
|
ACTION.SET_MANUAL_MODE()
|
|
if not STATUS.is_man_mode() or not STATUS.machine_is_on():
|
|
self.add_status(_translate("HandlerClass",'Machine must be ON and in Manual mode to jog'), CRITICAL)
|
|
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:
|
|
# incremental jogging?
|
|
if joint in (3,4,5,'A','B','C'): # angualar axis
|
|
if STATUS.get_jog_increment_angular() != 0: return
|
|
elif STATUS.get_jog_increment() != 0: return
|
|
|
|
# otherwise stop jogging when key released
|
|
ACTION.JOG(joint, 0, 0, 0)
|
|
|
|
def add_status(self, message, alertLevel = DEFAULT, noLog = False):
|
|
opt = 'TIME'
|
|
if alertLevel==DEFAULT:
|
|
self.set_style_default()
|
|
elif alertLevel==SUCCESS:
|
|
opt += ',SUCCESS'
|
|
self.set_style_default()
|
|
elif alertLevel==WARNING:
|
|
opt += ',WARNING'
|
|
self.set_style_warning()
|
|
elif alertLevel==ERROR:
|
|
opt += ',ERROR'
|
|
self.set_style_warning()
|
|
else:
|
|
opt += ',CRITICAL'
|
|
self.set_style_critical()
|
|
self.w.lineEdit_statusbar.setText(message)
|
|
if noLog:
|
|
return
|
|
STATUS.emit('update-machine-log', message, opt)
|
|
|
|
def enable_auto(self, state):
|
|
for widget in self.auto_list:
|
|
self.w[widget].setEnabled(state)
|
|
# need to let adjust stack function know the mode changed
|
|
# not auto
|
|
if state is True:
|
|
self.adjust_stacked_widgets(self.w.stackedWidget_mainTab.currentIndex(),mode_change = True)
|
|
# auto mode
|
|
else:
|
|
self.w.btn_main.setChecked(True)
|
|
self.adjust_stacked_widgets(TAB_MAIN,mode_change = True)
|
|
|
|
|
|
def enable_onoff(self, state):
|
|
if state:
|
|
self.add_status(_translate("HandlerClass","Machine ON"), SUCCESS)
|
|
else:
|
|
if not STATUS.estop_is_clear():
|
|
self.add_status(_translate("HandlerClass","ESTOP!"), CRITICAL)
|
|
else:
|
|
self.add_status(_translate("HandlerClass","MACHINE OFF"), ERROR)
|
|
self.w.btn_spindle_pause.setChecked(False)
|
|
self.h['eoffset-spindle-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_start.setText("{}\n{}".format(_translate("HandlerClass","START"), 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(_translate("HandlerClass",'Keyboard shortcuts are disabled'))
|
|
return False
|
|
|
|
def do_file_copy(self):
|
|
try:
|
|
copyfile(self.source_file, self.destination_file)
|
|
txt1 = _translate("HandlerClass","Copied file from")
|
|
txt2 = _translate("HandlerClass","to")
|
|
self.add_status("{} {} {} {}".format(txt1,self.source_file, txt2, self.destination_file))
|
|
except Exception as e:
|
|
self.add_status("{}. {}".format(_translate("HandlerClass","Unable to copy file"), e))
|
|
|
|
def update_rpm(self, speed):
|
|
if self.max_spindle_rpm < int(speed) < self.min_spindle_rpm:
|
|
if STATUS.is_spindle_on():
|
|
self.w.lbl_spindle_set.setProperty('in_range', False)
|
|
self.w.lbl_spindle_set.style().unpolish(self.w.lbl_spindle_set)
|
|
self.w.lbl_spindle_set.style().polish(self.w.lbl_spindle_set)
|
|
else:
|
|
self.w.lbl_spindle_set.setProperty('in_range', True)
|
|
self.w.lbl_spindle_set.style().unpolish(self.w.lbl_spindle_set)
|
|
self.w.lbl_spindle_set.style().polish(self.w.lbl_spindle_set)
|
|
|
|
def periodic_update(self):
|
|
# if waiting and up to speed, lower spindle
|
|
if self._spindle_wait:
|
|
if bool(self.h.hal.get_value('spindle.0.at-speed')):
|
|
self.lowerSpindle()
|
|
self._spindle_wait = False
|
|
|
|
self.update_runtimer()
|
|
|
|
def update_runtimer(self):
|
|
if not self.timer_on or STATUS.is_auto_paused():
|
|
return
|
|
|
|
tick = time.time()
|
|
elapsed_time = tick - self.timer_tick
|
|
self.run_time += elapsed_time
|
|
self.timer_tick = tick
|
|
|
|
hours, remainder = divmod(int(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.timer_on = True
|
|
self.timer_tick = time.time()
|
|
|
|
def pause_timer(self, state):
|
|
# stop the timer update but
|
|
# keep track of accumulated time
|
|
if state:
|
|
self.timer_on = False
|
|
tick = time.time()
|
|
self.run_time += tick - self.timer_tick
|
|
txt = _translate("HandlerClass","Run timer paused at")
|
|
self.add_status("{} {}".format(txt, self.w.lbl_runtime.text()))
|
|
|
|
# reset the time reference point to now
|
|
# but don't reset the accumulated time
|
|
else:
|
|
self.timer_on = True
|
|
self.timer_tick = time.time()
|
|
|
|
def stop_timer(self):
|
|
self.timer_on = False
|
|
if STATUS.is_auto_mode():
|
|
self.add_status("{} {}".format(_translate("HandlerClass","Run timer stopped at"), self.w.lbl_runtime.text()))
|
|
|
|
# web page zoom
|
|
def zoomWeb(self):
|
|
# webview
|
|
try:
|
|
f = self.w.webwidget.zoomFactor() +.5
|
|
if f > 2:f = 1
|
|
self.w.webwidget.setZoomFactor(f)
|
|
except:
|
|
pass
|
|
|
|
# PDF
|
|
try:
|
|
f = self.PDFView.zoomFactor() +.5
|
|
if f > 2:f = 1
|
|
self.PDFView.setZoomFactor(f)
|
|
except:
|
|
pass
|
|
|
|
# go directly the default HTML page
|
|
def homeWeb(self):
|
|
try:
|
|
if os.path.exists(self.default_setup):
|
|
self.w.webwidget.load(QtCore.QUrl.fromLocalFile(self.default_setup))
|
|
else:
|
|
self.w.webwidget.setHtml(self.html, self.swoopURL)
|
|
except:
|
|
pass
|
|
# setup tab's web page back button
|
|
def back(self):
|
|
try:
|
|
try:
|
|
self.w.webwidget.page().triggerAction(QWebEnginePage.Back)
|
|
except:
|
|
if os.path.exists(self.default_setup):
|
|
self.w.webwidget.load(QtCore.QUrl.fromLocalFile(self.default_setup))
|
|
else:
|
|
self.w.webwidget.setHtml(self.html, self.swoopURL)
|
|
except:
|
|
pass
|
|
|
|
# setup tab's web page forward button
|
|
def forward(self):
|
|
try:
|
|
try:
|
|
self.w.webwidget.page().triggerAction(QWebEnginePage.Forward)
|
|
except:
|
|
self.w.webwidget.load(QtCore.QUrl.fromLocalFile(self.docs))
|
|
except:
|
|
pass
|
|
|
|
# setup tab's web page - enable/disable buttons
|
|
def onLoadFinished(self):
|
|
try:
|
|
if self.w.webwidget.history().canGoBack():
|
|
self.backBtn.setEnabled(True)
|
|
else:
|
|
self.backBtn.setEnabled(False)
|
|
|
|
if self.w.webwidget.history().canGoForward():
|
|
self.forBtn.setEnabled(True)
|
|
else:
|
|
self.forBtn.setEnabled(False)
|
|
except:
|
|
pass
|
|
|
|
def writer(self):
|
|
WRITER.show()
|
|
|
|
def endcolor(self):
|
|
self.timer = QtCore.QTimer()
|
|
self.timer.timeout.connect(self.clear_status_bar)
|
|
self.timer.start(self.statusbar_reset_time)
|
|
|
|
def clear_status_bar(self):
|
|
self.set_style_default()
|
|
self.w.lineEdit_statusbar.setText('')
|
|
|
|
# change Status bar text color
|
|
def set_style_default(self):
|
|
c = self.w.screen_options.property('user1Color').name()
|
|
self.w.lineEdit_statusbar.setStyleSheet(
|
|
"background-color: {} ;color: rgb(0,0,0)".format(c)) #default white
|
|
|
|
def set_style_warning(self):
|
|
c = self.w.screen_options.property('user2Color').name()
|
|
self.w.lineEdit_statusbar.setStyleSheet(
|
|
"background-color: {} ;color: rgb(0,0,0)".format(c)) #yellow
|
|
self.endcolor()
|
|
|
|
def set_style_critical(self):
|
|
c = self.w.screen_options.property('user3Color').name()
|
|
self.w.lineEdit_statusbar.setStyleSheet(
|
|
"background-color: {} ;color: rgb(0,0,0)".format(c)) #orange
|
|
self.endcolor()
|
|
|
|
def adjust_stacked_widgets(self,requestedIndex,mode_change=False):
|
|
IGNORE = -1
|
|
SHOW_DRO = 0
|
|
mode = ['','','Auto','MDI'][STATUS.get_current_mode()]
|
|
if mode_change:
|
|
premode = ['','','Auto','MDI'][STATUS.get_previous_mode()]
|
|
else:
|
|
premode=mode
|
|
currentIndex = self.w.stackedWidget_mainTab.currentIndex()
|
|
indexList = ['main','file','offsets','tool','status','probe','cam',
|
|
'gcode','setup','settings','util','user']
|
|
if self.auto_mode_switch and not STATUS.is_auto_running():
|
|
opt = requestedIndex,PAGE_FILE,True,IGNORE,False
|
|
else:
|
|
opt = TAB_MAIN,PAGE_GCODE,False,SHOW_DRO,False
|
|
if mode == 'Auto':
|
|
seq = {TAB_MAIN: (TAB_MAIN,PAGE_GCODE,False,SHOW_DRO,False),
|
|
TAB_FILE: (opt),
|
|
TAB_OFFSETS: (TAB_MAIN,PAGE_GCODE,False,SHOW_DRO,False),
|
|
TAB_TOOL: (TAB_MAIN,PAGE_GCODE,False,SHOW_DRO,False),
|
|
TAB_STATUS: (requestedIndex,PAGE_GCODE,False,SHOW_DRO,False),
|
|
TAB_PROBE: (TAB_MAIN,PAGE_GCODE,False,SHOW_DRO,False),
|
|
TAB_CAMERA: (requestedIndex,PAGE_UNCHANGED,False,SHOW_DRO,False),
|
|
TAB_GCODES: (requestedIndex,PAGE_UNCHANGED,False,SHOW_DRO,False),
|
|
TAB_SETUP: (requestedIndex,PAGE_UNCHANGED,False,SHOW_DRO,False),
|
|
TAB_SETTINGS: (requestedIndex,PAGE_GCODE,False,SHOW_DRO,False),
|
|
TAB_UTILITIES: (TAB_MAIN,PAGE_GCODE,False,SHOW_DRO,False),
|
|
TAB_USER: (requestedIndex,PAGE_UNCHANGED,IGNORE,IGNORE,False) }
|
|
else:
|
|
seq = {TAB_MAIN: (requestedIndex,PAGE_GCODE,True,SHOW_DRO,True),
|
|
TAB_FILE: (requestedIndex,PAGE_FILE,True,IGNORE,False),
|
|
TAB_OFFSETS: (requestedIndex,PAGE_OFFSET,True,IGNORE,False),
|
|
TAB_TOOL: (requestedIndex,PAGE_TOOL,True,IGNORE,False),
|
|
TAB_STATUS: (requestedIndex,PAGE_UNCHANGED,True,SHOW_DRO,False),
|
|
TAB_PROBE: (requestedIndex,PAGE_GCODE,True,SHOW_DRO,False),
|
|
TAB_CAMERA: (requestedIndex,PAGE_UNCHANGED,True,IGNORE,True),
|
|
TAB_GCODES: (requestedIndex,PAGE_UNCHANGED,False,SHOW_DRO,False),
|
|
TAB_SETUP: (requestedIndex,PAGE_UNCHANGED,False,IGNORE,False),
|
|
TAB_SETTINGS: (requestedIndex,PAGE_UNCHANGED,False,SHOW_DRO,False),
|
|
TAB_UTILITIES: (requestedIndex,PAGE_UNCHANGED,True,SHOW_DRO,False),
|
|
TAB_USER: (requestedIndex,PAGE_UNCHANGED,IGNORE,IGNORE,True) }
|
|
|
|
rtn = seq.get(requestedIndex)
|
|
|
|
# if not found (None) use defaults
|
|
if rtn is None:
|
|
main_index = requestedIndex
|
|
stacked_index = 0
|
|
show_JogControls = True
|
|
show_dro = 0
|
|
show_macro = True
|
|
else:
|
|
main_index,stacked_index,show_JogControls,show_dro,show_macro = rtn
|
|
|
|
# user tab button covers multiple tabs so adjust name
|
|
# for extra user tabs
|
|
if currentIndex >= len(indexList):
|
|
tabId = 'user{}'.format(currentIndex - len(indexList))
|
|
else:
|
|
tabId = indexList[currentIndex]
|
|
name = 'splitterSettings-{}{}'.format(tabId,premode)
|
|
#print ('CURRENT:',name)
|
|
# record current qsplitter settings
|
|
self.w.settings.beginGroup("qtdragon-{}".format(self.w.splitter_h.objectName()))
|
|
self.w.settings.setValue(name, QtCore.QVariant(self.w.splitter_h.saveState().data()))
|
|
self.w.settings.endGroup()
|
|
|
|
# ignore, show or hide jog controls
|
|
if show_JogControls == IGNORE:
|
|
pass
|
|
elif show_JogControls:
|
|
self.w.jogging_frame.show()
|
|
else:
|
|
self.w.jogging_frame.hide()
|
|
|
|
# show DRO rather then keyboard.
|
|
if show_dro > IGNORE:
|
|
self.w.stackedWidget_dro.setCurrentIndex(0)
|
|
|
|
# macros can only be run in manual or mdi mode
|
|
if show_macro:
|
|
self.w.frame_macro_buttons.show()
|
|
else:
|
|
self.w.frame_macro_buttons.hide()
|
|
|
|
# show ngcgui info tab if utilities tab is selected
|
|
# but only if the utilities tab has ngcgui selected
|
|
if main_index == TAB_UTILITIES:
|
|
num = self.w.tabWidget_utilities.currentIndex()
|
|
if 'ngc' in self.w.tabWidget_utilities.tabText(num).lower():
|
|
self.w.stackedWidget.setCurrentIndex(PAGE_NGCGUI)
|
|
else:
|
|
self.w.stackedWidget.setCurrentIndex(PAGE_GCODE)
|
|
|
|
# adjust the stacked widget (left side MDI/Gcode stack)
|
|
if stacked_index > PAGE_UNCHANGED:
|
|
self.w.stackedWidget.setCurrentIndex(stacked_index)
|
|
|
|
# toggle home/tool offsets buttons in DRO section
|
|
if main_index == TAB_TOOL:
|
|
num = 1
|
|
else:
|
|
num = 0
|
|
for n,i in enumerate(INFO.AVAILABLE_AXES):
|
|
self.w['dro_button_stack_%s'%(n)].setCurrentIndex(num)
|
|
|
|
# user tabs button cycles between all user tabs
|
|
main_current = self.w.stackedWidget_mainTab.currentIndex()
|
|
if main_index == TAB_USER and main_current >= TAB_USER:
|
|
next = main_current +1
|
|
if next == self.w.stackedWidget_mainTab.count():
|
|
next = TAB_USER
|
|
self.w.stackedWidget_mainTab.setCurrentIndex(next)
|
|
else:
|
|
# set main tab to adjusted index
|
|
self.w.stackedWidget_mainTab.setCurrentIndex(main_index)
|
|
|
|
# switch modes if file is pressed in auto mode
|
|
if main_index == TAB_FILE and self.auto_mode_switch:
|
|
if STATUS.is_auto_mode():
|
|
ACTION.SET_MANUAL_MODE()
|
|
|
|
# if indexes don't match then request is disallowed
|
|
# give a warning and reset the button check
|
|
if main_index != requestedIndex and not main_index in(TAB_CAMERA, TAB_GCODES,
|
|
TAB_STATUS, TAB_SETUP):
|
|
self.add_status("Cannot switch pages while in AUTO mode", WARNING)
|
|
self.w.stackedWidget_mainTab.setCurrentIndex(0)
|
|
self.w.btn_main.setChecked(True)
|
|
|
|
# user tab button covers multiple tabs
|
|
# adjust name depending in what user tab is showing
|
|
cur = self.w.stackedWidget_mainTab.currentIndex()
|
|
if cur >= len(indexList):
|
|
tabId = 'user{}'.format(cur - len(indexList))
|
|
else:
|
|
tabId = indexList[cur]
|
|
|
|
# if in auto mode and the main tab button is pressed
|
|
# cycle between full gcode, split and pull graphics
|
|
if main_index == TAB_MAIN and mode =='Auto':
|
|
if self._maintab_cycle >3: self._maintab_cycle = 1
|
|
if self._maintab_cycle == 2:
|
|
self.w.frame_top_left.hide()
|
|
self.w.frm_backplot.show()
|
|
return
|
|
elif self._maintab_cycle == 3:
|
|
self.w.frame_top_left.show()
|
|
self.w.frm_backplot.hide()
|
|
return
|
|
else:
|
|
self.w.frame_top_left.show()
|
|
self.w.frm_backplot.show()
|
|
else:
|
|
self.w.frame_top_left.show()
|
|
self.w.frm_backplot.show()
|
|
|
|
# adjust window splitter size as per saved adjustments
|
|
|
|
name = 'splitterSettings-{}{}'.format(tabId,mode)
|
|
#print ('NOW:',name)
|
|
# restore new qsplitter setting
|
|
self.w.settings.beginGroup("qtdragon-{}".format(self.w.splitter_h.objectName()))
|
|
splitterSetting = self.w.settings.value(name)
|
|
self.w.settings.endGroup()
|
|
if not splitterSetting is None:
|
|
try:
|
|
self.w.splitter_h.restoreState(QtCore.QByteArray(splitterSetting))
|
|
except Exception as e:
|
|
print(e)
|
|
|
|
# if you click the current tab again, hide/show the lower page to
|
|
# expand the upper page.
|
|
if currentIndex == requestedIndex and not mode_change:
|
|
self.w.lower_widget.setVisible(not self.w.lower_widget.isVisible())
|
|
|
|
|
|
# set axis 4/5 dro widgets to the proper axis
|
|
# TODO do this with all the axes for more flexibility
|
|
def initiate_axis_dro(self, num, axis):
|
|
jnum = INFO.GET_JOG_FROM_NAME.get(axis)
|
|
# DRO uses axis index
|
|
index = "XYZABCUVW".index(axis)
|
|
self.w['dro_axis_{}'.format(num)].setProperty('Qjoint_number',index)
|
|
self.w['action_zero_{}'.format(num)].setProperty('axis_letter',axis)
|
|
try:
|
|
self.w['axis_select_{}'.format(num)].setProperty('axis_letter',axis)
|
|
self.w['axis_select_{}'.format(num)].setText('{}'.format(axis))
|
|
except Exception as e:
|
|
print(e)
|
|
try:
|
|
cmd = 'G90 G0 {}0'.format(axis)
|
|
self.w['action_cmd_{}'.format(num)].setProperty('command_text_string',cmd)
|
|
except:
|
|
pass
|
|
self.w['btn_home_{}'.format(num)].setProperty('axis_letter',axis)
|
|
self.w['btn_home_{}'.format(num)].setProperty('joint_number_status',jnum)
|
|
self.w['btn_home_{}'.format(num)].setProperty('joint',index)
|
|
self.w['offsettoolbutton_{}'.format(num)].setProperty('axis_letter',axis)
|
|
self.w['plus_jogbutton_{}'.format(num)].setProperty('axis_letter',axis)
|
|
self.w['plus_jogbutton_{}'.format(num)].setProperty('joint_number',jnum)
|
|
a = axis.lower()
|
|
try:
|
|
icn = QtGui.QIcon(QtGui.QPixmap(':/buttons/images/{}_plus_jog_button.png'.format(a)))
|
|
if icn.isNull(): raise Exception
|
|
self.w['plus_jogbutton_{}'.format(num)].setIcon(icn)
|
|
except Exception as e:
|
|
print(e)
|
|
self.w['plus_jogbutton_{}'.format(num)].setProperty('text','{}+'.format(axis))
|
|
self.w['minus_jogbutton_{}'.format(num)].setProperty('axis_letter',axis)
|
|
self.w['minus_jogbutton_{}'.format(num)].setProperty('joint_number',jnum)
|
|
try:
|
|
icn = QtGui.QIcon(QtGui.QPixmap(':/buttons/images/{}_minus_jog_button.png'.format(a)))
|
|
if icn.isNull(): raise Exception
|
|
self.w['minus_jogbutton_{}'.format(num)].setIcon(icn)
|
|
except Exception as e:
|
|
self.w['minus_jogbutton_{}'.format(num)].setProperty('text','{}-'.format(axis))
|
|
|
|
# MPG scrolling of program or MDI history
|
|
def external_mpg(self, count):
|
|
diff = count - self._last_count
|
|
if self.w.btn_mpg_scroll.isChecked():
|
|
currentIndex = self.w.stackedWidget_mainTab.currentIndex()
|
|
if isinstance(self.MPGFocusWidget, SLIDER):
|
|
#print('Slider',self.MPGFocusWidget.objectName(),self.MPGFocusWidget.parent())
|
|
if self.MPGFocusWidget is self.w.slider_feed_ovr:
|
|
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.MPGFocusWidget is self.w.slider_rapid_ovr:
|
|
scaled = (STATUS.stat.rapidrate * 100 + diff)
|
|
if scaled <0 :scaled = 0
|
|
elif scaled > 100:scaled = 100
|
|
ACTION.SET_RAPID_RATE(scaled)
|
|
elif self.MPGFocusWidget is self.w.slider_spindle_ovr:
|
|
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)
|
|
|
|
elif isinstance(self.MPGFocusWidget, GRAPHICS):
|
|
if self.w.actionbutton_pan_rpyaye.isChecked():
|
|
ACTION.ADJUST_GRAPHICS_ROTATE(diff,diff)
|
|
else:
|
|
ACTION.ADJUST_GRAPHICS_PAN(diff,0)
|
|
elif isinstance(self.MPGFocusWidget, GCODE):
|
|
self.w.gcode_editor.jump_line(diff)
|
|
self.w.gcode_viewer.jump_line(diff)
|
|
elif isinstance(self.MPGFocusWidget, MDI_WIDGET):
|
|
if diff <0:
|
|
self.MPGFocusWidget.line_down()
|
|
else:
|
|
self.MPGFocusWidget.line_up()
|
|
|
|
elif currentIndex == TAB_FILE:
|
|
if isinstance(self.MPGFocusWidget, FM):
|
|
widget = self.MPGFocusWidget
|
|
else:
|
|
widget = self.w.filemanager
|
|
if diff <0:
|
|
widget.down()
|
|
else:
|
|
widget.up()
|
|
elif currentIndex == TAB_TOOL:
|
|
if isinstance(self.MPGFocusWidget, TOOL_TABLE):
|
|
print('scroll offset view')
|
|
if diff <0:
|
|
self.MPGFocusWidget.down()
|
|
else:
|
|
self.MPGFocusWidget.up()
|
|
elif currentIndex == TAB_OFFSETS:
|
|
if isinstance(self.MPGFocusWidget, OFFSET_VIEW):
|
|
print('scroll offset view')
|
|
if diff <0:
|
|
self.MPGFocusWidget.down()
|
|
else:
|
|
self.MPGFocusWidget.up()
|
|
|
|
self._last_count = count
|
|
|
|
def dialog_ext_control(self, pin, value, answer):
|
|
if value:
|
|
# search for a visible notification first
|
|
# and close it
|
|
chk = self.w._NOTICE.find_visible()
|
|
if not chk is None:
|
|
chk.close()
|
|
return
|
|
|
|
# search the registered dialogs for a match
|
|
dlist = self.w.getRegisteredDialogList()
|
|
for i in (dlist):
|
|
if i.isVisible():
|
|
LOG.verbose('Found dialog',i.objectName())
|
|
name = i.getIdName()
|
|
STATUS.emit('dialog-update',{'NAME':name,'response':answer})
|
|
return
|
|
|
|
def log_version(self):
|
|
if INFO.RIP_FLAG:
|
|
t = _translate("HandlerClass","(RIP)")
|
|
else:
|
|
t = _translate("HandlerClass","(Installed)")
|
|
mess = "--- {} {} {} {} {} ---".format(
|
|
_translate("HandlerClass","QtDragon Version"),
|
|
VERSION,
|
|
_translate("HandlerClass","on Linuxcnc"),
|
|
STATUS.get_linuxcnc_version(),
|
|
t)
|
|
self.add_status(mess, CRITICAL,noLog=True)
|
|
STATUS.emit('update-machine-log', mess, None)
|
|
|
|
# show/hide macro buttons depending on the INI definitions
|
|
def configureMacroButtons(self):
|
|
# Show assigned macrobuttons define in INI under [MDI_COMMAND_LIST]
|
|
flag = True
|
|
for b in range(0,10):
|
|
button = self.w['macrobutton{}'.format(b)]
|
|
# prefer named INI MDI commands
|
|
key = button.property('ini_mdi_key')
|
|
if key == '' or INFO.get_ini_mdi_command(key) is None:
|
|
# fallback to legacy nth line
|
|
key = button.property('ini_mdi_number')
|
|
try:
|
|
code = INFO.get_ini_mdi_command(key)
|
|
if code is None: raise Exception
|
|
flag = False
|
|
except:
|
|
button.hide()
|
|
|
|
# add any INI defined macros if there is less then
|
|
# ten INI MDI commands
|
|
start = len(INFO.MDI_COMMAND_DICT)
|
|
for b in range(start,10):
|
|
button = self.w['macrobutton{}'.format(b)]
|
|
adj = b-start
|
|
# prefer named INI MDI commands
|
|
try:
|
|
# find what ini_mdi button we are going to convert
|
|
key = button.property('ini_mdi_key')
|
|
|
|
code = INFO.get_ini_macro_command(key)
|
|
if code is None:
|
|
code = INFO.get_ini_macro_command(b-start)
|
|
if code is None:
|
|
raise Exception
|
|
else: key = b-start
|
|
|
|
flag = False
|
|
button.show()
|
|
|
|
try:
|
|
label = INFO.get_ini_macro_label(key)
|
|
label = label.replace(r'\n', '\n')
|
|
button.setText(label)
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
tooltiplabel = 'INI MACRO CMD {}:\n'.format(key)
|
|
tooltiplabel += INFO.get_ini_macro_command(key).replace(';', '\n')
|
|
button.setToolTip(tooltiplabel)
|
|
except Exception as e:
|
|
pass
|
|
button.setProperty('ini_mdi_command_action', False)
|
|
button.setProperty('ini_macro_command_action', True)
|
|
button.setProperty('ini_macro_number',b-start)
|
|
button.setProperty('ini_macro_key',key)
|
|
except Exception as e:
|
|
button.hide()
|
|
|
|
# no buttons hide frame
|
|
if flag:
|
|
self.w.frame_macro_buttons.hide()
|
|
|
|
# if there are more then 10 add a menu to the last button for selection
|
|
if len(INFO.MDI_COMMAND_DICT)>10:
|
|
button.setText(_translate("HandlerClass",'MORE\nMACROS'))
|
|
button.setProperty('ini_mdi_command_action', False)
|
|
button.setProperty('no_action', True)
|
|
button.setToolTip('')
|
|
SettingMenu = QMenu(button)
|
|
button.setMenu(SettingMenu)
|
|
for i in range(0,len(INFO.MDI_COMMAND_DICT)-10):
|
|
try:
|
|
name = 'MACRO{}'.format(i+10)
|
|
label = INFO.MDI_COMMAND_DICT[name]['label']
|
|
except:
|
|
label = INFO.MDI_COMMAND_LABEL_LIST[i+10]
|
|
|
|
action = QAction(QIcon.fromTheme('application-exit'), label.replace("\\n"," "), button)
|
|
action.triggered.connect(lambda s, i=i, b=button : self.midiAction(b, i+10))
|
|
# tooltips don't work on Qmenu items unless in toolbar
|
|
#tooltiplabel = 'INI MDI CMD {}:\n'.format(name)
|
|
#tooltiplabel += INFO.get_ini_mdi_command(key).replace(';', '\n')
|
|
#action.setToolTip(tooltiplabel)
|
|
|
|
SettingMenu.addAction(action)
|
|
|
|
# use the button to run macros selected from the buttons menu
|
|
def midiAction(self, button, num):
|
|
name = 'MACRO{}'.format(num)
|
|
try:
|
|
code = INFO.MDI_COMMAND_DICT[name]['cmd']
|
|
button.setProperty('ini_mdi_key', name)
|
|
except:
|
|
code = INFO.MDI_COMMAND_LIST[num]
|
|
button.setProperty('ini_mdi_key', '')
|
|
button.setProperty('ini_mdi_number', num)
|
|
print('number',num,button.ini_mdi_num)
|
|
button.setProperty('ini_mdi_command_action', True)
|
|
|
|
button.pressed.emit()
|
|
button.setProperty('ini_mdi_command_action', False)
|
|
|
|
# show/hide a popup window panel (if defined in the INI)
|
|
def togglePopup(self):
|
|
if self.w['popup'].isVisible():
|
|
self.w['popup']._lastgeometry = self.w['popup'].geometry()
|
|
self.w['popup'].hide()
|
|
else:
|
|
self.w['popup'].show()
|
|
if not self.w['popup']._lastgeometry is None:
|
|
self.w['popup'].setGeometry(self.w['popup']._lastgeometry)
|
|
|
|
|
|
def onClosePopup(self, *args):
|
|
self.w['popup']._lastgeometry = self.w['popup'].geometry()
|
|
|
|
#####################
|
|
# 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 self.use_keyboard():
|
|
self.w.action_pause.click()
|
|
|
|
def on_keycall_jograte(self,event,state,shift,cntrl,value):
|
|
if state and self.use_keyboard():
|
|
if value == 1:
|
|
ACTION.SET_JOG_RATE_FASTER()
|
|
else:
|
|
ACTION.SET_JOG_RATE_SLOWER()
|
|
|
|
def on_keycall_angular_jograte(self,event,state,shift,cntrl,value):
|
|
if state and self.use_keyboard():
|
|
if value == 1:
|
|
ACTION.SET_JOG_RATE_ANGULAR_FASTER()
|
|
else:
|
|
ACTION.SET_JOG_RATE_ANGULAR_SLOWER()
|
|
|
|
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_F12(self,event,state,shift,cntrl):
|
|
if state:
|
|
STYLEEDITOR.load_dialog()
|
|
|
|
def on_keycall_F11(self,event,state,shift,cntrl):
|
|
if self.w.isFullScreen() == False and state == True:
|
|
self.w.showFullScreen()
|
|
else:
|
|
if self.w.isFullScreen() == True and state == True:
|
|
self.w.showNormal()
|
|
|
|
|
|
|
|
##############################
|
|
# required class boiler code #
|
|
##############################
|
|
def __getitem__(self, item):
|
|
return getattr(self, item)
|
|
|
|
def __setitem__(self, item, value):
|
|
return setattr(self, item, value)
|
|
|
|
################################
|
|
# required handler boiler code #
|
|
################################
|
|
|
|
def get_handlers(halcomp, widgets, paths):
|
|
return [HandlerClass(halcomp, widgets, paths)]
|