#!/usr/bin/env python3 # qtvcp # # Copyright (c) 2017 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 import QtWidgets from qtpy.QtCore import Property, Signal from qtvcp.widgets.widget_baseclass import _HalWidgetBase from qtvcp.core import Status, Action, Info from qtvcp import logger # Instantiate the libraries with global reference # STATUS gives us status messages from linuxcnc # ACTION gives commands to linuxcnc # INFO is INI file details # LOG is for running code logging STATUS = Status() ACTION = Action() INFO = Info() LOG = logger.getLogger(__name__) # Force the log level for this module # LOG.setLevel(logger.INFO) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL # Based on https://stackoverflow.com/questions/42820380/use-float-for-qslider class DoubleSlider(QtWidgets.QSlider): # create our our signal that we can connect to if necessary doubleValueChanged = Signal(float) def __init__(self, *args, **kargs): super(DoubleSlider, self).__init__( *args, **kargs) self._multi = 1 ** 2 # arbitrarily set # not needed at this time self.valueChanged.connect(self.emitDoubleValueChanged) def emitDoubleValueChanged(self): value = float(super(DoubleSlider, self).value())/self._multi self.doubleValueChanged.emit(value) def value(self): return float(super(DoubleSlider, self).value()) / self._multi def setMinimum(self, value): return super(DoubleSlider, self).setMinimum(int(value * self._multi)) def setMaximum(self, value): return super(DoubleSlider, self).setMaximum(int(value * self._multi)) def setSingleStep(self, value): return super(DoubleSlider, self).setSingleStep(value * self._multi) def singleStep(self): return float(super(DoubleSlider, self).singleStep()) / self._multi def setValue(self, value): super(DoubleSlider, self).setValue(int(value * self._multi)) class StatusSlider(DoubleSlider, _HalWidgetBase): def __init__(self, parent=None): super(StatusSlider, self).__init__(parent) self._block_signal = False self._halpin_option = True self.rapid = True self.feed = False self.spindle = False self.jograte = False self.jograte_angular = False self.max_velocity = False # for syslesheet dynamic property self._alertState = '' self._alertOver = 100.0 self._alertUnder = 50.0 def _hal_init(self): STATUS.connect('state-estop', lambda w: self.setEnabled(False)) STATUS.connect('state-estop-reset', lambda w: self.setEnabled(True)) 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.setMinimum(int(INFO.MIN_LINEAR_JOG_VEL)) self.setMaximum(int(INFO.MAX_LINEAR_JOG_VEL)) elif self.jograte_angular: STATUS.connect('jograte-angular-changed', lambda w, data: self.setValue(data)) self.setMinimum(int(INFO.MIN_ANGULAR_JOG_VEL)) self.setMaximum(int(INFO.MAX_ANGULAR_JOG_VEL)) elif self.max_velocity: 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 self._halpin_option: if self._pin_name_ == '': pname = self.HAL_NAME_ else: pname = self._pin_name_ self.hal_pin = self.HAL_GCOMP_.newpin(str(pname), hal.HAL_FLOAT, hal.HAL_OUT) # connect a signal and callback function to the button self.doubleValueChanged.connect(self._action) # If the widget uses dynamic properties in stylesheet... self._style_polish(state= self.get_alert_cmd(self.value())) # catch any programmed settings and update HAL pin def setValue(self, v): super(StatusSlider, self).setValue(v) if self._halpin_option: self.hal_pin.set(v) def _action(self, value): self._style_polish(state= self.get_alert_cmd(value)) if self._halpin_option: self.hal_pin.set(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.max_velocity: ACTION.SET_MAX_VELOCITY_RATE(value) else: LOG.error('{} : no action recognised'.format(self.HAL_NAME_)) # convert numeric value to string command def get_alert_cmd(self, value): if value > self._alertOver: return 'over' elif value < self._alertUnder: return 'under' else: return'normal' # polish widget so stylesheet sees the property change # some stylesheets color the widget based on the arbitrary hi/lo range def _style_polish(self, prop = 'alertState',state = 'normal'): if self._alertState != state: self.setProperty(prop, state) self.style().unpolish(self) self.style().polish(self) ######################################################################### # 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', 'max-velocity') 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 setjograte_angular(self, data): self.jograte_angular = data if data: self._toggle_properties('jograte_angular') def getjograte_angular(self): return self.jograte_angular def resetjograte_angular(self): self.jograte_angular = False def setmax_velocity(self, data): self.max_velocity = data if data: self._toggle_properties('max_velocity') def getmax_velocity(self): return self.max_velocity def resetmax_velocity(self): self.max_velocity = False 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 def set_pin_name(self, value): self._pin_name_ = value def get_pin_name(self): return self._pin_name_ def reset_pin_name(self): self._pin_name_ = '' pin_name = Property(str, get_pin_name, set_pin_name, reset_pin_name) halpin_option = Property(bool, get_halpin_option, set_halpin_option, reset_halpin_option) 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, getjograte_angular, setjograte_angular, resetjograte_angular) max_velocity_rate = Property(bool, getmax_velocity, setmax_velocity, resetmax_velocity) def setAlertState(self, data): self._alertState = data def getAlertState(self): return self._alertState def setAlertUnder(self, data): self._alertUnder = data def getAlertUnder(self): return self._alertUnder def resetAlertUnder(self): self._alertUnder = 50.0 def setAlertOver(self, data): self._alertOver = data def getAlertOver(self): return self._alertOver def resetAlertOver(self): self._alertOver = 100.0 alertState = Property(str, getAlertState, setAlertState) alertUnder = Property(float, getAlertUnder, setAlertUnder, resetAlertUnder) alertOver = Property(float, getAlertOver, setAlertOver, resetAlertOver) ############################## # required class boiler code # ############################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)