Source code for labscript_utils.qtwidgets.analogoutput

#####################################################################
#                                                                   #
# analogoutput.py                                                   #
#                                                                   #
# Copyright 2013, Monash University                                 #
#                                                                   #
# This file is part of the labscript suite (see                     #
# http://labscriptsuite.org) and is licensed under the Simplified   #
# BSD License. See the license.txt file in the root of the project  #
# for the full license.                                             #
#                                                                   #
#####################################################################
import sys

from qtutils.qt.QtCore import *
from qtutils.qt.QtGui import *
from qtutils.qt.QtWidgets import *


[docs]class NoStealFocusDoubleSpinBox(QDoubleSpinBox): """A QDoubleSpinBox that doesn't steal focus as you scroll over it with a mouse wheel."""
[docs] def __init__(self, *args, **kwargs): QDoubleSpinBox.__init__(self, *args, **kwargs) self.setFocusPolicy(Qt.StrongFocus)
[docs] def focusInEvent(self, event): self.setFocusPolicy(Qt.WheelFocus) return QDoubleSpinBox.focusInEvent(self, event)
[docs] def focusOutEvent(self, event): self.setFocusPolicy(Qt.StrongFocus) return QDoubleSpinBox.focusOutEvent(self, event)
[docs] def wheelEvent(self, event): if self.hasFocus(): return QDoubleSpinBox.wheelEvent(self, event) else: event.ignore()
[docs]class AnalogOutput(QWidget):
[docs] def __init__(self, hardware_name, connection_name='-', display_name=None, horizontal_alignment=False, parent=None): QWidget.__init__(self,parent) self._connection_name = connection_name self._hardware_name = hardware_name label_text = (self._hardware_name + '\n' + self._connection_name) if display_name is None else display_name self._label = QLabel(label_text) self._label.setAlignment(Qt.AlignCenter) self._label.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Minimum) self._spin_widget = NoStealFocusDoubleSpinBox() self._spin_widget.setSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Minimum) self._spin_widget.setKeyboardTracking(False) self._combobox = QComboBox() self._combobox.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Minimum) self._combobox.currentIndexChanged.connect(self._on_combobox_change) self._value_changed_function = None self.setSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Minimum) # Handle spinbox context menu # Lock/Unlock action self._lock_action = QAction("Lock",self._spin_widget) self._lock_action.triggered.connect(lambda:self._menu_triggered(self._lock_action)) #change step size action self._stepsize_action = QAction("Set step size",self._spin_widget) self._stepsize_action.triggered.connect(self._change_step) # Stepup/down Actions self._stepup_action = QAction("Step up",self._spin_widget) self._stepup_action.triggered.connect(lambda:self._spin_widget.stepBy(1)) self._stepdown_action = QAction("Step down",self._spin_widget) self._stepdown_action.triggered.connect(lambda:self._spin_widget.stepBy(-1)) self.menu = None def deletemenu(menu): menu.deleteLater() if menu == self.menu: self.menu = None def context_menu(pos): self.menu = menu = self._spin_widget.lineEdit().createStandardContextMenu() # Add Divider menu.addSeparator() # Add step up/Stepdown actions (grey out if at min/max or locked) menu.addAction(self._stepup_action) menu.addAction(self._stepdown_action) if self._spin_widget.value() == self._spin_widget.minimum(): self._stepdown_action.setEnabled(False) else: self._stepdown_action.setEnabled(True) if self._spin_widget.value() == self._spin_widget.maximum(): self._stepup_action.setEnabled(False) else: self._stepup_action.setEnabled(True) # Add divider menu.addSeparator() # Add lock action menu.addAction(self._lock_action) menu.addAction(self._stepsize_action) # connect signal for when menu is destroyed menu.aboutToHide.connect(lambda menu=menu: deletemenu(menu)) # Show the menu menu.popup(self.mapToGlobal(pos)) self._spin_widget.lineEdit().setContextMenuPolicy(Qt.CustomContextMenu) self._spin_widget.lineEdit().customContextMenuRequested.connect(context_menu) # Create widgets and layouts if horizontal_alignment: self._layout = QHBoxLayout(self) self._layout.addWidget(self._label) self._layout.addWidget(self._spin_widget) self._layout.addWidget(self._combobox) self._layout.setContentsMargins(0,0,0,0) else: self._layout = QGridLayout(self) self._layout.setVerticalSpacing(3) self._layout.setHorizontalSpacing(0) self._layout.setContentsMargins(3,3,3,3) self._label.setSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Minimum) #self._layout.addWidget(self._label) #self._layout.addItem(QSpacerItem(0,0,QSizePolicy.MinimumExpanding,QSizePolicy.Minimum),0,1) h_widget = QWidget() h_layout = QHBoxLayout(h_widget) h_layout.setContentsMargins(0,0,0,0) h_layout.addWidget(self._spin_widget) h_layout.addWidget(self._combobox) self._layout.addWidget(self._label,0,0) self._layout.addWidget(h_widget,1,0) #self._layout.addItem(QSpacerItem(0,0,QSizePolicy.MinimumExpanding,QSizePolicy.Minimum),1,1) self._layout.addItem(QSpacerItem(0,0,QSizePolicy.Minimum,QSizePolicy.MinimumExpanding),2,0) # Install the event filter that will allow us to catch right click mouse release events so we can popup a menu even when the button is disabled self.installEventFilter(self) # The Analog Out object that is in charge of this button self._AO = None
# Setting and getting methods for the Digitl Out object in charge of this button
[docs] def set_AO(self,AO,notify_old_AO=True,notify_new_AO=True): # If we are setting a new AO, remove this widget from the old one (if it isn't None) and add it to the new one (if it isn't None) if AO != self._AO: if self._AO is not None and notify_old_AO: self._AO.remove_widget(self,False) if AO is not None and notify_new_AO: AO.add_widget(self) # Store a reference to the digital out object self._AO = AO
[docs] def get_AO(self): return self._AO
[docs] def connect_value_change(self,func): self._value_changed_function = lambda value,self=self: func(value,self.selected_unit,True) self._spin_widget.valueChanged.connect(self._value_changed_function)
[docs] def disconnect_value_change(self): self._spin_widget.valueChanged.disconnect(self._value_changed_function)
[docs] def set_combobox_model(self,model): self._combobox.setModel(model)
def _on_combobox_change(self): selected_text = self.selected_unit if self._AO is not None: self._AO.change_unit(selected_text)
[docs] def block_spinbox_signals(self): return self._spin_widget.blockSignals(True)
[docs] def unblock_spinbox_signals(self): return self._spin_widget.blockSignals(False)
[docs] def set_spinbox_value(self,value,unit): if self._AO is not None: # get the value in the selected unit value = self._AO.convert_value_to_base(value,unit) value = self._AO.convert_value_from_base(value,self.selected_unit) self._spin_widget.setValue(value)
@property def selected_unit(self): return str(self._combobox.currentText())
[docs] def block_combobox_signals(self): return self._combobox.blockSignals(True)
[docs] def unblock_combobox_signals(self): return self._combobox.blockSignals(False)
[docs] def set_selected_unit(self,unit): if unit != self.selected_unit: item = self._combobox.model().findItems(unit) if item: model_index = self._combobox.model().indexFromItem(item[0]) self._combobox.setCurrentIndex(model_index.row())
[docs] def set_num_decimals(self,decimals): self._spin_widget.setDecimals(decimals)
[docs] def set_limits(self,lower,upper): self._spin_widget.setRange(lower,upper)
[docs] def set_step_size(self,step): self._spin_widget.setSingleStep(step)
def _change_step(self): maximum_step = abs(self._spin_widget.maximum()-self._spin_widget.minimum()) new_step,ok = QInputDialog.getDouble(self,"Set step size","Set step size",self._spin_widget.singleStep(),0.0,maximum_step,self._spin_widget.decimals()) if ok: self.set_step_size(new_step) if self._AO: self._AO.set_step_size(new_step,self.selected_unit) # The event filter that pops up a context menu on a right click, even when the button is disabled
[docs] def eventFilter(self, obj, event): if event.type() == QEvent.MouseButtonRelease and event.button() == Qt.RightButton: menu = QMenu(self) menu.addAction("Lock" if self._spin_widget.isEnabled() else "Unlock") menu.triggered.connect(self._menu_triggered) menu.popup(self.mapToGlobal(event.pos())) return QWidget.eventFilter(self, obj, event)
# This method is called whenever an entry in the context menu is clicked def _menu_triggered(self,action): if action.text() == "Lock": self.lock() elif action.text() == "Unlock": self.unlock() # This method locks (disables) the widget, and if the widget has a parent AO object, notifies it of the lock
[docs] def lock(self,notify_ao=True): self._spin_widget.setEnabled(False) self._lock_action.setText("Unlock") if self._AO is not None and notify_ao: self._AO.lock()
# This method unlocks (enables) the widget, and if the widget has a parent AO object, notifies it of the unlock
[docs] def unlock(self,notify_ao=True): self._spin_widget.setEnabled(True) self._lock_action.setText("Lock") if self._AO is not None and notify_ao: self._AO.unlock()
# A simple test! if __name__ == '__main__': qapplication = QApplication(sys.argv) window = QWidget() layout = QVBoxLayout(window) button = AnalogOutput('AO1') layout.addWidget(button) window.show() sys.exit(qapplication.exec_())