def _dock_add_spin_box(self, name, value, rng, callback, *, compact=True, double=True, step=None, tooltip=None, layout=None): layout = self._dock_named_layout(name=name, layout=layout, compact=compact) value = value if double else int(value) widget = QDoubleSpinBox() if double else QSpinBox() _set_widget_tooltip(widget, tooltip) widget.setAlignment(Qt.AlignCenter) widget.setMinimum(rng[0]) widget.setMaximum(rng[1]) widget.setKeyboardTracking(False) if step is None: inc = (rng[1] - rng[0]) / 20. inc = max(int(round(inc)), 1) if not double else inc widget.setSingleStep(inc) else: widget.setSingleStep(step) widget.setValue(value) widget.valueChanged.connect(callback) self._layout_add_widget(layout, widget) return _QtWidget(widget)
def __add_option(self, name: str, option: QDoubleSpinBox) -> None: """Add widgets for option.""" layout = QHBoxLayout() label = QLabel(name, self) option.setValue(1) option.setMaximum(10000) option.setMinimum(0.01) layout.addWidget(label) layout.addWidget(option) self.main_layout.addLayout(layout)
def createDoubleSpinBox(self, variable_name, variable_value, variable_type, analysis_module_variables_model): spinner = QDoubleSpinBox() spinner.setDecimals(6) spinner.setMinimumWidth(75) spinner.setMaximum(analysis_module_variables_model.getVariableMaximumValue(variable_name)) spinner.setMinimum(analysis_module_variables_model.getVariableMinimumValue(variable_name)) spinner.setSingleStep(analysis_module_variables_model.getVariableStepValue(variable_name)) spinner.setValue(variable_value) spinner.valueChanged.connect(partial(self.valueChanged, variable_name, variable_type, spinner)) return spinner
def spinbox(v: float = 0., *, minimum: float = 0., maximum: float = 9999., prefix: bool = False) -> QDoubleSpinBox: double_spinbox = QDoubleSpinBox() double_spinbox.setMinimum(minimum) double_spinbox.setMaximum(maximum) double_spinbox.setValue(v) if prefix: double_spinbox.setPrefix("±") return double_spinbox
def createEditor(self, parent, option, index): """Override. Create and editor based on the cell type """ if index.column() == 2: editor = QDoubleSpinBox(parent) editor.setDecimals(3) editor.setMaximum(10) editor.setMinimum(0) editor.setSingleStep(0.001) return editor
def add_float_box(layout, default, minimum, maximum, label, step, row, column=0): box = QDoubleSpinBox() box.setMinimum(minimum) box.setMaximum(maximum) box.setValue(default) box.setSingleStep(step) layout.addWidget(QLabel(label), row, column) layout.addWidget(box, row, column + 1) return box
class PMGNumberSpinCtrl(BaseExtendedWidget): """ 利用spinbox的控制面板,当最大值、最小值、初始值和步长均为整数的时候,类型为整数;、反之只要有任意一个是float, 类型就是浮点数了。 """ def __init__(self, layout_dir: str, title: str, initial_value: Union[int, float], unit: str = '', val_range: Tuple[Union[float, int], Union[float, int]] = (None, None), step: int = 1): super().__init__(layout_dir) self.on_check_callback = None self.prefix = QLabel(text=title) entryLayout = QHBoxLayout() entryLayout.setContentsMargins(0, 0, 0, 0) self.min, self.max = val_range self.step = step if isinstance(self.min, int) and isinstance(self.max, int) and isinstance(self.step, int) \ and isinstance(initial_value, int): self.ctrl = QSpinBox() else: self.ctrl = QDoubleSpinBox() self.ctrl.valueChanged.connect(self.on_value_changed) self.postfix = QLabel(text=unit) # self.central_layout.addWidget(self.prefix) self.central_layout.addLayout(entryLayout) entryLayout.addWidget(self.prefix) entryLayout.addWidget(self.ctrl) entryLayout.addWidget(self.postfix) if self.min is not None: self.ctrl.setMinimum(self.min) if self.max is not None: self.ctrl.setMaximum(self.max) self.ctrl.setSingleStep(step) self.accury = initial_value self.set_value(initial_value) def set_value(self, value: Union[float, int]) -> None: self.ctrl.setValue(value) def get_value(self) -> Union[int, float]: return self.ctrl.value() def on_value_changed(self): self.signal_param_changed.emit(self.name)
class OffsetPlots(BetterLayout): """Create a visual offset in the plots""" # TODO: implement the offset code def __init__(self, *args, **kwargs): super(OffsetPlots, self).__init__(*args, **kwargs) self.offset_box = QDoubleSpinBox() self.offset_box.setMinimum(0.0) self.offset_box.setDecimals(1) self.offset_box.setSingleStep(0.1) self.offset_button = QPushButton("Enable Offset") self.offset_button.setCheckable(True) self.offset_button.toggled.connect(self._offset_toggled) layout = QHBoxLayout() layout.addWidget(self.offset_box) layout.addWidget(self.offset_button) self.add_widget_to_bottom(layout) def _offset_toggled(self, enabled): if enabled: self.offset_button.setText("Disable Offset") else: self.offset_button.setText("Enable Offset")
def setupParameters(self, model): pBias = model.biasParameters() pSpecific = model.modelSpecificParameters() parameters = pBias + pSpecific self.setRowCount(len(parameters)) self.siScale = {} self.rowMap = {} for row, parameter in enumerate(parameters): name, latex, unit, siScale, minimum, maximum, text = parameter guessSb = QDoubleSpinBox() if len(unit): guessSb.setSuffix(' %s' % unit) guessSb.setMinimum(minimum) guessSb.setMaximum(maximum) guessSb.setKeyboardTracking(False) item = QTableWidgetItem(name) item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEditable | Qt.ItemIsEnabled) item.setCheckState(Qt.Checked) item.setToolTip(text) self.setItem(row, 0, item) self.setCellWidget(row, 1, guessSb) minSb = QDoubleSpinBox() maxSb = QDoubleSpinBox() minSb.setMinimum(minimum) minSb.setMaximum(maximum) minSb.setValue(minimum) maxSb.setMinimum(minimum) maxSb.setMaximum(maximum) maxSb.setValue(maximum) minSb.setKeyboardTracking(False) maxSb.setKeyboardTracking(False) maxSb.valueChanged.connect(minSb.setMaximum) maxSb.valueChanged.connect(guessSb.setMaximum) minSb.valueChanged.connect(maxSb.setMinimum) minSb.valueChanged.connect(guessSb.setMinimum) self.setCellWidget(row, 2, minSb) self.setCellWidget(row, 3, maxSb) guessSb.valueChanged.connect(self.parameterGuessChanged) self.siScale[name] = siScale self.rowMap[name] = row
class QtVectorsProperties(QtLayerProperties): def __init__(self, layer): super().__init__(layer) self.layer.events.edge_width.connect(self._on_width_change) self.layer.events.length.connect(self._on_len_change) # vector color adjustment and widget face_comboBox = QComboBox() colors = self.layer._colors for c in colors: face_comboBox.addItem(c) index = face_comboBox.findText(self.layer.edge_color, Qt.MatchFixedString) if index >= 0: face_comboBox.setCurrentIndex(index) face_comboBox.activated[str].connect( lambda text=face_comboBox: self.change_edge_color(text)) row = self.grid_layout.rowCount() self.grid_layout.addWidget(QLabel('color:'), row, self.name_column) self.grid_layout.addWidget(face_comboBox, row, self.property_column) # line width in pixels self.width_field = QDoubleSpinBox() self.width_field.setSingleStep(0.1) self.width_field.setMinimum(0.1) value = self.layer.edge_width self.width_field.setValue(value) self.width_field.valueChanged.connect(self.change_width) row = self.grid_layout.rowCount() self.grid_layout.addWidget(QLabel('width:'), row, self.name_column) self.grid_layout.addWidget(self.width_field, row, self.property_column) # line length self.length_field = QDoubleSpinBox() self.length_field.setSingleStep(0.1) value = self.layer.length self.length_field.setValue(value) self.length_field.setMinimum(0.1) self.length_field.valueChanged.connect(self.change_length) row = self.grid_layout.rowCount() self.grid_layout.addWidget(QLabel('length:'), row, self.name_column) self.grid_layout.addWidget(self.length_field, row, self.property_column) self.setExpanded(False) def change_edge_color(self, text): self.layer.edge_color = text def change_connector_type(self, text): self.layer.connector = text def change_width(self, value): self.layer.edge_width = value def change_length(self, value): self.layer.length = value def _on_len_change(self, event): with self.layer.events.length.blocker(): self.length_field.setValue(self.layer.length) def _on_width_change(self, event): with self.layer.events.edge_width.blocker(): self.width_field.setValue(self.layer.edge_width)
class QtVectorsControls(QtLayerControls): """Qt view and controls for the napari Vectors layer. Parameters ---------- layer : napari.layers.Vectors An instance of a napari Vectors layer. Attributes ---------- edge_color_label : qtpy.QtWidgets.QLabel Label for edgeColorSwatch edgeColorSwatch : qtpy.QtWidgets.QFrame Color swatch showing display color of vectors. edgeComboBox : qtpy.QtWidgets.QComboBox Dropdown widget to select display color for vectors. color_mode_comboBox : qtpy.QtWidgets.QComboBox Dropdown widget to select edge_color_mode for the vectors. color_prop_box : qtpy.QtWidgets.QComboBox Dropdown widget to select _edge_color_property for the vectors. edge_prop_label : qtpy.QtWidgets.QLabel Label for color_prop_box grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : napari.layers.Vectors An instance of a napari Vectors layer. lengthSpinBox : qtpy.QtWidgets.QDoubleSpinBox Spin box widget controlling line length of vectors. Multiplicative factor on projections for length of all vectors. widthSpinBox : qtpy.QtWidgets.QDoubleSpinBox Spin box widget controlling edge line width of vectors. """ def __init__(self, layer): super().__init__(layer) self.layer.events.edge_width.connect(self._on_edge_width_change) self.layer.events.length.connect(self._on_length_change) self.layer.events.edge_color_mode.connect( self._on_edge_color_mode_change) self.layer.events.edge_color.connect(self._on_edge_color_change) # dropdown to select the property for mapping edge_color color_properties = self._get_property_values() color_prop_box = QComboBox(self) color_prop_box.activated[str].connect(self.change_edge_color_property) color_prop_box.addItems(color_properties) self.color_prop_box = color_prop_box self.edge_prop_label = QLabel(trans._('edge property:')) # vector direct color mode adjustment and widget self.edgeColorEdit = QColorSwatchEdit( initial_color=self.layer.edge_color, tooltip=trans._('click to set current edge color', ), ) self.edgeColorEdit.color_changed.connect(self.change_edge_color_direct) self.edge_color_label = QLabel(trans._('edge color:')) self._on_edge_color_change() # dropdown to select the edge color mode colorModeComboBox = QComboBox(self) color_modes = [e.value for e in ColorMode] colorModeComboBox.addItems(color_modes) colorModeComboBox.activated[str].connect(self.change_edge_color_mode) self.color_mode_comboBox = colorModeComboBox self._on_edge_color_mode_change() # line width in pixels self.widthSpinBox = QDoubleSpinBox() self.widthSpinBox.setKeyboardTracking(False) self.widthSpinBox.setSingleStep(0.1) self.widthSpinBox.setMinimum(0.1) self.widthSpinBox.setMaximum(np.inf) self.widthSpinBox.setValue(self.layer.edge_width) self.widthSpinBox.valueChanged.connect(self.change_width) # line length self.lengthSpinBox = QDoubleSpinBox() self.lengthSpinBox.setKeyboardTracking(False) self.lengthSpinBox.setSingleStep(0.1) self.lengthSpinBox.setValue(self.layer.length) self.lengthSpinBox.setMinimum(0.1) self.lengthSpinBox.setMaximum(np.inf) self.lengthSpinBox.valueChanged.connect(self.change_length) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addWidget(QLabel(trans._('opacity:')), 0, 0) self.grid_layout.addWidget(self.opacitySlider, 0, 1, 1, 2) self.grid_layout.addWidget(QLabel(trans._('width:')), 1, 0) self.grid_layout.addWidget(self.widthSpinBox, 1, 1, 1, 2) self.grid_layout.addWidget(QLabel(trans._('length:')), 2, 0) self.grid_layout.addWidget(self.lengthSpinBox, 2, 1, 1, 2) self.grid_layout.addWidget(QLabel(trans._('blending:')), 3, 0) self.grid_layout.addWidget(self.blendComboBox, 3, 1, 1, 2) self.grid_layout.addWidget(QLabel(trans._('edge color mode:')), 4, 0) self.grid_layout.addWidget(self.color_mode_comboBox, 4, 1, 1, 2) self.grid_layout.addWidget(self.edge_color_label, 5, 0) self.grid_layout.addWidget(self.edgeColorEdit, 5, 1, 1, 2) self.grid_layout.addWidget(self.edge_prop_label, 6, 0) self.grid_layout.addWidget(self.color_prop_box, 6, 1, 1, 2) self.grid_layout.setRowStretch(7, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def change_edge_color_property(self, property: str): """Change edge_color_property of vectors on the layer model. This property is the property the edge color is mapped to. Parameters ---------- property : str property to map the edge color to """ mode = self.layer.edge_color_mode try: self.layer.edge_color = property self.layer.edge_color_mode = mode except TypeError: # if the selected property is the wrong type for the current color mode # the color mode will be changed to the appropriate type, so we must update self._on_edge_color_mode_change() raise def change_edge_color_mode(self, mode: str): """Change edge color mode of vectors on the layer model. Parameters ---------- mode : str Edge color for vectors. Must be: 'direct', 'cycle', or 'colormap' """ old_mode = self.layer.edge_color_mode with self.layer.events.edge_color_mode.blocker(): try: self.layer.edge_color_mode = mode self._update_edge_color_gui(mode) except ValueError: # if the color mode was invalid, revert to the old mode self.layer.edge_color_mode = old_mode raise def change_edge_color_direct(self, color: np.ndarray): """Change edge color of vectors on the layer model. Parameters ---------- color : np.ndarray Edge color for vectors, in an RGBA array """ self.layer.edge_color = color def change_width(self, value): """Change edge line width of vectors on the layer model. Parameters ---------- value : float Line width of vectors. """ self.layer.edge_width = value self.widthSpinBox.clearFocus() self.setFocus() def change_length(self, value): """Change length of vectors on the layer model. Multiplicative factor on projections for length of all vectors. Parameters ---------- value : float Length of vectors. """ self.layer.length = value self.lengthSpinBox.clearFocus() self.setFocus() def _update_edge_color_gui(self, mode: str): """Update the GUI element associated with edge_color. This is typically used when edge_color_mode changes Parameters ---------- mode : str The new edge_color mode the GUI needs to be updated for. Should be: 'direct', 'cycle', 'colormap' """ if mode in ('cycle', 'colormap'): self.edgeColorEdit.setHidden(True) self.edge_color_label.setHidden(True) self.color_prop_box.setHidden(False) self.edge_prop_label.setHidden(False) elif mode == 'direct': self.edgeColorEdit.setHidden(False) self.edge_color_label.setHidden(False) self.color_prop_box.setHidden(True) self.edge_prop_label.setHidden(True) def _get_property_values(self): """Get the current property values from the Vectors layer Returns ------- property_values : np.ndarray array of all of the union of the property names (keys) in Vectors.properties and Vectors._property_choices """ property_choices = [*self.layer._property_choices] properties = [*self.layer.properties] property_values = np.union1d(property_choices, properties) return property_values def _on_length_change(self, event=None): """Change length of vectors. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ with self.layer.events.length.blocker(): self.lengthSpinBox.setValue(self.layer.length) def _on_edge_width_change(self, event=None): """Receive layer model width change event and update width spinbox. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ with self.layer.events.edge_width.blocker(): self.widthSpinBox.setValue(self.layer.edge_width) def _on_edge_color_mode_change(self, event=None): """Receive layer model edge color mode change event & update dropdown. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ with qt_signals_blocked(self.color_mode_comboBox): mode = self.layer._edge.color_mode index = self.color_mode_comboBox.findText(mode, Qt.MatchFixedString) self.color_mode_comboBox.setCurrentIndex(index) self._update_edge_color_gui(mode) def _on_edge_color_change(self, event=None): """Receive layer model edge color change event & update dropdown. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ if self.layer._edge.color_mode == ColorMode.DIRECT: with qt_signals_blocked(self.edgeColorEdit): self.edgeColorEdit.setColor(self.layer.edge_color[0]) elif self.layer._edge.color_mode in ( ColorMode.CYCLE, ColorMode.COLORMAP, ): with qt_signals_blocked(self.color_prop_box): prop = self.layer._edge.color_properties.name index = self.color_prop_box.findText(prop, Qt.MatchFixedString) self.color_prop_box.setCurrentIndex(index)
def createEditor(self, parent, option, index): editor = QDoubleSpinBox(parent) editor.setMinimum(0) editor.setDecimals(5) editor.setSingleStep(0.0001) return editor
class QtVectorsLayer(QtLayer): def __init__(self, layer): super().__init__(layer) self.layer.events.averaging.connect(self._on_avg_change) self.layer.events.width.connect(self._on_width_change) self.layer.events.length.connect(self._on_len_change) # vector color adjustment and widget face_comboBox = QComboBox() colors = self.layer._colors for c in colors: face_comboBox.addItem(c) index = face_comboBox.findText(self.layer.color, Qt.MatchFixedString) if index >= 0: face_comboBox.setCurrentIndex(index) face_comboBox.activated[str].connect( lambda text=face_comboBox: self.change_face_color(text)) self.grid_layout.addWidget(QLabel('color:'), 3, 0) self.grid_layout.addWidget(face_comboBox, 3, 1) # line width in pixels self.width_field = QDoubleSpinBox() self.width_field.setSingleStep(0.1) self.width_field.setMinimum(0.1) value = self.layer.width self.width_field.setValue(value) self.width_field.valueChanged.connect(self.change_width) self.grid_layout.addWidget(QLabel('width:'), 4, 0) self.grid_layout.addWidget(self.width_field, 4, 1) # averaging spinbox self.averaging_spinbox = QSpinBox() self.averaging_spinbox.setSingleStep(1) self.averaging_spinbox.setValue(1) self.averaging_spinbox.setMinimum(1) self.averaging_spinbox.valueChanged.connect(self.change_average_type) self.grid_layout.addWidget(QLabel('avg kernel'), 5, 0) self.grid_layout.addWidget(self.averaging_spinbox, 5, 1) # line length self.length_field = QDoubleSpinBox() self.length_field.setSingleStep(0.1) value = self.layer.length self.length_field.setValue(value) self.length_field.setMinimum(0.1) self.length_field.valueChanged.connect(self.change_length) self.grid_layout.addWidget(QLabel('length:'), 6, 0) self.grid_layout.addWidget(self.length_field, 6, 1) self.setExpanded(False) def change_face_color(self, text): self.layer.color = text def change_connector_type(self, text): self.layer.connector = text def change_average_type(self, value): self.layer.averaging = value def change_width(self, value): self.layer.width = value def change_length(self, value): self.layer.length = value def _on_avg_change(self, event): with self.layer.events.averaging.blocker(): self.averaging_spinbox.setValue(self.layer.averaging) def _on_len_change(self, event): with self.layer.events.length.blocker(): self.length_field.setValue(self.layer.length) def _on_width_change(self, event): with self.layer.events.width.blocker(): self.width_field.setValue(self.layer.width)
class QtVectorsControls(QtLayerControls): """Qt view and controls for the napari Vectors layer. Parameters ---------- layer : napari.layers.Vectors An instance of a napari Vectors layer. Attributes ---------- edgeColorSwatch : qtpy.QtWidgets.QFrame Color swatch showing display color of vectors. edgeComboBox : qtpy.QtWidgets.QComboBox Dropdown widget to select display color for vectors. grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : napari.layers.Vectors An instance of a napari Vectors layer. lengthSpinBox : qtpy.QtWidgets.QDoubleSpinBox Spin box widget controlling line length of vectors. Multiplicative factor on projections for length of all vectors. widthSpinBox : qtpy.QtWidgets.QDoubleSpinBox Spin box widget controlling edge line width of vectors. """ def __init__(self, layer): super().__init__(layer) self.layer.events.edge_width.connect(self._on_width_change) self.layer.events.length.connect(self._on_len_change) self.layer.events.edge_color.connect(self._on_edge_color_change) # vector color adjustment and widget edge_comboBox = QComboBox() edge_comboBox.addItems(self.layer._colors) edge_comboBox.activated[str].connect(self.change_edge_color) self.edgeComboBox = edge_comboBox self.edgeColorSwatch = QFrame() self.edgeColorSwatch.setObjectName('swatch') self.edgeColorSwatch.setToolTip('Edge color swatch') self._on_edge_color_change() # line width in pixels self.widthSpinBox = QDoubleSpinBox() self.widthSpinBox.setKeyboardTracking(False) self.widthSpinBox.setSingleStep(0.1) self.widthSpinBox.setMinimum(0.1) self.widthSpinBox.setValue(self.layer.edge_width) self.widthSpinBox.valueChanged.connect(self.change_width) # line length self.lengthSpinBox = QDoubleSpinBox() self.lengthSpinBox.setKeyboardTracking(False) self.lengthSpinBox.setSingleStep(0.1) self.lengthSpinBox.setValue(self.layer.length) self.lengthSpinBox.setMinimum(0.1) self.lengthSpinBox.valueChanged.connect(self.change_length) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addWidget(QLabel('opacity:'), 0, 0) self.grid_layout.addWidget(self.opacitySlider, 0, 1, 1, 2) self.grid_layout.addWidget(QLabel('width:'), 1, 0) self.grid_layout.addWidget(self.widthSpinBox, 1, 1, 1, 2) self.grid_layout.addWidget(QLabel('length:'), 2, 0) self.grid_layout.addWidget(self.lengthSpinBox, 2, 1, 1, 2) self.grid_layout.addWidget(QLabel('blending:'), 3, 0) self.grid_layout.addWidget(self.blendComboBox, 3, 1, 1, 2) self.grid_layout.addWidget(QLabel('edge color:'), 4, 0) self.grid_layout.addWidget(self.edgeComboBox, 4, 2) self.grid_layout.addWidget(self.edgeColorSwatch, 4, 1) self.grid_layout.setRowStretch(5, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def change_edge_color(self, text): """Change edge color of vectors on the layer model. Parameters ---------- text : str Edge color for vectors, color name or hex string. Eg: 'white', 'red', 'blue', '#00ff00', etc. """ self.layer.edge_color = text def change_width(self, value): """Change edge line width of vectors on the layer model. Parameters ---------- value : float Line width of vectors. """ self.layer.edge_width = value self.widthSpinBox.clearFocus() self.setFocus() def change_length(self, value): """Change length of vectors on the layer model. Multiplicative factor on projections for length of all vectors. Parameters ---------- value : float Length of vectors. """ self.layer.length = value self.lengthSpinBox.clearFocus() self.setFocus() def _on_len_change(self, event=None): """Change length of vectors. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context, by default None. """ with self.layer.events.length.blocker(): self.lengthSpinBox.setValue(self.layer.length) def _on_width_change(self, event=None): """"Receive layer model width change event and update width spinbox. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context, by default None. """ with self.layer.events.edge_width.blocker(): self.widthSpinBox.setValue(self.layer.edge_width) def _on_edge_color_change(self, event=None): """"Receive layer model edge color change event & update color swatch. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context, by default None. """ with self.layer.events.edge_color.blocker(): index = self.edgeComboBox.findText( self.layer.edge_color, Qt.MatchFixedString ) self.edgeComboBox.setCurrentIndex(index) color = Color(self.layer.edge_color).hex self.edgeColorSwatch.setStyleSheet("background-color: " + color)
class QtVectorsControls(QtLayerControls): def __init__(self, layer): super().__init__(layer) self.layer.events.edge_width.connect(self._on_width_change) self.layer.events.length.connect(self._on_len_change) self.layer.events.edge_color.connect(self._on_edge_color_change) # vector color adjustment and widget edge_comboBox = QComboBox() colors = self.layer._colors for c in colors: edge_comboBox.addItem(c) edge_comboBox.activated[str].connect( lambda text=edge_comboBox: self.change_edge_color(text)) self.edgeComboBox = edge_comboBox self.edgeColorSwatch = QFrame() self.edgeColorSwatch.setObjectName('swatch') self.edgeColorSwatch.setToolTip('Edge color swatch') self._on_edge_color_change(None) # line width in pixels self.widthSpinBox = QDoubleSpinBox() self.widthSpinBox.setKeyboardTracking(False) self.widthSpinBox.setSingleStep(0.1) self.widthSpinBox.setMinimum(0.1) value = self.layer.edge_width self.widthSpinBox.setValue(value) self.widthSpinBox.valueChanged.connect(self.change_width) # line length self.lengthSpinBox = QDoubleSpinBox() self.lengthSpinBox.setKeyboardTracking(False) self.lengthSpinBox.setSingleStep(0.1) value = self.layer.length self.lengthSpinBox.setValue(value) self.lengthSpinBox.setMinimum(0.1) self.lengthSpinBox.valueChanged.connect(self.change_length) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addWidget(QLabel('opacity:'), 0, 0) self.grid_layout.addWidget(self.opacitySilder, 0, 1, 1, 2) self.grid_layout.addWidget(QLabel('width:'), 1, 0) self.grid_layout.addWidget(self.widthSpinBox, 1, 1, 1, 2) self.grid_layout.addWidget(QLabel('length:'), 2, 0) self.grid_layout.addWidget(self.lengthSpinBox, 2, 1, 1, 2) self.grid_layout.addWidget(QLabel('blending:'), 3, 0) self.grid_layout.addWidget(self.blendComboBox, 3, 1, 1, 2) self.grid_layout.addWidget(QLabel('edge color:'), 4, 0) self.grid_layout.addWidget(self.edgeComboBox, 4, 2) self.grid_layout.addWidget(self.edgeColorSwatch, 4, 1) self.grid_layout.setRowStretch(5, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def change_edge_color(self, text): self.layer.edge_color = text def change_connector_type(self, text): self.layer.connector = text def change_width(self, value): self.layer.edge_width = value self.widthSpinBox.clearFocus() self.setFocus() def change_length(self, value): self.layer.length = value self.lengthSpinBox.clearFocus() self.setFocus() def _on_len_change(self, event): with self.layer.events.length.blocker(): self.lengthSpinBox.setValue(self.layer.length) def _on_width_change(self, event): with self.layer.events.edge_width.blocker(): self.widthSpinBox.setValue(self.layer.edge_width) def _on_edge_color_change(self, event): with self.layer.events.edge_color.blocker(): index = self.edgeComboBox.findText(self.layer.edge_color, Qt.MatchFixedString) self.edgeComboBox.setCurrentIndex(index) color = Color(self.layer.edge_color).hex self.edgeColorSwatch.setStyleSheet("background-color: " + color)
class SliderSpinBox(QWidget): valueChanged = Signal(float) def __init__(self, parent=None, value_range=(0, 100), slider_steps=100, spinbox_steps=1000, decimals=None): QWidget.__init__(self, parent) layout = QHBoxLayout(self) self.setLayout(layout) self.horizontalSlider = QSlider(Qt.Horizontal, self) self.doubleSpinBox = QDoubleSpinBox(self) self.decimals = decimals layout.setMargin(0) layout.addWidget(self.doubleSpinBox) layout.addWidget(self.horizontalSlider) self.doubleSpinBox.valueChanged.connect(self.spinbox_changed) self.horizontalSlider.valueChanged.connect(self.slider_changed) self.wt = [] self.changer = None self.slider_steps = slider_steps self.horizontalSlider.setMaximum(slider_steps) self.spinbox_steps = spinbox_steps self.set_range(value_range) def set_value(self, value): self.doubleSpinBox.setValue(value) value = property(lambda self: self.doubleSpinBox.value(), set_value) def set_range(self, value_range): self.changer = "set_range" spinbox_step_decimal = np.ceil(np.log10(1. / (float(value_range[1] - value_range[0]) / self.spinbox_steps))) self.slider_decimals = int(np.ceil(np.log10(self.slider_steps) - np.log10(value_range[1] - value_range[0]))) if self.decimals is None: self.doubleSpinBox.setDecimals(spinbox_step_decimal + 1) else: self.doubleSpinBox.setDecimals(self.decimals) spinbox_step_decimal = min(self.decimals, spinbox_step_decimal) self.slider_decimals = min(self.decimals, self.slider_decimals) self.doubleSpinBox.setSingleStep(10 ** -spinbox_step_decimal) self.value_range = value_range self.doubleSpinBox.setMinimum(value_range[0]) self.doubleSpinBox.setMaximum(value_range[1]) self.horizontalSlider.setValue(np.floor(value_range[0])) self.changer = None self.doubleSpinBox.setValue(value_range[0]) def range(self): return self.doubleSpinBox.minimum(), self.doubleSpinBox.maximum() def slider_changed(self, value): if self.changer is None: self.changer = 'slider' value = np.round(value * (self.value_range[1] - self.value_range[0]) / self.slider_steps + self.value_range[0], self.slider_decimals) self.doubleSpinBox.setValue(value) self.changer = None self.value_changed(value) def spinbox_changed(self, value): if self.changer is None: self.changer = 'spinbox' slider_value = .5 + (value - self.value_range[0]) * self.slider_steps / (self.value_range[1] - self.value_range[0]) self.horizontalSlider.setValue(slider_value) self.changer = None self.value_changed(value) @postpone_until_last_call_finishes def value_changed(self, value): self.valueChanged.emit(value)
class BPMIntlkLimSPWidget(BaseObject, QWidget): """BPM Orbit Interlock Limit Setpoint Widget.""" def __init__(self, parent=None, metric='', prefix=''): """Init.""" BaseObject.__init__(self, prefix) QWidget.__init__(self, parent) # initialize auxiliary attributes self.metric = metric.lower() if 'sum' in self.metric: self.lim_sp = [ 'IntlkLmtMinSum-SP', ] self.title = 'Min.Sum. Thresholds' elif 'trans' in self.metric: self.lim_sp = [ 'IntlkLmtTransMinX-SP', 'IntlkLmtTransMaxX-SP', 'IntlkLmtTransMinY-SP', 'IntlkLmtTransMaxY-SP' ] self.title = 'Translation Thresholds' elif 'ang' in self.metric: self.lim_sp = [ 'IntlkLmtAngMinX-SP', 'IntlkLmtAngMaxX-SP', 'IntlkLmtAngMinY-SP', 'IntlkLmtAngMaxY-SP' ] self.title = 'Angulation Thresholds' else: raise ValueError(metric) if 'sum' in self.metric: self._create_pvs('Sum-Mon') self._summon = _np.zeros(len(self.BPM_NAMES), dtype=float) else: self._set_ref_orb('ref_orb') self.prefix = prefix # initialize super self.setObjectName('SIApp') self._setupUi() self.setFocusPolicy(Qt.StrongFocus) def _setupUi(self): title = QLabel(self.title, self, alignment=Qt.AlignCenter) title.setStyleSheet("font-weight: bold;") title.setSizePolicy(QSzPlcy.Preferred, QSzPlcy.Maximum) # limit setpoints self._wid_lims = QGroupBox('Select Thresholds: ') lay_lims = QGridLayout(self._wid_lims) lay_lims.setAlignment(Qt.AlignTop) row = 0 if 'sum' in self.metric: text = '\nThresholds must be set in Monit1 rate\n'\ 'counts. Here we make available you \n'\ 'to read Sum-Mon values, in Monit rate\n'\ 'counts, and convert them to Monit1\n'\ 'rate counts using calibration curves.\n'\ 'You can also apply a scale factor to\n'\ 'the values read.\n\n' self._label_help = QLabel(text, self) lay_lims.addWidget(self._label_help, row, 0, 1, 2) row += 1 self._pb_get = QPushButton('Get current Sum-Mon', self) self._pb_get.released.connect(self._get_new_sum) lay_lims.addWidget(self._pb_get, row, 0, 1, 2) row += 1 self._label_getsts = QLabel('\n', self) lay_lims.addWidget(self._label_getsts, row, 0, 1, 2) row += 1 self._label_scl = QLabel('Scale: ', self) lay_lims.addWidget(self._label_scl, row, 0) self._spin_scl = QDoubleSpinBox(self) self._spin_scl.setDecimals(2) self._spin_scl.setValue(1.00) self._spin_scl.setMinimum(-100.00) self._spin_scl.setMaximum(+100.00) lay_lims.addWidget(self._spin_scl, row, 1) else: self._head_value = QLabel('Value', self, alignment=Qt.AlignCenter) lay_lims.addWidget(self._head_value, 0, 1) self._head_send = QLabel('Apply', self, alignment=Qt.AlignCenter) lay_lims.addWidget(self._head_send, 0, 2) self._spins, self._checks = dict(), dict() for lsp in self.lim_sp: row += 1 text = lsp.split('-')[0].split('Lmt')[1] + ' [nm]: ' label = QLabel(text, self, alignment=Qt.AlignRight) lay_lims.addWidget(label, row, 0) spin = QSpinBox(self) self._spins[lsp] = spin spin.setValue(0) spin.setMinimum(-1e9) spin.setMaximum(1e9) lay_lims.addWidget(spin, row, 1) check = QCheckBox(self) self._checks[lsp] = check lay_lims.addWidget(check, row, 2, alignment=Qt.AlignCenter) row += 1 self._label_reforb = QLabel( '\nChoose Ref.Orb. to calculate reference\n' + self.title.lower() + ' around which we\napply the values above:') lay_lims.addWidget(self._label_reforb, row, 0, 1, 3) row += 1 self._cb_reforb = RefOrbComboBox(self) self._cb_reforb.refOrbChanged.connect(self._set_ref_orb) lay_lims.addWidget(self._cb_reforb, row, 0, 1, 3) if 'trans' in self.metric: text = '\n\nBPM calculation consider the translation\n' \ 'estimative:' \ '\n\n(pos@downstream+pos@upstream)/2\n\n' \ 'where the pairs upstream/downstream\nfolow:\n' \ ' - M1/M2\n' \ ' - C1-1/C1-2\n' \ ' - C2/C3-1\n' \ ' - C3-2/C4\n' elif 'ang' in self.metric: text = '\n\nBPM calculation consider the angulation\n' \ 'estimative:' \ '\n\n(pos@downstream - pos@upstream)\n\n' \ 'where the pairs upstream/downstream\nfolow:\n' \ ' - M1/M2\n' \ ' - C1-1/C1-2\n' \ ' - C2/C3-1\n' \ ' - C3-2/C4\n' row += 1 self._label_help = QLabel(text, self) lay_lims.addWidget(self._label_help, row, 0, 1, 3) # BPM selection groupsel = QGroupBox('Select BPMs:') groupsel.setObjectName('groupsel') groupsel.setStyleSheet('#groupsel{min-width: 27em; max-width: 27em;}') self._bpm_sel = _BPMSelectionWidget(self, show_toggle_all_false=False, toggle_all_true_text='Select All', prefix=self.prefix) lay_groupsel = QGridLayout(groupsel) lay_groupsel.addWidget(self._bpm_sel) # connect signals and slots self._bpm_sel.applyChangesClicked.connect(self.send_value) lay = QGridLayout(self) lay.addWidget(title, 0, 0, 1, 2) lay.addWidget(self._wid_lims, 1, 0) lay.addWidget(groupsel, 1, 1) def _set_ref_orb(self, text): self._reforb = dict() if text.lower() == 'zero': self._reforb['x'] = _np.zeros(len(self.BPM_NAMES), dtype=float) self._reforb['y'] = _np.zeros(len(self.BPM_NAMES), dtype=float) else: data = self.get_ref_orb(text) self._reforb['x'] = data['x'] self._reforb['y'] = data['y'] def _get_new_sum(self): self._summon = _np.array(self._get_values('Sum-Mon'), dtype=float) text = 'Read at ' + _time.strftime('%d/%m/%Y %H:%M:%S\n', _time.localtime(_time.time())) self._label_getsts.setText(text) def send_value(self): """Send new value.""" idxsel, namesel = list(), list() for idx, wid in enumerate(self._bpm_sel.widgets): name = self.BPM_NAMES[idx] check = wid.findChild(QCheckBox) if check.isChecked(): idxsel.append(idx) namesel.append(name) check.setChecked(False) if not namesel: return if 'sum' in self.metric: _av = self.CONV_POLY_MONIT1_2_MONIT[0, :] _bv = self.CONV_POLY_MONIT1_2_MONIT[1, :] allvals = self._spin_scl.value() * (self._summon - _bv) / _av values = allvals[_np.array(idxsel)] pvs = [b.substitute(propty=self.lim_sp[0]) for b in namesel] else: pvs, values = list(), list() for lsp in self.lim_sp: if not self._checks[lsp].isChecked(): continue lval = self._spins[lsp].value() plan = 'y' if 'y' in lsp.lower() else 'x' metric = self.calc_intlk_metric(self._reforb[plan], metric=self.metric) allvals = _np.round(_np.array(metric) + lval) allvals = allvals[_np.array(idxsel)] values.extend(allvals) pvs.extend([b.substitute(propty=lsp) for b in namesel]) delays = [ 0.0, ] * len(pvs) self._items_success = list() self._items_fail = list() conn = EpicsConnector(pvs, parent=self) task_set = EpicsSetter(pvs, values, delays, parent=self) task_sleep = EpicsWait([ None, ] * 10, wait_time=2.0, parent=self) task_check = EpicsChecker(pvs, values, delays, parent=self) task_check.itemChecked.connect(self._check_status) dlg = ProgressDialog( ['Connecting...', 'Setting...', 'Waiting...', 'Checking...'], [conn, task_set, task_sleep, task_check], parent=self) ret = dlg.exec_() if ret == dlg.Rejected: return if self._items_fail: report = ReportDialog(self._items_fail, self) report.exec_() return @Slot(str, bool) def _check_status(self, item, status): if status: self._items_success.append((item, True)) else: self._items_fail.append(item)
class ImageRotationDialog(ExToolWindow): def __init__(self, signal, axes, parent, plugin): super(ImageRotationDialog, self).__init__(parent) self.ui = parent self.create_controls() self.accepted.connect(self.ok) self.rejected.connect(self.close_new) self.signal = signal self.plugin = plugin self.new_out = None self._connected_updates = False if isinstance(axes, str): axm = signal.signal.axes_manager if axes.startswith("nav"): axes = (axm._axes.index(axm.navigation_axes[0]), axm._axes.index(axm.navigation_axes[1])) elif axes.startswith("sig"): axes = (axm._axes.index(axm.signal_axes[0]), axm._axes.index(axm.signal_axes[1])) self.axes = axes self.setWindowTitle(tr("Rotate")) # TODO: TAG: Functionality check if not hasattr(signal.signal, 'events'): self.gbo_preview.setVisible(False) # TODO: Add dynamic rotation, e.g. one that rotates when source # signal's data_changed event triggers def connect(self): # TODO: Don't have to con/dis those in gbo self.opt_new.toggled.connect(self.close_new) self.num_angle.valueChanged.connect(self.update) self.chk_grid.toggled.connect(self.update) self.num_grid.valueChanged.connect(self.update) self.chk_reshape.toggled.connect(self.update) self.opt_new.toggled.connect(self.update) self.opt_replace.toggled.connect(self.update) def disconnect(self): self.num_angle.valueChanged.disconnect(self.update) self.chk_grid.toggled.disconnect(self.update) self.num_grid.valueChanged.disconnect(self.update) self.chk_reshape.toggled.disconnect(self.update) self.opt_new.toggled.disconnect(self.update) self.opt_replace.toggled.disconnect(self.update) def ok(self): # Draw figure if not already done # TODO: TAG: Functionality check if not hasattr(self.signal.signal, 'events') or \ not self.gbo_preview.isChecked(): self.update() angle = self.num_angle.value() reshape = self.chk_reshape.isChecked() self.plugin.record_code( r"<p>.rotate_signal({0}, reshape={1}, axes={2})".format( angle, reshape, self.axes)) # Clean up event connections if self.new_out is not None: self.connect_update_plot(self.new_out.signal, disconnect=True) def close_new(self, value=False): if self.new_out is not None and not value: self.new_out.close() self.new_out = None self._connected_updates = False def set_preview(self, value): if not hasattr(self.signal.signal, 'events'): return if value: self.connect() self.update() else: self.disconnect() self.close_new() def _axes_in_nav(self): axm = self.signal.signal.axes_manager navidx = [axm._axes.index(ax) for ax in axm.navigation_axes] if self.axes[0] in navidx: return True return False def connect_update_plot(self, signal, disconnect=False): if self._connected_updates != disconnect: return # Nothing to do, prevent double connections if self._axes_in_nav(): f = signal._plot.navigator_plot.update else: f = signal._plot.signal_plot.update # TODO: TAG: Functionality check if hasattr(signal, 'events') and hasattr( signal.events, 'data_changed'): if disconnect: signal.events.data_changed.disconnect(f) else: signal.events.data_changed.connect(f, []) self._connected_updates = not disconnect def update(self): angle = self.num_angle.value() reshape = self.chk_reshape.isChecked() if self.opt_new.isChecked(): if self.new_out is None: out = None else: out = self.new_out.signal elif self.opt_replace.isChecked(): out = self.signal.signal else: return # Indeterminate state, do nothing s = self.plugin.rotate_signal(angle, self.signal.signal, record=False, reshape=reshape, out=out, axes=self.axes) if out is None: s.metadata.General.title = self.signal.name + "[Rotated]" s.plot() self.connect_update_plot(s) if (self.gbo_preview.isChecked() and self.opt_new.isChecked() and self.new_out is None): self.new_out = self.ui.lut_signalwrapper[s] else: s = out if self.chk_grid.isChecked() is True: pass # TODO: Draw grid def create_controls(self): """ Create UI controls. """ vbox = QVBoxLayout() form = QFormLayout() self.num_angle = QDoubleSpinBox() self.num_angle.setValue(0.0) self.num_angle.setMinimum(-360) self.num_angle.setMaximum(360) form.addRow(tr("Angle:"), self.num_angle) vbox.addLayout(form) self.gbo_preview = QGroupBox(tr("Preview")) self.gbo_preview.setCheckable(True) self.gbo_preview.setChecked(False) gbo_vbox = QVBoxLayout() self.chk_grid = QCheckBox(tr("Grid")) self.chk_grid.setChecked(False) self.num_grid = QSpinBox() self.num_grid.setValue(4) self.num_grid.setMinimum(1) self.num_grid.setEnabled(False) self.chk_grid.toggled[bool].connect(self.num_grid.setEnabled) gbo_vbox.addWidget(self.chk_grid) gbo_vbox.addWidget(self.num_grid) self.gbo_preview.setLayout(gbo_vbox) vbox.addWidget(self.gbo_preview) self.gbo_preview.toggled[bool].connect(self.set_preview) self.gbo_output = QGroupBox(tr("Output")) self.opt_new = QRadioButton(tr("New signal")) self.opt_replace = QRadioButton(tr("In place")) self.opt_new.setChecked(True) gbo_vbox2 = QVBoxLayout() gbo_vbox2.addWidget(self.opt_new) gbo_vbox2.addWidget(self.opt_replace) self.gbo_output.setLayout(gbo_vbox2) vbox.addWidget(self.gbo_output) self.chk_reshape = QCheckBox(tr("Resize to fit")) self.chk_reshape.setChecked(False) vbox.addWidget(self.chk_reshape) self.btn_ok = QPushButton(tr("&OK")) self.btn_ok.setDefault(True) self.btn_ok.clicked.connect(self.accept) self.btn_cancel = QPushButton(tr("&Cancel")) self.btn_cancel.clicked.connect(self.reject) hbox = QHBoxLayout() hbox.addWidget(self.btn_ok) hbox.addWidget(self.btn_cancel) vbox.addLayout(hbox) vbox.addStretch(1) self.setLayout(vbox)
class ImageRotationDialog(ExToolWindow): def __init__(self, signal, axes, parent, plugin): super(ImageRotationDialog, self).__init__(parent) self.ui = parent self.create_controls() self.accepted.connect(self.ok) self.rejected.connect(self.close_new) self.signal = signal self.plugin = plugin self.new_out = None self._connected_updates = False if isinstance(axes, str): axm = signal.signal.axes_manager if axes.startswith("nav"): axes = (axm._axes.index(axm.navigation_axes[0]), axm._axes.index(axm.navigation_axes[1])) elif axes.startswith("sig"): axes = (axm._axes.index(axm.signal_axes[0]), axm._axes.index(axm.signal_axes[1])) self.axes = axes self.setWindowTitle(tr("Rotate")) # TODO: TAG: Functionality check if not hasattr(signal.signal, 'events'): self.gbo_preview.setVisible(False) # TODO: Add dynamic rotation, e.g. one that rotates when source # signal's data_changed event triggers def connect(self): # TODO: Don't have to con/dis those in gbo self.opt_new.toggled.connect(self.close_new) self.num_angle.valueChanged.connect(self.update) self.chk_grid.toggled.connect(self.update) self.num_grid.valueChanged.connect(self.update) self.chk_reshape.toggled.connect(self.update) self.opt_new.toggled.connect(self.update) self.opt_replace.toggled.connect(self.update) def disconnect(self): self.num_angle.valueChanged.disconnect(self.update) self.chk_grid.toggled.disconnect(self.update) self.num_grid.valueChanged.disconnect(self.update) self.chk_reshape.toggled.disconnect(self.update) self.opt_new.toggled.disconnect(self.update) self.opt_replace.toggled.disconnect(self.update) def ok(self): # Draw figure if not already done # TODO: TAG: Functionality check if not hasattr(self.signal.signal, 'events') or \ not self.gbo_preview.isChecked(): self.update() angle = self.num_angle.value() reshape = self.chk_reshape.isChecked() self.plugin.record_code( r"<p>.rotate_signal({0}, reshape={1}, axes={2})".format( angle, reshape, self.axes)) # Clean up event connections if self.new_out is not None: self.connect_update_plot(self.new_out.signal, disconnect=True) def close_new(self, value=False): if self.new_out is not None and not value: self.new_out.close() self.new_out = None self._connected_updates = False def set_preview(self, value): if not hasattr(self.signal.signal, 'events'): return if value: self.connect() self.update() else: self.disconnect() self.close_new() def _axes_in_nav(self): axm = self.signal.signal.axes_manager navidx = [axm._axes.index(ax) for ax in axm.navigation_axes] if self.axes[0] in navidx: return True return False def connect_update_plot(self, signal, disconnect=False): if self._connected_updates != disconnect: return # Nothing to do, prevent double connections if self._axes_in_nav(): f = signal._plot.navigator_plot.update else: f = signal._plot.signal_plot.update # TODO: TAG: Functionality check if hasattr(signal, 'events') and hasattr(signal.events, 'data_changed'): if disconnect: signal.events.data_changed.disconnect(f) else: signal.events.data_changed.connect(f, []) self._connected_updates = not disconnect def update(self): angle = self.num_angle.value() reshape = self.chk_reshape.isChecked() if self.opt_new.isChecked(): if self.new_out is None: out = None else: out = self.new_out.signal elif self.opt_replace.isChecked(): out = self.signal.signal else: return # Indeterminate state, do nothing s = self.plugin.rotate_signal(angle, self.signal.signal, record=False, reshape=reshape, out=out, axes=self.axes) if out is None: s.metadata.General.title = self.signal.name + "[Rotated]" s.plot() self.connect_update_plot(s) if (self.gbo_preview.isChecked() and self.opt_new.isChecked() and self.new_out is None): self.new_out = self.ui.lut_signalwrapper[s] else: s = out if self.chk_grid.isChecked() is True: pass # TODO: Draw grid def create_controls(self): """ Create UI controls. """ vbox = QVBoxLayout() form = QFormLayout() self.num_angle = QDoubleSpinBox() self.num_angle.setValue(0.0) self.num_angle.setMinimum(-360) self.num_angle.setMaximum(360) form.addRow(tr("Angle:"), self.num_angle) vbox.addLayout(form) self.gbo_preview = QGroupBox(tr("Preview")) self.gbo_preview.setCheckable(True) self.gbo_preview.setChecked(False) gbo_vbox = QVBoxLayout() self.chk_grid = QCheckBox(tr("Grid")) self.chk_grid.setChecked(False) self.num_grid = QSpinBox() self.num_grid.setValue(4) self.num_grid.setMinimum(1) self.num_grid.setEnabled(False) self.chk_grid.toggled[bool].connect(self.num_grid.setEnabled) gbo_vbox.addWidget(self.chk_grid) gbo_vbox.addWidget(self.num_grid) self.gbo_preview.setLayout(gbo_vbox) vbox.addWidget(self.gbo_preview) self.gbo_preview.toggled[bool].connect(self.set_preview) self.gbo_output = QGroupBox(tr("Output")) self.opt_new = QRadioButton(tr("New signal")) self.opt_replace = QRadioButton(tr("In place")) self.opt_new.setChecked(True) gbo_vbox2 = QVBoxLayout() gbo_vbox2.addWidget(self.opt_new) gbo_vbox2.addWidget(self.opt_replace) self.gbo_output.setLayout(gbo_vbox2) vbox.addWidget(self.gbo_output) self.chk_reshape = QCheckBox(tr("Resize to fit")) self.chk_reshape.setChecked(False) vbox.addWidget(self.chk_reshape) self.btn_ok = QPushButton(tr("&OK")) self.btn_ok.setDefault(True) self.btn_ok.clicked.connect(self.accept) self.btn_cancel = QPushButton(tr("&Cancel")) self.btn_cancel.clicked.connect(self.reject) hbox = QHBoxLayout() hbox.addWidget(self.btn_ok) hbox.addWidget(self.btn_cancel) vbox.addLayout(hbox) vbox.addStretch(1) self.setLayout(vbox)
class StyleChooser(QWidget): def __init__(self, line_style_set=STYLESET_DEFAULT): QWidget.__init__(self) self._style = PlotStyle("StyleChooser internal style") self._styles = (STYLES["default"] if line_style_set not in STYLES else STYLES[line_style_set]) self.setMinimumWidth(140) self.setMaximumHeight(25) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(2) self.line_chooser = QComboBox() self.line_chooser.setToolTip("Select line style.") for style in self._styles: self.line_chooser.addItem(*style) self.marker_chooser = QComboBox() self.marker_chooser.setToolTip("Select marker style.") for marker in MARKERS: self.marker_chooser.addItem(*marker) self.thickness_spinner = QDoubleSpinBox() self.thickness_spinner.setToolTip("Line thickness") self.thickness_spinner.setMinimum(0.1) self.thickness_spinner.setDecimals(1) self.thickness_spinner.setSingleStep(0.1) self.size_spinner = QDoubleSpinBox() self.size_spinner.setToolTip("Marker size") self.size_spinner.setMinimum(0.1) self.size_spinner.setDecimals(1) self.size_spinner.setSingleStep(0.1) # the text content of the spinner varies, but shouldn't push the control # out of boundaries self.line_chooser.setMinimumWidth(110) layout.addWidget(self.line_chooser) layout.addWidget(self.thickness_spinner) layout.addWidget(self.marker_chooser) layout.addWidget(self.size_spinner) self.setLayout(layout) self.line_chooser.currentIndexChanged.connect(self._updateStyle) self.marker_chooser.currentIndexChanged.connect(self._updateStyle) self.thickness_spinner.valueChanged.connect(self._updateStyle) self.size_spinner.valueChanged.connect(self._updateStyle) self._updateLineStyleAndMarker( self._style.line_style, self._style.marker, self._style.width, self._style.size, ) self._layout = layout def getItemSizes(self): line_style_combo_width = self._layout.itemAt(0).sizeHint().width() thickness_spinner_width = self._layout.itemAt(1).sizeHint().width() marker_combo_width = self._layout.itemAt(2).sizeHint().width() size_spinner_width = self._layout.itemAt(3).sizeHint().width() return ( line_style_combo_width, thickness_spinner_width, marker_combo_width, size_spinner_width, ) def _findLineStyleIndex(self, line_style): for index, style in enumerate(self._styles): if style[1] == line_style: return index elif style[1] is None and line_style == "": return index return -1 def _findMarkerStyleIndex(self, marker): for index, style in enumerate(MARKERS): if style[1] == marker: return index elif style[1] is None and marker == "": return index return -1 def _updateLineStyleAndMarker(self, line_style, marker, thickness, size): self.line_chooser.setCurrentIndex(self._findLineStyleIndex(line_style)) self.marker_chooser.setCurrentIndex(self._findMarkerStyleIndex(marker)) self.thickness_spinner.setValue(thickness) self.size_spinner.setValue(size) def _updateStyle(self): self.marker_chooser.setEnabled( self.line_chooser.currentText() != "Area") line_style = self.line_chooser.itemData( self.line_chooser.currentIndex()) marker_style = self.marker_chooser.itemData( self.marker_chooser.currentIndex()) thickness = float(self.thickness_spinner.value()) size = float(self.size_spinner.value()) self._style.line_style = line_style self._style.marker = marker_style self._style.width = thickness self._style.size = size def setStyle(self, style): """@type style: PlotStyle""" self._style.copyStyleFrom(style) self._updateLineStyleAndMarker(style.line_style, style.marker, style.width, style.size) def getStyle(self): """@rtype: PlotStyle""" style = PlotStyle("Generated style from StyleChooser") style.copyStyleFrom(self._style) return style def createLabelLayout(self, layout=None): if layout is None: layout = QHBoxLayout() titles = ["Line style", "Width", "Marker style", "Size"] sizes = self.getItemSizes() for title, size in zip(titles, sizes): label = QLabel(title) label.setFixedWidth(size) layout.addWidget(label) return layout
class EpochDialog(QDialog): def __init__(self, parent, events): super().__init__(parent) self.setWindowTitle("Create Epochs") grid = QGridLayout(self) label = QLabel("Events:") label.setAlignment(Qt.AlignTop) grid.addWidget(label, 0, 0, 1, 1) self.events = QListWidget() self.events.insertItems(0, unique(events[:, 2]).astype(str)) self.events.setSelectionMode(QListWidget.ExtendedSelection) grid.addWidget(self.events, 0, 1, 1, 2) grid.addWidget(QLabel("Interval around events:"), 1, 0, 1, 1) self.tmin = QDoubleSpinBox() self.tmin.setMinimum(-10000) self.tmin.setValue(-0.2) self.tmin.setSingleStep(0.1) self.tmin.setAlignment(Qt.AlignRight) self.tmax = QDoubleSpinBox() self.tmax.setMinimum(-10000) self.tmax.setValue(0.5) self.tmax.setSingleStep(0.1) self.tmax.setAlignment(Qt.AlignRight) grid.addWidget(self.tmin, 1, 1, 1, 1) grid.addWidget(self.tmax, 1, 2, 1, 1) self.baseline = QCheckBox("Baseline Correction:") self.baseline.setChecked(True) self.baseline.stateChanged.connect(self.toggle_baseline) grid.addWidget(self.baseline, 2, 0, 1, 1) self.a = QDoubleSpinBox() self.a.setMinimum(-10000) self.a.setValue(-0.2) self.a.setSingleStep(0.1) self.a.setAlignment(Qt.AlignRight) self.b = QDoubleSpinBox() self.b.setMinimum(-10000) self.b.setValue(0) self.b.setSingleStep(0.1) self.b.setAlignment(Qt.AlignRight) grid.addWidget(self.a, 2, 1, 1, 1) grid.addWidget(self.b, 2, 2, 1, 1) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) grid.addWidget(self.buttonbox, 3, 0, 1, -1) self.events.itemSelectionChanged.connect(self.toggle_ok) self.toggle_ok() grid.setSizeConstraint(QGridLayout.SetFixedSize) @Slot() def toggle_ok(self): if self.events.selectedItems(): self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(True) else: self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(False) @Slot() def toggle_baseline(self): if self.baseline.isChecked(): self.a.setEnabled(True) self.b.setEnabled(True) else: self.a.setEnabled(False) self.b.setEnabled(False)
class DepthGUI(QMainWindow): def __init__(self): super(DepthGUI, self).__init__() self.display_string = None # Serial port config self.ser = serial.Serial() self.ser.baudrate = 19200 self.depth_stream = None self._prev_port = None self.setup_ui() def setup_ui(self): self.setWindowTitle('Neuroport DBS - Electrodes Depth') self.setWindowFlags(Qt.FramelessWindowHint) self.move(WINDOWDIMS_DEPTH[0], WINDOWDIMS_DEPTH[1]) self.setFixedSize(WINDOWDIMS_DEPTH[2], WINDOWDIMS_DEPTH[3]) self.show() self.plot_widget = QWidget() self.setCentralWidget(self.plot_widget) # define Qt GUI elements v_layout = QVBoxLayout() v_layout.setSpacing(0) v_layout.setContentsMargins(10, 0, 10, 10) h_layout = QHBoxLayout() self.comboBox_com_port = QComboBox() h_layout.addWidget(self.comboBox_com_port) self.pushButton_open = QPushButton("Open") h_layout.addWidget(self.pushButton_open) h_layout.addStretch() self.label_offset = QLabel("Offset: ") h_layout.addWidget(self.label_offset) self.doubleSpinBox_offset = QDoubleSpinBox() self.doubleSpinBox_offset.setMinimum(-100.00) self.doubleSpinBox_offset.setMaximum(100.00) self.doubleSpinBox_offset.setSingleStep(1.00) self.doubleSpinBox_offset.setDecimals(2) self.doubleSpinBox_offset.setValue(-10.00) self.doubleSpinBox_offset.setFixedWidth(60) h_layout.addWidget(self.doubleSpinBox_offset) h_layout.addStretch() h_layout.addWidget(QLabel("Stream to :")) self.chk_NSP = QCheckBox("NSP") self.chk_NSP.setChecked(True) h_layout.addWidget(self.chk_NSP) h_layout.addSpacing(5) self.chk_LSL = QCheckBox("LSL") self.chk_LSL.clicked.connect(self.on_chk_LSL_clicked) self.chk_LSL.click( ) # default is enabled, click call to trigger LSL stream creation. h_layout.addWidget(self.chk_LSL) h_layout.addSpacing(5) send_btn = QPushButton("Send") send_btn.clicked.connect(self.send) h_layout.addWidget(send_btn) h_layout.addSpacing(5) quit_btn = QPushButton('X') quit_btn.setMaximumWidth(20) quit_btn.clicked.connect(QApplication.instance().quit) quit_btn.setStyleSheet("QPushButton { color: white; " "background-color : red; " "border-color : red; " "border-width: 2px}") h_layout.addWidget(quit_btn) v_layout.addLayout(h_layout) # define Qt GUI elements # add a frame for the LCD numbers self.lcd_frame = QFrame() self.lcd_frame.setFrameShape(1) lcd_layout = QGridLayout() self.lcd_frame.setLayout(lcd_layout) # RAW reading from DDU self.raw_ddu = QLCDNumber() self.raw_ddu.setDigitCount(7) self.raw_ddu.setFrameShape(0) self.raw_ddu.setSmallDecimalPoint(True) self.raw_ddu.setFixedHeight(50) self.raw_ddu.display("{0:.3f}".format(0)) lcd_layout.addWidget(self.raw_ddu, 0, 3, 2, 3) self.offset_ddu = QLCDNumber() self.offset_ddu.setDigitCount(7) self.offset_ddu.setFixedHeight(150) self.offset_ddu.display("{0:.3f}".format(-10)) self.offset_ddu.setFrameShape(0) lcd_layout.addWidget(self.offset_ddu, 2, 0, 5, 6) v_layout.addWidget(self.lcd_frame) self.plot_widget.setLayout(v_layout) # Populate control panel items for port in serial.tools.list_ports.comports(): self.comboBox_com_port.addItem(port.device) self.comboBox_com_port.addItem("cbsdk playback") # Connect signals & slots self.pushButton_open.clicked.connect(self.on_open_clicked) self.comboBox_com_port.currentIndexChanged.connect( self.on_comboBox_com_port_changed) # TODO: Add signal for comboBox_com_port --> when cbsdk playback, uncheck NSP then re-open connection. def on_chk_LSL_clicked(self, state): print(f"LSL clicked state: {state}") if self.chk_LSL.isChecked(): outlet_info = pylsl.StreamInfo(name='electrode_depth', type='depth', channel_count=1, nominal_srate=pylsl.IRREGULAR_RATE, channel_format=pylsl.cf_float32, source_id='depth1214') self.depth_stream = pylsl.StreamOutlet(outlet_info) else: self.depth_stream = None def _do_close(self, from_port): if from_port == "cbsdk playback": CbSdkConnection().disconnect() else: self.ser.close() self.pushButton_open.setText("Open") def on_comboBox_com_port_changed(self, new_ix): # If a connection was already open, close it before proceeding. if self.pushButton_open.text() == "Close": self._do_close(self._prev_port) # at this point the Open/Close pushbutton should show: Open # we will only enable/disable the Send to NSP button to leave the current checked status. # The default should be checked. if self.comboBox_com_port.currentText() == "cbsdk playback": # If switching _to_ cbsdk playback, disable sending out comments. self.chk_NSP.setEnabled(False) elif self._prev_port == "cbsdk playback": # If switching _from_ cbsdk playback, re-enable sending out comments. self.chk_NSP.setEnabled(True) self._prev_port = self.comboBox_com_port.currentText() def on_open_clicked(self): com_port = self.comboBox_com_port.currentText() cmd_text = self.pushButton_open.text() if cmd_text == 'Open': if com_port == "cbsdk playback": CbSdkConnection().connect() CbSdkConnection().cbsdk_config = { 'reset': True, 'get_events': False, 'get_comments': True, 'buffer_parameter': { 'comment_length': 10 } } self.pushButton_open.setText("Close") else: if self.chk_NSP.isEnabled() and self.chk_NSP.isChecked(): CbSdkConnection().connect() CbSdkConnection().cbsdk_config = { 'reset': True, 'get_events': False, 'get_comments': False } if not self.ser.is_open: self.ser.port = com_port try: self.ser.open() # TODO: Add timeout; Add error. self.ser.write('AXON+\r'.encode()) except serial.serialutil.SerialException: print("Could not open serial port") finally: self.pushButton_open.setText( "Close" if self.ser.is_open else "Open") else: self._do_close(com_port) def update(self): # Added new_value handling for playback if we ever want to post-process depth # on previously recorded sessions. new_value = False out_value = None if self.comboBox_com_port.currentText() == "cbsdk playback": cbsdk_conn = CbSdkConnection() if cbsdk_conn.is_connected: comments = cbsdk_conn.get_comments() if comments: comment_strings = [x[1].decode('utf8') for x in comments] else: comment_strings = "" dtts = [] for comm_str in comment_strings: if 'DTT:' in comm_str: dtts.append(float(comm_str[4:])) if len(dtts) > 0: out_value = dtts[-1] new_value = True self.offset_ddu.display("{0:.3f}".format(out_value)) offset = self.doubleSpinBox_offset.value() self.raw_ddu.display("{0:.3f}".format(out_value - offset)) elif self.ser.is_open: in_str = self.ser.readline().decode('utf-8').strip() if in_str: try: in_value = float(in_str) # in_value /= DDUSCALEFACTOR # Uncomment this for FHC DDU V2. self.raw_ddu.display("{0:.3f}".format(in_value)) out_value = in_value + self.doubleSpinBox_offset.value() display_string = "{0:.3f}".format(out_value) self.offset_ddu.display(display_string) # Check if new value if display_string != self.display_string: new_value = True self.display_string = display_string # Push to NSP cbsdk_conn = CbSdkConnection() if cbsdk_conn.is_connected: if self.chk_NSP.isChecked() and self.chk_NSP.isEnabled( ) and new_value: cbsdk_conn.set_comments("DTT:" + display_string) else: # try connecting if not connected but button is active if self.chk_NSP.isChecked() and self.chk_NSP.isEnabled( ): cbsdk_conn.connect() cbsdk_conn.cbsdk_config = { 'reset': True, 'get_events': False, 'get_comments': False } # set button to connection status self.chk_NSP.setChecked(cbsdk_conn.is_connected) except ValueError: print("DDU result: {}".format(in_str)) # Push to LSL if self.depth_stream is not None and new_value: self.depth_stream.push_sample([out_value]) def send(self): self.display_string = None # make sure the update function runs self.update()