Source code for vent.gui.widgets.monitor

import numpy as np
import time
from PySide2 import QtWidgets, QtCore

from vent.gui import styles, mono_font
from vent.gui.widgets.components import RangeSlider
from vent.common import message


[docs]class Monitor(QtWidgets.QWidget): alarm = QtCore.Signal(tuple) limits_changed = QtCore.Signal(tuple)
[docs] def __init__(self, value, update_period=styles.MONITOR_UPDATE_INTERVAL, enum_name = None): """ Args: value (:class:`~vent.values.Value`): update_period (float): update period of monitor in s """ super(Monitor, self).__init__() self.name = value.name self.units = value.units self.abs_range = value.abs_range self.safe_range = value.safe_range self.decimals = value.decimals self.update_period = update_period self.enum_name = enum_name self.value = None # whether we are currently styled as being in an alarm state self._alarm = False self.init_ui() self.timed_update()
[docs] def init_ui(self): self.layout = QtWidgets.QVBoxLayout() self.setLayout(self.layout) ######### # create widgets # make range slider self.range_slider = RangeSlider(self.abs_range, self.safe_range, decimals=self.decimals, orientation=QtCore.Qt.Orientation.Horizontal) # make comboboxes to display numerical value self.max_safe = QtWidgets.QDoubleSpinBox() self.max_safe.setDecimals(self.decimals) self.max_safe.setRange(self.abs_range[0], self.abs_range[1]) self.max_safe.setSingleStep(10 ** (self.decimals * -1)) self.max_safe.setValue(self.safe_range[1]) self.min_safe = QtWidgets.QDoubleSpinBox() self.min_safe.setDecimals(self.decimals) self.min_safe.setRange(self.abs_range[0], self.abs_range[1]) self.min_safe.setSingleStep(10 ** (self.decimals * -1)) self.min_safe.setValue(self.safe_range[0]) # labels to display values self.value_label = QtWidgets.QLabel() self.value_label.setStyleSheet(styles.DISPLAY_VALUE) self.value_label.setFont(mono_font()) self.value_label.setAlignment(QtCore.Qt.AlignRight) self.value_label.setMargin(0) self.value_label.setContentsMargins(0,0,0,0) self.value_label.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) self.name_label = QtWidgets.QLabel() self.name_label.setStyleSheet(styles.DISPLAY_NAME) self.name_label.setText(self.name) self.name_label.setAlignment(QtCore.Qt.AlignRight) self.units_label = QtWidgets.QLabel() self.units_label.setStyleSheet(styles.DISPLAY_UNITS) self.units_label.setText(self.units) self.units_label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTop) self.units_label.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) # toggle button to expand control self.toggle_button = QtWidgets.QToolButton(checkable=True, checked=False) self.toggle_button.setStyleSheet(styles.DISPLAY_NAME) self.toggle_button.toggled.connect(self.toggle_control) self.toggle_button.setToolButtonStyle( QtCore.Qt.ToolButtonIconOnly ) self.toggle_button.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Expanding) self.toggle_button.setArrowType(QtCore.Qt.RightArrow) ######### # connect widgets # update boxes when slider changed self.range_slider.valueChanged.connect(self.update_boxes) # and vice versa self.min_safe.valueChanged.connect(self.range_slider.setLow) self.max_safe.valueChanged.connect(self.range_slider.setHigh) # and connect them all to a general limits_changed method # that also checks the alarm self.range_slider.valueChanged.connect(self._limits_changed) self.min_safe.valueChanged.connect(self._limits_changed) self.max_safe.valueChanged.connect(self._limits_changed) ######### # layout widgets # first make label layout label_layout = QtWidgets.QGridLayout() label_layout.setContentsMargins(0,0,0,0) label_layout.addWidget(self.toggle_button, 0,0,2,1) label_layout.addWidget(self.value_label, 0,1,2,1) label_layout.addWidget(self.name_label, 0,2,1,1) label_layout.addWidget(self.units_label, 1,2,1,1) # label_layout.addStretch() self.layout.addLayout(label_layout, 5) # then combine sliders and boxes self.slider_layout = QtWidgets.QVBoxLayout() minmax_layout = QtWidgets.QHBoxLayout() minmax_layout.addWidget(QtWidgets.QLabel('Min:')) minmax_layout.addWidget(self.min_safe) minmax_layout.addStretch() minmax_layout.addWidget(QtWidgets.QLabel('Max:')) minmax_layout.addWidget(self.max_safe) self.slider_layout.addLayout(minmax_layout) self.slider_layout.addWidget(self.range_slider) # and wrap them in a widget so we can show/hide more robustly self.slider_frame = QtWidgets.QFrame() self.slider_frame.setLayout(self.slider_layout) self.slider_frame.setVisible(False)
#self.layout.addLayout(self.slider_layout)
[docs] @QtCore.Slot(bool) def toggle_control(self, state): if state == True: self.toggle_button.setArrowType(QtCore.Qt.DownArrow) self.layout.addWidget(self.slider_frame) self.slider_frame.setVisible(True) self.adjustSize() else: self.toggle_button.setArrowType(QtCore.Qt.LeftArrow) self.layout.removeWidget(self.slider_frame) self.slider_frame.setVisible(False) self.adjustSize()
[docs] def update_boxes(self, new_values): self.min_safe.setValue(new_values[0]) self.max_safe.setValue(new_values[1])
[docs] @QtCore.Slot(int) @QtCore.Slot(float) def update_value(self, new_value): # stash numerical value try: new_value = np.clip(new_value, self.abs_range[0], self.abs_range[1]) except TypeError: # if given None, can't clip it. # just return # TODO: Should this raise an alarm? return self.value = new_value self.check_alarm()
[docs] @QtCore.Slot(tuple) def update_limits(self, new_limits): self.range_slider.setLow(new_limits[0]) self.range_slider.setHigh(new_limits[1]) self.update_boxes(new_limits)
[docs] def timed_update(self): # format value based on decimals if self.value: value_str = str(np.round(self.value, self.decimals)) self.value_label.setText(value_str) QtCore.QTimer.singleShot(round(self.update_period*1000), self.timed_update)
[docs] def _limits_changed(self, val): # ignore value, just emit changes and check alarm self.check_alarm() self.limits_changed.emit((self.min_safe.value(), self.max_safe.value()))
@property def alarm_state(self): return self._alarm @alarm_state.setter def alarm_state(self, alarm): if alarm == True: self.value_label.setStyleSheet(styles.DISPLAY_VALUE_ALARM) self.name_label.setStyleSheet(styles.DISPLAY_NAME_ALARM) self.units_label.setStyleSheet(styles.DISPLAY_UNITS_ALARM) self._alarm = True elif alarm == False: self.value_label.setStyleSheet(styles.DISPLAY_VALUE) self.name_label.setStyleSheet(styles.DISPLAY_NAME) self.units_label.setStyleSheet(styles.DISPLAY_UNITS) self._alarm = False
[docs] @QtCore.Slot(bool) def set_alarm(self, alarm): """ Simple wrapper to set alarm state from a qt signal Args: alarm (bool): Whether to set as alarm state or not """ self.alarm_state = alarm
[docs] def toggle_alarm(self): if self.alarm_state == False: self.alarm_state = True else: self.alarm_state = False
[docs] def check_alarm(self, value=None): if value is None: value = self.value if value: if (value > self.max_safe.value()) or (value < self.min_safe.value()): self.alarm.emit((self.enum_name, value, time.time())) if self.alarm == False: self.toggle_alarm_state(True) else: if self.alarm == True: self.toggle_alarm_state(False)