Import LinuxCNC source tree
This commit is contained in:
544
lib/python/qtvcp/widgets/bar.py
Normal file
544
lib/python/qtvcp/widgets/bar.py
Normal file
@@ -0,0 +1,544 @@
|
||||
import enum
|
||||
from qtpy import QtWidgets
|
||||
from qtpy.QtGui import QColor, QBrush, QPainter, QLinearGradient
|
||||
from qtpy.QtCore import (Qt, Slot, Property, Signal, QRectF,
|
||||
QSize)
|
||||
try:
|
||||
from qtpy.QtCore import QEnum
|
||||
except ImportError:
|
||||
try:
|
||||
from qtpy.QtCore import Q_ENUM as QEnum
|
||||
except ImportError:
|
||||
# before Qt 5.10
|
||||
from qtpy.QtCore import Q_ENUMS as QEnum
|
||||
|
||||
from qtvcp.widgets.widget_baseclass import _HalWidgetBase
|
||||
import hal
|
||||
|
||||
# Set up logging
|
||||
from qtvcp import logger
|
||||
LOG = logger.getLogger(__name__)
|
||||
|
||||
# Force the log level for this module
|
||||
#LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
|
||||
|
||||
class Bar(QtWidgets.QWidget):
|
||||
valueChanged = Signal([int],[float])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self._vertical = False
|
||||
self._opposite = False
|
||||
self._minimum = 0
|
||||
self._maximum = 200
|
||||
self._value = 100
|
||||
|
||||
self.setSizePolicy(
|
||||
QtWidgets.QSizePolicy.MinimumExpanding,
|
||||
QtWidgets.QSizePolicy.MinimumExpanding
|
||||
)
|
||||
|
||||
# set default number of colored steps
|
||||
self.setSteps(None)
|
||||
|
||||
self._bar_solid_percent = 0.9
|
||||
self._background_color = QColor('black')
|
||||
self._padding = 2.0 # n-pixel gap around edge.
|
||||
self._split = .2 # percent of full scale indicator
|
||||
self._useMultiColor = True
|
||||
self._singleIndicatorColor = QColor('blue')
|
||||
|
||||
def paintEvent(self, e):
|
||||
painter = QPainter(self)
|
||||
|
||||
brush = QBrush()
|
||||
brush.setColor(self._background_color)
|
||||
brush.setStyle(Qt.SolidPattern)
|
||||
rect = QRectF(0, 0, painter.device().width(), painter.device().height())
|
||||
painter.fillRect(rect, brush)
|
||||
|
||||
# Get current state.
|
||||
vmin, vmax = self.minimum(), self.maximum()
|
||||
value = self.value()
|
||||
|
||||
# Define our canvas.
|
||||
d_height = painter.device().height() - (self._padding * 2)
|
||||
d_width = painter.device().width() - (self._padding * 2)
|
||||
|
||||
if self._vertical:
|
||||
# Draw the vertical bars.
|
||||
step_size = d_height / self.n_steps
|
||||
bar_height = step_size * self._bar_solid_percent
|
||||
bar_spacer = step_size * (1 - self._bar_solid_percent) / 2
|
||||
|
||||
# Calculate the y-stop position, from the value in range.
|
||||
pc = (value - vmin) / (vmax - vmin)
|
||||
n_steps_to_draw = int(pc * self.n_steps)
|
||||
partial = (pc * self.n_steps) % 1
|
||||
#print('pre',value,pc, n_steps_to_draw, partial)
|
||||
|
||||
n =-1 # default fall through value if n_steps_to_draw = 0
|
||||
# draw complete step bars
|
||||
for n in range(self.n_steps):
|
||||
brush.setColor(QColor(self._step_color_list[n]))
|
||||
if self._opposite:
|
||||
top = self._padding + (( + n) * step_size) + bar_spacer
|
||||
else:
|
||||
top = self._padding + d_height - ((1 + n) * step_size) + bar_spacer
|
||||
|
||||
# full scale indicator:
|
||||
# QRextF(left, top, width and height )
|
||||
rect = QRectF(
|
||||
self._padding,
|
||||
top,
|
||||
d_width * self._split,
|
||||
bar_height
|
||||
)
|
||||
painter.fillRect(rect, brush)
|
||||
|
||||
# value indicator:
|
||||
if n < n_steps_to_draw:
|
||||
rect2 = QRectF(
|
||||
self._padding + (d_width * self._split),
|
||||
top ,
|
||||
d_width * (1-self._split),
|
||||
bar_height
|
||||
)
|
||||
if self._useMultiColor:
|
||||
color = QColor(self._step_color_list[n])
|
||||
else:
|
||||
color = self._singleIndicatorColor
|
||||
gradient = QLinearGradient(0, 0, 1, 0)
|
||||
gradient.setColorAt(0.0, color)
|
||||
gradient.setColorAt(0.5, color.lighter())
|
||||
gradient.setColorAt(1.0, color)
|
||||
gradient.setCoordinateMode(gradient.ObjectMode)
|
||||
painter.fillRect(rect2, QBrush(gradient))
|
||||
|
||||
# draw partial step bar for in-between values
|
||||
if partial > 0:
|
||||
n = n_steps_to_draw
|
||||
brush.setColor(QColor(self._step_color_list[n]))
|
||||
# right to left
|
||||
height = (bar_height* partial)
|
||||
if self._opposite:
|
||||
top = self._padding + (n * step_size) + bar_spacer
|
||||
else:
|
||||
top = self._padding - (n * step_size) + d_height - height- bar_spacer
|
||||
|
||||
# full scale indicator
|
||||
rect = QRectF(
|
||||
self._padding,
|
||||
top,
|
||||
d_width * self._split,
|
||||
height
|
||||
)
|
||||
painter.fillRect(rect, brush)
|
||||
|
||||
# value indicator
|
||||
rect2 = QRectF(
|
||||
self._padding + d_width * self._split,
|
||||
top ,
|
||||
d_width *(1- self._split),
|
||||
height
|
||||
)
|
||||
|
||||
if self._useMultiColor:
|
||||
color = QColor(self._step_color_list[n])
|
||||
else:
|
||||
color = self._singleIndicatorColor
|
||||
|
||||
gradient = QLinearGradient(0, 0, 1, 0)
|
||||
gradient.setColorAt(0.0, color)
|
||||
gradient.setColorAt(0.5, color.lighter())
|
||||
gradient.setColorAt(1.0, color)
|
||||
gradient.setCoordinateMode(gradient.ObjectMode)
|
||||
painter.fillRect(rect2, QBrush(gradient))
|
||||
|
||||
|
||||
else:
|
||||
# Draw the horizontal bars.
|
||||
step_size = d_width / self.n_steps
|
||||
bar_width = step_size * self._bar_solid_percent
|
||||
bar_spacer = step_size * (1 - self._bar_solid_percent) / 2
|
||||
|
||||
# Calculate the y-stop position, from the value in range.
|
||||
pc = (value - vmin) / (vmax - vmin)
|
||||
n_steps_to_draw = int(pc * self.n_steps)
|
||||
partial = (pc * self.n_steps) % 1
|
||||
#print('pre',value,pc, n_steps_to_draw, partial)
|
||||
|
||||
n=-1 # default fall through value if n_steps_to_draw = 0
|
||||
for n in range(self.n_steps):
|
||||
brush.setColor(QColor(self._step_color_list[n]))
|
||||
# left, top, width and height floats
|
||||
if self._opposite:
|
||||
left = self._padding + d_width - ((1 + n) * step_size) + bar_spacer
|
||||
else:
|
||||
left = self._padding + bar_spacer + ((0 + n) * step_size)
|
||||
rect = QRectF(
|
||||
left ,
|
||||
self._padding ,
|
||||
bar_width,
|
||||
d_height * self._split
|
||||
)
|
||||
painter.fillRect(rect, brush)
|
||||
|
||||
if n < n_steps_to_draw:
|
||||
rect2 = QRectF(
|
||||
left ,
|
||||
self._padding + d_height * self._split,
|
||||
bar_width,
|
||||
d_height * (1-self._split)
|
||||
)
|
||||
|
||||
if self._useMultiColor:
|
||||
color = QColor(self._step_color_list[n])
|
||||
else:
|
||||
color = self._singleIndicatorColor
|
||||
|
||||
gradient = QLinearGradient(0, 1, 0, 0)
|
||||
gradient.setColorAt(0.0, color)
|
||||
gradient.setColorAt(0.5, color.lighter())
|
||||
gradient.setColorAt(1.0, color)
|
||||
gradient.setCoordinateMode(gradient.ObjectMode)
|
||||
painter.fillRect(rect2, QBrush(gradient))
|
||||
|
||||
# draw partial step bar for in-between values
|
||||
if partial > 0:
|
||||
n = n_steps_to_draw
|
||||
brush.setColor(QColor(self._step_color_list[n]))
|
||||
|
||||
# right to left
|
||||
width = (bar_width* partial)
|
||||
if self._opposite:
|
||||
left = self._padding - (n * step_size) + d_width - width - bar_spacer
|
||||
# left to right
|
||||
else:
|
||||
left = self._padding + bar_spacer + (n * step_size)
|
||||
|
||||
# full scale indicator
|
||||
rect = QRectF(
|
||||
left,
|
||||
self._padding ,
|
||||
width,
|
||||
d_height * self._split
|
||||
)
|
||||
painter.fillRect(rect, brush)
|
||||
|
||||
# value indicator
|
||||
rect2 = QRectF(
|
||||
left ,
|
||||
self._padding + d_height * self._split,
|
||||
width,
|
||||
d_height * (1-self._split)
|
||||
)
|
||||
if self._useMultiColor:
|
||||
color = QColor(self._step_color_list[n])
|
||||
else:
|
||||
color = self._singleIndicatorColor
|
||||
gradient = QLinearGradient(0, 1, 0, 0)
|
||||
gradient.setColorAt(0.0, color)
|
||||
gradient.setColorAt(0.5, color.lighter())
|
||||
gradient.setColorAt(1.0, color)
|
||||
gradient.setCoordinateMode(gradient.ObjectMode)
|
||||
painter.fillRect(rect2, QBrush(gradient))
|
||||
|
||||
painter.end()
|
||||
|
||||
# list of color strings or an integer of steps
|
||||
def setSteps(self, steps):
|
||||
if steps is None:
|
||||
steps = ['green', 'green', '#00b600', '#00b600', '#00d600',
|
||||
'#00d600', 'yellow', 'yellow', 'red', 'red']
|
||||
self.n_steps = len(steps)
|
||||
self._step_color_list = steps
|
||||
|
||||
if isinstance(steps, list):
|
||||
# list of colors.
|
||||
self.n_steps = len(steps)
|
||||
self._step_color_list = steps
|
||||
|
||||
elif isinstance(steps, int):
|
||||
# int number of bars, defaults to red.
|
||||
self.n_steps = steps
|
||||
self._step_color_list = ['red'] * steps
|
||||
else:
|
||||
raise TypeError('steps must be a list or int')
|
||||
|
||||
def value(self):
|
||||
return self._value
|
||||
|
||||
@Slot(float)
|
||||
@Slot(int)
|
||||
def setValue(self, data):
|
||||
if data != self._value:
|
||||
if data > self._maximum: data = self._maximum
|
||||
if data < self._minimum : data = self._minimum
|
||||
self._value = data
|
||||
self.valueChanged.emit(int(data))
|
||||
self.valueChanged[float].emit(float(data))
|
||||
self.update()
|
||||
|
||||
|
||||
def sizeHint(self):
|
||||
if self._vertical:
|
||||
return QSize(40, 120)
|
||||
else:
|
||||
return QSize(40, 20)
|
||||
|
||||
def minimum(self):
|
||||
return self._minimum
|
||||
|
||||
def maximum(self):
|
||||
return self._maximum
|
||||
|
||||
def setRange(self, low, hi):
|
||||
self._minimum = low
|
||||
self._maximum = hi
|
||||
|
||||
def setFormat(self, data):
|
||||
pass
|
||||
|
||||
def invertBool(self, value):
|
||||
if value:
|
||||
return False
|
||||
return True
|
||||
|
||||
def getInvertedAppearance(self):
|
||||
return self._opposite
|
||||
def setInvertedAppearance(self, data):
|
||||
self._opposite = bool(data)
|
||||
self.update()
|
||||
def resetInvertedAppearance(self, data):
|
||||
self._opposite = False
|
||||
self.update()
|
||||
|
||||
#########################################################################
|
||||
# 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_step_color_l(self, data):
|
||||
# convert stylesheet list of single string to list of multiple
|
||||
if ',' in data[0]:
|
||||
self.setSteps(data[0].split(','))
|
||||
else:
|
||||
self.setSteps(data)
|
||||
def get_step_color_l(self):
|
||||
return self._step_color_list
|
||||
def reset_step_color_l(self):
|
||||
self.setSteps( ['green', 'green', '#00b600', '#00b600',
|
||||
'#00d600', '#00d600', 'yellow', 'yellow', 'red', 'red'])
|
||||
|
||||
def getBackgroundColor(self):
|
||||
return self._background_color
|
||||
@Slot(QColor)
|
||||
def setBackgroundColor(self, value):
|
||||
self._background_color = value
|
||||
self.update()
|
||||
|
||||
def get_indicatorColor(self):
|
||||
return self._singleIndicatorColor
|
||||
@Slot(QColor)
|
||||
def set_indicatorColor(self, value):
|
||||
self._singleIndicatorColor = value
|
||||
self.update()
|
||||
def reset_indicatorColor(self, value):
|
||||
self._singleIndicatorColor = QColor('blue')
|
||||
self.update()
|
||||
|
||||
def getSplit(self):
|
||||
return int(self._split * 100)
|
||||
def setSplit(self, data):
|
||||
if data <0: data == 0
|
||||
elif data >100: data == 100
|
||||
self._split = data/100
|
||||
self.update()
|
||||
def resetSplit(self):
|
||||
self._split = .2
|
||||
self.update()
|
||||
|
||||
def getMax(self):
|
||||
return self._maximum
|
||||
def setMax(self, data):
|
||||
self._maximum = data
|
||||
def resetMax(self):
|
||||
self._maximum = 200
|
||||
|
||||
def getMin(self):
|
||||
return self._minimum
|
||||
def setMin(self, data):
|
||||
self._minimum = data
|
||||
def resetMin(self):
|
||||
self._minimum = 0
|
||||
|
||||
def getMultiColor(self):
|
||||
return self._useMultiColor
|
||||
def setMultiColor(self, data):
|
||||
self._useMultiColor = data
|
||||
self.update()
|
||||
def resetMultiColor(self):
|
||||
self._useMultiColor = False
|
||||
self.update()
|
||||
|
||||
def getVert(self):
|
||||
return self._vertical
|
||||
def setVert(self, data):
|
||||
self._vertical = data
|
||||
self.update()
|
||||
def resetVert(self):
|
||||
self._vertical = False
|
||||
self.update()
|
||||
|
||||
stepColorList = Property(
|
||||
'QStringList',
|
||||
get_step_color_l, set_step_color_l, reset_step_color_l)
|
||||
|
||||
backgroundColor = Property(QColor, getBackgroundColor, setBackgroundColor)
|
||||
indicatorColor = Property(QColor, get_indicatorColor, set_indicatorColor, reset_indicatorColor)
|
||||
useMultiColorIndicator = Property(bool, getMultiColor, setMultiColor, resetMultiColor)
|
||||
split = Property(int, getSplit, setSplit, resetSplit)
|
||||
setMaximum = Property(int, getMax, setMax, resetMax)
|
||||
setMinimum = Property(int, getMin, setMin, resetMin)
|
||||
setVertical = Property(bool, getVert, setVert, resetVert)
|
||||
setInverted = Property(bool, getInvertedAppearance, setInvertedAppearance, resetInvertedAppearance)
|
||||
|
||||
class HALPinType(enum.IntEnum):
|
||||
NONE = 0
|
||||
S32 = hal.HAL_S32
|
||||
FLOAT = hal.HAL_FLOAT
|
||||
|
||||
|
||||
class HalBar(Bar, _HalWidgetBase):
|
||||
HALPinType = HALPinType
|
||||
QEnum(HALPinType)
|
||||
|
||||
# older version of pyqt5 need this as well as QEnum
|
||||
NONE = 0
|
||||
S32 = hal.HAL_S32
|
||||
FLOAT = hal.HAL_FLOAT
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(). __init__( *args, **kwargs)
|
||||
self._pin_type = HALPinType.S32
|
||||
self._pin_name = ''
|
||||
self._invert_negative = False
|
||||
self._superOpposite = self._opposite
|
||||
|
||||
def _hal_init(self):
|
||||
if self._pin_name == '':
|
||||
pname = self.HAL_NAME_
|
||||
else:
|
||||
pname = self._pin_name
|
||||
|
||||
self._superOpposite = self._opposite
|
||||
|
||||
if self._pin_type == HALPinType.FLOAT:
|
||||
self.hal_pin = self.HAL_GCOMP_.newpin(pname, hal.HAL_FLOAT, hal.HAL_IN)
|
||||
self.hal_pin.value_changed.connect(lambda data: self.updateDisplay(data))
|
||||
elif self._pin_type == HALPinType.S32:
|
||||
self.hal_pin = self.HAL_GCOMP_.newpin(pname, hal.HAL_S32, hal.HAL_IN)
|
||||
self.hal_pin.value_changed.connect(lambda data: self.updateDisplay(data))
|
||||
|
||||
def updateDisplay(self, data):
|
||||
if data < 0 and self._invert_negative:
|
||||
self._opposite = self.invertBool(self._superOpposite)
|
||||
data = data * -1
|
||||
else:
|
||||
self._opposite = self._superOpposite
|
||||
self.setValue(data)
|
||||
|
||||
def set_pin_type(self, value):
|
||||
self._pin_type = value
|
||||
def get_pin_type(self):
|
||||
return self._pin_type
|
||||
def reset_pin_type(self):
|
||||
self._pin_type = HALPinType.S32
|
||||
|
||||
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 = ''
|
||||
|
||||
def set_invert_negative(self, value):
|
||||
self._invert_negative = value
|
||||
def get_invert_negative(self):
|
||||
return self._invert_negative
|
||||
def reset_invert_negative(self):
|
||||
self._invert_negative = False
|
||||
|
||||
pinType = Property(HALPinType, get_pin_type, set_pin_type, reset_pin_type)
|
||||
pinName = Property(str, get_pin_name, set_pin_name, reset_pin_name)
|
||||
invertOnNegative = Property(bool, get_invert_negative, set_invert_negative, reset_invert_negative)
|
||||
|
||||
if __name__ == '__main__':
|
||||
from qtpy.QtWidgets import (QLabel, QSlider, QWidget, QVBoxLayout,
|
||||
QHBoxLayout, QPushButton, QCheckBox)
|
||||
|
||||
app = QtWidgets.QApplication([])
|
||||
w = QWidget()
|
||||
w.setGeometry(100, 100, 200, 100)
|
||||
w.setWindowTitle('Bar widget')
|
||||
layout = QVBoxLayout(w)
|
||||
|
||||
label = QLabel()
|
||||
|
||||
bar = HalBar()
|
||||
bar.setProperty('setInverted',True)
|
||||
bar.setProperty('setVertical',False)
|
||||
|
||||
#bar.setValue(51)
|
||||
|
||||
slider = QSlider(Qt.Horizontal)
|
||||
slider.setMinimum(0)
|
||||
slider.setMaximum(200)
|
||||
slider.setSingleStep(10)
|
||||
slider.setPageStep(100)
|
||||
slider.valueChanged.connect(bar.setValue)
|
||||
slider.valueChanged.connect(label.setNum)
|
||||
slider.setValue(51)
|
||||
|
||||
layout.addWidget(label)
|
||||
layout.addWidget(bar)
|
||||
layout.addWidget(slider)
|
||||
|
||||
button = QPushButton('Vertical')
|
||||
button.setCheckable(True)
|
||||
button.setChecked(bar.getVert())
|
||||
button.toggled.connect(bar.setVert)
|
||||
|
||||
layout.addWidget(button)
|
||||
|
||||
button = QPushButton('Use Multi Color')
|
||||
button.setCheckable(True)
|
||||
button.setChecked(bar.getMultiColor())
|
||||
button.toggled.connect(bar.setMultiColor)
|
||||
|
||||
layout.addWidget(button)
|
||||
|
||||
lyt = QHBoxLayout()
|
||||
check1 = QCheckBox('o%')
|
||||
check1.setAutoExclusive (True)
|
||||
check1.stateChanged.connect(lambda value: bar.setSplit(0))
|
||||
|
||||
check2 = QCheckBox('20%')
|
||||
check2.setAutoExclusive (True)
|
||||
check2.stateChanged.connect(lambda value: bar.setSplit(20))
|
||||
|
||||
check3 = QCheckBox('50%')
|
||||
check3.setAutoExclusive (True)
|
||||
check3.stateChanged.connect(lambda value: bar.setSplit(50))
|
||||
|
||||
lyt.addWidget(check1)
|
||||
lyt.addWidget(check2)
|
||||
lyt.addWidget(check3)
|
||||
layout.addLayout(lyt)
|
||||
|
||||
w.show()
|
||||
app.exec()
|
||||
|
||||
Reference in New Issue
Block a user