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

403 lines
14 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.
###############################################################################
from qtpy.QtWidgets import QWidget, QProgressBar, QToolButton, QHBoxLayout, QMenu, QAction
from qtpy.QtCore import Qt, Property, QBasicTimer, Signal
from qtpy.QtGui import QPainter, QFont, QIcon
from qtvcp.widgets.widget_baseclass import _HalWidgetBase
from qtvcp.core import Status, Action, Info
from qtvcp.lib.aux_program_loader import Aux_program_loader
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()
AUX_PRGM = Aux_program_loader()
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
# So we can customize the label
# meaning we can use a new line
# to have two levels of text
class LabeledBar(QProgressBar):
def __init__(self, parent=None):
super(LabeledBar, self).__init__(parent)
self.text = ''
self.setTextVisible(False)
self.font = QFont()
self.font.setPointSize(8)
self.font.setBold(True)
self.font.setWeight(75)
def paintEvent(self, event):
super(LabeledBar, self).paintEvent(event)
qp = QPainter()
qp.begin(self)
self.drawText(qp)
qp.end()
def drawText(self, qp):
qp.setOpacity(1.0)
qp.setFont(self.font)
qp.drawText(self.rect(), Qt.AlignCenter, self.text)
class HAdjustmentBar(QWidget):
valueChanged = Signal(int)
def __init__(self, parent=None):
super(HAdjustmentBar, self).__init__(parent)
self.value = 50
self.step = 1
self.hi_value = 100
self.low_value = 50
self.timer_value = 100
self.texttemplate = 'Value = %s'
self.timer = QBasicTimer()
self.showToggleButton = True
self.showSettingMenu = True
self.bar = LabeledBar()
#self.bar.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)
# black magic class patching
# so calling these functions actually calls the self.bar's functions.
self.minimum = self.bar.minimum
self.maximum = self.bar.maximum
def buildWidget(self):
layout = QHBoxLayout(self)
layout.setContentsMargins(0,0,0,0)
SettingMenu = QMenu()
exitButton = QAction(QIcon('exit24.png'), 'Set As High', self)
exitButton.triggered.connect(self.setHigh)
SettingMenu.addAction(exitButton)
setlowButton = QAction(QIcon('exit24.png'), 'Set As Low', self)
setlowButton.triggered.connect(self.setLow)
SettingMenu.addAction(setlowButton)
self.tb_down = QToolButton()
self.tb_down.pressed.connect(self.on_click_down)
self.tb_down.released.connect(self.on_released)
self.tb_down.setArrowType(Qt.LeftArrow)
self.tb_up = QToolButton()
self.tb_up.pressed.connect(self.on_click_up)
self.tb_up.released.connect(self.on_released)
self.tb_up.setArrowType(Qt.RightArrow)
if self.showToggleButton:
tb_set = QToolButton()
tb_set.clicked.connect(self.on_click_set_value)
tb_set.setText('<>')
if self.showSettingMenu:
tb_set.setMenu(SettingMenu)
tb_set.setPopupMode(QToolButton.DelayedPopup)
layout.addWidget(self.tb_down)
layout.addWidget(self.bar)
layout.addWidget(self.tb_up)
if self.showToggleButton:
layout.addWidget(tb_set)
layout.setSpacing(0)
def on_click_up(self):
self.timer.start(self.timer_value, self)
self.value += self.step
self._setValue()
def on_click_down(self):
self.timer.start(self.timer_value, self)
self.value -= self.step
self._setValue()
def on_released(self):
self.timer.stop()
def on_click_set_value(self):
if self.value == self.hi_value:
self.value = self.low_value
else:
self.value = self.hi_value
self._setValue()
def timerEvent(self, e):
if self.value < self.maximum() and self.value > self.minimum():
if self.tb_down.isDown():
self.value -= self.step
else:
self.value += self.step
self._setValue()
else:
self.timer.stop()
def _setValue(self):
if self.value < self.minimum():self. value = self.minimum()
if self.value > self.maximum(): self.value = self.maximum()
self.valueChanged.emit(self.value)
def setValue(self, value):
if value < self.minimum(): value = self.minimum()
if value > self.maximum(): value = self.maximum()
self.value = int(value)
tmpl = lambda s: self.texttemplate % s
try:
self.bar.text = tmpl(int(value))
except:
self.bar.text = self.texttemplate
self.bar.setValue(int(value))
self.bar.update()
def setHigh(self):
self.hi_value = self.value
def setLow(self):
self.low_value = self.value
def setMaximum(self, data):
if data < self.hi_value:
self.hi_value = data
self.bar.setMaximum(data)
def setMinimum(self, data):
if data > self.low_value:
self.low_value = data
self.bar.setMinimum(data)
def setStep(self, data):
self.step = data
class StatusAdjustmentBar(HAdjustmentBar, _HalWidgetBase):
def __init__(self, parent=None):
super(StatusAdjustmentBar, self).__init__(parent)
self._block_signal = False
self.rapid = True
self.feed = False
self.spindle = False
self.jograte = False
self.jograte_angular = False
self.maxv = False
self.texttemplate = 'Value = %s'
# self.PREFS_
# self.HAL_NAME_
# comes from base class
def _hal_init(self):
# if we build the widget before now, designer options are not
# taken account
self.buildWidget()
# when estopped disable buttons
STATUS.connect('state-estop', lambda w: self.setEnabled(False))
STATUS.connect('state-estop-reset', lambda w: self.setEnabled(True))
# set options
if self.rapid:
STATUS.connect('rapid-override-changed', lambda w, data: self.setValue(data))
self.setMaximum(100)
elif self.feed:
STATUS.connect('feed-override-changed', lambda w, data: self.setValue(data))
self.setMaximum(int(INFO.MAX_FEED_OVERRIDE))
elif self.spindle:
STATUS.connect('spindle-override-changed', lambda w, data: self.setValue(data))
self.setMaximum(int(INFO.MAX_SPINDLE_OVERRIDE))
self.setMinimum(int(INFO.MIN_SPINDLE_OVERRIDE))
elif self.jograte:
STATUS.connect('jograte-changed', lambda w, data: self.setValue(data))
self.setMaximum(int(INFO.MAX_LINEAR_JOG_VEL))
elif self.jograte_angular:
STATUS.connect('jograte-angular-changed', lambda w, data: self.setValue(data))
print(int(INFO.MAX_ANGULAR_JOG_VEL))
self.setMaximum(int(INFO.MAX_ANGULAR_JOG_VEL))
elif self.maxv:
STATUS.connect('max-velocity-override-changed', lambda w, data: self.setValue(data))
self.setMaximum(int(INFO.MAX_TRAJ_VELOCITY))
else:
LOG.error('{} : no option recognised'.format(self.HAL_NAME_))
# If there is a preference file object use it to load the hi/low toggle points
if self.PREFS_:
self.hi_value = self.PREFS_.getpref(self.HAL_NAME_+'-hi-value', 75, int, 'SCREEN_CONTROL_LAST_SETTING')
self.low_value = self.PREFS_.getpref(self.HAL_NAME_+'-low-value', 25, int, 'SCREEN_CONTROL_LAST_SETTING')
# connect a signal and callback function to the button
self.valueChanged.connect(self._action)
# when qtvcp closes this gets called
def _hal_cleanup(self):
if self.PREFS_:
LOG.debug('Saving {} data to file.'.format(self.HAL_NAME_))
self.PREFS_.putpref(self.HAL_NAME_+'-hi-value', self.hi_value, int, 'SCREEN_CONTROL_LAST_SETTING')
self.PREFS_.putpref(self.HAL_NAME_+'-low-value', self.low_value, int, 'SCREEN_CONTROL_LAST_SETTING')
def _action(self, value):
if self.rapid:
ACTION.SET_RAPID_RATE(value)
elif self.feed:
ACTION.SET_FEED_RATE(value)
elif self.spindle:
ACTION.SET_SPINDLE_RATE(value)
elif self.jograte:
ACTION.SET_JOG_RATE(value)
elif self.jograte_angular:
ACTION.SET_JOG_RATE_ANGULAR(value)
elif self.maxv:
ACTION.SET_MAX_VELOCITY_RATE(value)
else:
LOG.error('{} no action recognised'.format(self.HAL_NAME_))
#########################################################################
# 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
#
# _toggle_properties makes it so we can only select one option
########################################################################
def _toggle_properties(self, picked):
data = ('rapid', 'feed', 'spindle', 'jograte', 'jograte_angular', 'maxv')
for i in data:
if not i == picked:
self[i+'_rate'] = False
def setrapid(self, data):
self.rapid = data
if data:
self._toggle_properties('rapid')
def getrapid(self):
return self.rapid
def resetrapid(self):
self.rapid = False
def setfeed(self, data):
self.feed = data
if data:
self._toggle_properties('feed')
def getfeed(self):
return self.feed
def resetfeed(self):
self.feed = False
def setspindle(self, data):
self.spindle = data
if data:
self._toggle_properties('spindle')
def getspindle(self):
return self.spindle
def resetspindle(self):
self.spindle = False
def setjograte(self, data):
self.jograte = data
if data:
self._toggle_properties('jograte')
def getjograte(self):
return self.jograte
def resetjograte(self):
self.jograte = False
def setjograteangular(self, data):
self.jograte_angular = data
if data:
self._toggle_properties('jograte_angular')
def getjograteangular(self):
return self.jograte_angular
def resetjograteangular(self):
self.jograte_angular = False
def setmaxv(self, data):
self.maxv = data
if data:
self._toggle_properties('maxv')
def getmaxv(self):
return self.maxv
def resetmaxv(self):
self.maxv = False
def setshowtoggle(self, data):
self.showToggleButton = data
def getshowtoggle(self):
return self.showToggleButton
def resetshowtoggle(self):
self.showToggleButton = True
def setsettingmenu(self, data):
self.showSettingMenu = data
def getsettingmenu(self):
return self.showSettingMenu
def resetsettingmenu(self):
self.showSettingMenu = True
# Designer makes '\n' into '\\n' if added
# we put fix it here so we actually get the newline
def settexttemplate(self, data):
data = data.replace('\\n' , '\n')
self.texttemplate = data
def gettexttemplate(self):
return self.texttemplate
def resettexttemplate(self):
self.texttemplate = 'Value = %s'
def setsteprate(self, data):
self.step = data
def getsteprate(self):
return self.step
def resetsteprate(self):
self.steprate = 1
rapid_rate = Property(bool, getrapid, setrapid, resetrapid)
feed_rate = Property(bool, getfeed, setfeed, resetfeed)
spindle_rate = Property(bool, getspindle, setspindle, resetspindle)
jograte_rate = Property(bool, getjograte, setjograte, resetjograte)
jograte_angular_rate = Property(bool, getjograteangular, setjograteangular, resetjograteangular)
max_velocity_rate = Property(bool, getmaxv, setmaxv, resetmaxv)
show_toggle_button = Property(bool, getshowtoggle, setshowtoggle, resetshowtoggle)
show_setting_menu = Property(bool, getsettingmenu, setsettingmenu, resetsettingmenu)
text_template = Property(str, gettexttemplate, settexttemplate, resettexttemplate)
step_rate = Property(int, getsteprate, setsteprate, resetsteprate)
##############################
# 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 = StatusAdjustmentBar()
widget.PREFS_=None
widget.buildWidget()
widget.settexttemplate('Text %s')
widget.setValue(75)
widget.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()