Files
linuxcncAllWasm/lib/python/qtvcp/widgets/axis_tool_button.py
2026-08-20 06:17:39 +08:00

405 lines
16 KiB
Python

#!/usr/bin/env python3
#
# QTVcp Widget
# Copyright (c) 2018 Chris Morley
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
###############################################################################
import hal
from qtpy.QtWidgets import QToolButton, QMenu, QAction
from qtpy.QtCore import Property
from qtpy.QtGui import QIcon
from qtvcp.widgets.widget_baseclass import _HalWidgetBase
from qtvcp.widgets.indicatorMixIn import IndicatedMixIn
from qtvcp.core import Status, Action, Info
from qtvcp import logger
# Instiniate the libraries with global reference
# STATUS gives us status messages from linuxcnc
# AUX_PRGM holds helper program loader
# INI holds ini details
# ACTION gives commands to linuxcnc
# LOG is for running code logging
STATUS = Status()
INFO = Info()
ACTION = Action()
LOG = logger.getLogger(__name__)
# Force the log level for this module
#LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
class AxisToolButton(QToolButton, IndicatedMixIn):
def __init__(self, parent=None):
super(AxisToolButton, self).__init__(parent)
self._joint = 0
self._axis = ''
self._block_signal = False
self._halpin_option = True
self.dialog_code = 'ENTRY'
self.display_units_mm = 0
homeOption = False
self._showGoto = False
self._showZero = True
self._showSet = True
self._showLast = True
self._showDivide = True
SettingMenu = QMenu(self)
self.settingMenu = SettingMenu
self.zeroButton = QAction(QIcon('exit24.png'), 'Zero', self)
self.zeroButton.triggered.connect(self.Zero)
SettingMenu.addAction(self.zeroButton)
self.setButton = QAction(QIcon('exit24.png'), 'Set', self)
self.setButton.triggered.connect(self.SetOrigin)
SettingMenu.addAction(self.setButton)
self.divideButton = QAction(QIcon('exit24.png'), 'Divide By 2', self)
self.divideButton.triggered.connect(self.Divide)
SettingMenu.addAction(self.divideButton)
self.lastButton = QAction(QIcon('exit24.png'), 'Set To Last', self)
self.lastButton.triggered.connect(self.Last)
SettingMenu.addAction(self.lastButton)
self.goToG53Button = QAction(self)
self.goToG53Button.triggered.connect(self.goToG53)
SettingMenu.addAction(self.goToG53Button)
self.goToG5xButton = QAction(self)
self.goToG5xButton.triggered.connect(self.goToG5x)
SettingMenu.addAction(self.goToG5xButton)
# option for later - should put a sub menu in for joints homing though
if homeOption:
SettingMenu.addSeparator()
self.homeButton = QAction(QIcon('exit24.png'), 'Home', self)
self.homeButton.triggered.connect(self.Home)
SettingMenu.addAction(self.homeButton)
#self.homeButton.setEnabled(False)
self.UnHomeButton = QAction(QIcon('exit24.png'), 'Unhome', self)
self.UnHomeButton.triggered.connect(self.Unhome)
SettingMenu.addAction(self.UnHomeButton)
#self.homeButton.setEnabled(False)
self.setMenu(SettingMenu)
self.clicked.connect(self.selectJoint)
self.toggled.connect(self.selectJoint)
# Override setText function so we can toggle displayed text
def setText(self, text):
if not self._state_text:
super(AxisToolButton,self).setText(text)
return
if self.isCheckable():
if self.isChecked():
super(AxisToolButton,self).setText(self._true_string)
else:
super(AxisToolButton,self).setText(self._false_string)
elif self._indicator_state:
super(AxisToolButton,self).setText(self._true_string)
else:
super(AxisToolButton,self).setText(self._false_string)
def _hal_init(self):
super(AxisToolButton, self)._hal_init()
def homed_on_test():
return (STATUS.machine_is_on()
and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED))
STATUS.connect('metric-mode-changed', self._switch_units)
STATUS.connect('state-off', lambda w: self._enableGroup(True,False))
STATUS.connect('state-on', lambda w: self._enableGroup(True, True))
STATUS.connect('interp-idle', lambda w: self._enableGroup(homed_on_test(),STATUS.machine_is_on()))
STATUS.connect('interp-run', lambda w: self._enableGroup(False, False))
STATUS.connect('all-homed', lambda w: self._enableGroup(homed_on_test(), STATUS.machine_is_on()))
STATUS.connect('not-all-homed', lambda w, data: self._enableGroup(False,STATUS.machine_is_on()))
STATUS.connect('interp-paused', lambda w: self._enableGroup(False,False))
STATUS.connect('motion-mode-changed', lambda w,data: self.modeChanged(data))
STATUS.connect('joint-selection-changed', lambda w,data: self.ChangeState(joint = data, axis= STATUS.get_selected_axis()))
STATUS.connect('axis-selection-changed', lambda w,data: self.ChangeState(joint = STATUS.get_selected_joint(), axis = data))
if self._halpin_option and self._joint != -1:
if self._pin_name_ == '':
pname = self.HAL_NAME_
else:
pname = self._pin_name_
self.hal_pin_joint = self.HAL_GCOMP_.newpin(str(pname + '-joint'), hal.HAL_BIT, hal.HAL_OUT)
self.hal_pin_axis = self.HAL_GCOMP_.newpin(str(pname + '-axis'), hal.HAL_BIT, hal.HAL_OUT)
STATUS.connect('general',self.return_value)
def _enableGroup(self, state,bstate):
for i in(self.zeroButton, self.setButton,self.divideButton,
self.lastButton,self.goToG53Button, self.goToG5xButton ):
i.setEnabled(state)
self.setEnabled(bstate)
def Zero(self):
axis, now = self._a_from_j(self._axis)
if axis:
ACTION.SET_AXIS_ORIGIN(axis, 0)
STATUS.emit('update-machine-log', 'Zeroed Axis %s' % axis, 'TIME,SUCCESS')
def SetOrigin(self):
axis, now = self._a_from_j(self._axis)
if axis:
mess = {'NAME':self.dialog_code,'ID':'%s__' % self.objectName(),
'AXIS':axis,'CURRENT':now,
'TITLE':'Set %s Origin'% axis,
'GEONAME':'axisToolButtonDialog_{}'.format(self.dialog_code),
'AXIS':axis}
STATUS.emit('dialog-request', mess)
LOG.debug('message sent:{}'.format (mess))
# process the STATUS return message
def return_value(self, w, message):
num = message['RETURN']
code = bool(message.get('ID') == '%s__'% self.objectName())
name = bool(message.get('NAME') == self.dialog_code)
if code and name and num is not None:
LOG.debug('message return:{}'.format (message))
axis = message.get('AXIS')
ACTION.SET_AXIS_ORIGIN(axis, num)
STATUS.emit('update-machine-log', 'Set Origin of Axis %s to %f' %(axis, num), 'TIME,SUCCESS')
def Divide(self):
axis, now = self._a_from_j(self._axis)
if axis:
try:
x = now/2.0
ACTION.SET_AXIS_ORIGIN(axis, x)
text = 'Divided Axis %s in Half - %f'% (axis, x)
STATUS.emit('update-machine-log', text, 'TIME,SUCCESS')
except ZeroDivisionError:
pass
def Last(self):
axis, now = self._a_from_j(self._axis)
if axis:
last = ACTION.GET_LAST_RECORDED_ORIGIN(axis)
ACTION.SET_AXIS_ORIGIN(axis, last)
text = 'Reset Axis %s from %f to Last Value: %f' %(axis, now, last)
STATUS.emit('update-machine-log', text, 'TIME,SUCCESS')
def Home(self):
#axis, now = self._a_from_j(self._axis)
#if axis:
ACTION.SET_MACHINE_HOMING(self._joint)
STATUS.emit('update-machine-log', 'Homed Axis %s' % self._joint, 'TIME,SUCCESS')
def Unhome(self):
#axis, now = self._a_from_j(self._axis)
#if axis:
ACTION.SET_MACHINE_UNHOMED(self._joint)
STATUS.emit('update-machine-log', 'Unhomed Axis %s' % self._joint, 'TIME,WARNING')
def goToG53(self):
ACTION.CALL_MDI('G53 G0 {}0'.format(self._axis))
def goToG5x(self):
ACTION.CALL_MDI('G0 {}0'.format(self._axis))
# if in joint mode -nope
# if axis is '' the find the selected axis from gstat
# find the joint number so we can get current position
def _a_from_j(self, axis):
if STATUS.is_joint_mode():
return None, None
if axis == '':
axis = STATUS.get_selected_axis()
j = "XYZABCUVW"
try:
jnum = j.find(axis)
except:
return None, None
p,r,d = STATUS.get_position()
if self.display_units_mm != INFO.MACHINE_IS_METRIC:
r = INFO.convert_units_9(r)
return axis, r[jnum]
def selectJoint(self):
if self._block_signal or self._joint == -1 or self._axis == '': return
if self.isChecked() == True:
if STATUS.is_joint_mode():
ACTION.SET_SELECTED_JOINT(self._joint)
if self._halpin_option:
self.hal_pin_joint.set(self.isChecked())
else:
ACTION.SET_SELECTED_AXIS(self._axis)
# set this without causing a STATUS message output
# in case we are selecting an axis to un/home
STATUS.selected_joint = self._joint
if self._halpin_option:
self.hal_pin_axis.set(self.isChecked())
else:
if STATUS.is_joint_mode():
ACTION.SET_SELECTED_JOINT(-1)
else:
ACTION.SET_SELECTED_AXIS('None')
# set this without causing a STATUS message output
# in case we are selecting an axis to un/home
STATUS.selected_joint = -1
if self._halpin_option:
self.hal_pin_axis.set(False)
def ChangeState(self, joint = None, axis = None):
#print(self.objectName(),'change',joint,axis,self._axis)
# joint mode
if STATUS.is_joint_mode():
if int(joint) != self._joint:
self._block_signal = True
self.setChecked(False)
self._block_signal = False
if self._halpin_option and self._joint != -1:
self.hal_pin_joint.set(False)
else:
self._block_signal = True
self.setChecked(True)
self._block_signal = False
if self._halpin_option and self._axis != '':
self.hal_pin_joint.set(True)
# axis mode
else:
if str(axis) != self._axis and self.isChecked():
self._block_signal = True
self.setChecked(False)
self._block_signal = False
if self._halpin_option and self._axis != '':
self.hal_pin_joint.set(False)
elif str(axis) == self._axis and not self.isChecked():
self._block_signal = True
self.setChecked(True)
self._block_signal = False
if self._halpin_option and self._axis != '':
self.hal_pin_joint.set(True)
def modeChanged(self, mode):
self.selectJoint()
def _switch_units(self, widget, data):
self.display_units_mm = data
#########################################################################
# This is how designer can interact with our widget properties.
# designer will show the Property properties in the editor
# it will use the get set and reset calls to do those actions
#
########################################################################
def set_joint(self, data):
self._joint = data
if data == -1:
self.setPopupMode(QToolButton.InstantPopup)
def get_joint(self):
return self._joint
def reset_joint(self):
self._joint = -1
joint_number = Property(int, get_joint, set_joint, reset_joint)
def set_axis(self, data):
if data.upper() in('X','Y','Z','A','B','C','U','V','W'):
self._axis = str(data.upper())
text = 'Go To G53 Origin in {}'.format(self._axis)
self.goToG53Button.setText(text)
text = 'Go To G5x Origin in {}'.format(self._axis)
self.goToG5xButton.setText(text)
else:
self._axis = str('')
def get_axis(self):
return self._axis
def reset_axis(self):
self._axis = 'X'
axis_letter = Property(str, get_axis, set_axis, reset_axis)
def set_halpin_option(self, value):
self._halpin_option = value
def get_halpin_option(self):
return self._halpin_option
def reset_halpin_option(self):
self._halpin_option = True
halpin_option = Property(bool, get_halpin_option, set_halpin_option, reset_halpin_option)
def set_dialog_code(self, data):
self.dialog_code = data
def get_dialog_code(self):
return self.dialog_code
def reset_dialog_code(self):
self.dialog_code = 'ENTRY'
dialog_code_string = Property(str, get_dialog_code, set_dialog_code, reset_dialog_code)
####################
## menu properties
####################
def set_showSet(self, data):
self._showSet = data
self.setButton.setVisible(data)
def get_showSet(self):
return self._showSet
def reset_showSet(self):
self._showSet = True
showSetOrigin = Property(bool, get_showSet, set_showSet, reset_showSet)
def set_showZero(self, data):
self._showZero = data
self.zeroButton.setVisible(data)
def get_showZero(self):
return self._showZero
def reset_showZero(self):
self._showZero = True
showZeroOrigin = Property(bool, get_showZero, set_showZero, reset_showZero)
def set_showGoto(self, data):
self._showGoto = data
self.goToG53Button.setVisible(data)
self.goToG5xButton.setVisible(data)
def get_showGoto(self):
return self._showGoto
def reset_showGoto(self):
self._showGoto = False
showGotoOrigin = Property(bool, get_showGoto, set_showGoto, reset_showGoto)
def set_showLast(self, data):
self._showLast = data
self.lastButton.setVisible(data)
def get_showLast(self):
return self._showLast
def reset_showLast(self):
self._showLast = True
showLast = Property(bool, get_showLast, set_showLast, reset_showLast)
def set_showDivide(self, data):
self._showDivide = data
self.divideButton.setVisible(data)
def get_showDivide(self):
return self._showDivide
def reset_showDivide(self):
self._showDivide = True
showDivide = Property(bool, get_showDivide, set_showDivide, reset_showDivide)
def __getitem__(self, item):
return getattr(self, item)
def __setitem__(self, item, value):
return setattr(self, item, value)
# for testing without editor:
def main():
import sys
from qtpy.QtWidgets import QApplication
app = QApplication(sys.argv)
widget = AxisToolButton()
widget.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()