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

299 lines
11 KiB
Python

#!/usr/bin/env python3
# QtVcp Widget - user temporary DRO label widget
# This widgets displays linuxcnc axis position information.
#
# Copyright (c) 2025 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 linuxcnc
from math import isclose
from qtpy import QtCore
from qtpy.QtWidgets import QMenu, QAction
from qtpy.QtGui import QIcon, QColor
from qtvcp.widgets.simple_widgets import ScaledLabel
from qtvcp.widgets.widget_baseclass import _HalWidgetBase
from qtvcp.core import Status, Info, Action
from qtvcp import logger
# Instantiate the libraries with global reference
# INFO holds INI file details
# STATUS gives us status messages from 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.INFO) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
class UserDROLabel(ScaledLabel, _HalWidgetBase):
def __init__(self, parent=None):
super(UserDROLabel, self).__init__(parent)
# axis index of 9 axis
self.axis_index = 0
# linuxcnc joint number
self._joint_type = 1
self.display_units_mm = 0
self.metric_text_template = '%10.3f'
self.imperial_text_template = '%9.4f'
self.angular_text_template = '%9.2f'
self._text =' -000.0000'
self._offset = [0.0] * 9
self._lastOffset = [0.0] * 9
self._negativeColor = QColor(0, 0, 100, 150)
self._positiveColor = QColor(100, 0, 0, 150)
self._zeroColor = QColor(0, 100, 0, 150)
self._imperialTolerence = .00005
self._metricTolerence = .005
self._currentTolerence = .0005
self._lastNum = 0.0
# menu stuff
self.dialog_code = 'CALCULATOR'
def mousePressEvent(self, event):
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.lastButton = QAction(QIcon('exit24.png'), 'Set To Last', self)
self.lastButton.triggered.connect(self.Last)
SettingMenu.addAction(self.lastButton)
SettingMenu.popup(event.globalPos())
def _hal_init(self):
super(UserDROLabel, self)._hal_init()
STATUS.connect('current-position', self.update)
STATUS.connect('metric-mode-changed', self._switch_units)
STATUS.connect('general',self.return_value)
if self._joint_type == linuxcnc.ANGULAR:
self._current_text_template = self.angular_text_template
elif self.display_units_mm:
self._current_text_template = self.metric_text_template
else:
self._current_text_template = self.imperial_text_template
if INFO.MACHINE_IS_METRIC:
self._currentTolerence = self._metricTolerence
else:
self._currentTolerence = self._imperialTolerence
def update(self, widget, absolute, relative, dtg, joint):
if self.display_units_mm != INFO.MACHINE_IS_METRIC:
absolute = INFO.convert_units_9(absolute)
tmpl = lambda s: self._current_text_template % s
try:
num = absolute[self.axis_index] + self._offset[self.axis_index]
self.setColor(num)
self.setText(tmpl(num))
except Exception as e:
print(e)
pass
def setColor(self, data):
if not data == self._lastNum:
if isclose(data, 0,abs_tol = self._currentTolerence) :
self.setStyleSheet("QLabel { background-color :"+self._zeroColor.name()+" ;}");
elif data <0:
self.setStyleSheet("QLabel { background-color :"+self._negativeColor.name()+" ;}");
else:
self.setStyleSheet("QLabel { background-color :"+self._positiveColor.name()+" ;}");
self._lastNum = data
def _switch_units(self, widget, data):
self.display_units_mm = data
self.update_units()
def update_units(self):
if self._joint_type == linuxcnc.ANGULAR:
self._current_text_template = self.angular_text_template
elif self.display_units_mm:
self._current_text_template = self.metric_text_template
else:
self._current_text_template = self.imperial_text_template
def set_to_inch(self):
self.display_units_mm = 0
if self._joint_type == linuxcnc.ANGULAR:
self._current_text_template = self.angular_text_template
else:
self._current_text_template = self.imperial_text_template
def set_to_mm(self):
self.display_units_mm = 1
if self._joint_type == linuxcnc.ANGULAR:
self._current_text_template = self.angular_text_template
else:
self._current_text_template = self.metric_text_template
def get_current_position(self):
if STATUS.is_joint_mode():
return None
p,r,d = STATUS.get_position()
if self.display_units_mm != INFO.MACHINE_IS_METRIC:
p = INFO.convert_units_9(p)
return p[self.axis_index]
@QtCore.Slot(bool)
def zero (self, data=True):
p = self.get_current_position()
self._lastOffset[self.axis_index] = self._offset[self.axis_index]
self._offset[self.axis_index] = - p
@QtCore.Slot(int)
def setAxisIndex(self, num):
self.axis_index = num
@QtCore.Slot(str)
def setAxisLetter(self, let):
self.axis_index = self.axisToIndex(let)
print(let,self.axis_index)
def axisToIndex(self, let):
conversion = {"X":0, "Y":1, "Z":2, "A":3, "B":4, "C":5, "U":6, "V":7, "W":8}
return conversion[let]
def indexToAxis(self, index):
conversion = {0:"X", 1:"Y", 2:"Z", 3:"A", 4:"B", 5:"C", 6:"U", 7:"V", 8:"W"}
return conversion[index]
#####################
# Menu callback code
#####################
def Zero(self):
self.zero()
def SetOrigin(self):
now = self.get_current_position()
axis = self.indexToAxis(self.axis_index)
if axis:
mess = {'NAME':self.dialog_code,'ID':'%s__' % self.objectName(),
'AXIS':axis,'CURRENT':now,
'TITLE':'Set %s Reference'% axis,
'GEONAME':'UserDROLabelDialog_{}'.format(self.dialog_code),
'AXIS':axis}
STATUS.emit('dialog-request', mess)
LOG.debug('message sent:{}'.format (mess))
def Last(self):
axis = self.indexToAxis(self.axis_index)
now = self.get_current_position() + self._offset[self.axis_index]
if axis:
temp = self._offset[self.axis_index]
self._offset[self.axis_index] = self._lastOffset[self.axis_index]
self._lastOffset[self.axis_index] = temp
text = 'Reset Axis %s from %f to Last Value: %f' %(axis, now, self._lastOffset[self.axis_index] )
# 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')
# do something
p = self.get_current_position()
self._lastOffset[self.axis_index] = self._offset[self.axis_index]
self._offset[self.axis_index] = - (p-num)
#########################################################################
# 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 setmetrictemplate(self, data):
self.metric_text_template = data
self.update_units()
def getmetrictemplate(self):
return self.metric_text_template
def resetmetrictemplate(self):
self.metric_text_template = '%10.3f'
metric_template = QtCore.Property(str, getmetrictemplate, setmetrictemplate, resetmetrictemplate)
def setimperialtexttemplate(self, data):
self.imperial_text_template = data
self.update_units()
def getimperialtexttemplate(self):
return self.imperial_text_template
def resetimperialtexttemplate(self):
self.imperial_text_template = '%9.4f'
imperial_template = QtCore.Property(str, getimperialtexttemplate, setimperialtexttemplate, resetimperialtexttemplate)
def setangulartexttemplate(self, data):
self.angular_text_template = data
self.update_units()
def getangulartexttemplate(self):
return self.angular_text_template
def resetangulartexttemplate(self):
self.angular_text_template = '%9.2f'
angularTemplate = QtCore.Property(str, getangulartexttemplate, setangulartexttemplate, resetangulartexttemplate)
def getNegativeColor(self):
return self._negativeColor
def setNegativeColor(self, value):
self._negativeColor = value
negativeColor = QtCore.Property(QColor, getNegativeColor, setNegativeColor)
def getPositiveColor(self):
return self._positiveColor
def setPositiveColor(self, value):
self._positiveColor = value
positiveColor = QtCore.Property(QColor, getPositiveColor, setPositiveColor)
####################
## menu properties
####################
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 = 'CALCULATOR'
dialogName = QtCore.Property(str, get_dialog_code, set_dialog_code, reset_dialog_code)
##############################
# required class boiler code #
##############################
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 = UserDROLabel()
widget.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()