def setup_page(self): lblMethod = QLabel(_("Method")) self.rbAuto = QRadioButton(_("Auto")) self.rbEqual = QRadioButton(_("Equal")) self.rbFixed = QRadioButton(_("Fixed")) bgSystem = QButtonGroup() bgSystem.addButton(self.rbAuto) bgSystem.addButton(self.rbEqual) bgSystem.addButton(self.rbFixed) hbox = QHBoxLayout() hbox.addWidget(lblMethod) hbox.addWidget(self.rbAuto) hbox.addWidget(self.rbEqual) hbox.addWidget(self.rbFixed) self.layout.addLayout(hbox) text = _("Fixed ratio Y/X") self.ratio = self.create_lineedit(text, default='1.0') self.layout.addWidget(self.ratio) action = self.create_action() self.layout.addWidget(action) self.rbAuto.toggled.connect(lambda: self.set_method(self.rbAuto)) self.rbEqual.toggled.connect(lambda: self.set_method(self.rbEqual)) self.rbFixed.toggled.connect(lambda: self.set_method(self.rbFixed)) self.rbEqual.setChecked(True) self.ratio.setEnabled(False)
class LandscapeView(ViewBase): def setup_ui(self): """Initialize widgets.""" info_label = QLabel() info_label.setText("Hit enter to capture your image!") self.image_label = QLabel() self.image_label.setMinimumSize(320, 240) self.image_label.setScaledContents(True) button_layout = QVBoxLayout() button_layout.setAlignment(Qt.AlignLeft) self.style_buttons = [ QRadioButton(settings.STYLE_SHORTCUTS[i] + ". " + style.name if i < len(settings.STYLE_SHORTCUTS) else style.name) for i, style in enumerate(self.styles) ] self.style_buttons[self.styles.index( self.selected_style)].setChecked(True) self.style_button_group = QButtonGroup() for i, btn in enumerate(self.style_buttons): button_layout.addWidget(btn) self.style_button_group.addButton(btn, i) btn.clicked.connect( partial(lambda style: self.style_button_clicked(style), self.styles[i])) button_layout.addStretch(1) ctrl_layout = QHBoxLayout() if not settings.KIOSK: fullscreen_button = QPushButton('[ ]') fullscreen_button.setMaximumWidth( fullscreen_button.fontMetrics().boundingRect('[ ]').width() + 10) fullscreen_button.clicked.connect(self.toggle_fullscreen) ctrl_layout.addWidget(fullscreen_button) ctrl_layout.addStretch(1) self.size_combo = QComboBox() for s in settings.SIZES: self.size_combo.addItem(s) self.size_combo.setCurrentIndex(settings.SIZES.index(self.quality)) ctrl_layout.addWidget(self.size_combo) self.size_combo.activated[str].connect(self.quality_choice) button_layout.addLayout(ctrl_layout) sub_layout = QHBoxLayout() sub_layout.addLayout(button_layout) sub_layout.addWidget(self.image_label, 1) main_layout = QVBoxLayout() main_layout.addWidget(info_label) main_layout.addLayout(sub_layout) self.setLayout(main_layout)
def _add_toolbar_group(self, group, func, default_value): button_group = QButtonGroup(parent=self.toolbar) for element in group: icon = self.icons.get(element, None) button = QToolButton() button.setFixedSize(QSize(*self.icon_size)) button.setIcon(icon) button.setCheckable(True) if default_value is not None and element is default_value: button.setChecked(True) button.toggled.connect(DefaultFunction(func, element)) button_group.addButton(button) self.toolbar.addWidget(button)
def create_layout(self): ok_cancel_box = QHBoxLayout() ok_cancel_box.addWidget(self.apply_button) ok_cancel_box.addWidget(self.ok_button) ok_cancel_box.addWidget(self.cancel_button) grid = QGridLayout() irow = 0 grid.addWidget(self.name, irow, 0) grid.addWidget(self.name_edit, irow, 1) irow += 1 grid.addWidget(self.color, irow, 0) grid.addWidget(self.color_edit, irow, 1) irow += 1 grid.addWidget(self.opacity, irow, 0) grid.addWidget(self.opacity_edit, irow, 1) irow += 1 grid.addWidget(self.line_width, irow, 0) grid.addWidget(self.line_width_edit, irow, 1) irow += 1 grid.addWidget(self.point_size, irow, 0) grid.addWidget(self.point_size_edit, irow, 1) irow += 1 checkboxs = QButtonGroup(self) checkboxs.addButton(self.checkbox_show) checkboxs.addButton(self.checkbox_hide) vbox = QVBoxLayout() vbox.addWidget(self.table) vbox.addLayout(grid) if 0: vbox.addWidget(self.checkbox_show) vbox.addWidget(self.checkbox_hide) else: vbox1 = QVBoxLayout() vbox1.addWidget(self.checkbox_show) vbox1.addWidget(self.checkbox_hide) vbox.addLayout(vbox1) vbox.addStretch() #vbox.addWidget(self.check_apply) vbox.addLayout(ok_cancel_box) self.setLayout(vbox)
class EngineControl(QDialogButtonBox): """ RunEngine through a QComboBox Listens to the state of the RunEngine and shows the available commands for the current state. Attributes ---------- available_commands: dict Mapping of state to available RunEngine commands """ available_commands = { 'running': ['Halt', 'Pause'], 'idle': ['Start'], 'paused': ['Abort', 'Halt', 'Resume', 'Stop'] } command_flags = { 'Halt': QDialogButtonBox.DestructiveRole, 'Pause': QDialogButtonBox.HelpRole, 'Start': QDialogButtonBox.AcceptRole, 'Abort': QDialogButtonBox.RejectRole, 'Resume': QDialogButtonBox.AcceptRole, 'Stop': QDialogButtonBox.ResetRole } def __init__(self, *args, **kwargs): super(EngineControl, self).__init__(*args, **kwargs) self.buttonGroup = QButtonGroup() self._buttons = { name: QPushButton(name) for command in self.available_commands.values() for name in command } for name, button in self._buttons.items(): self.addButton(button, self.command_flags[name]) self.buttonGroup.addButton(button) @Slot('QString', 'QString') def on_state_change(self, state, old_state): for name, button in self._buttons.items(): button.setEnabled(name in self.available_commands[state])
def _setup_rgb_element(self, n_row, *, rb_check=0): """ Parameters ---------- rb_check: int The number of QRadioButton to check. Typically this would be the row number. """ combo_elements = ComboBoxNamed(name=f"{n_row}") # combo_elements.setSizeAdjustPolicy(QComboBox.AdjustToContents) # Set text color for QComboBox widget (necessary if the program is used with Dark theme) pal = combo_elements.palette() pal.setColor(QPalette.ButtonText, Qt.black) combo_elements.setPalette(pal) # Set text color for drop-down view (necessary if the program is used with Dark theme) pal = combo_elements.view().palette() pal.setColor(QPalette.Text, Qt.black) combo_elements.view().setPalette(pal) btns = [QRadioButton(), QRadioButton(), QRadioButton()] if 0 <= rb_check < len(btns): btns[rb_check].setChecked(True) # Color is set for operation with Dark theme for btn in btns: pal = btn.palette() pal.setColor(QPalette.Text, Qt.black) btn.setPalette(pal) btn_group = QButtonGroup() for btn in btns: btn_group.addButton(btn) rng = RangeManager(name=f"{n_row}", add_sliders=True) rng.setTextColor([0, 0, 0]) # Set text color to 'black' # Set some text in edit boxes (just to demonstrate how the controls will look like) rng.le_min_value.setText("0.0") rng.le_max_value.setText("1.0") rng.setAlignment(Qt.AlignCenter) return combo_elements, btns, rng, btn_group
def load_data(self, data: Tuple[BaseModuleData], msg: str, saved_item: Optional[str] = None) -> QButtonGroup: """ The provided data is converted into cards in the GUI. If the module isn't supported on the current OS, it's not added to avoid confusion. If it's not installed, it's disabled but it's still appended to the layout. """ self.init_title(msg) layout = self.init_scroll_layout() # The cards are inside a group so that their selection is exclusive. group = QButtonGroup() # The disabled APIs will always be at the end of the layout, so # they're saved in a list to add them later. disabled = [] for module in data: if not module.compatible: continue selected = saved_item is not None \ and module.id == saved_item.upper() card = ModuleCard(module, selected) if module.installed: layout.addWidget(card) else: disabled.append(card) group.addButton(card.button) logging.info("Created API card: %s (enabled=%s, selected=%s)", module.id, module.installed, selected) for card in disabled: layout.addWidget(card) return group
def _dock_add_radio_buttons(self, value, rng, callback, *, vertical=True, layout=None): layout = self._dock_layout if layout is None else layout group_layout = QVBoxLayout() if vertical else QHBoxLayout() group = QButtonGroup() for val in rng: button = QRadioButton(val) if val == value: button.setChecked(True) group.addButton(button) self._layout_add_widget(group_layout, button) def func(button): callback(button.text()) group.buttonClicked.connect(func) self._layout_add_widget(layout, group_layout) return _QtWidgetList(group)
def create_control_panel(self): # Create control panel # +/- range cntrl_layout = QHBoxLayout() cntrl_layout.addWidget(QLabel("+/-")) self.range_edit = QLineEdit("{:.2f}".format(YRANGE)) self.range_edit.editingFinished.connect( self.on_range_edit_editingFinished) cntrl_layout.addWidget(self.range_edit) # buttons for audio monitoring cntrl_layout.addStretch(1) cntrl_layout.addWidget(QLabel("Monitor: ")) monitor_group = QButtonGroup(parent=self) none_button = QRadioButton("None") none_button.setChecked(True) monitor_group.addButton(none_button) monitor_group.setId(none_button, 0) cntrl_layout.addWidget(none_button) for chan_ix in range(len(self.group_info)): new_button = QRadioButton(self.group_info[chan_ix]['label']) monitor_group.addButton(new_button) monitor_group.setId(new_button, chan_ix + 1) cntrl_layout.addWidget(new_button) monitor_group.buttonClicked[int].connect(self.on_monitor_group_clicked) # Checkbox for HP filter filter_checkbox = QCheckBox("HP") filter_checkbox.stateChanged.connect(self.on_hp_filter_changed) filter_checkbox.setChecked(True) cntrl_layout.addWidget(filter_checkbox) # Checkbox for Comb filter filter_checkbox = QCheckBox("LN") filter_checkbox.setEnabled(False) filter_checkbox.stateChanged.connect(self.on_ln_filter_changed) filter_checkbox.setChecked(False) cntrl_layout.addWidget(filter_checkbox) # Finish self.layout().addLayout(cntrl_layout)
class QtLabelsControls(QtLayerControls): def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self.set_mode) self.panzoom_button = QtModeButton( layer, 'zoom', Mode.PAN_ZOOM, 'Pan/zoom mode' ) self.pick_button = QtModeButton( layer, 'picker', Mode.PICKER, 'Pick mode' ) self.paint_button = QtModeButton( layer, 'paint', Mode.PAINT, 'Paint mode' ) self.fill_button = QtModeButton(layer, 'fill', Mode.FILL, 'Fill mode') self.button_group = QButtonGroup(self) self.button_group.addButton(self.panzoom_button) self.button_group.addButton(self.paint_button) self.button_group.addButton(self.pick_button) self.button_group.addButton(self.fill_button) layout = QVBoxLayout() layout.setContentsMargins(12, 20, 10, 10) layout.addWidget(self.panzoom_button) layout.addWidget(self.paint_button) layout.addWidget(self.pick_button) layout.addWidget(self.fill_button) layout.addWidget(QtColorBox(layer)) layout.addStretch(0) self.setLayout(layout) self.setMouseTracking(True) self.panzoom_button.setChecked(True) def mouseMoveEvent(self, event): self.layer.status = str(self.layer.mode) def set_mode(self, event): mode = event.mode if mode == Mode.PAN_ZOOM: self.panzoom_button.setChecked(True) elif mode == Mode.PICKER: self.pick_button.setChecked(True) elif mode == Mode.PAINT: self.paint_button.setChecked(True) elif mode == Mode.FILL: self.fill_button.setChecked(True) else: raise ValueError("Mode not recongnized")
class QtPointsControls(QtLayerControls): def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self.set_mode) self.select_button = QtSelectButton(layer) self.addition_button = QtAdditionButton(layer) self.panzoom_button = QtPanZoomButton(layer) self.delete_button = QtDeletePointsButton(layer) self.button_group = QButtonGroup(self) self.button_group.addButton(self.select_button) self.button_group.addButton(self.addition_button) self.button_group.addButton(self.panzoom_button) layout = QVBoxLayout() layout.setContentsMargins(12, 20, 10, 10) layout.addWidget(self.panzoom_button) layout.addWidget(self.select_button) layout.addWidget(self.addition_button) layout.addWidget(self.delete_button) layout.addStretch(0) self.setLayout(layout) self.setMouseTracking(True) def mouseMoveEvent(self, event): self.layer.status = self.layer.mode def set_mode(self, event): mode = event.mode if mode == Mode.ADD: self.addition_button.setChecked(True) elif mode == Mode.SELECT: self.select_button.setChecked(True) elif mode == Mode.PAN_ZOOM: self.panzoom_button.setChecked(True) else: raise ValueError("Mode not recongnized")
class QtMarkersControls(QFrame): def __init__(self, layer): super().__init__() self.layer = layer self.layer.events.mode.connect(self.set_mode) self.select_button = QtSelectButton(layer) self.addition_button = QtAdditionButton(layer) self.panzoom_button = QtPanZoomButton(layer) self.button_group = QButtonGroup(self) self.button_group.addButton(self.select_button) self.button_group.addButton(self.addition_button) self.button_group.addButton(self.panzoom_button) layout = QVBoxLayout() layout.setContentsMargins(12, 20, 10, 10) layout.addWidget(self.panzoom_button) layout.addWidget(self.select_button) layout.addWidget(self.addition_button) layout.addStretch(0) self.setLayout(layout) self.setMouseTracking(True) def mouseMoveEvent(self, event): self.layer.status = self.layer.mode def set_mode(self, event): mode = event.mode if mode == 'add': self.addition_button.setChecked(True) elif mode == 'select': self.select_button.setChecked(True) elif mode == 'pan/zoom': self.panzoom_button.setChecked(True) else: raise ValueError("Mode not recongnized")
class QtLabelsControls(QtLayerControls): def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self._on_mode_change) self.layer.events.selected_label.connect(self._on_selection_change) self.layer.events.brush_size.connect(self._on_brush_size_change) self.layer.events.contiguous.connect(self._on_contig_change) self.layer.events.n_dimensional.connect(self._on_n_dim_change) self.layer.events.editable.connect(self._on_editable_change) # shuffle colormap button self.colormapUpdate = QPushButton('shuffle colors') self.colormapUpdate.setObjectName('shuffle') self.colormapUpdate.clicked.connect(self.changeColor) self.colormapUpdate.setFixedHeight(28) # selection spinbox self.selectionSpinBox = QSpinBox() self.selectionSpinBox.setKeyboardTracking(False) self.selectionSpinBox.setSingleStep(1) self.selectionSpinBox.setMinimum(0) self.selectionSpinBox.setMaximum(2147483647) self.selectionSpinBox.valueChanged.connect(self.changeSelection) self._on_selection_change(None) sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(1) sld.setMaximum(40) sld.setSingleStep(1) sld.valueChanged[int].connect(lambda value=sld: self.changeSize(value)) self.brushSizeSlider = sld self._on_brush_size_change(None) contig_cb = QCheckBox() contig_cb.setToolTip('contiguous editing') contig_cb.stateChanged.connect( lambda state=contig_cb: self.change_contig(state)) self.contigCheckBox = contig_cb self._on_contig_change(None) ndim_cb = QCheckBox() ndim_cb.setToolTip('n-dimensional editing') ndim_cb.stateChanged.connect( lambda state=ndim_cb: self.change_ndim(state)) self.ndimCheckBox = ndim_cb self._on_n_dim_change(None) self.panzoom_button = QtModeButton(layer, 'zoom', Mode.PAN_ZOOM, 'Pan/zoom mode') self.pick_button = QtModeButton(layer, 'picker', Mode.PICKER, 'Pick mode') self.paint_button = QtModeButton(layer, 'paint', Mode.PAINT, 'Paint mode') self.fill_button = QtModeButton(layer, 'fill', Mode.FILL, 'Fill mode') self.button_group = QButtonGroup(self) self.button_group.addButton(self.panzoom_button) self.button_group.addButton(self.paint_button) self.button_group.addButton(self.pick_button) self.button_group.addButton(self.fill_button) self.panzoom_button.setChecked(True) self._on_editable_change(None) button_row = QHBoxLayout() button_row.addWidget(self.pick_button) button_row.addWidget(self.fill_button) button_row.addWidget(self.paint_button) button_row.addWidget(self.panzoom_button) button_row.addStretch(1) button_row.setSpacing(4) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_row, 0, 1, 1, 2) self.grid_layout.addWidget(self.colormapUpdate, 0, 0) self.grid_layout.addWidget(QLabel('label:'), 1, 0) self.grid_layout.addWidget(self.selectionSpinBox, 1, 2) self.grid_layout.addWidget(QtColorBox(layer), 1, 1) self.grid_layout.addWidget(QLabel('opacity:'), 2, 0) self.grid_layout.addWidget(self.opacitySilder, 2, 1, 1, 2) self.grid_layout.addWidget(QLabel('brush size:'), 3, 0) self.grid_layout.addWidget(self.brushSizeSlider, 3, 1, 1, 2) self.grid_layout.addWidget(QLabel('blending:'), 4, 0) self.grid_layout.addWidget(self.blendComboBox, 4, 1, 1, 2) self.grid_layout.addWidget(QLabel('contiguous:'), 5, 0) self.grid_layout.addWidget(self.contigCheckBox, 5, 1) self.grid_layout.addWidget(QLabel('n-dim:'), 6, 0) self.grid_layout.addWidget(self.ndimCheckBox, 6, 1) self.grid_layout.setRowStretch(7, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def mouseMoveEvent(self, event): self.layer.status = str(self.layer.mode) def _on_mode_change(self, event): mode = event.mode if mode == Mode.PAN_ZOOM: self.panzoom_button.setChecked(True) elif mode == Mode.PICKER: self.pick_button.setChecked(True) elif mode == Mode.PAINT: self.paint_button.setChecked(True) elif mode == Mode.FILL: self.fill_button.setChecked(True) else: raise ValueError("Mode not recongnized") def changeColor(self): self.layer.new_colormap() def changeSelection(self, value): self.layer.selected_label = value self.selectionSpinBox.clearFocus() self.setFocus() def changeSize(self, value): self.layer.brush_size = value def change_contig(self, state): if state == Qt.Checked: self.layer.contiguous = True else: self.layer.contiguous = False def change_ndim(self, state): if state == Qt.Checked: self.layer.n_dimensional = True else: self.layer.n_dimensional = False def _on_selection_change(self, event): with self.layer.events.selected_label.blocker(): value = self.layer.selected_label self.selectionSpinBox.setValue(int(value)) def _on_brush_size_change(self, event): with self.layer.events.brush_size.blocker(): value = self.layer.brush_size value = np.clip(int(value), 1, 40) self.brushSizeSlider.setValue(value) def _on_n_dim_change(self, event): with self.layer.events.n_dimensional.blocker(): self.ndimCheckBox.setChecked(self.layer.n_dimensional) def _on_contig_change(self, event): with self.layer.events.contiguous.blocker(): self.contigCheckBox.setChecked(self.layer.contiguous) def _on_editable_change(self, event): self.pick_button.setEnabled(self.layer.editable) self.paint_button.setEnabled(self.layer.editable) self.fill_button.setEnabled(self.layer.editable)
class PeriodicTableWidget(QWidget): selectionChanged = Signal() def __init__(self, parent=None): QWidget.__init__(self, parent) # Widgets, layouts and signals self._group = QButtonGroup() layout = QGridLayout() layout.setSpacing(0) ## Element for z, position in _ELEMENT_POSITIONS.items(): widget = ElementPushButton(z) widget.setCheckable(True) layout.addWidget(widget, *position) self._group.addButton(widget, z) ## Labels layout.addWidget(QLabel(''), 7, 0) # Dummy layout.addWidget(QLabel('*'), 5, 2, Qt.AlignRight) layout.addWidget(QLabel('*'), 8, 2, Qt.AlignRight) layout.addWidget(QLabel('**'), 6, 2, Qt.AlignRight) layout.addWidget(QLabel('**'), 9, 2, Qt.AlignRight) for row in [0, 1, 2, 3, 4, 5, 6, 8, 9]: layout.setRowStretch(row, 1) self.setLayout(layout) # Signals self._group.buttonClicked.connect(self.selectionChanged) # Default self.setColorFunction(_category_color_function) def setColorFunction(self, func): if not callable(func): raise ValueError('Not a function') self._color_function = func # Redraw for widget in self._group.buttons(): z = self._group.id(widget) bcolor = func(z) fcolor = 'white' if _calculate_brightness(bcolor) < 128 else 'black' sheet = 'background-color: %s; color: %s' % (bcolor.name(), fcolor) widget.setStyleSheet(sheet) def colorFunction(self): return self._color_function def setMultipleSelection(self, multiple): self._group.setExclusive(not multiple) def isMultipleSelection(self): return not self._group.exclusive() def setSelection(self, selection): def _uncheckedAll(): for widget in self._group.buttons(): widget.setChecked(False) if selection is None: _uncheckedAll() self.selectionChanged.emit() return if isinstance(selection, (int, six.string_types)): selection = [selection] if not self.isMultipleSelection() and len(selection) > 1: raise ValueError('Multiple selection mode is off. Cannot select more than one element') _uncheckedAll() for z in selection: if isinstance(z, six.string_types): z = get_atomic_number(z) self._group.button(z).setChecked(True) self.selectionChanged.emit() # def selection(self): selection = set() for widget in self._group.buttons(): if widget.isChecked(): selection.add(self._group.id(widget)) if self.isMultipleSelection(): return frozenset(selection) else: if len(selection) > 0: return list(selection)[0] else: return None def selectionSymbol(self): selection = self.selection() if self.isMultipleSelection(): return frozenset(map(get_symbol, selection)) else: if selection is None: return None else: return get_symbol(selection)
def initialize_content(self): # Validators self._summary.detector_offset_edit.setValidator(QDoubleValidator(self._summary.detector_offset_edit)) self._summary.sample_dist_edit.setValidator(QDoubleValidator(self._summary.sample_dist_edit)) self._summary.n_q_bins_edit.setValidator(QIntValidator(self._summary.n_q_bins_edit)) # Event connections self._summary.detector_offset_chk.clicked.connect(self._det_offset_clicked) self._summary.sample_dist_chk.clicked.connect(self._sample_dist_clicked) self._summary.help_button.clicked.connect(self._show_help) self._summary.dark_current_check.clicked.connect(self._dark_clicked) self._summary.dark_browse_button.clicked.connect(self._dark_browse) self._summary.dark_plot_button.clicked.connect(self._dark_plot_clicked) # Output directory g2 = QButtonGroup(self) g2.addButton(self._summary.select_output_dir_radio) g2.addButton(self._summary.use_data_dir_radio) g2.setExclusive(True) self._summary.select_output_dir_radio.clicked.connect(self._output_dir_clicked) self._summary.use_data_dir_radio.clicked.connect(self._output_dir_clicked) self._summary.output_dir_browse_button.clicked.connect(self._output_dir_browse) self._output_dir_clicked() # Lin/log option g3 = QButtonGroup(self) g3.addButton(self._summary.log_binning_radio) g3.addButton(self._summary.lin_binning_radio) g3.setExclusive(True) # Q range self._summary.n_q_bins_edit.setText("100") self._summary.scale_edit.setText("1") self._summary.instr_name_label.hide() self._dark_clicked(self._summary.dark_current_check.isChecked()) # Mask Connections self._summary.mask_browse_button.clicked.connect(self._mask_browse_clicked) self._summary.mask_plot_button.clicked.connect(self._mask_plot_clicked) self._summary.mask_check.clicked.connect(self._mask_checked) # Absolute scale connections and validators self._summary.scale_edit.setValidator(QDoubleValidator(self._summary.scale_edit)) self._summary.scale_beam_radius_edit.setValidator(QDoubleValidator(self._summary.scale_beam_radius_edit)) self._summary.scale_att_trans_edit.setValidator(QDoubleValidator(self._summary.scale_att_trans_edit)) self._summary.scale_data_browse_button.clicked.connect(self._scale_data_browse) self._summary.scale_data_plot_button.clicked.connect(self._scale_data_plot_clicked) self._summary.beamstop_chk.clicked.connect(self._beamstop_clicked) self._summary.scale_chk.clicked.connect(self._scale_clicked) self._scale_clicked(self._summary.scale_chk.isChecked()) # TOF cut validator self._summary.low_tof_edit.setValidator(QDoubleValidator(self._summary.low_tof_edit)) self._summary.high_tof_edit.setValidator(QDoubleValidator(self._summary.high_tof_edit)) # TOF connections self._summary.tof_cut_chk.clicked.connect(self._tof_clicked) # Monitor normalization self._summary.beam_monitor_chk.clicked.connect(self._beam_monitor_clicked) self._summary.beam_monitor_browse_button.clicked.connect(self._beam_monitor_reference_browse) # Resolution validator self._summary.sample_apert_edit.setValidator(QDoubleValidator(self._summary.sample_apert_edit)) self._summary.resolution_chk.clicked.connect(self._resolution_clicked) # Since EQSANS does not currently use the absolute scale calculation, expose it in debug mode only for now if not self._settings.debug: self._summary.config_options_layout.deleteLater() self._summary.abs_scale_options_layout.deleteLater() self._summary.abs_scale_direct_beam_layout.deleteLater() self._summary.monitor_layout.deleteLater() self._summary.direct_beam_label.hide() self._summary.att_trans_label.hide() self._summary.beamstop_chk.hide() self._summary.scale_data_edit.hide() self._summary.scale_data_plot_button.hide() self._summary.scale_data_browse_button.hide() self._summary.scale_att_trans_edit.hide() self._summary.scale_beam_radius_edit.hide() self._summary.scale_chk.hide() self._summary.beam_monitor_chk.hide() self._summary.tof_correction_chk.hide() self._summary.beam_monitor_edit.hide() self._summary.beam_monitor_browse_button.hide() # Same thing for sample-detector distance and offset: not yet hooked in self._summary.geometry_options_groupbox.hide() # Hide expert options #self._summary.config_mask_chk.hide() self._summary.tof_cut_chk.hide() self._summary.low_tof_edit.hide() self._summary.high_tof_edit.hide() self._summary.low_tof_label.hide() self._summary.high_tof_label.hide() if not self._settings.advanced: self._summary.att_scale_factor_label.hide() self._summary.scale_edit.hide() self._summary.mask_groupbox.hide() self._summary.solid_angle_chk.hide() self._summary.resolution_chk.hide() self._summary.sample_apert_edit.hide() self._summary.sample_apert_label.hide() # We need the EQSANS data proxy for a quick load of a file for masking purposes, but # we don't want to show the plot button. Turn this off for the moment. if True or not self._has_instrument_view: self._summary.dark_plot_button.hide() self._summary.scale_data_plot_button.hide()
def create_layout(self): cancel_box = QHBoxLayout() cancel_box.addWidget(self.close_button) grid1 = QGridLayout() grid2 = QGridLayout() #----------------------------------------- # setup self.radio_rectangular = QRadioButton('Rectangular') self.radio_cylindrical = QRadioButton('Cylindrical') self.radio_spherical = QRadioButton('Spherical') coord_type_layout = QHBoxLayout() coord_type_layout.addWidget(self.radio_rectangular) coord_type_layout.addWidget(self.radio_cylindrical) coord_type_layout.addWidget(self.radio_spherical) location_layout = QHBoxLayout() location_layout.addWidget(self.location_x_edit) location_layout.addWidget(self.location_y_edit) location_layout.addWidget(self.location_z_edit) checkboxs = QButtonGroup(self) checkboxs.addButton(self.checkbox_show) checkboxs.addButton(self.checkbox_hide) vbox1 = QVBoxLayout() vbox1.addWidget(self.checkbox_show) vbox1.addWidget(self.checkbox_hide) #vbox1.addLayout(checkboxs) #----------------------------------------- irow = 0 grid1.addWidget(self.all_nodes_header, irow, 0) irow += 1 grid1.addWidget(self.color, irow, 0) grid1.addWidget(self.color_edit, irow, 1) irow += 1 grid1.addWidget(self.opacity, irow, 0) grid1.addWidget(self.opacity_edit, irow, 1) irow += 1 grid1.addWidget(self.point_size, irow, 0) grid1.addWidget(self.point_size_edit, irow, 1) irow += 1 #----------------------------------------- irow = 0 grid2.addWidget(self.nodes_header, irow, 0) irow += 1 grid2.addWidget(self.name, irow, 0) grid2.addWidget(self.name_edit, irow, 1) irow += 1 grid2.addWidget(self.description, irow, 0) grid2.addWidget(self.description_edit, irow, 1) irow += 1 #| All Nodes: | #| Color red | #| PointSize 3 | #| Opacity 0.3 | #| Show/Hide | #| | #| Name LEwingTip | #| Location X Y Z | #| Coord 0 | #| CoordType R, C, S | #| | #| Previous Next | grid2.addWidget(self.coord, irow, 0) grid2.addWidget(self.coord_edit, irow, 1) irow += 1 grid2.addWidget(self.coord_type, irow, 0) grid2.addLayout(coord_type_layout, irow, 1) irow += 1 grid2.addWidget(self.location, irow, 0) grid2.addLayout(location_layout, irow, 1) irow += 1 #------------------------------------ vbox = QVBoxLayout() vbox.addLayout(grid1) vbox.addLayout(vbox1) vbox.addStretch() vbox.addWidget(self.table) vbox.addLayout(grid2) vbox.addStretch() #vbox.addWidget(self.check_apply) vbox.addLayout(cancel_box) self.setLayout(vbox)
class ObjectExplorer(BaseDialog, SpyderConfigurationAccessor): """Object explorer main widget window.""" CONF_SECTION = 'variable_explorer' def __init__(self, obj, name='', expanded=False, resize_to_contents=True, parent=None, attribute_columns=DEFAULT_ATTR_COLS, attribute_details=DEFAULT_ATTR_DETAILS, readonly=None, reset=False): """ Constructor :param name: name of the object as it will appear in the root node :param expanded: show the first visible root element expanded :param resize_to_contents: resize columns to contents ignoring width of the attributes :param obj: any Python object or variable :param attribute_columns: list of AttributeColumn objects that define which columns are present in the table and their defaults :param attribute_details: list of AttributeDetails objects that define which attributes can be selected in the details pane. :param reset: If true the persistent settings, such as column widths, are reset. """ QDialog.__init__(self, parent=parent) self.setAttribute(Qt.WA_DeleteOnClose) # Options show_callable_attributes = self.get_conf('show_callable_attributes') show_special_attributes = self.get_conf('show_special_attributes') # Model self._attr_cols = attribute_columns self._attr_details = attribute_details self.readonly = readonly self.btn_save_and_close = None self.btn_close = None self._tree_model = TreeModel(obj, obj_name=name, attr_cols=self._attr_cols) self._proxy_tree_model = TreeProxyModel( show_callable_attributes=show_callable_attributes, show_special_attributes=show_special_attributes) self._proxy_tree_model.setSourceModel(self._tree_model) # self._proxy_tree_model.setSortRole(RegistryTableModel.SORT_ROLE) self._proxy_tree_model.setDynamicSortFilter(True) # self._proxy_tree_model.setSortCaseSensitivity(Qt.CaseInsensitive) # Tree widget self.obj_tree = ToggleColumnTreeView() self.obj_tree.setAlternatingRowColors(True) self.obj_tree.setModel(self._proxy_tree_model) self.obj_tree.setSelectionBehavior(QAbstractItemView.SelectRows) self.obj_tree.setUniformRowHeights(True) self.obj_tree.add_header_context_menu() # Views self._setup_actions() self._setup_menu(show_callable_attributes=show_callable_attributes, show_special_attributes=show_special_attributes) self._setup_views() if name: name = "{} -".format(name) self.setWindowTitle("{} {}".format(name, EDITOR_NAME)) self.setWindowFlags(Qt.Window) self._resize_to_contents = resize_to_contents self._readViewSettings(reset=reset) # Update views with model self.toggle_show_special_attribute_action.setChecked( show_special_attributes) self.toggle_show_callable_action.setChecked(show_callable_attributes) # Select first row so that a hidden root node will not be selected. first_row_index = self._proxy_tree_model.firstItemIndex() self.obj_tree.setCurrentIndex(first_row_index) if self._tree_model.inspectedNodeIsVisible or expanded: self.obj_tree.expand(first_row_index) def get_value(self): """Get editor current object state.""" return self._tree_model.inspectedItem.obj def _make_show_column_function(self, column_idx): """Creates a function that shows or hides a column.""" show_column = lambda checked: self.obj_tree.setColumnHidden( column_idx, not checked) return show_column def _setup_actions(self): """Creates the main window actions.""" # Show/hide callable objects self.toggle_show_callable_action = QAction( _("Show callable attributes"), self, checkable=True, shortcut=QKeySequence("Alt+C"), statusTip=_("Shows/hides attributes that are callable " "(functions, methods, etc)")) self.toggle_show_callable_action.toggled.connect( self._proxy_tree_model.setShowCallables) self.toggle_show_callable_action.toggled.connect( self.obj_tree.resize_columns_to_contents) # Show/hide special attributes self.toggle_show_special_attribute_action = QAction( _("Show __special__ attributes"), self, checkable=True, shortcut=QKeySequence("Alt+S"), statusTip=_("Shows or hides __special__ attributes")) self.toggle_show_special_attribute_action.toggled.connect( self._proxy_tree_model.setShowSpecialAttributes) self.toggle_show_special_attribute_action.toggled.connect( self.obj_tree.resize_columns_to_contents) def _setup_menu(self, show_callable_attributes=False, show_special_attributes=False): """Sets up the main menu.""" self.tools_layout = QHBoxLayout() callable_attributes = create_toolbutton( self, text=_("Show callable attributes"), icon=ima.icon("class"), toggled=self._toggle_show_callable_attributes_action) callable_attributes.setCheckable(True) callable_attributes.setChecked(show_callable_attributes) self.tools_layout.addWidget(callable_attributes) special_attributes = create_toolbutton( self, text=_("Show __special__ attributes"), icon=ima.icon("private2"), toggled=self._toggle_show_special_attributes_action) special_attributes.setCheckable(True) special_attributes.setChecked(show_special_attributes) self.tools_layout.addWidget(special_attributes) self.tools_layout.addStretch() self.options_button = create_toolbutton(self, text=_('Options'), icon=ima.icon('tooloptions')) self.options_button.setPopupMode(QToolButton.InstantPopup) self.show_cols_submenu = QMenu(self) self.options_button.setMenu(self.show_cols_submenu) # Don't show menu arrow and remove padding if is_dark_interface(): self.options_button.setStyleSheet( ("QToolButton::menu-indicator{image: none;}\n" "QToolButton{padding: 3px;}")) else: self.options_button.setStyleSheet( "QToolButton::menu-indicator{image: none;}") self.tools_layout.addWidget(self.options_button) @Slot() def _toggle_show_callable_attributes_action(self): """Toggle show callable atributes action.""" action_checked = not self.toggle_show_callable_action.isChecked() self.toggle_show_callable_action.setChecked(action_checked) self.set_conf('show_callable_attributes', action_checked) @Slot() def _toggle_show_special_attributes_action(self): """Toggle show special attributes action.""" action_checked = ( not self.toggle_show_special_attribute_action.isChecked()) self.toggle_show_special_attribute_action.setChecked(action_checked) self.set_conf('show_special_attributes', action_checked) def _setup_views(self): """Creates the UI widgets.""" self.central_splitter = QSplitter(self, orientation=Qt.Vertical) layout = create_plugin_layout(self.tools_layout, self.central_splitter) self.setLayout(layout) # Stretch last column? # It doesn't play nice when columns are hidden and then shown again. obj_tree_header = self.obj_tree.header() obj_tree_header.setSectionsMovable(True) obj_tree_header.setStretchLastSection(False) add_actions(self.show_cols_submenu, self.obj_tree.toggle_column_actions_group.actions()) self.central_splitter.addWidget(self.obj_tree) # Bottom pane bottom_pane_widget = QWidget() bottom_layout = QHBoxLayout() bottom_layout.setSpacing(0) bottom_layout.setContentsMargins(5, 5, 5, 5) # left top right bottom bottom_pane_widget.setLayout(bottom_layout) self.central_splitter.addWidget(bottom_pane_widget) group_box = QGroupBox(_("Details")) bottom_layout.addWidget(group_box) v_group_layout = QVBoxLayout() h_group_layout = QHBoxLayout() h_group_layout.setContentsMargins(2, 2, 2, 2) # left top right bottom group_box.setLayout(v_group_layout) v_group_layout.addLayout(h_group_layout) # Radio buttons radio_widget = QWidget() radio_layout = QVBoxLayout() radio_layout.setContentsMargins(0, 0, 0, 0) # left top right bottom radio_widget.setLayout(radio_layout) self.button_group = QButtonGroup(self) for button_id, attr_detail in enumerate(self._attr_details): radio_button = QRadioButton(attr_detail.name) radio_layout.addWidget(radio_button) self.button_group.addButton(radio_button, button_id) self.button_group.buttonClicked[int].connect( self._change_details_field) self.button_group.button(0).setChecked(True) radio_layout.addStretch(1) h_group_layout.addWidget(radio_widget) # Editor widget self.editor = SimpleCodeEditor(self) self.editor.setReadOnly(True) h_group_layout.addWidget(self.editor) # Save and close buttons btn_layout = QHBoxLayout() btn_layout.addStretch() if not self.readonly: self.btn_save_and_close = QPushButton(_('Save and Close')) self.btn_save_and_close.setDisabled(True) self.btn_save_and_close.clicked.connect(self.accept) btn_layout.addWidget(self.btn_save_and_close) self.btn_close = QPushButton(_('Close')) self.btn_close.setAutoDefault(True) self.btn_close.setDefault(True) self.btn_close.clicked.connect(self.reject) btn_layout.addWidget(self.btn_close) v_group_layout.addLayout(btn_layout) # Splitter parameters self.central_splitter.setCollapsible(0, False) self.central_splitter.setCollapsible(1, True) self.central_splitter.setSizes([500, 320]) # Connect signals # Keep a temporary reference of the selection_model to prevent # segfault in PySide. # See http://permalink.gmane.org/gmane.comp.lib.qt.pyside.devel/222 selection_model = self.obj_tree.selectionModel() selection_model.currentChanged.connect(self._update_details) # Check if the values of the model have been changed self._proxy_tree_model.sig_setting_data.connect( self.save_and_close_enable) self._proxy_tree_model.sig_update_details.connect( self._update_details_for_item) # End of setup_methods def _readViewSettings(self, reset=False): """ Reads the persistent program settings. :param reset: If True, the program resets to its default settings. """ pos = QPoint(20, 20) window_size = QSize(825, 650) details_button_idx = 0 header = self.obj_tree.header() header_restored = False if reset: logger.debug("Resetting persistent view settings") else: pos = pos window_size = window_size details_button_idx = details_button_idx # splitter_state = settings.value("central_splitter/state") splitter_state = None if splitter_state: self.central_splitter.restoreState(splitter_state) # header_restored = self.obj_tree.read_view_settings( # 'table/header_state', # settings, reset) header_restored = False if not header_restored: column_sizes = [col.width for col in self._attr_cols] column_visible = [col.col_visible for col in self._attr_cols] for idx, size in enumerate(column_sizes): if not self._resize_to_contents and size > 0: # Just in case header.resizeSection(idx, size) else: header.resizeSections(QHeaderView.ResizeToContents) break for idx, visible in enumerate(column_visible): elem = self.obj_tree.toggle_column_actions_group.actions()[idx] elem.setChecked(visible) self.resize(window_size) button = self.button_group.button(details_button_idx) if button is not None: button.setChecked(True) @Slot() def save_and_close_enable(self): """Handle the data change event to enable the save and close button.""" if self.btn_save_and_close: self.btn_save_and_close.setEnabled(True) self.btn_save_and_close.setAutoDefault(True) self.btn_save_and_close.setDefault(True) @Slot(QModelIndex, QModelIndex) def _update_details(self, current_index, _previous_index): """Shows the object details in the editor given an index.""" tree_item = self._proxy_tree_model.treeItem(current_index) self._update_details_for_item(tree_item) def _change_details_field(self, _button_id=None): """Changes the field that is displayed in the details pane.""" # logger.debug("_change_details_field: {}".format(_button_id)) current_index = self.obj_tree.selectionModel().currentIndex() tree_item = self._proxy_tree_model.treeItem(current_index) self._update_details_for_item(tree_item) @Slot(TreeItem) def _update_details_for_item(self, tree_item): """Shows the object details in the editor given an tree_item.""" try: # obj = tree_item.obj button_id = self.button_group.checkedId() assert button_id >= 0, ("No radio button selected. " "Please report this bug.") attr_details = self._attr_details[button_id] data = attr_details.data_fn(tree_item) self.editor.setPlainText(data) self.editor.setWordWrapMode(attr_details.line_wrap) self.editor.setup_editor( font=get_font(font_size_delta=DEFAULT_SMALL_DELTA), show_blanks=False, color_scheme=CONF.get('appearance', 'selected'), scroll_past_end=False, ) self.editor.set_text(data) if attr_details.name == 'Source code': self.editor.set_language('Python') else: self.editor.set_language('Rst') except Exception as ex: self.editor.setStyleSheet("color: red;") stack_trace = traceback.format_exc() self.editor.setPlainText("{}\n\n{}".format(ex, stack_trace)) self.editor.setWordWrapMode( QTextOption.WrapAtWordBoundaryOrAnywhere) @classmethod def create_explorer(cls, *args, **kwargs): """ Creates and shows and ObjectExplorer window. The *args and **kwargs will be passed to the ObjectExplorer constructor A (class attribute) reference to the browser window is kept to prevent it from being garbage-collected. """ object_explorer = cls(*args, **kwargs) object_explorer.exec_() return object_explorer
class StackedCanvasView(CanvasView): """ View that can display intents in their corresponding canvases. Currently, uses a stacked widget with several pages: one for tabview and others for different split views. Can be adapted as long as its internal widgets are CanvasDisplayWidgets. """ def __init__(self, parent=None, model=None): super(StackedCanvasView, self).__init__(parent) if model is not None: self.setModel(model) self.canvas_display_widgets = [ CanvasDisplayTabWidget(), SplitHorizontal(), SplitVertical(), SplitThreeView(), SplitGridView() ] ### Create stacked widget and fill pages with different canvas display widgets self.stackedwidget = QStackedWidget(self) # Create a visual layout section for the buttons that are used to switch the widgets self.buttonpanel = QHBoxLayout() self.buttonpanel.addStretch(10) # Create a logical button grouping that will: # - show the currently selected view (button will be checked/pressed) # - allow for switching the buttons/views in a mutually exclusive manner (only one can be pressed at a time) self.buttongroup = QButtonGroup() def add_canvas_display_widgets(): for i in range(len(self.canvas_display_widgets)): # Add the view to the stacked widget self.stackedwidget.addWidget(self.canvas_display_widgets[i]) # Create a button, using the view's recommended display icon button = QPushButton(self) button.setCheckable(True) button.setIcon(self.canvas_display_widgets[i].icon) # Add the button to the logical button group self.buttongroup.addButton(button, i) # Add the button to the visual layout section self.buttonpanel.addWidget(button) add_canvas_display_widgets() def set_default_canvas_display_widget(): # The first button added to the buttongroup will be the currently selected button (and therefore view) self.buttongroup.button(0).setChecked(True) set_default_canvas_display_widget() # Whenever a button is switched, capture its id (corresponds to integer index in our case); # this will handle switching the view and displaying canvases. self.buttongroup.idToggled.connect(self.switch_view) # define outer layout & add stacked widget and button panel self.layout = QVBoxLayout() self.layout.addWidget(self.stackedwidget) self.layout.addLayout(self.buttonpanel) self.setLayout(self.layout) # INTERFACING WITH TOOLBAR (see XPCSToolbar) def view(self): # from xicam.gui.canvases import ImageIntentCanvas view = self.stackedwidget.currentWidget() return view.getView() # return None # DONE INTERFACING def switch_view(self, id, toggled): # when toggled==True, the the button is the new button that was switched to. # when False, the button is the previous button view = self.canvas_display_widgets[id] if not toggled: ... # TODO: is there anything we need to do here (re: cleanup)? else: self.stackedwidget.setCurrentIndex(id) self.show_canvases() def show_canvases(self): self.stackedwidget.currentWidget().clear_canvases() self.stackedwidget.currentWidget().show_canvases( self._canvas_manager.canvases(self.model()))
class QtPointsControls(QtLayerControls): """Qt view and controls for the napari Points layer. Parameters ---------- layer : napari.layers.Points An instance of a napari Points layer. Attributes ---------- addition_button : qtpy.QtWidgets.QtModeRadioButton Button to add points to layer. button_group : qtpy.QtWidgets.QButtonGroup Button group of points layer modes (ADD, PAN_ZOOM, SELECT). delete_button : qtpy.QtWidgets.QtModePushButton Button to delete points from layer. edgeColorSwatch : qtpy.QtWidgets.QFrame Color swatch showing shapes edge display color. edgeComboBox : qtpy.QtWidgets.QComboBox Dropdown widget to select display color for shape edges. faceColorSwatch : qtpy.QtWidgets.QFrame Color swatch showing shapes face display color. faceComboBox : qtpy.QtWidgets.QComboBox Dropdown widget to select display color for shape faces. grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : napari.layers.Points An instance of a napari Points layer. ndimCheckBox : qtpy.QtWidgets.QCheckBox Checkbox to indicate whether layer is n-dimensional. panzoom_button : qtpy.QtWidgets.QtModeRadioButton Button for pan/zoom mode. select_button : qtpy.QtWidgets.QtModeRadioButton Button to select points from layer. sizeSlider : qtpy.QtWidgets.QSlider Slider controlling size of points. symbolComboBox : qtpy.QtWidgets.QComboBox Drop down list of symbol options for points markers. Raises ------ ValueError Raise error if points mode is not recognized. Points mode must be one of: ADD, PAN_ZOOM, or SELECT. """ def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self._on_mode_change) self.layer.events.n_dimensional.connect(self._on_n_dimensional_change) self.layer.events.symbol.connect(self._on_symbol_change) self.layer.events.size.connect(self._on_size_change) self.layer.events.current_edge_color.connect( self._on_current_edge_color_change) self.layer.events.current_face_color.connect( self._on_current_face_color_change) self.layer.events.editable.connect(self._on_editable_change) self.layer.text.events.visible.connect(self._on_text_visibility_change) sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(1) sld.setMaximum(100) sld.setSingleStep(1) value = self.layer.current_size sld.setValue(int(value)) sld.valueChanged.connect(self.changeSize) self.sizeSlider = sld self.faceColorEdit = QColorSwatchEdit( initial_color=self.layer.current_face_color, tooltip='click to set current face color', ) self.edgeColorEdit = QColorSwatchEdit( initial_color=self.layer.current_edge_color, tooltip='click to set current edge color', ) self.faceColorEdit.color_changed.connect(self.changeFaceColor) self.edgeColorEdit.color_changed.connect(self.changeEdgeColor) symbol_comboBox = QComboBox() symbol_comboBox.addItems([str(s) for s in Symbol]) index = symbol_comboBox.findText(self.layer.symbol, Qt.MatchFixedString) symbol_comboBox.setCurrentIndex(index) symbol_comboBox.activated[str].connect(self.changeSymbol) self.symbolComboBox = symbol_comboBox ndim_cb = QCheckBox() ndim_cb.setToolTip('N-dimensional points') ndim_cb.setChecked(self.layer.n_dimensional) ndim_cb.stateChanged.connect(self.change_ndim) self.ndimCheckBox = ndim_cb self.select_button = QtModeRadioButton(layer, 'select_points', Mode.SELECT, tooltip='Select points') self.addition_button = QtModeRadioButton(layer, 'add_points', Mode.ADD, tooltip='Add points') self.panzoom_button = QtModeRadioButton(layer, 'pan_zoom', Mode.PAN_ZOOM, tooltip='Pan/zoom', checked=True) self.delete_button = QtModePushButton( layer, 'delete_shape', slot=self.layer.remove_selected, tooltip='Delete selected points', ) text_disp_cb = QCheckBox() text_disp_cb.setToolTip('toggle text visibility') text_disp_cb.setChecked(self.layer.text.visible) text_disp_cb.stateChanged.connect(self.change_text_visibility) self.textDispCheckBox = text_disp_cb self.button_group = QButtonGroup(self) self.button_group.addButton(self.select_button) self.button_group.addButton(self.addition_button) self.button_group.addButton(self.panzoom_button) button_row = QHBoxLayout() button_row.addStretch(1) button_row.addWidget(self.delete_button) button_row.addWidget(self.addition_button) button_row.addWidget(self.select_button) button_row.addWidget(self.panzoom_button) button_row.setContentsMargins(0, 0, 0, 5) button_row.setSpacing(4) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_row, 0, 1) self.grid_layout.addWidget(QLabel('opacity:'), 1, 0) self.grid_layout.addWidget(self.opacitySlider, 1, 1) self.grid_layout.addWidget(QLabel('point size:'), 2, 0) self.grid_layout.addWidget(self.sizeSlider, 2, 1) self.grid_layout.addWidget(QLabel('blending:'), 3, 0) self.grid_layout.addWidget(self.blendComboBox, 3, 1) self.grid_layout.addWidget(QLabel('symbol:'), 4, 0) self.grid_layout.addWidget(self.symbolComboBox, 4, 1) self.grid_layout.addWidget(QLabel('face color:'), 5, 0) self.grid_layout.addWidget(self.faceColorEdit, 5, 1) self.grid_layout.addWidget(QLabel('edge color:'), 6, 0) self.grid_layout.addWidget(self.edgeColorEdit, 6, 1) self.grid_layout.addWidget(QLabel('display text:'), 7, 0) self.grid_layout.addWidget(self.textDispCheckBox, 7, 1) self.grid_layout.addWidget(QLabel('n-dim:'), 8, 0) self.grid_layout.addWidget(self.ndimCheckBox, 8, 1) self.grid_layout.setRowStretch(9, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def _on_mode_change(self, event): """Update ticks in checkbox widgets when points layer mode is changed. Available modes for points layer are: * ADD * SELECT * PAN_ZOOM Parameters ---------- event : napari.utils.event.Event The napari event that triggered this method. Raises ------ ValueError Raise error if event.mode is not ADD, PAN_ZOOM, or SELECT. """ mode = event.mode if mode == Mode.ADD: self.addition_button.setChecked(True) elif mode == Mode.SELECT: self.select_button.setChecked(True) elif mode == Mode.PAN_ZOOM: self.panzoom_button.setChecked(True) else: raise ValueError("Mode not recognized") def changeSymbol(self, text): """Change marker symbol of the points on the layer model. Parameters ---------- text : str Marker symbol of points, eg: '+', '.', etc. """ self.layer.symbol = text def changeSize(self, value): """Change size of points on the layer model. Parameters ---------- value : float Size of points. """ self.layer.current_size = value def change_ndim(self, state): """Toggle n-dimensional state of label layer. Parameters ---------- state : QCheckBox Checkbox indicating if label layer is n-dimensional. """ if state == Qt.Checked: self.layer.n_dimensional = True else: self.layer.n_dimensional = False def change_text_visibility(self, state): """Toggle the visibiltiy of the text. Parameters ---------- state : QCheckBox Checkbox indicating if text is visible. """ if state == Qt.Checked: self.layer.text.visible = True else: self.layer.text.visible = False def _on_text_visibility_change(self, event): """Receive layer model text visibiltiy change change event and update checkbox. Parameters ---------- event : qtpy.QtCore.QEvent Event from the Qt context. """ with self.layer.text.events.visible.blocker(): self.textDispCheckBox.setChecked(self.layer.text.visible) def _on_n_dimensional_change(self, event): """Receive layer model n-dimensional change event and update checkbox. Parameters ---------- event : napari.utils.event.Event The napari event that triggered this method. """ with self.layer.events.n_dimensional.blocker(): self.ndimCheckBox.setChecked(self.layer.n_dimensional) def _on_symbol_change(self, event): """Receive marker symbol change event and update the dropdown menu. Parameters ---------- event : napari.utils.event.Event The napari event that triggered this method. """ with self.layer.events.symbol.blocker(): index = self.symbolComboBox.findText(self.layer.symbol, Qt.MatchFixedString) self.symbolComboBox.setCurrentIndex(index) def _on_size_change(self, event=None): """Receive layer model size change event and update point size slider. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method. """ with self.layer.events.size.blocker(): value = self.layer.current_size self.sizeSlider.setValue(int(value)) @Slot(np.ndarray) def changeFaceColor(self, color: np.ndarray): """Update face color of layer model from color picker user input.""" with self.layer.events.current_face_color.blocker(): self.layer.current_face_color = color @Slot(np.ndarray) def changeEdgeColor(self, color: np.ndarray): """Update edge color of layer model from color picker user input.""" with self.layer.events.current_edge_color.blocker(): self.layer.current_edge_color = color def _on_current_face_color_change(self, event=None): """Receive layer.current_face_color() change event and update view.""" with qt_signals_blocked(self.faceColorEdit): self.faceColorEdit.setColor(self.layer.current_face_color) def _on_current_edge_color_change(self, event=None): """Receive layer.current_edge_color() change event and update view.""" with qt_signals_blocked(self.edgeColorEdit): self.edgeColorEdit.setColor(self.layer.current_edge_color) def _on_editable_change(self, event=None): """Receive layer model editable change event & enable/disable buttons. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ disable_with_opacity( self, ['select_button', 'addition_button', 'delete_button'], self.layer.editable, ) def close(self): """Disconnect events when widget is closing.""" disconnect_events(self.layer.text.events, self) super().close()
class QtReaderDialog(QDialog): """Dialog for user to select a reader plugin for a given file extension or folder""" def __init__( self, pth: str = '', parent: QWidget = None, extension: str = '', readers: Dict[str, str] = {}, error_message: str = '', ): super().__init__(parent) self.setObjectName('Choose reader') self.setWindowTitle('Choose reader') self._current_file = pth self._extension = extension self._reader_buttons = [] self.setup_ui(error_message, readers) def setup_ui(self, error_message, readers): """Build UI using given error_messsage and readers dict""" # add instruction label layout = QVBoxLayout() label = QLabel( f"{error_message}Choose reader for {self._current_file}:" ) layout.addWidget(label) # add radio button for each reader plugin self.reader_btn_group = QButtonGroup(self) self.add_reader_buttons(layout, readers) if self.reader_btn_group.buttons(): self.reader_btn_group.buttons()[0].toggle() # OK & cancel buttons for the dialog btns = QDialogButtonBox.Ok | QDialogButtonBox.Cancel self.btn_box = QDialogButtonBox(btns) self.btn_box.accepted.connect(self.accept) self.btn_box.rejected.connect(self.reject) # checkbox to remember the choice (doesn't pop up for folders) extension = os.path.splitext(self._current_file)[1] if extension: self.persist_checkbox = QCheckBox( f'Remember this choice for files with a {extension} extension' ) self.persist_checkbox.toggle() layout.addWidget(self.persist_checkbox) layout.addWidget(self.btn_box) self.setLayout(layout) def add_reader_buttons(self, layout, readers): """Add radio button to layout for each reader in readers""" for display_name in sorted(readers): button = QRadioButton(f"{display_name}") self.reader_btn_group.addButton(button) layout.addWidget(button) def _get_plugin_choice(self): """Get user's plugin choice based on the checked button""" checked_btn = self.reader_btn_group.checkedButton() if checked_btn: return checked_btn.text() def _get_persist_choice(self): """Get persistence checkbox choice""" return ( hasattr(self, 'persist_checkbox') and self.persist_checkbox.isChecked() ) def get_user_choices(self) -> Optional[Tuple[str, bool]]: """Execute dialog and get user choices""" dialog_result = self.exec_() # user pressed cancel if not dialog_result: return None # grab the selected radio button text display_name = self._get_plugin_choice() # grab the persistence checkbox choice persist_choice = self._get_persist_choice() return display_name, persist_choice
class PlotSettingsWidget(QWidget): def __init__(self, settings, plotWidget, parent=None): super().__init__(parent) self.mainwindow = parent self.plotWidget = plotWidget # xmin self.xminLabel = QLabel(self.tr('xmin:')) self.xminSpinBox = CoordSpinBox() self.xminLabel.setBuddy(self.xminSpinBox) self.xminSpinBox.editingFinished.connect(self.change_limits) # xmax self.xmaxLabel = QLabel(self.tr('xmax:')) self.xmaxSpinBox = CoordSpinBox() self.xmaxLabel.setBuddy(self.xmaxSpinBox) self.xmaxSpinBox.editingFinished.connect(self.change_limits) # ymin self.yminLabel = QLabel(self.tr('ymin:')) self.yminSpinBox = CoordSpinBox() self.yminLabel.setBuddy(self.yminSpinBox) self.yminSpinBox.editingFinished.connect(self.change_limits) # ymax self.ymaxLabel = QLabel(self.tr('ymax:')) self.ymaxSpinBox = CoordSpinBox() self.ymaxLabel.setBuddy(self.ymaxSpinBox) self.ymaxSpinBox.editingFinished.connect(self.change_limits) # Autoscale Radio Group self.autoscaleButtonGroup = QButtonGroup() # Autoscale Group Box self.autoscaleGroupBox = QGroupBox() # autoscale self.autoscaleRadioButton = QRadioButton(self.tr("autoscale")) self.autoscaleButtonGroup.addButton(self.autoscaleRadioButton) # autotrack self.autoscrollRadioButton = QRadioButton(self.tr("autoscroll")) self.autoscaleButtonGroup.addButton(self.autoscrollRadioButton) # no autoscale self.manualscaleRadioButton = QRadioButton(self.tr("manual")) self.autoscaleButtonGroup.addButton(self.manualscaleRadioButton) layout = QVBoxLayout() layout.addWidget(self.autoscaleRadioButton) layout.addWidget(self.autoscrollRadioButton) layout.addWidget(self.manualscaleRadioButton) self.autoscaleGroupBox.setLayout(layout) # Layout layout = QGridLayout() layout.addWidget(self.xminLabel, 1, 0) layout.addWidget(self.xminSpinBox, 1, 1) layout.addWidget(self.xmaxLabel, 2, 0) layout.addWidget(self.xmaxSpinBox, 2, 1) layout.addWidget(self.yminLabel, 3, 0) layout.addWidget(self.yminSpinBox, 3, 1) layout.addWidget(self.ymaxLabel, 4, 0) layout.addWidget(self.ymaxSpinBox, 4, 1) layout.addWidget(self.autoscaleGroupBox, 5, 0, 1, 2) layout.setRowStretch(6, 1) self.setLayout(layout) # settings self.settings = settings self.settings.add_handler(S_XMIN, self.xminSpinBox) self.settings.add_handler(S_XMAX, self.xmaxSpinBox) self.settings.add_handler(S_YMIN, self.yminSpinBox) self.settings.add_handler(S_YMAX, self.ymaxSpinBox) self.settings.add_handler(S_AUTOSCALE, self.autoscaleButtonGroup) def refresh(self, state): pass def change_limits(self): if self.autoscale != AUTOSCALE_COMPLETE: self.plotWidget.xmin = self.xmin self.plotWidget.xmax = self.xmax self.plotWidget.ymin = self.ymin self.plotWidget.ymax = self.ymax self.plotWidget.draw() @property def xmin(self): return self.xminSpinBox.value() @property def xmax(self): return self.xmaxSpinBox.value() @property def ymin(self): return self.yminSpinBox.value() @property def ymax(self): return self.ymaxSpinBox.value() @property def autoscale(self): return self.autoscaleButtonGroup.checkedId()
class DataCorrectionsWidget(BaseWidget): """ Widget that presents data correction options to the user. """ ## Widget name name = "Data Corrections" _old_backgnd_sub = None _old_norm_button = None incident_beam_norm_grp = None def __init__(self, parent=None, state=None, settings=None, data_type=None): super(DataCorrectionsWidget, self).__init__(parent, state, settings, data_type=data_type) class DataCorrsFrame(QFrame): def __init__(self, parent=None): QFrame.__init__(self, parent) self.ui = load_ui(__file__, '../../../ui/inelastic/dgs_data_corrections.ui', baseinstance=self) self._content = DataCorrsFrame(self) self._layout.addWidget(self._content) self.initialize_content() self._instrument_name = settings.instrument_name if state is not None: self.set_state(state) else: self.set_state(DataCorrectionsScript(self._instrument_name)) def initialize_content(self): # Set some validators self._content.monint_low_edit.setValidator(QIntValidator(self._content.monint_low_edit)) self._content.monint_high_edit.setValidator(QIntValidator(self._content.monint_high_edit)) self._content.tof_start_edit.setValidator(QIntValidator(self._content.tof_start_edit)) self._content.tof_end_edit.setValidator(QIntValidator(self._content.tof_end_edit)) # Make group for incident beam normalisation radio buttons self.incident_beam_norm_grp = QButtonGroup() self.incident_beam_norm_grp.addButton(self._content.none_rb, 0) self.incident_beam_norm_grp.addButton(self._content.current_rb, 1) self.incident_beam_norm_grp.addButton(self._content.monitor1_rb, 2) self._monitor_intrange_widgets_state(self._content.monitor1_rb.isChecked()) self._content.monitor1_rb.toggled.connect(self._monitor_intrange_widgets_state) self._detvan_intrange_widgets_state(self._content.van_int_cb.isChecked()) self._content.van_int_cb.toggled.connect(self._detvan_intrange_widgets_state) self._content.use_procdetvan_cb.toggled.connect(self._detvan_widgets_opp_state) self._save_detvan_widgets_state(self._content.save_procdetvan_cb.isChecked()) self._content.save_procdetvan_cb.toggled.connect(self._save_detvan_widgets_state) # Connections self._content.van_input_browse.clicked.connect(self._detvan_browse) self._content.save_procdetvan_save.clicked.connect(self._save_procdetvan_save) def _monitor_intrange_widgets_state(self, state=False): self._content.monint_label.setEnabled(state) self._content.monint_low_edit.setEnabled(state) self._content.monint_high_edit.setEnabled(state) def _detvan_intrange_widgets_state(self, state=False): self._content.van_int_range_label.setEnabled(state) self._content.van_int_range_low_edit.setEnabled(state) self._content.van_int_range_high_edit.setEnabled(state) self._content.van_int_range_units_cb.setEnabled(state) def _detvan_widgets_opp_state(self, state=False): self._content.van_int_cb.setEnabled(not state) if self._content.van_int_cb.isChecked(): self._detvan_intrange_widgets_state(not state) self._content.van_int_cb.setChecked(False) self._content.save_procdetvan_cb.setEnabled(not state) if self._content.save_procdetvan_cb.isChecked(): self._content.save_procdetvan_cb.setChecked(False) def _save_detvan_widgets_state(self, state=False): self._content.save_procdetvan_label.setEnabled(state) self._content.save_procdetvan_edit.setEnabled(state) self._content.save_procdetvan_save.setEnabled(state) def _detvan_browse(self): fname = self.data_browse_dialog() if fname: self._content.van_input_edit.setText(fname) def _save_procdetvan_save(self): fname = self.data_save_dialog("*.nxs") if fname: self._content.save_procdetvan_edit.setText(fname) def set_state(self, state): """ Populate the UI elements with the data from the given state. @param state: DataCorrectionsScript object """ self._content.filter_bad_pulses_chkbox.setChecked(state.filter_bad_pulses) button_index = DataCorrectionsScript.INCIDENT_BEAM_NORM_TYPES.index(state.incident_beam_norm) cbutton = self.incident_beam_norm_grp.button(button_index) cbutton.setChecked(True) self._content.monint_low_edit.setText(str(state.monitor_int_low)) self._content.monint_high_edit.setText(str(state.monitor_int_high)) self._content.background_sub_gb.setChecked(state.tib_subtraction) self._content.tof_start_edit.setText(str(state.tib_tof_start)) self._content.tof_end_edit.setText(str(state.tib_tof_end)) self._content.correct_kikf_cb.setChecked(state.correct_kikf) self._content.van_input_edit.setText(state.detector_vanadium) self._content.van_int_cb.setChecked(state.detvan_integration) self._content.van_int_range_low_edit.setText(str(state.detvan_int_range_low)) self._content.van_int_range_high_edit.setText(str(state.detvan_int_range_high)) entry_index = self._content.van_int_range_units_cb.findText(state.detvan_int_range_units) self._content.van_int_range_units_cb.setCurrentIndex(entry_index) self._content.save_procdetvan_cb.setChecked(state.save_proc_detvan) self._content.save_procdetvan_edit.setText(str(state.save_proc_detvan_file)) self._content.use_procdetvan_cb.setChecked(state.use_proc_detvan) def get_state(self): """ Returns an object with the state of the interface """ d = DataCorrectionsScript(self._instrument_name) d.filter_bad_pulses = self._content.filter_bad_pulses_chkbox.isChecked() d.incident_beam_norm = DataCorrectionsScript.INCIDENT_BEAM_NORM_TYPES[self.incident_beam_norm_grp.checkedId()] d.monitor_int_low = util._check_and_get_float_line_edit(self._content.monint_low_edit) d.monitor_int_high = util._check_and_get_float_line_edit(self._content.monint_high_edit) d.tib_subtraction = self._content.background_sub_gb.isChecked() d.tib_tof_start = util._check_and_get_float_line_edit(self._content.tof_start_edit) d.tib_tof_end = util._check_and_get_float_line_edit(self._content.tof_end_edit) d.correct_kikf = self._content.correct_kikf_cb.isChecked() d.detector_vanadium = self._content.van_input_edit.text() d.detvan_integration = self._content.van_int_cb.isChecked() d.detvan_int_range_low = util._check_and_get_float_line_edit(self._content.van_int_range_low_edit) d.detvan_int_range_high = util._check_and_get_float_line_edit(self._content.van_int_range_high_edit) d.detvan_int_range_units = self._content.van_int_range_units_cb.currentText() d.save_proc_detvan = self._content.save_procdetvan_cb.isChecked() d.save_proc_detvan_file = self._content.save_procdetvan_edit.text() d.use_proc_detvan = self._content.use_procdetvan_cb.isChecked() return d def live_button_toggled_actions(self,checked): if checked: self._old_norm_button = self.incident_beam_norm_grp.checkedId() self._old_backgnd_sub = self._content.background_sub_gb.isChecked() self._content.none_rb.setChecked(True) self._content.background_sub_gb.setChecked(False) else: try: self.incident_beam_norm_grp.button(self._old_norm_button).setChecked(True) self._content.background_sub_gb.setChecked(self._old_backgnd_sub) except: # This is for if the live button started out checked pass self._content.incident_beam_norm_gb.setEnabled(not checked) self._content.background_sub_gb.setEnabled(not checked)
def __init__(self, parent, **kwargs): QWidget.__init__(self, parent) self.plugin = parent.get_plugin() # self.plugin_actions = [] # outer_layout # +----------------------------------------------|-----------+ # | inner_layout | | # | +---------|-----------------------------+ | | # upper | | | +----------------------+ | | | # layout | |obj_radio| |objbox_layout | | | | # | | | +----------------------+ | | update | # | ----------------------------------------- | button | # | | expr | | | | # | | radio | exprbox | | | # | +---------|-----------------------------+ | | # | | | # -----------------------------------------------|------------ # | | # | msgbox | # | | # +----------------------------------------------------------+ # button_group = QButtonGroup(parent=self) self.object_radio = object_radio = QRadioButton("Object") self.expr_radio = expr_radio = QRadioButton("Expression") button_group.addButton(object_radio) button_group.addButton(expr_radio) object_radio.toggled.connect(self.activateObject) expr_radio.toggled.connect(self.activateExpression) update_button = QPushButton(text="Update", parent=self) update_button.clicked.connect(self.update_data) # txt = _("Expression") # if sys.platform == 'darwin': # expr_label = QLabel(" " + txt) # else: # expr_label = QLabel(txt) font = self.plugin.get_font() self.exprbox = MxPyExprLineEdit(self, font=font) self.objbox = QLabel(parent=self) self.argbox = MxPyExprLineEdit(self, font=font) self.msgbox = QLabel(parent=self) self.msgbox.setText("") self.msgbox.setWordWrap(True) outer_layout = QVBoxLayout() upper_layout = QHBoxLayout() outer_layout.addLayout(upper_layout) outer_layout.addWidget(self.msgbox) inner_layout = QGridLayout() inner_layout.addWidget(object_radio, 0, 0) inner_layout.addWidget(expr_radio, 1, 0) inner_layout.addWidget(self.exprbox, 1, 1) objbox_layout = QHBoxLayout() objbox_layout.addWidget(self.objbox) objbox_layout.addWidget(self.argbox) objbox_layout.setStretch(0, 3) # 3:1 objbox_layout.setStretch(1, 1) inner_layout.addLayout(objbox_layout, 0, 1) upper_layout.addLayout(inner_layout) upper_layout.addWidget(update_button) # widget = QWidget(parent=self) # widget.setLayout(outer_layout) # Create main widget self.widget = QWidget(parent=self) # MxDataFrameViewer(self) self.main_layout = main_layout = QVBoxLayout() main_layout.addLayout(outer_layout) main_layout.addWidget(self.widget) main_layout.setStretch(1, 1) # Main layout of this widget # layout = create_plugin_layout(self.tools_layout) # layout.addLayout(main_layout) margins = (0, 0, 0, 0) for lo in [outer_layout, upper_layout, inner_layout, self.msgbox]: lo.setContentsMargins(*margins) objbox_layout.setContentsMargins(0, 0, 0, 0) self.setLayout(main_layout) self.attrdict = None object_radio.setChecked(True)
class QtShapesControls(QtLayerControls): def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self.set_mode) self.layer.events.edge_width.connect(self._on_edge_width_change) self.layer.events.edge_color.connect(self._on_edge_color_change) self.layer.events.face_color.connect(self._on_face_color_change) self.layer.events.editable.connect(self._on_editable_change) sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(40) sld.setSingleStep(1) value = self.layer.edge_width if isinstance(value, Iterable): if isinstance(value, list): value = np.asarray(value) value = value.mean() sld.setValue(int(value)) sld.valueChanged[int].connect( lambda value=sld: self.changeWidth(value) ) self.widthSlider = sld face_comboBox = QComboBox() colors = self.layer._colors for c in colors: face_comboBox.addItem(c) face_comboBox.activated[str].connect( lambda text=face_comboBox: self.changeFaceColor(text) ) self.faceComboBox = face_comboBox self.faceColorSwatch = QFrame() self.faceColorSwatch.setObjectName('swatch') self.faceColorSwatch.setToolTip('Face color swatch') self._on_face_color_change(None) 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.changeEdgeColor(text) ) self.edgeComboBox = edge_comboBox self.edgeColorSwatch = QFrame() self.edgeColorSwatch.setObjectName('swatch') self.edgeColorSwatch.setToolTip('Edge color swatch') self._on_edge_color_change(None) self.select_button = QtModeButton( layer, 'select', Mode.SELECT, 'Select shapes' ) self.direct_button = QtModeButton( layer, 'direct', Mode.DIRECT, 'Select vertices' ) self.panzoom_button = QtModeButton( layer, 'zoom', Mode.PAN_ZOOM, 'Pan/zoom' ) self.rectangle_button = QtModeButton( layer, 'rectangle', Mode.ADD_RECTANGLE, 'Add rectangles' ) self.ellipse_button = QtModeButton( layer, 'ellipse', Mode.ADD_ELLIPSE, 'Add ellipses' ) self.line_button = QtModeButton( layer, 'line', Mode.ADD_LINE, 'Add lines' ) self.path_button = QtModeButton( layer, 'path', Mode.ADD_PATH, 'Add paths' ) self.polygon_button = QtModeButton( layer, 'polygon', Mode.ADD_POLYGON, 'Add polygons' ) self.vertex_insert_button = QtModeButton( layer, 'vertex_insert', Mode.VERTEX_INSERT, 'Insert vertex' ) self.vertex_remove_button = QtModeButton( layer, 'vertex_remove', Mode.VERTEX_REMOVE, 'Remove vertex' ) self.move_front_button = QtMoveFrontButton(layer) self.move_back_button = QtMoveBackButton(layer) self.delete_button = QtDeleteShapeButton(layer) self.panzoom_button.setChecked(True) self.button_group = QButtonGroup(self) self.button_group.addButton(self.select_button) self.button_group.addButton(self.direct_button) self.button_group.addButton(self.panzoom_button) self.button_group.addButton(self.rectangle_button) self.button_group.addButton(self.ellipse_button) self.button_group.addButton(self.line_button) self.button_group.addButton(self.path_button) self.button_group.addButton(self.polygon_button) self.button_group.addButton(self.vertex_insert_button) self.button_group.addButton(self.vertex_remove_button) button_grid = QGridLayout() button_grid.addWidget(self.vertex_remove_button, 0, 1) button_grid.addWidget(self.vertex_insert_button, 0, 2) button_grid.addWidget(self.delete_button, 0, 3) button_grid.addWidget(self.direct_button, 0, 4) button_grid.addWidget(self.select_button, 0, 5) button_grid.addWidget(self.panzoom_button, 0, 6) button_grid.addWidget(self.move_back_button, 1, 0) button_grid.addWidget(self.move_front_button, 1, 1) button_grid.addWidget(self.ellipse_button, 1, 2) button_grid.addWidget(self.rectangle_button, 1, 3) button_grid.addWidget(self.polygon_button, 1, 4) button_grid.addWidget(self.line_button, 1, 5) button_grid.addWidget(self.path_button, 1, 6) button_grid.setColumnStretch(2, 2) button_grid.setSpacing(4) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_grid, 0, 0, 1, 3) self.grid_layout.addWidget(QLabel('opacity:'), 1, 0) self.grid_layout.addWidget(self.opacitySilder, 1, 1, 1, 2) self.grid_layout.addWidget(QLabel('edge width:'), 2, 0) self.grid_layout.addWidget(self.widthSlider, 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('face color:'), 4, 0) self.grid_layout.addWidget(self.faceComboBox, 4, 2) self.grid_layout.addWidget(self.faceColorSwatch, 4, 1) self.grid_layout.addWidget(QLabel('edge color:'), 5, 0) self.grid_layout.addWidget(self.edgeComboBox, 5, 2) self.grid_layout.addWidget(self.edgeColorSwatch, 5, 1) self.grid_layout.setRowStretch(6, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def mouseMoveEvent(self, event): self.layer.status = str(self.layer.mode) def set_mode(self, event): mode = event.mode if mode == Mode.SELECT: self.select_button.setChecked(True) elif mode == Mode.DIRECT: self.direct_button.setChecked(True) elif mode == Mode.PAN_ZOOM: self.panzoom_button.setChecked(True) elif mode == Mode.ADD_RECTANGLE: self.rectangle_button.setChecked(True) elif mode == Mode.ADD_ELLIPSE: self.ellipse_button.setChecked(True) elif mode == Mode.ADD_LINE: self.line_button.setChecked(True) elif mode == Mode.ADD_PATH: self.path_button.setChecked(True) elif mode == Mode.ADD_POLYGON: self.polygon_button.setChecked(True) elif mode == Mode.VERTEX_INSERT: self.vertex_insert_button.setChecked(True) elif mode == Mode.VERTEX_REMOVE: self.vertex_remove_button.setChecked(True) else: raise ValueError("Mode not recongnized") def changeFaceColor(self, text): self.layer.face_color = text def changeEdgeColor(self, text): self.layer.edge_color = text def changeWidth(self, value): self.layer.edge_width = float(value) / 2 def _on_edge_width_change(self, event): with self.layer.events.edge_width.blocker(): value = self.layer.edge_width value = np.clip(int(2 * value), 0, 40) self.widthSlider.setValue(value) 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) def _on_face_color_change(self, event): with self.layer.events.face_color.blocker(): index = self.faceComboBox.findText( self.layer.face_color, Qt.MatchFixedString ) self.faceComboBox.setCurrentIndex(index) color = Color(self.layer.face_color).hex self.faceColorSwatch.setStyleSheet("background-color: " + color) def _on_editable_change(self, event): self.select_button.setEnabled(self.layer.editable) self.direct_button.setEnabled(self.layer.editable) self.rectangle_button.setEnabled(self.layer.editable) self.ellipse_button.setEnabled(self.layer.editable) self.line_button.setEnabled(self.layer.editable) self.path_button.setEnabled(self.layer.editable) self.polygon_button.setEnabled(self.layer.editable) self.vertex_remove_button.setEnabled(self.layer.editable) self.vertex_insert_button.setEnabled(self.layer.editable) self.delete_button.setEnabled(self.layer.editable) self.move_back_button.setEnabled(self.layer.editable) self.move_front_button.setEnabled(self.layer.editable)
def create_widgets(self): """creates the menu objects""" # Name self.name = QLabel("Title:") self.name_edit = QLineEdit(str(self._default_name)) self.name_button = QPushButton("Default") # Min self.min = QLabel("Min:") self.min_edit = QLineEdit(str(self._default_min)) self.min_button = QPushButton("Default") # Max self.max = QLabel("Max:") self.max_edit = QLineEdit(str(self._default_max)) self.max_button = QPushButton("Default") #--------------------------------------- # Format self.format = QLabel("Format (e.g. %.3f, %g, %.6e):") self.format_edit = QLineEdit(str(self._format)) self.format_button = QPushButton("Default") #--------------------------------------- # Scale self.scale = QLabel("Scale:") self.scale_edit = QLineEdit(str(self._scale)) self.scale_button = QPushButton("Default") if self._default_scale == 0.0: self.scale.setVisible(False) self.scale_edit.setVisible(False) self.scale_button.setVisible(False) # Phase self.phase = QLabel("Phase (deg):") self.phase_edit = QLineEdit(str(self._phase)) self.phase_button = QPushButton("Default") if self._default_phase is None: self.phase.setVisible(False) self.phase_edit.setVisible(False) self.phase_button.setVisible(False) self.phase_edit.setText('0.0') #tip = QtGui.QToolTip() #tip.setTe #self.format_edit.toolTip(tip) #--------------------------------------- # nlabels self.nlabels = QLabel("Number of Labels:") self.nlabels_edit = QLineEdit(str(self._nlabels)) self.nlabels_button = QPushButton("Default") self.labelsize = QLabel("Label Size:") self.labelsize_edit = QLineEdit(str(self._labelsize)) self.labelsize_button = QPushButton("Default") self.ncolors = QLabel("Number of Colors:") self.ncolors_edit = QLineEdit(str(self._ncolors)) self.ncolors_button = QPushButton("Default") self.colormap = QLabel("Color Map:") self.colormap_edit = QComboBox(self) self.colormap_button = QPushButton("Default") for key in colormap_keys: self.colormap_edit.addItem(key) self.colormap_edit.setCurrentIndex(colormap_keys.index(self._colormap)) # -------------------------------------------------------------- # the header self.grid2_title = QLabel("Color Scale:") # red/blue or blue/red self.low_to_high_radio = QRadioButton('Low -> High') self.high_to_low_radio = QRadioButton('High -> Low') widget = QWidget(self) low_to_high_group = QButtonGroup(widget) low_to_high_group.addButton(self.low_to_high_radio) low_to_high_group.addButton(self.high_to_low_radio) self.low_to_high_radio.setChecked(self._default_is_low_to_high) self.high_to_low_radio.setChecked(not self._default_is_low_to_high) # horizontal / vertical self.horizontal_radio = QRadioButton("Horizontal") self.vertical_radio = QRadioButton("Vertical") widget = QWidget(self) horizontal_vertical_group = QButtonGroup(widget) horizontal_vertical_group.addButton(self.horizontal_radio) horizontal_vertical_group.addButton(self.vertical_radio) self.horizontal_radio.setChecked(self._default_is_horizontal) self.vertical_radio.setChecked(not self._default_is_horizontal) # on / off self.show_radio = QRadioButton("Show") self.hide_radio = QRadioButton("Hide") widget = QWidget(self) show_hide_group = QButtonGroup(widget) show_hide_group.addButton(self.show_radio) show_hide_group.addButton(self.hide_radio) self.show_radio.setChecked(self._default_is_shown) self.hide_radio.setChecked(not self._default_is_shown) # -------------------------------------------------------------- if self._is_normals: self.max.hide() self.min.hide() self.max_edit.hide() self.min_edit.hide() self.max_button.hide() self.min_button.hide() self.format.hide() self.format_edit.hide() self.format_button.hide() self.nlabels.hide() self.nlabels_edit.hide() self.nlabels_button.hide() self.ncolors.hide() self.ncolors_edit.hide() self.ncolors_button.hide() self.grid2_title.hide() self.vertical_radio.hide() self.horizontal_radio.hide() self.show_radio.hide() self.hide_radio.hide() self.low_to_high_radio.hide() self.high_to_low_radio.hide() self.colormap.hide() self.colormap_edit.hide() self.colormap_button.hide() self.animate_button = QPushButton('Create Animation') if self._default_scale == 0.0: self.animate_button.setEnabled(False) self.animate_button.setToolTip( 'This must be a displacement-like result to animate') # closing self.apply_button = QPushButton("Apply") self.ok_button = QPushButton("OK") self.cancel_button = QPushButton("Cancel")
class PyDMEnumButton(QWidget, PyDMWritableWidget, WidgetType): """ A QWidget that renders buttons for every option of Enum Items. For now three types of buttons can be rendered: - Push Button - Radio Button Parameters ---------- parent : QWidget The parent widget for the Label init_channel : str, optional The channel to be used by the widget. Signals ------- send_value_signal : int, float, str, bool or np.ndarray Emitted when the user changes the value. """ Q_ENUMS(WidgetType) WidgetType = WidgetType def __init__(self, parent=None, init_channel=None): QWidget.__init__(self, parent) PyDMWritableWidget.__init__(self, init_channel=init_channel) self._has_enums = False self.setLayout(QGridLayout(self)) self._btn_group = QButtonGroup() self._btn_group.setExclusive(True) self._btn_group.buttonClicked[int].connect(self.handle_button_clicked) self._widget_type = WidgetType.PushButton self._orientation = Qt.Vertical self._widgets = [] self.rebuild_widgets() def minimumSizeHint(self): """ This property holds the recommended minimum size for the widget. Returns ------- QSize """ # This is totally arbitrary, I just want *some* visible nonzero size return QSize(50, 100) @Property(WidgetType) def widgetType(self): """ The widget type to be used when composing the group. Returns ------- WidgetType """ return self._widget_type @widgetType.setter def widgetType(self, new_type): """ The widget type to be used when composing the group. Parameters ---------- new_type : WidgetType """ if new_type != self._widget_type: self._widget_type = new_type self.rebuild_widgets() @Property(Qt.Orientation) def orientation(self): """ Whether to lay out the bit indicators vertically or horizontally. Returns ------- int """ return self._orientation @orientation.setter def orientation(self, new_orientation): """ Whether to lay out the bit indicators vertically or horizontally. Parameters ------- new_orientation : Qt.Orientation, int """ if new_orientation != self._orientation: self._orientation = new_orientation self.rebuild_layout() @Slot(int) def handle_button_clicked(self, id): """ Handles the event of a button being clicked. Parameters ---------- id : int The clicked button id. """ self.send_value_signal.emit(id) def clear(self): """ Remove all inner widgets from the layout """ for col in range(0, self.layout().columnCount()): for row in range(0, self.layout().rowCount()): item = self.layout().itemAtPosition(row, col) if item is not None: w = item.widget() if w is not None: self.layout().removeWidget(w) def rebuild_widgets(self): """ Rebuild the list of widgets based on a new enum or generates a default list of fake strings so we can see something at Designer. """ def generate_widgets(items): while len(self._widgets) != 0: w = self._widgets.pop(0) self._btn_group.removeButton(w) w.deleteLater() for idx, entry in enumerate(items): w = class_for_type[self._widget_type](parent=self) w.setCheckable(True) w.setText(entry) self._widgets.append(w) self._btn_group.addButton(w, idx) self.clear() if self._has_enums: generate_widgets(self.enum_strings) else: generate_widgets(["Item 1", "Item 2", "Item ..."]) self.rebuild_layout() def rebuild_layout(self): """ Method to reorganize the top-level widget and its contents according to the layout property values. """ self.clear() if self.orientation == Qt.Vertical: for i, widget in enumerate(self._widgets): self.layout().addWidget(widget, i, 0) elif self.orientation == Qt.Horizontal: for i, widget in enumerate(self._widgets): self.layout().addWidget(widget, 0, i) def check_enable_state(self): """ Checks whether or not the widget should be disable. This method also disables the widget and add a Tool Tip with the reason why it is disabled. """ status = self._write_access and self._connected and self._has_enums tooltip = "" if not self._connected: tooltip += "Channel is disconnected." elif not self._write_access: if data_plugins.is_read_only(): tooltip += "Running PyDM on Read-Only mode." else: tooltip += "Access denied by Channel Access Security." elif not self._has_enums: tooltip += "Enums not available." self.setToolTip(tooltip) self.setEnabled(status) def value_changed(self, new_val): """ Callback invoked when the Channel value is changed. Parameters ---------- new_val : int The new value from the channel. """ if new_val is not None and new_val != self.value: super(PyDMEnumButton, self).value_changed(new_val) btn = self._btn_group.button(new_val) if btn: btn.setChecked(True) def enum_strings_changed(self, new_enum_strings): """ Callback invoked when the Channel has new enum values. This callback also triggers a value_changed call so the new enum values to be broadcasted. Parameters ---------- new_enum_strings : tuple The new list of values """ if new_enum_strings is not None \ and new_enum_strings != self.enum_strings: super(PyDMEnumButton, self).enum_strings_changed(new_enum_strings) self._has_enums = True self.check_enable_state() self.rebuild_widgets()
def create_widgets(self, show_animation_button=True): """creates the menu objects""" # title self.title_label = QLabel("Title:") self.title_edit = QLineEdit(str(self._default_title)) self.title_button = QPushButton("Default") # Min self.min_label = QLabel("Min:") self.min_edit = QLineEdit(str(self._default_min)) self.min_button = QPushButton("Default") # Max self.max_label = QLabel("Max:") self.max_edit = QLineEdit(str(self._default_max)) self.max_button = QPushButton("Default") #--------------------------------------- # Format self.format_label = QLabel("Format (e.g. %.3f, %g, %.6e):") self.format_edit = QLineEdit(str(self._format)) self.format_button = QPushButton("Default") #--------------------------------------- # Scale self.scale_label = QLabel("True Scale:") self.scale_edit = QLineEdit(str(self._scale)) self.scale_button = QPushButton("Default") if self._icase_disp is None: self.scale_label.setVisible(False) self.scale_edit.setVisible(False) self.scale_button.setVisible(False) # Phase self.phase_label = QLabel("Phase (deg):") self.phase_edit = QLineEdit(str(self._phase)) self.phase_button = QPushButton("Default") if self._icase_disp is None or self._default_phase is None: self.phase_label.setVisible(False) self.phase_edit.setVisible(False) self.phase_button.setVisible(False) self.phase_edit.setText('0.0') #--------------------------------------- self.arrow_scale_label = QLabel("Arrow Scale:") self.arrow_scale_edit = QLineEdit(str(self._arrow_scale)) self.arrow_scale_button = QPushButton("Default") if self._icase_vector is None: self.arrow_scale_label.setVisible(False) self.arrow_scale_edit.setVisible(False) self.arrow_scale_button.setVisible(False) #tip = QtGui.QToolTip() #tip.setTe #self.format_edit.toolTip(tip) #--------------------------------------- # nlabels self.nlabels_label = QLabel("Number of Labels:") self.nlabels_edit = QLineEdit(str(self._nlabels)) self.nlabels_button = QPushButton("Default") self.labelsize_label = QLabel("Label Size:") self.labelsize_edit = QLineEdit(str(self._labelsize)) self.labelsize_button = QPushButton("Default") self.ncolors_label = QLabel("Number of Colors:") self.ncolors_edit = QLineEdit(str(self._ncolors)) self.ncolors_button = QPushButton("Default") self.colormap_label = QLabel("Color Map:") self.colormap_edit = QComboBox(self) self.colormap_button = QPushButton("Default") for key in colormap_keys: self.colormap_edit.addItem(key) self.colormap_edit.setCurrentIndex(colormap_keys.index(self._colormap)) # -------------------------------------------------------------- # the header self.grid2_title = QLabel("Color Scale:") # red/blue or blue/red self.low_to_high_radio = QRadioButton('Low -> High') self.high_to_low_radio = QRadioButton('High -> Low') widget = QWidget(self) low_to_high_group = QButtonGroup(widget) low_to_high_group.addButton(self.low_to_high_radio) low_to_high_group.addButton(self.high_to_low_radio) self.low_to_high_radio.setChecked(self._default_is_low_to_high) self.high_to_low_radio.setChecked(not self._default_is_low_to_high) # horizontal / vertical self.horizontal_radio = QRadioButton("Horizontal") self.vertical_radio = QRadioButton("Vertical") widget = QWidget(self) horizontal_vertical_group = QButtonGroup(widget) horizontal_vertical_group.addButton(self.horizontal_radio) horizontal_vertical_group.addButton(self.vertical_radio) self.horizontal_radio.setChecked(self._default_is_horizontal) self.vertical_radio.setChecked(not self._default_is_horizontal) # on / off self.show_radio = QRadioButton("Show") self.hide_radio = QRadioButton("Hide") widget = QWidget(self) show_hide_group = QButtonGroup(widget) show_hide_group.addButton(self.show_radio) show_hide_group.addButton(self.hide_radio) self.show_radio.setChecked(self._default_is_shown) self.hide_radio.setChecked(not self._default_is_shown) # -------------------------------------------------------------- if self._icase_fringe is None: self.title_label.setVisible(False) self.title_edit.setVisible(False) self.title_button.setVisible(False) if not self._is_fringe: self.max_label.hide() self.min_label.hide() self.max_edit.hide() self.min_edit.hide() self.max_button.hide() self.min_button.hide() self.format_label.hide() self.format_edit.hide() self.format_button.hide() self.nlabels_label.hide() self.nlabels_edit.hide() self.nlabels_button.hide() self.ncolors_label.hide() self.ncolors_edit.hide() self.ncolors_button.hide() self.grid2_title.hide() self.vertical_radio.hide() self.horizontal_radio.hide() self.show_radio.hide() self.hide_radio.hide() self.low_to_high_radio.hide() self.high_to_low_radio.hide() self.colormap_label.hide() self.colormap_edit.hide() self.colormap_button.hide() self.animate_button = QPushButton('Create Animation') self.animate_button.setVisible(show_animation_button) #self.advanced_button = QPushButton('Advanced') if self._default_icase_disp is None: # or self._default_icase_vector is None: self.animate_button.setEnabled(False) self.animate_button.setToolTip(ANIMATE_TOOLTIP_OFF) else: self.animate_button.setEnabled(True) self.animate_button.setToolTip(ANIMATE_TOOLTIP_ON) # closing self.apply_button = QPushButton("Apply") self.ok_button = QPushButton("OK") self.cancel_button = QPushButton("Cancel")
class QtShapesControls(QtLayerControls): """Qt view and controls for the napari Shapes layer. Parameters ---------- layer : napari.layers.Shapes An instance of a napari Shapes layer. Attributes ---------- button_group : qtpy.QtWidgets.QButtonGroup Button group for shapes layer modes (SELECT, DIRECT, PAN_ZOOM, ADD_RECTANGLE, ADD_ELLIPSE, ADD_LINE, ADD_PATH, ADD_POLYGON, VERTEX_INSERT, VERTEX_REMOVE). delete_button : qtpy.QtWidgets.QtModePushButton Button to delete selected shapes direct_button : qtpy.QtWidgets.QtModeRadioButton Button to select individual vertices in shapes. edgeColorSwatch : qtpy.QtWidgets.QFrame Thumbnail display of points edge color. edgeComboBox : qtpy.QtWidgets.QComboBox Drop down list allowing user to set edge color of points. ellipse_button : qtpy.QtWidgets.QtModeRadioButton Button to add ellipses to shapes layer. faceColorSwatch : qtpy.QtWidgets.QFrame Thumbnail display of points face color. faceComboBox : qtpy.QtWidgets.QComboBox Drop down list allowing user to set face color of points. grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : napari.layers.Shapes An instance of a napari Shapes layer. line_button : qtpy.QtWidgets.QtModeRadioButton Button to add lines to shapes layer. move_back_button : qtpy.QtWidgets.QtModePushButton Button to move selected shape(s) to the back. move_front_button : qtpy.QtWidgets.QtModePushButton Button to move shape(s) to the front. panzoom_button : qtpy.QtWidgets.QtModeRadioButton Button to pan/zoom shapes layer. path_button : qtpy.QtWidgets.QtModeRadioButton Button to add paths to shapes layer. polygon_button : qtpy.QtWidgets.QtModeRadioButton Button to add polygons to shapes layer. rectangle_button : qtpy.QtWidgets.QtModeRadioButton Button to add rectangles to shapes layer. select_button : qtpy.QtWidgets.QtModeRadioButton Button to select shapes. vertex_insert_button : qtpy.QtWidgets.QtModeRadioButton Button to insert vertex into shape. vertex_remove_button : qtpy.QtWidgets.QtModeRadioButton Button to remove vertex from shapes. widthSlider : qtpy.QtWidgets.QSlider Slider controlling line edge width of shapes. Raises ------ ValueError Raise error if shapes mode is not recognized. """ def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self.set_mode) self.layer.events.edge_width.connect(self._on_edge_width_change) self.layer.events.edge_color.connect(self._on_edge_color_change) self.layer.events.face_color.connect(self._on_face_color_change) self.layer.events.editable.connect(self._on_editable_change) sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(40) sld.setSingleStep(1) value = self.layer.current_edge_width if isinstance(value, Iterable): if isinstance(value, list): value = np.asarray(value) value = value.mean() sld.setValue(int(value)) sld.valueChanged.connect(self.changeWidth) self.widthSlider = sld face_comboBox = QComboBox() face_comboBox.addItems(self.layer._colors) face_comboBox.activated[str].connect(self.changeFaceColor) self.faceComboBox = face_comboBox self.faceColorSwatch = QFrame() self.faceColorSwatch.setObjectName('colorSwatch') self.faceColorSwatch.setToolTip('Face color swatch') self._on_face_color_change() edge_comboBox = QComboBox() edge_comboBox.addItems(self.layer._colors) edge_comboBox.activated[str].connect(self.changeEdgeColor) self.edgeComboBox = edge_comboBox self.edgeColorSwatch = QFrame() self.edgeColorSwatch.setObjectName('colorSwatch') self.edgeColorSwatch.setToolTip('Edge color swatch') self._on_edge_color_change() self.select_button = QtModeRadioButton(layer, 'select', Mode.SELECT, tooltip='Select shapes') self.direct_button = QtModeRadioButton(layer, 'direct', Mode.DIRECT, tooltip='Select vertices') self.panzoom_button = QtModeRadioButton(layer, 'zoom', Mode.PAN_ZOOM, tooltip='Pan/zoom', checked=True) self.rectangle_button = QtModeRadioButton(layer, 'rectangle', Mode.ADD_RECTANGLE, tooltip='Add rectangles') self.ellipse_button = QtModeRadioButton(layer, 'ellipse', Mode.ADD_ELLIPSE, tooltip='Add ellipses') self.line_button = QtModeRadioButton(layer, 'line', Mode.ADD_LINE, tooltip='Add lines') self.path_button = QtModeRadioButton(layer, 'path', Mode.ADD_PATH, tooltip='Add paths') self.polygon_button = QtModeRadioButton(layer, 'polygon', Mode.ADD_POLYGON, tooltip='Add polygons') self.vertex_insert_button = QtModeRadioButton(layer, 'vertex_insert', Mode.VERTEX_INSERT, tooltip='Insert vertex') self.vertex_remove_button = QtModeRadioButton(layer, 'vertex_remove', Mode.VERTEX_REMOVE, tooltip='Remove vertex') self.move_front_button = QtModePushButton( layer, 'move_front', slot=self.layer.move_to_front, tooltip='Move to front', ) self.move_back_button = QtModePushButton( layer, 'move_back', slot=self.layer.move_to_back, tooltip='Move to back', ) self.delete_button = QtModePushButton( layer, 'delete_shape', slot=self.layer.remove_selected, tooltip='Delete selected shapes', ) self.button_group = QButtonGroup(self) self.button_group.addButton(self.select_button) self.button_group.addButton(self.direct_button) self.button_group.addButton(self.panzoom_button) self.button_group.addButton(self.rectangle_button) self.button_group.addButton(self.ellipse_button) self.button_group.addButton(self.line_button) self.button_group.addButton(self.path_button) self.button_group.addButton(self.polygon_button) self.button_group.addButton(self.vertex_insert_button) self.button_group.addButton(self.vertex_remove_button) button_grid = QGridLayout() button_grid.addWidget(self.vertex_remove_button, 0, 2) button_grid.addWidget(self.vertex_insert_button, 0, 3) button_grid.addWidget(self.delete_button, 0, 4) button_grid.addWidget(self.direct_button, 0, 5) button_grid.addWidget(self.select_button, 0, 6) button_grid.addWidget(self.panzoom_button, 0, 7) button_grid.addWidget(self.move_back_button, 1, 1) button_grid.addWidget(self.move_front_button, 1, 2) button_grid.addWidget(self.ellipse_button, 1, 3) button_grid.addWidget(self.rectangle_button, 1, 4) button_grid.addWidget(self.polygon_button, 1, 5) button_grid.addWidget(self.line_button, 1, 6) button_grid.addWidget(self.path_button, 1, 7) button_grid.setContentsMargins(5, 0, 0, 5) button_grid.setColumnStretch(0, 1) button_grid.setSpacing(4) face_color_layout = QHBoxLayout() face_color_layout.addWidget(self.faceColorSwatch) face_color_layout.addWidget(self.faceComboBox) edge_color_layout = QHBoxLayout() edge_color_layout.addWidget(self.edgeColorSwatch) edge_color_layout.addWidget(self.edgeComboBox) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_grid, 0, 0, 1, 2) self.grid_layout.addWidget(QLabel('opacity:'), 1, 0) self.grid_layout.addWidget(self.opacitySlider, 1, 1) self.grid_layout.addWidget(QLabel('edge width:'), 2, 0) self.grid_layout.addWidget(self.widthSlider, 2, 1) self.grid_layout.addWidget(QLabel('blending:'), 3, 0) self.grid_layout.addWidget(self.blendComboBox, 3, 1) self.grid_layout.addWidget(QLabel('face color:'), 4, 0) self.grid_layout.addLayout(face_color_layout, 4, 1) self.grid_layout.addWidget(QLabel('edge color:'), 5, 0) self.grid_layout.addLayout(edge_color_layout, 5, 1) self.grid_layout.setRowStretch(6, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def mouseMoveEvent(self, event): """On mouse move, update layer mode status. Parameters ---------- event : qtpy.QtCore.QEvent Event from the Qt context. """ self.layer.status = str(self.layer.mode) def set_mode(self, event): """"Update ticks in checkbox widgets when shapes layer mode changed. Available modes for shapes layer are: * SELECT * DIRECT * PAN_ZOOM * ADD_RECTANGLE * ADD_ELLIPSE * ADD_LINE * ADD_PATH * ADD_POLYGON * VERTEX_INSERT * VERTEX_REMOVE Parameters ---------- event : qtpy.QtCore.QEvent Event from the Qt context. Raises ------ ValueError Raise error if event.mode is not ADD, PAN_ZOOM, or SELECT. """ mode_buttons = { Mode.SELECT: self.select_button, Mode.DIRECT: self.direct_button, Mode.PAN_ZOOM: self.panzoom_button, Mode.ADD_RECTANGLE: self.rectangle_button, Mode.ADD_ELLIPSE: self.ellipse_button, Mode.ADD_LINE: self.line_button, Mode.ADD_PATH: self.path_button, Mode.ADD_POLYGON: self.polygon_button, Mode.VERTEX_INSERT: self.vertex_insert_button, Mode.VERTEX_REMOVE: self.vertex_remove_button, } if event.mode in mode_buttons: mode_buttons[event.mode].setChecked(True) else: raise ValueError(f"Mode '{event.mode}'not recognized") def changeFaceColor(self, text): """Change face color of shapes. Parameters ---------- text : str Face color for shapes, color name or hex string. Eg: 'white', 'red', 'blue', '#00ff00', etc. """ self.layer.current_face_color = text def changeEdgeColor(self, text): """Change edge color of shapes. Parameters ---------- text : str Edge color for shapes, color name or hex string. Eg: 'white', 'red', 'blue', '#00ff00', etc. """ self.layer.current_edge_color = text def changeWidth(self, value): """Change edge line width of shapes on the layer model. Parameters ---------- value : float Line width of shapes. """ self.layer.current_edge_width = float(value) / 2 def changeOpacity(self, value): """Change opacity value of shapes on the layer model. Parameters ---------- value : float Opacity value for shapes. Input range 0 - 100 (transparent to fully opaque). """ with self.layer.events.blocker(self._on_opacity_change): self.layer.current_opacity = value / 100 def _on_edge_width_change(self, event=None): """Receive layer model edge line width change event and update slider. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context, by default None. """ with self.layer.events.edge_width.blocker(): value = self.layer.current_edge_width value = np.clip(int(2 * value), 0, 40) self.widthSlider.setValue(value) def _on_edge_color_change(self, event=None): """Receive layer model edge color change event and 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.current_edge_color, Qt.MatchFixedString) self.edgeComboBox.setCurrentIndex(index) color = Color(self.layer.current_edge_color).hex self.edgeColorSwatch.setStyleSheet("background-color: " + color) def _on_face_color_change(self, event=None): """Receive layer model face color change event and update color swatch. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context, by default None. """ with self.layer.events.face_color.blocker(): index = self.faceComboBox.findText(self.layer.current_face_color, Qt.MatchFixedString) self.faceComboBox.setCurrentIndex(index) color = Color(self.layer.current_face_color).hex self.faceColorSwatch.setStyleSheet("background-color: " + color) def _on_opacity_change(self, event=None): """Receive layer model opacity change event and update opacity slider. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context, by default None. """ with self.layer.events.opacity.blocker(): self.opacitySlider.setValue(self.layer.current_opacity * 100) def _on_editable_change(self, event=None): """Receive layer model editable change event & enable/disable buttons. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context, by default None. """ disable_with_opacity( self, [ 'select_button', 'direct_button', 'rectangle_button', 'ellipse_button', 'line_button', 'path_button', 'polygon_button', 'vertex_remove_button', 'vertex_insert_button', 'delete_button', 'move_back_button', 'move_front_button', ], self.layer.editable, )
def __init__(self, import_dict, local_dict, viewer, parent=None): """ :type import_dict: dict[str, object] :type local_dict: dict[str, object] :param import_dict: :param local_dict: :param viewer: """ super().__init__(parent=parent) self.setWindowTitle("Import") self.viewer = viewer() self.local_viewer = viewer() self.import_dict = import_dict self.local_dict = local_dict conflicts = set(local_dict.keys()) & set(import_dict.keys()) # print(conflicts) self.list_view = QTreeWidget() self.list_view.setColumnCount(4) self.radio_group_list = [] self.checked_num = len(import_dict) def rename_func(ob_name, new_name_field, rename_radio): end_reg = re.compile(r"(.*) \((\d+)\)$") def in_func(): if not rename_radio.isChecked() or str(new_name_field.text()).strip() != "": return match = end_reg.match(ob_name) if match: new_name_format = match.group(1) + " ({})" i = int(match.group(2)) + 1 else: new_name_format = ob_name + " ({})" i = 1 while new_name_format.format(i) in self.local_dict: i += 1 new_name_field.setText(new_name_format.format(i)) return in_func def block_import(radio_btn, name_field): def inner_func(): text = str(name_field.text()).strip() if text == "" and radio_btn.isChecked(): self.import_btn.setDisabled(True) else: self.import_btn.setEnabled(True) return inner_func for name in sorted(import_dict.keys()): item = QTreeWidgetItem() item.setText(0, name) # noinspection PyTypeChecker item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(0, Qt.Checked) self.list_view.addTopLevelItem(item) if name in conflicts: group = QButtonGroup() overwrite = QRadioButton("Overwrite") overwrite.setChecked(True) rename = QRadioButton("Rename") new_name = QLineEdit() new_name.textChanged.connect(block_import(rename, new_name)) rename.toggled.connect(block_import(rename, new_name)) overwrite.toggled.connect(block_import(rename, new_name)) rename.toggled.connect(rename_func(name, new_name, rename)) group.addButton(overwrite) group.addButton(rename) self.radio_group_list.append(group) self.list_view.setItemWidget(item, 1, overwrite) self.list_view.setItemWidget(item, 2, rename) self.list_view.setItemWidget(item, 3, new_name) self.import_btn = QPushButton("Import") self.cancel_btn = QPushButton("Cancel") self.check_btn = QPushButton("Check all") self.uncheck_btn = QPushButton("Uncheck all") self.cancel_btn.clicked.connect(self.close) self.import_btn.clicked.connect(self.accept) self.check_btn.clicked.connect(self.check_all) self.uncheck_btn.clicked.connect(self.uncheck_all) self.list_view.itemSelectionChanged.connect(self.preview) self.list_view.itemChanged.connect(self.checked_change) layout = QVBoxLayout() info_layout = QHBoxLayout() info_layout.addWidget(self.list_view, 2) v1_lay = QVBoxLayout() v1_lay.addWidget(QLabel("Import:")) v1_lay.addWidget(self.viewer) info_layout.addLayout(v1_lay, 1) # info_layout.addWidget(self.local_viewer, 1) v2_lay = QVBoxLayout() v2_lay.addWidget(QLabel("Local:")) v2_lay.addWidget(self.local_viewer) info_layout.addLayout(v2_lay, 1) layout.addLayout(info_layout) btn_layout = QHBoxLayout() btn_layout.addWidget(self.check_btn) btn_layout.addWidget(self.uncheck_btn) btn_layout.addStretch() btn_layout.addWidget(self.import_btn) btn_layout.addWidget(self.cancel_btn) layout.addLayout(btn_layout) self.setLayout(layout)
def initialize_content(self): """ Declare the validators and event connections for the widgets loaded through the .ui file. """ # Sample data # Validators self._content.transmission_edit.setValidator(QDoubleValidator(self._content.transmission_edit)) self._content.dtransmission_edit.setValidator(QDoubleValidator(self._content.dtransmission_edit)) self._content.beam_radius_edit.setValidator(QDoubleValidator(self._content.beam_radius_edit)) self._content.sample_thickness_edit.setValidator(QDoubleValidator(self._content.sample_thickness_edit)) # Connections self._content.data_file_browse_button.clicked.connect(self._data_file_browse) self._content.calculate_radio.clicked.connect(self._calculate_clicked) self._content.fix_trans_radio.clicked.connect(self._calculate_clicked) self._content.empty_button.clicked.connect(self._empty_browse) self._content.sample_button.clicked.connect(self._sample_browse) self._content.data_file_plot_button.clicked.connect(self._data_file_plot) self._content.empty_plot_button.clicked.connect(self._empty_plot) self._content.sample_plot_button.clicked.connect(self._sample_plot) # Calculate/Fix radio button g1 = QButtonGroup(self) g1.addButton(self._content.calculate_radio) g1.addButton(self._content.fix_trans_radio) g1.setExclusive(True) if not self._settings.debug: self._content.fix_transmission_layout.deleteLater() self._content.calculate_radio.hide() self._content.fix_trans_radio.hide() self._content.plus_minus_label.hide() self._content.transmission_edit.hide() self._content.dtransmission_edit.hide() if not self._has_instrument_view: self._content.data_file_plot_button.hide() self._content.empty_plot_button.hide() self._content.sample_plot_button.hide() # Background ########## # Validators self._content.bck_transmission_edit.setValidator(QDoubleValidator(self._content.bck_transmission_edit)) self._content.bck_dtransmission_edit.setValidator(QDoubleValidator(self._content.bck_dtransmission_edit)) self._content.bck_beam_radius_edit.setValidator(QDoubleValidator(self._content.beam_radius_edit)) #self._content.bck_thickness_edit.setValidator(QDoubleValidator(self._content.bck_thickness_edit)) # Connections self._content.background_chk.clicked.connect(self._background_clicked) self._content.background_browse.clicked.connect(self._background_browse) self._content.bck_calculate_radio.clicked.connect(self._bck_calculate_clicked) self._content.bck_fix_trans_radio.clicked.connect(self._bck_calculate_clicked) self._content.bck_empty_button.clicked.connect(self._bck_empty_browse) self._content.bck_sample_button.clicked.connect(self._bck_sample_browse) self._content.background_plot_button.clicked.connect(self._background_plot_clicked) self._content.bck_empty_plot_button.clicked.connect(self._bck_empty_plot) self._content.bck_sample_plot_button.clicked.connect(self._bck_sample_plot) # Calculate/Fix radio button g2 = QButtonGroup(self) g2.addButton(self._content.bck_calculate_radio) g2.addButton(self._content.bck_fix_trans_radio) g2.setExclusive(True) if not self._settings.debug: self._content.bck_fix_transmission_layout.deleteLater() self._content.bck_calculate_radio.hide() self._content.bck_fix_trans_radio.hide() self._content.bck_plus_minus_label.hide() self._content.bck_transmission_edit.hide() self._content.bck_dtransmission_edit.hide() if not self._settings.advanced: self._content.theta_dep_chk.hide() self._content.bck_theta_dep_chk.hide() self._content.sample_thickness_label.hide() self._content.sample_thickness_edit.hide() #self._content.bck_thickness_label.hide() #self._content.bck_thickness_edit.hide() if not self._has_instrument_view: self._content.background_plot_button.hide() self._content.bck_empty_plot_button.hide() self._content.bck_sample_plot_button.hide()
class PreviewPlotSpectrum(QWidget): def __init__(self, *, gpc, gui_vars): super().__init__() # Global processing classes self.gpc = gpc # Global GUI variables (used for control of GUI state) self.gui_vars = gui_vars self.cb_plot_type = QComboBox() self.cb_plot_type.addItems(["LinLog", "Linear"]) self.cb_plot_type.setCurrentIndex(self.gpc.get_preview_plot_type()) self.cb_plot_type.currentIndexChanged.connect(self.cb_plot_type_current_index_changed) self.rb_selected_region = QRadioButton("Selected region") self.rb_selected_region.setChecked(True) self.rb_full_spectrum = QRadioButton("Full spectrum") if self.gpc.get_preview_energy_range() == EnergyRangePresets.SELECTED_RANGE: self.rb_selected_region.setChecked(True) elif self.gpc.get_preview_energy_range() == EnergyRangePresets.FULL_SPECTRUM: self.rb_full_spectrum.setChecked(True) else: logger.error( "Spectrum preview: incorrect Enum value for energy range was used:\n" " Report the error to the development team." ) self.btn_group_region = QButtonGroup() self.btn_group_region.addButton(self.rb_selected_region) self.btn_group_region.addButton(self.rb_full_spectrum) self.btn_group_region.buttonToggled.connect(self.btn_group_region_button_toggled) self.mpl_canvas = FigureCanvas(self.gpc.plot_model._fig_preview) self.mpl_toolbar = NavigationToolbar(self.mpl_canvas, self) # Keep layout without change when canvas is hidden (invisible) sp_retain = self.mpl_canvas.sizePolicy() sp_retain.setRetainSizeWhenHidden(True) self.mpl_canvas.setSizePolicy(sp_retain) vbox = QVBoxLayout() hbox = QHBoxLayout() hbox.addWidget(self.cb_plot_type) hbox.addStretch(1) hbox.addWidget(self.rb_selected_region) hbox.addWidget(self.rb_full_spectrum) vbox.addLayout(hbox) vbox.addWidget(self.mpl_toolbar) vbox.addWidget(self.mpl_canvas) self.setLayout(vbox) self._set_tooltips() def _set_tooltips(self): set_tooltip(self.cb_plot_type, "Use <b>Linear</b> or <b>LinLog</b> axes to plot spectra") set_tooltip( self.rb_selected_region, "Plot spectrum in the <b>selected range</b> of energies. The range may be set " "in the 'Model' tab. Click the button <b>'Find Automatically ...'</b> " "to set the range of energies before finding the emission lines. The range " "may be changed in General Settings dialog (button <b>'General ...'</b>) at any time.", ) set_tooltip(self.rb_full_spectrum, "Plot full spectrum over <b>all available eneriges</b>.") def update_widget_state(self, condition=None): if condition == "tooltips": self._set_tooltips() self.mpl_toolbar.setVisible(self.gui_vars["show_matplotlib_toolbar"]) # Hide Matplotlib canvas during computations state_compute = global_gui_variables["gui_state"]["running_computations"] self.mpl_canvas.setVisible(not state_compute) @Slot() @Slot(bool) def redraw_preview_plot(self): # It is assumed that the plot is visible self.gpc.update_preview_spectrum_plot() def btn_group_region_button_toggled(self, button, checked): if checked: if button == self.rb_selected_region: self.gpc.set_preview_energy_range(EnergyRangePresets.SELECTED_RANGE) self.gpc.update_preview_spectrum_plot() logger.debug("GUI: Display only selected region") elif button == self.rb_full_spectrum: self.gpc.set_preview_energy_range(EnergyRangePresets.FULL_SPECTRUM) self.gpc.update_preview_spectrum_plot() logger.debug("GUI: Display full spectrum") else: logger.error( "Spectrum preview: unknown button was toggled. " "Please, report the error to the development team." ) def cb_plot_type_current_index_changed(self, index): try: self.gpc.set_preview_plot_type(PlotTypes(index)) self.gpc.plot_model.update_preview_spectrum_plot() except ValueError: logger.error( "Spectrum preview: incorrect index for energy range preset was detected.\n" "Please report the error to the development team." )
def __init__(self, parent, adjacency): QSplitter.__init__(self, parent, orientation=Qt.Vertical) self.plugin = parent.plugin self.adjacency = adjacency treepane = QWidget(parent=self) # Create main widget self.model = MxAnalyzerModel( adjacency=adjacency, root=None, parent=self # parent must be self, # because self.model access self.tab ) # from .modeltest import ModelTest # self.modeltest = ModelTest(self.model, self) self.tree = MxAnalyzerTree(treepane, self.model) self.shellwidget = None # Set by parent button_group = QButtonGroup(parent=self) self.object_radio = object_radio = QRadioButton("Object") self.expr_radio = expr_radio = QRadioButton("Expression") button_group.addButton(object_radio) button_group.addButton(expr_radio) object_radio.setChecked(True) object_radio.toggled.connect(self.toggleObject) # Layout of the top area in the plugin widget expr_layout = QHBoxLayout() expr_layout.setContentsMargins(0, 0, 0, 0) # Add Object textbox expr_layout.addSpacing(10) txt = _("Object") if sys.platform == 'darwin': obj_label = QLabel(" " + txt) else: obj_label = QLabel(txt) expr_layout.addWidget(obj_label) if spyder.version_info < (4,): font = parent.plugin.get_plugin_font() else: font = parent.plugin.get_font() self.objbox = QLabel(parent=self) self.argbox = MxPyExprLineEdit(self, font=font) self.attrdict = None objbox_layout = QHBoxLayout() objbox_layout.addWidget(self.objbox) objbox_layout.addWidget(self.argbox) objbox_layout.setStretch(0, 3) # 3:1 objbox_layout.setStretch(1, 1) self.exprobjbox = MxPyExprLineEdit(treepane, font=font) expr_layout.addWidget(self.exprobjbox) expr_layout.addSpacing(10) # Add Object textbox txt = _("Args") if sys.platform == 'darwin': arg_label = QLabel(" " + txt) else: arg_label = QLabel(txt) expr_layout.addWidget(arg_label) self.exprargbox = MxPyExprLineEdit(treepane, font=font) expr_layout.addWidget(self.exprargbox) # expr_layout.addSpacing(5) top_layout = QGridLayout() top_layout.addWidget(object_radio, 0, 0) top_layout.addWidget(expr_radio, 1, 0) top_layout.addLayout(objbox_layout, 0, 1) objbox_layout.setContentsMargins(0, 0, 0, 5) top_layout.addLayout(expr_layout, 1, 1) top_layout.setContentsMargins(5, 5, 5, 5) # Main layout of this widget layout = create_plugin_layout(top_layout, self.tree) treepane.setLayout(layout) self.status = QLabel() layout.addWidget(self.status) self.codepane = AnalyzerCodePane(parent=self)
class QtLabelsControls(QtLayerControls): """Qt view and controls for the napari Labels layer. Parameters ---------- layer : napari.layers.Labels An instance of a napari Labels layer. Attributes ---------- button_group : qtpy.QtWidgets.QButtonGroup Button group of labels layer modes: PAN_ZOOM, PICKER, PAINT, ERASE, or FILL. colormapUpdate : qtpy.QtWidgets.QPushButton Button to update colormap of label layer. contigCheckBox : qtpy.QtWidgets.QCheckBox Checkbox to control if label layer is contiguous. fill_button : qtpy.QtWidgets.QtModeRadioButton Button to select FILL mode on Labels layer. grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : napari.layers.Labels An instance of a napari Labels layer. ndimCheckBox : qtpy.QtWidgets.QCheckBox Checkbox to control if label layer is n-dimensional. paint_button : qtpy.QtWidgets.QtModeRadioButton Button to select PAINT mode on Labels layer. panzoom_button : qtpy.QtWidgets.QtModeRadioButton Button to select PAN_ZOOM mode on Labels layer. pick_button : qtpy.QtWidgets.QtModeRadioButton Button to select PICKER mode on Labels layer. erase_button : qtpy.QtWidgets.QtModeRadioButton Button to select ERASE mode on Labels layer. selectionSpinBox : qtpy.QtWidgets.QSpinBox Widget to select a specific label by its index. Raises ------ ValueError Raise error if label mode is not PAN_ZOOM, PICKER, PAINT, ERASE, or FILL. """ def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self._on_mode_change) self.layer.events.selected_label.connect(self._on_selection_change) self.layer.events.brush_size.connect(self._on_brush_size_change) self.layer.events.contiguous.connect(self._on_contig_change) self.layer.events.n_dimensional.connect(self._on_n_dim_change) self.layer.events.editable.connect(self._on_editable_change) self.layer.events.preserve_labels.connect( self._on_preserve_labels_change ) self.layer.events.color_mode.connect(self._on_color_mode_change) # selection spinbox self.selectionSpinBox = QSpinBox() self.selectionSpinBox.setKeyboardTracking(False) self.selectionSpinBox.setSingleStep(1) self.selectionSpinBox.setMinimum(0) self.selectionSpinBox.setMaximum(2147483647) self.selectionSpinBox.valueChanged.connect(self.changeSelection) self.selectionSpinBox.setAlignment(Qt.AlignCenter) self._on_selection_change() sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(1) sld.setMaximum(40) sld.setSingleStep(1) sld.valueChanged.connect(self.changeSize) self.brushSizeSlider = sld self._on_brush_size_change() contig_cb = QCheckBox() contig_cb.setToolTip('contiguous editing') contig_cb.stateChanged.connect(self.change_contig) self.contigCheckBox = contig_cb self._on_contig_change() ndim_cb = QCheckBox() ndim_cb.setToolTip('n-dimensional editing') ndim_cb.stateChanged.connect(self.change_ndim) self.ndimCheckBox = ndim_cb self._on_n_dim_change() preserve_labels_cb = QCheckBox() preserve_labels_cb.setToolTip( 'preserve existing labels while painting' ) preserve_labels_cb.stateChanged.connect(self.change_preserve_labels) self.preserveLabelsCheckBox = preserve_labels_cb self._on_preserve_labels_change() # shuffle colormap button self.colormapUpdate = QtModePushButton( None, 'shuffle', slot=self.changeColor, tooltip='shuffle colors', ) self.panzoom_button = QtModeRadioButton( layer, 'zoom', Mode.PAN_ZOOM, tooltip='Pan/zoom mode (Space)', checked=True, ) self.pick_button = QtModeRadioButton( layer, 'picker', Mode.PICK, tooltip='Pick mode' ) self.paint_button = QtModeRadioButton( layer, 'paint', Mode.PAINT, tooltip='Paint mode' ) btn = 'Cmd' if sys.platform == 'darwin' else 'Ctrl' self.fill_button = QtModeRadioButton( layer, 'fill', Mode.FILL, tooltip=f'Fill mode ({btn})' ) self.erase_button = QtModeRadioButton( layer, 'erase', Mode.ERASE, tooltip='Erase mode (Alt)' ) self.button_group = QButtonGroup(self) self.button_group.addButton(self.panzoom_button) self.button_group.addButton(self.paint_button) self.button_group.addButton(self.pick_button) self.button_group.addButton(self.fill_button) self.button_group.addButton(self.erase_button) self._on_editable_change() button_row = QHBoxLayout() button_row.addStretch(1) button_row.addWidget(self.colormapUpdate) button_row.addWidget(self.erase_button) button_row.addWidget(self.fill_button) button_row.addWidget(self.paint_button) button_row.addWidget(self.pick_button) button_row.addWidget(self.panzoom_button) button_row.setSpacing(4) button_row.setContentsMargins(0, 0, 0, 5) color_mode_comboBox = QComboBox(self) color_mode_comboBox.addItems(LabelColorMode.keys()) index = color_mode_comboBox.findText( self.layer.color_mode, Qt.MatchFixedString ) color_mode_comboBox.setCurrentIndex(index) color_mode_comboBox.activated[str].connect(self.change_color_mode) self.colorModeComboBox = color_mode_comboBox self._on_color_mode_change() color_layout = QHBoxLayout() color_layout.addWidget(QtColorBox(layer)) color_layout.addWidget(self.selectionSpinBox) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_row, 0, 0, 1, 4) self.grid_layout.addWidget(QLabel('label:'), 1, 0, 1, 1) self.grid_layout.addLayout(color_layout, 1, 1, 1, 3) self.grid_layout.addWidget(QLabel('opacity:'), 2, 0, 1, 1) self.grid_layout.addWidget(self.opacitySlider, 2, 1, 1, 3) self.grid_layout.addWidget(QLabel('brush size:'), 3, 0, 1, 1) self.grid_layout.addWidget(self.brushSizeSlider, 3, 1, 1, 3) self.grid_layout.addWidget(QLabel('blending:'), 4, 0, 1, 1) self.grid_layout.addWidget(self.blendComboBox, 4, 1, 1, 3) self.grid_layout.addWidget(QLabel('color mode:'), 5, 0, 1, 1) self.grid_layout.addWidget(self.colorModeComboBox, 5, 1, 1, 3) self.grid_layout.addWidget(QLabel('contiguous:'), 6, 0, 1, 1) self.grid_layout.addWidget(self.contigCheckBox, 6, 1, 1, 1) self.grid_layout.addWidget(QLabel('n-dim:'), 6, 2, 1, 1) self.grid_layout.addWidget(self.ndimCheckBox, 6, 3, 1, 1) self.grid_layout.addWidget(QLabel('preserve labels:'), 7, 0, 1, 2) self.grid_layout.addWidget(self.preserveLabelsCheckBox, 7, 1, 1, 1) self.grid_layout.setRowStretch(8, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def mouseMoveEvent(self, event): """On mouse move, set layer status equal to the current selected mode. Available mode options are: PAN_ZOOM, PICKER, PAINT, ERASE or FILL Parameters ---------- event : qtpy.QtCore.QEvent Event from the Qt context. """ self.layer.status = str(self.layer.mode) def _on_mode_change(self, event): """Receive layer model mode change event and update checkbox ticks. Parameters ---------- event : qtpy.QtCore.QEvent Event from the Qt context. Raises ------ ValueError Raise error if event.mode is not PAN_ZOOM, PICK, PAINT, ERASE, or FILL """ mode = event.mode if mode == Mode.PAN_ZOOM: self.panzoom_button.setChecked(True) elif mode == Mode.PICK: self.pick_button.setChecked(True) elif mode == Mode.PAINT: self.paint_button.setChecked(True) elif mode == Mode.FILL: self.fill_button.setChecked(True) elif mode == Mode.ERASE: self.erase_button.setChecked(True) else: raise ValueError("Mode not recognized") def changeColor(self): """Change colormap of the label layer.""" self.layer.new_colormap() def changeSelection(self, value): """Change currently selected label. Parameters ---------- value : int Index of label to select. """ self.layer.selected_label = value self.selectionSpinBox.clearFocus() self.setFocus() def changeSize(self, value): """Change paint brush size. Parameters ---------- value : float Size of the paint brush. """ self.layer.brush_size = value def change_contig(self, state): """Toggle contiguous state of label layer. Parameters ---------- state : QCheckBox Checkbox indicating if labels are contiguous. """ if state == Qt.Checked: self.layer.contiguous = True else: self.layer.contiguous = False def change_ndim(self, state): """Toggle n-dimensional state of label layer. Parameters ---------- state : QCheckBox Checkbox indicating if label layer is n-dimensional. """ if state == Qt.Checked: self.layer.n_dimensional = True else: self.layer.n_dimensional = False def change_preserve_labels(self, state): """Toggle preserve_labels state of label layer. Parameters ---------- state : QCheckBox Checkbox indicating if overwriting label is enabled. """ if state == Qt.Checked: self.layer.preserve_labels = True else: self.layer.preserve_labels = False def change_color_mode(self, new_mode): """Change color mode of label layer. Parameters ---------- new_mode : str AUTO (default) allows color to be set via a hash function with a seed. DIRECT allows color of each label to be set directly by a color dictionary. """ self.layer.color_mode = new_mode def _on_selection_change(self, event=None): """Receive layer model label selection change event and update spinbox. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context. """ with self.layer.events.selected_label.blocker(): value = self.layer.selected_label self.selectionSpinBox.setValue(int(value)) def _on_brush_size_change(self, event=None): """Receive layer model brush size change event and update the slider. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context. """ with self.layer.events.brush_size.blocker(): value = self.layer.brush_size value = np.clip(int(value), 1, 40) self.brushSizeSlider.setValue(value) def _on_n_dim_change(self, event=None): """Receive layer model n-dim mode change event and update the checkbox. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context. """ with self.layer.events.n_dimensional.blocker(): self.ndimCheckBox.setChecked(self.layer.n_dimensional) def _on_contig_change(self, event=None): """Receive layer model contiguous change event and update the checkbox. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context. """ with self.layer.events.contiguous.blocker(): self.contigCheckBox.setChecked(self.layer.contiguous) def _on_preserve_labels_change(self, event=None): """Receive layer model preserve_labels event and update the checkbox. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context. """ with self.layer.events.preserve_labels.blocker(): self.preserveLabelsCheckBox.setChecked(self.layer.preserve_labels) def _on_color_mode_change(self, event=None): """Receive layer model color. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context. """ with self.layer.events.color_mode.blocker(): index = self.colorModeComboBox.findText( self.layer.color_mode, Qt.MatchFixedString ) self.blendComboBox.setCurrentIndex(index) def _on_editable_change(self, event=None): """Receive layer model editable change event & enable/disable buttons. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context. """ disable_with_opacity( self, ['pick_button', 'paint_button', 'fill_button'], self.layer.editable, )
class PyDMEnumButton(QWidget, PyDMWritableWidget, WidgetType): """ A QWidget that renders buttons for every option of Enum Items. For now three types of buttons can be rendered: - Push Button - Radio Button Parameters ---------- parent : QWidget The parent widget for the Label init_channel : str, optional The channel to be used by the widget. Signals ------- send_value_signal : int, float, str, bool or np.ndarray Emitted when the user changes the value. """ Q_ENUMS(WidgetType) WidgetType = WidgetType def __init__(self, parent=None, init_channel=None): QWidget.__init__(self, parent) PyDMWritableWidget.__init__(self, init_channel=init_channel) self._has_enums = False self._checkable = True self.setLayout(QGridLayout(self)) self._btn_group = QButtonGroup() self._btn_group.setExclusive(True) self._btn_group.buttonClicked[int].connect(self.handle_button_clicked) self._widget_type = WidgetType.PushButton self._orientation = Qt.Vertical self._widgets = [] self.rebuild_widgets() def minimumSizeHint(self): """ This property holds the recommended minimum size for the widget. Returns ------- QSize """ # This is totally arbitrary, I just want *some* visible nonzero size return QSize(50, 100) @Property("QStringList") def items(self): """ Items to be displayed in the button group. This property can be overridden by the items coming from the control system. Because C++ QStringList expects a list type, we need to make sure that None is never returned. Returns ------- List[str] """ return self.enum_strings or [] @items.setter def items(self, value): self.enum_strings_changed(value) @Property(WidgetType) def widgetType(self): """ The widget type to be used when composing the group. Returns ------- WidgetType """ return self._widget_type @widgetType.setter def widgetType(self, new_type): """ The widget type to be used when composing the group. Parameters ---------- new_type : WidgetType """ if new_type != self._widget_type: self._widget_type = new_type self.rebuild_widgets() @Property(Qt.Orientation) def orientation(self): """ Whether to lay out the bit indicators vertically or horizontally. Returns ------- int """ return self._orientation @orientation.setter def orientation(self, new_orientation): """ Whether to lay out the bit indicators vertically or horizontally. Parameters ------- new_orientation : Qt.Orientation, int """ if new_orientation != self._orientation: self._orientation = new_orientation self.rebuild_layout() @Property(bool) def checkable(self): """ Whether or not the button should be checkable. Returns ------- bool """ return self._checkable @checkable.setter def checkable(self, value): if value != self._checkable: self._checkable = value for widget in self._widgets: widget.setCheckable(value) @Slot(int) def handle_button_clicked(self, id): """ Handles the event of a button being clicked. Parameters ---------- id : int The clicked button id. """ self.send_value_signal.emit(id) def clear(self): """ Remove all inner widgets from the layout """ for col in range(0, self.layout().columnCount()): for row in range(0, self.layout().rowCount()): item = self.layout().itemAtPosition(row, col) if item is not None: w = item.widget() if w is not None: self.layout().removeWidget(w) def rebuild_widgets(self): """ Rebuild the list of widgets based on a new enum or generates a default list of fake strings so we can see something at Designer. """ def generate_widgets(items): while len(self._widgets) != 0: w = self._widgets.pop(0) self._btn_group.removeButton(w) w.deleteLater() for idx, entry in enumerate(items): w = class_for_type[self._widget_type](parent=self) w.setCheckable(self.checkable) w.setText(entry) self._widgets.append(w) self._btn_group.addButton(w, idx) self.clear() if self._has_enums: generate_widgets(self.enum_strings) else: generate_widgets(["Item 1", "Item 2", "Item ..."]) self.rebuild_layout() def rebuild_layout(self): """ Method to reorganize the top-level widget and its contents according to the layout property values. """ self.clear() if self.orientation == Qt.Vertical: for i, widget in enumerate(self._widgets): self.layout().addWidget(widget, i, 0) elif self.orientation == Qt.Horizontal: for i, widget in enumerate(self._widgets): self.layout().addWidget(widget, 0, i) def check_enable_state(self): """ Checks whether or not the widget should be disable. This method also disables the widget and add a Tool Tip with the reason why it is disabled. """ status = self._write_access and self._connected and self._has_enums tooltip = "" if not self._connected: tooltip += "Channel is disconnected." elif not self._write_access: if data_plugins.is_read_only(): tooltip += "Running PyDM on Read-Only mode." else: tooltip += "Access denied by Channel Access Security." elif not self._has_enums: tooltip += "Enums not available." self.setToolTip(tooltip) self.setEnabled(status) def value_changed(self, new_val): """ Callback invoked when the Channel value is changed. Parameters ---------- new_val : int The new value from the channel. """ if new_val is not None and new_val != self.value: super(PyDMEnumButton, self).value_changed(new_val) btn = self._btn_group.button(new_val) if btn: btn.setChecked(True) def enum_strings_changed(self, new_enum_strings): """ Callback invoked when the Channel has new enum values. This callback also triggers a value_changed call so the new enum values to be broadcasted. Parameters ---------- new_enum_strings : tuple The new list of values """ if new_enum_strings is not None \ and new_enum_strings != self.enum_strings: super(PyDMEnumButton, self).enum_strings_changed(new_enum_strings) self._has_enums = True self.check_enable_state() self.rebuild_widgets() def paintEvent(self, _): """ Paint events are sent to widgets that need to update themselves, for instance when part of a widget is exposed because a covering widget was moved. At PyDMDrawing this method handles the alarm painting with parameters from the stylesheet, configures the brush, pen and calls ```draw_item``` so the specifics can be performed for each of the drawing classes. Parameters ---------- event : QPaintEvent """ painter = QPainter(self) opt = QStyleOption() opt.initFrom(self) self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self) painter.setRenderHint(QPainter.Antialiasing)
class QtShapesControls(QtLayerControls): """Qt view and controls for the napari Shapes layer. Parameters ---------- layer : napari.layers.Shapes An instance of a napari Shapes layer. Attributes ---------- button_group : qtpy.QtWidgets.QButtonGroup Button group for shapes layer modes (SELECT, DIRECT, PAN_ZOOM, ADD_RECTANGLE, ADD_ELLIPSE, ADD_LINE, ADD_PATH, ADD_POLYGON, VERTEX_INSERT, VERTEX_REMOVE). delete_button : qtpy.QtWidgets.QtModePushButton Button to delete selected shapes direct_button : qtpy.QtWidgets.QtModeRadioButton Button to select individual vertices in shapes. edgeColorSwatch : qtpy.QtWidgets.QFrame Thumbnail display of points edge color. edgeComboBox : qtpy.QtWidgets.QComboBox Drop down list allowing user to set edge color of points. ellipse_button : qtpy.QtWidgets.QtModeRadioButton Button to add ellipses to shapes layer. faceColorSwatch : qtpy.QtWidgets.QFrame Thumbnail display of points face color. faceComboBox : qtpy.QtWidgets.QComboBox Drop down list allowing user to set face color of points. grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : napari.layers.Shapes An instance of a napari Shapes layer. line_button : qtpy.QtWidgets.QtModeRadioButton Button to add lines to shapes layer. move_back_button : qtpy.QtWidgets.QtModePushButton Button to move selected shape(s) to the back. move_front_button : qtpy.QtWidgets.QtModePushButton Button to move shape(s) to the front. panzoom_button : qtpy.QtWidgets.QtModeRadioButton Button to pan/zoom shapes layer. path_button : qtpy.QtWidgets.QtModeRadioButton Button to add paths to shapes layer. polygon_button : qtpy.QtWidgets.QtModeRadioButton Button to add polygons to shapes layer. rectangle_button : qtpy.QtWidgets.QtModeRadioButton Button to add rectangles to shapes layer. select_button : qtpy.QtWidgets.QtModeRadioButton Button to select shapes. vertex_insert_button : qtpy.QtWidgets.QtModeRadioButton Button to insert vertex into shape. vertex_remove_button : qtpy.QtWidgets.QtModeRadioButton Button to remove vertex from shapes. widthSlider : qtpy.QtWidgets.QSlider Slider controlling line edge width of shapes. Raises ------ ValueError Raise error if shapes mode is not recognized. """ def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self._on_mode_change) self.layer.events.edge_width.connect(self._on_edge_width_change) self.layer.events.current_edge_color.connect( self._on_current_edge_color_change ) self.layer.events.current_face_color.connect( self._on_current_face_color_change ) self.layer.events.editable.connect(self._on_editable_change) self.layer.text.events.visible.connect(self._on_text_visibility_change) sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(40) sld.setSingleStep(1) value = self.layer.current_edge_width if isinstance(value, Iterable): if isinstance(value, list): value = np.asarray(value) value = value.mean() sld.setValue(int(value)) sld.valueChanged.connect(self.changeWidth) self.widthSlider = sld def _radio_button( parent, btn_name, mode, action_name, extra_tooltip_text='', **kwargs, ): """ Convenience local function to create a RadioButton and bind it to an action at the same time. Parameters ---------- parent : Any Parent of the generated QtModeRadioButton btn_name : str name fo the button mode : Enum Value Associated to current button action_name : str Action triggered when button pressed extra_tooltip_text : str Text you want added after the automatic tooltip set by the action manager **kwargs: Passed to QtModeRadioButton Returns ------- button: QtModeRadioButton button bound (or that will be bound to) to action `action_name` Notes ----- When shortcuts are modifed/added/removed via the action manager, the tooltip will be updated to reflect the new shortcut. """ action_name = 'napari:' + action_name btn = QtModeRadioButton(parent, btn_name, mode, **kwargs) action_manager.bind_button( action_name, btn, extra_tooltip_text='', ) return btn self.select_button = _radio_button( layer, 'select', Mode.SELECT, "activate_select_mode" ) self.direct_button = _radio_button( layer, 'direct', Mode.DIRECT, "activate_direct_mode" ) self.panzoom_button = _radio_button( layer, 'zoom', Mode.PAN_ZOOM, "napari:activate_shape_pan_zoom_mode", extra_tooltip_text=trans._('(or hold Space)'), checked=True, ) self.rectangle_button = _radio_button( layer, 'rectangle', Mode.ADD_RECTANGLE, "activate_add_rectangle_mode", ) self.ellipse_button = _radio_button( layer, 'ellipse', Mode.ADD_ELLIPSE, "activate_add_ellipse_mode", ) self.line_button = _radio_button( layer, 'line', Mode.ADD_LINE, "activate_add_line_mode" ) self.path_button = _radio_button( layer, 'path', Mode.ADD_PATH, "activate_add_path_mode" ) self.polygon_button = _radio_button( layer, 'polygon', Mode.ADD_POLYGON, "activate_add_polygon_mode", ) self.vertex_insert_button = _radio_button( layer, 'vertex_insert', Mode.VERTEX_INSERT, "activate_vertex_insert_mode", ) self.vertex_remove_button = _radio_button( layer, 'vertex_remove', Mode.VERTEX_REMOVE, "activate_vertex_remove_mode", ) self.move_front_button = QtModePushButton( layer, 'move_front', slot=self.layer.move_to_front, tooltip=trans._('Move to front'), ) action_manager.bind_button( 'napari:move_shapes_selection_to_front', self.move_front_button ) self.move_back_button = QtModePushButton( layer, 'move_back', slot=self.layer.move_to_back, tooltip=trans._('Move to back'), ) action_manager.bind_button( 'napari:move_shapes_selection_to_back', self.move_back_button ) self.delete_button = QtModePushButton( layer, 'delete_shape', slot=self.layer.remove_selected, tooltip=trans._( "Delete selected shapes ({shortcut})", shortcut=Shortcut('Backspace').platform, ), ) self.button_group = QButtonGroup(self) self.button_group.addButton(self.select_button) self.button_group.addButton(self.direct_button) self.button_group.addButton(self.panzoom_button) self.button_group.addButton(self.rectangle_button) self.button_group.addButton(self.ellipse_button) self.button_group.addButton(self.line_button) self.button_group.addButton(self.path_button) self.button_group.addButton(self.polygon_button) self.button_group.addButton(self.vertex_insert_button) self.button_group.addButton(self.vertex_remove_button) button_grid = QGridLayout() button_grid.addWidget(self.vertex_remove_button, 0, 2) button_grid.addWidget(self.vertex_insert_button, 0, 3) button_grid.addWidget(self.delete_button, 0, 4) button_grid.addWidget(self.direct_button, 0, 5) button_grid.addWidget(self.select_button, 0, 6) button_grid.addWidget(self.panzoom_button, 0, 7) button_grid.addWidget(self.move_back_button, 1, 1) button_grid.addWidget(self.move_front_button, 1, 2) button_grid.addWidget(self.ellipse_button, 1, 3) button_grid.addWidget(self.rectangle_button, 1, 4) button_grid.addWidget(self.polygon_button, 1, 5) button_grid.addWidget(self.line_button, 1, 6) button_grid.addWidget(self.path_button, 1, 7) button_grid.setContentsMargins(5, 0, 0, 5) button_grid.setColumnStretch(0, 1) button_grid.setSpacing(4) self.faceColorEdit = QColorSwatchEdit( initial_color=self.layer.current_face_color, tooltip=trans._('click to set current face color'), ) self._on_current_face_color_change() self.edgeColorEdit = QColorSwatchEdit( initial_color=self.layer.current_edge_color, tooltip=trans._('click to set current edge color'), ) self._on_current_edge_color_change() self.faceColorEdit.color_changed.connect(self.changeFaceColor) self.edgeColorEdit.color_changed.connect(self.changeEdgeColor) text_disp_cb = QCheckBox() text_disp_cb.setToolTip(trans._('toggle text visibility')) text_disp_cb.setChecked(self.layer.text.visible) text_disp_cb.stateChanged.connect(self.change_text_visibility) self.textDispCheckBox = text_disp_cb # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_grid, 0, 0, 1, 2) self.grid_layout.addWidget(QLabel(trans._('opacity:')), 1, 0) self.grid_layout.addWidget(self.opacitySlider, 1, 1) self.grid_layout.addWidget(QLabel(trans._('edge width:')), 2, 0) self.grid_layout.addWidget(self.widthSlider, 2, 1) self.grid_layout.addWidget(QLabel(trans._('blending:')), 3, 0) self.grid_layout.addWidget(self.blendComboBox, 3, 1) self.grid_layout.addWidget(QLabel(trans._('face color:')), 4, 0) self.grid_layout.addWidget(self.faceColorEdit, 4, 1) self.grid_layout.addWidget(QLabel(trans._('edge color:')), 5, 0) self.grid_layout.addWidget(self.edgeColorEdit, 5, 1) self.grid_layout.addWidget(QLabel(trans._('display text:')), 6, 0) self.grid_layout.addWidget(self.textDispCheckBox, 6, 1) self.grid_layout.setRowStretch(7, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def _on_mode_change(self, event): """Update ticks in checkbox widgets when shapes layer mode changed. Available modes for shapes layer are: * SELECT * DIRECT * PAN_ZOOM * ADD_RECTANGLE * ADD_ELLIPSE * ADD_LINE * ADD_PATH * ADD_POLYGON * VERTEX_INSERT * VERTEX_REMOVE Parameters ---------- event : napari.utils.event.Event The napari event that triggered this method. Raises ------ ValueError Raise error if event.mode is not ADD, PAN_ZOOM, or SELECT. """ mode_buttons = { Mode.SELECT: self.select_button, Mode.DIRECT: self.direct_button, Mode.PAN_ZOOM: self.panzoom_button, Mode.ADD_RECTANGLE: self.rectangle_button, Mode.ADD_ELLIPSE: self.ellipse_button, Mode.ADD_LINE: self.line_button, Mode.ADD_PATH: self.path_button, Mode.ADD_POLYGON: self.polygon_button, Mode.VERTEX_INSERT: self.vertex_insert_button, Mode.VERTEX_REMOVE: self.vertex_remove_button, } if event.mode in mode_buttons: mode_buttons[event.mode].setChecked(True) else: raise ValueError( trans._("Mode '{mode}'not recognized", mode=event.mode) ) def changeFaceColor(self, color: np.ndarray): """Change face color of shapes. Parameters ---------- color : np.ndarray Face color for shapes, color name or hex string. Eg: 'white', 'red', 'blue', '#00ff00', etc. """ with self.layer.events.current_face_color.blocker(): self.layer.current_face_color = color def changeEdgeColor(self, color: np.ndarray): """Change edge color of shapes. Parameters ---------- color : np.ndarray Edge color for shapes, color name or hex string. Eg: 'white', 'red', 'blue', '#00ff00', etc. """ with self.layer.events.current_edge_color.blocker(): self.layer.current_edge_color = color def changeWidth(self, value): """Change edge line width of shapes on the layer model. Parameters ---------- value : float Line width of shapes. """ self.layer.current_edge_width = float(value) / 2 def changeOpacity(self, value): """Change opacity value of shapes on the layer model. Parameters ---------- value : float Opacity value for shapes. Input range 0 - 100 (transparent to fully opaque). """ with self.layer.events.blocker(self._on_opacity_change): self.layer.opacity = value / 100 def change_text_visibility(self, state): """Toggle the visibiltiy of the text. Parameters ---------- state : QCheckBox Checkbox indicating if text is visible. """ if state == Qt.Checked: self.layer.text.visible = True else: self.layer.text.visible = False def _on_text_visibility_change(self, event): """Receive layer model text visibiltiy change change event and update checkbox. Parameters ---------- event : qtpy.QtCore.QEvent Event from the Qt context. """ with self.layer.text.events.visible.blocker(): self.textDispCheckBox.setChecked(self.layer.text.visible) def _on_edge_width_change(self, event=None): """Receive layer model edge line width change event and update slider. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ with self.layer.events.edge_width.blocker(): value = self.layer.current_edge_width value = np.clip(int(2 * value), 0, 40) self.widthSlider.setValue(value) def _on_current_edge_color_change(self, event=None): """Receive layer model edge color change event and update color swatch. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ with qt_signals_blocked(self.edgeColorEdit): self.edgeColorEdit.setColor(self.layer.current_edge_color) def _on_current_face_color_change(self, event=None): """Receive layer model face color change event and update color swatch. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ with qt_signals_blocked(self.faceColorEdit): self.faceColorEdit.setColor(self.layer.current_face_color) def _on_editable_change(self, event=None): """Receive layer model editable change event & enable/disable buttons. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ disable_with_opacity( self, [ 'select_button', 'direct_button', 'rectangle_button', 'ellipse_button', 'line_button', 'path_button', 'polygon_button', 'vertex_remove_button', 'vertex_insert_button', 'delete_button', 'move_back_button', 'move_front_button', ], self.layer.editable, ) def close(self): """Disconnect events when widget is closing.""" disconnect_events(self.layer.text.events, self) super().close()
class MCSDialog(QDialog): """A dialog to perform minimal cut set computation""" def __init__(self, appdata: CnaData, centralwidget): QDialog.__init__(self) self.setWindowTitle("Minimal Cut Sets Computation") self.appdata = appdata self.centralwidget = centralwidget self.eng = appdata.engine self.out = io.StringIO() self.err = io.StringIO() self.layout = QVBoxLayout() l1 = QLabel("Target Region(s)") self.layout.addWidget(l1) s1 = QHBoxLayout() completer = QCompleter( self.appdata.project.cobra_py_model.reactions.list_attr("id"), self) completer.setCaseSensitivity(Qt.CaseInsensitive) self.target_list = QTableWidget(1, 4) self.target_list.setHorizontalHeaderLabels( ["region no", "T", "≥/≤", "t"]) self.target_list.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.target_list.horizontalHeader().setSectionResizeMode(0, QHeaderView.Fixed) self.target_list.horizontalHeader().resizeSection(0, 100) self.target_list.horizontalHeader().setSectionResizeMode(2, QHeaderView.Fixed) self.target_list.horizontalHeader().resizeSection(2, 50) item = QLineEdit("1") self.target_list.setCellWidget(0, 0, item) item2 = QLineEdit("") item2.setCompleter(completer) self.target_list.setCellWidget(0, 1, item2) combo = QComboBox(self.target_list) combo.insertItem(1, "≤") combo.insertItem(2, "≥") self.target_list.setCellWidget(0, 2, combo) item = QLineEdit("0") self.target_list.setCellWidget(0, 3, item) s1.addWidget(self.target_list) s11 = QVBoxLayout() self.add_target = QPushButton("+") self.add_target.clicked.connect(self.add_target_region) self.rem_target = QPushButton("-") self.rem_target.clicked.connect(self.rem_target_region) s11.addWidget(self.add_target) s11.addWidget(self.rem_target) s1.addItem(s11) self.layout.addItem(s1) l2 = QLabel("Desired Region(s)") self.layout.addWidget(l2) s2 = QHBoxLayout() self.desired_list = QTableWidget(1, 4) self.desired_list.setHorizontalHeaderLabels( ["region no", "D", "≥/≤", "d"]) self.desired_list.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.desired_list.horizontalHeader().setSectionResizeMode(0, QHeaderView.Fixed) self.desired_list.horizontalHeader().resizeSection(0, 100) self.desired_list.horizontalHeader().setSectionResizeMode(2, QHeaderView.Fixed) self.desired_list.horizontalHeader().resizeSection(2, 50) item = QLineEdit("1") self.desired_list.setCellWidget(0, 0, item) item2 = QLineEdit("") item2.setCompleter(completer) self.desired_list.setCellWidget(0, 1, item2) combo = QComboBox(self.desired_list) combo.insertItem(1, "≤") combo.insertItem(2, "≥") self.desired_list.setCellWidget(0, 2, combo) item = QLineEdit("0") self.desired_list.setCellWidget(0, 3, item) s2.addWidget(self.desired_list) s21 = QVBoxLayout() self.add_desire = QPushButton("+") self.add_desire.clicked.connect(self.add_desired_region) self.rem_desire = QPushButton("-") self.rem_desire.clicked.connect(self.rem_desired_region) s21.addWidget(self.add_desire) s21.addWidget(self.rem_desire) s2.addItem(s21) self.layout.addItem(s2) s3 = QHBoxLayout() sgx = QVBoxLayout() self.gen_kos = QCheckBox("Gene KOs") self.exclude_boundary = QCheckBox( "Exclude boundary\nreactions as cuts") sg1 = QHBoxLayout() s31 = QVBoxLayout() l = QLabel("Max. Solutions") s31.addWidget(l) l = QLabel("Max. Size") s31.addWidget(l) l = QLabel("Time Limit [sec]") s31.addWidget(l) sg1.addItem(s31) s32 = QVBoxLayout() self.max_solu = QLineEdit("inf") self.max_solu.setMaximumWidth(50) s32.addWidget(self.max_solu) self.max_size = QLineEdit("7") self.max_size.setMaximumWidth(50) s32.addWidget(self.max_size) self.time_limit = QLineEdit("inf") self.time_limit.setMaximumWidth(50) s32.addWidget(self.time_limit) sg1.addItem(s32) sgx.addWidget(self.gen_kos) sgx.addWidget(self.exclude_boundary) sgx.addItem(sg1) s3.addItem(sgx) g3 = QGroupBox("Solver") s33 = QVBoxLayout() self.bg1 = QButtonGroup() optlang_solver_name = interface_to_str( appdata.project.cobra_py_model.problem) self.solver_optlang = QRadioButton(f"{optlang_solver_name} (optlang)") self.solver_optlang.setToolTip( "Uses the solver specified by the current model.") s33.addWidget(self.solver_optlang) self.bg1.addButton(self.solver_optlang) self.solver_cplex_matlab = QRadioButton("CPLEX (MATLAB)") self.solver_cplex_matlab.setToolTip( "Only enabled with MATLAB and CPLEX") s33.addWidget(self.solver_cplex_matlab) self.bg1.addButton(self.solver_cplex_matlab) self.solver_cplex_java = QRadioButton("CPLEX (Octave)") self.solver_cplex_java.setToolTip("Only enabled with Octave and CPLEX") s33.addWidget(self.solver_cplex_java) self.bg1.addButton(self.solver_cplex_java) self.solver_intlinprog = QRadioButton("intlinprog (MATLAB)") self.solver_intlinprog.setToolTip("Only enabled with MATLAB") s33.addWidget(self.solver_intlinprog) self.bg1.addButton(self.solver_intlinprog) self.solver_glpk = QRadioButton("GLPK (Octave/MATLAB)") s33.addWidget(self.solver_glpk) self.bg1.addButton(self.solver_glpk) self.bg1.buttonClicked.connect(self.configure_solver_options) g3.setLayout(s33) s3.addWidget(g3) g4 = QGroupBox("MCS search") s34 = QVBoxLayout() self.bg2 = QButtonGroup() self.any_mcs = QRadioButton("any MCS (fast)") self.any_mcs.setChecked(True) s34.addWidget(self.any_mcs) self.bg2.addButton(self.any_mcs) # Search type: by cardinality only with CPLEX possible self.mcs_by_cardinality = QRadioButton("by cardinality") s34.addWidget(self.mcs_by_cardinality) self.bg2.addButton(self.mcs_by_cardinality) self.smalles_mcs_first = QRadioButton("smallest MCS first") s34.addWidget(self.smalles_mcs_first) self.bg2.addButton(self.smalles_mcs_first) g4.setLayout(s34) s3.addWidget(g4) self.layout.addItem(s3) # Disable incompatible combinations if appdata.selected_engine == 'None': self.solver_optlang.setChecked(True) self.solver_cplex_matlab.setEnabled(False) self.solver_cplex_java.setEnabled(False) self.solver_glpk.setEnabled(False) self.solver_intlinprog.setEnabled(False) if optlang_solver_name != 'cplex': self.mcs_by_cardinality.setEnabled(False) else: self.solver_glpk.setChecked(True) if not self.eng.is_cplex_matlab_ready(): self.solver_cplex_matlab.setEnabled(False) if not self.eng.is_cplex_java_ready(): self.solver_cplex_java.setEnabled(False) if self.appdata.is_matlab_set(): self.solver_cplex_java.setEnabled(False) if not self.appdata.is_matlab_set(): self.solver_cplex_matlab.setEnabled(False) self.solver_intlinprog.setEnabled(False) self.configure_solver_options() s4 = QVBoxLayout() self.consider_scenario = QCheckBox( "Consider constraint given by scenario") s4.addWidget(self.consider_scenario) self.advanced = QCheckBox( "Advanced: Define knockout/addition costs for genes/reactions") self.advanced.setEnabled(False) s4.addWidget(self.advanced) self.layout.addItem(s4) buttons = QHBoxLayout() # self.save = QPushButton("save") # buttons.addWidget(self.save) # self.load = QPushButton("load") # buttons.addWidget(self.load) self.compute_mcs = QPushButton("Compute MCS") buttons.addWidget(self.compute_mcs) # self.compute_mcs2 = QPushButton("Compute MCS2") # buttons.addWidget(self.compute_mcs2) self.cancel = QPushButton("Close") buttons.addWidget(self.cancel) self.layout.addItem(buttons) # max width for buttons self.add_target.setMaximumWidth(20) self.rem_target.setMaximumWidth(20) self.add_desire.setMaximumWidth(20) self.rem_desire.setMaximumWidth(20) self.setLayout(self.layout) # Connecting the signal self.cancel.clicked.connect(self.reject) self.compute_mcs.clicked.connect(self.compute) @Slot() def configure_solver_options(self): optlang_solver_name = interface_to_str( self.appdata.project.cobra_py_model.problem) if self.solver_optlang.isChecked(): self.gen_kos.setChecked(False) self.gen_kos.setEnabled(False) self.exclude_boundary.setEnabled(True) if optlang_solver_name != 'cplex': if self.mcs_by_cardinality.isChecked(): self.mcs_by_cardinality.setChecked(False) self.any_mcs.setChecked(True) self.mcs_by_cardinality.setEnabled(False) self.mcs_by_cardinality.setChecked(False) else: self.gen_kos.setEnabled(True) self.exclude_boundary.setChecked(False) self.exclude_boundary.setEnabled(False) self.mcs_by_cardinality.setEnabled(True) def add_target_region(self): i = self.target_list.rowCount() self.target_list.insertRow(i) completer = QCompleter( self.appdata.project.cobra_py_model.reactions.list_attr("id"), self) completer.setCaseSensitivity(Qt.CaseInsensitive) item = QLineEdit("1") self.target_list.setCellWidget(i, 0, item) item2 = QLineEdit("") item2.setCompleter(completer) self.target_list.setCellWidget(i, 1, item2) combo = QComboBox(self.target_list) combo.insertItem(1, "≤") combo.insertItem(2, "≥") self.target_list.setCellWidget(i, 2, combo) item = QLineEdit("0") self.target_list.setCellWidget(i, 3, item) def add_desired_region(self): i = self.desired_list.rowCount() self.desired_list.insertRow(i) completer = QCompleter( self.appdata.project.cobra_py_model.reactions.list_attr("id"), self) completer.setCaseSensitivity(Qt.CaseInsensitive) item = QLineEdit("1") self.desired_list.setCellWidget(i, 0, item) item2 = QLineEdit("") item2.setCompleter(completer) self.desired_list.setCellWidget(i, 1, item2) combo = QComboBox(self.desired_list) combo.insertItem(1, "≤") combo.insertItem(2, "≥") self.desired_list.setCellWidget(i, 2, combo) item = QLineEdit("0") self.desired_list.setCellWidget(i, 3, item) def rem_target_region(self): i = self.target_list.rowCount() self.target_list.removeRow(i-1) def rem_desired_region(self): i = self.desired_list.rowCount() self.desired_list.removeRow(i-1) def compute(self): if self.solver_optlang.isChecked(): self.compute_optlang() else: self.compute_legacy() def compute_legacy(self): self.setCursor(Qt.BusyCursor) # create CobraModel for matlab with self.appdata.project.cobra_py_model as model: if self.consider_scenario.isChecked(): # integrate scenario into model bounds for r in self.appdata.project.scen_values.keys(): model.reactions.get_by_id( r).bounds = self.appdata.project.scen_values[r] cobra.io.save_matlab_model(model, os.path.join( self.appdata.cna_path, "cobra_model.mat"), varname="cbmodel") self.eng.eval("load('cobra_model.mat')", nargout=0) try: self.eng.eval("cnap = CNAcobra2cna(cbmodel);", nargout=0, stdout=self.out, stderr=self.err) except Exception: output = io.StringIO() traceback.print_exc(file=output) exstr = output.getvalue() print(exstr) QMessageBox.warning(self, 'Unknown exception occured!', exstr+'\nPlease report the problem to:\n\ \nhttps://github.com/cnapy-org/CNApy/issues') return self.eng.eval("genes = [];", nargout=0, stdout=self.out, stderr=self.err) cmd = "maxSolutions = " + str(float(self.max_solu.text())) + ";" self.eng.eval(cmd, nargout=0, stdout=self.out, stderr=self.err) cmd = "maxSize = " + str(int(self.max_size.text())) + ";" self.eng.eval(cmd, nargout=0, stdout=self.out, stderr=self.err) cmd = "milp_time_limit = " + str(float(self.time_limit.text())) + ";" self.eng.eval(cmd, nargout=0, stdout=self.out, stderr=self.err) if self.gen_kos.isChecked(): self.eng.eval("gKOs = 1;", nargout=0) else: self.eng.eval("gKOs = 0;", nargout=0) if self.advanced.isChecked(): self.eng.eval("advanced_on = 1;", nargout=0) else: self.eng.eval("advanced_on = 0;", nargout=0) if self.solver_intlinprog.isChecked(): self.eng.eval("solver = 'intlinprog';", nargout=0) if self.solver_cplex_java.isChecked(): self.eng.eval("solver = 'java_cplex_new';", nargout=0) if self.solver_cplex_matlab.isChecked(): self.eng.eval("solver = 'matlab_cplex';", nargout=0) if self.solver_glpk.isChecked(): self.eng.eval("solver = 'glpk';", nargout=0) if self.any_mcs.isChecked(): self.eng.eval("mcs_search_mode = 'search_1';", nargout=0) elif self.mcs_by_cardinality.isChecked(): self.eng.eval("mcs_search_mode = 'search_2';", nargout=0) elif self.smalles_mcs_first.isChecked(): self.eng.eval("mcs_search_mode = 'search_3';", nargout=0) rows = self.target_list.rowCount() for i in range(0, rows): p1 = self.target_list.cellWidget(i, 0).text() p2 = self.target_list.cellWidget(i, 1).text() if self.target_list.cellWidget(i, 2).currentText() == '≤': p3 = "<=" else: p3 = ">=" p4 = self.target_list.cellWidget(i, 3).text() cmd = "dg_T = {[" + p1+"], '" + p2 + \ "', '" + p3 + "', [" + p4 + "']};" self.eng.eval(cmd, nargout=0, stdout=self.out, stderr=self.err) rows = self.desired_list.rowCount() for i in range(0, rows): p1 = self.desired_list.cellWidget(i, 0).text() p2 = self.desired_list.cellWidget(i, 1).text() if self.desired_list.cellWidget(i, 2).currentText() == '≤': p3 = "<=" else: p3 = ">=" p4 = self.desired_list.cellWidget(i, 3).text() cmd = "dg_D = {[" + p1+"], '" + p2 + \ "', '" + p3 + "', [" + p4 + "']};" self.eng.eval(cmd, nargout=0) # get some data self.eng.eval("reac_id = cellstr(cnap.reacID).';", nargout=0, stdout=self.out, stderr=self.err) mcs = [] values = [] reactions = [] reac_id = [] if self.appdata.is_matlab_set(): reac_id = self.eng.workspace['reac_id'] try: self.eng.eval("[mcs] = cnapy_compute_mcs(cnap, genes, maxSolutions, maxSize, milp_time_limit, gKOs, advanced_on, solver, mcs_search_mode, dg_T,dg_D);", nargout=0) except Exception: output = io.StringIO() traceback.print_exc(file=output) exstr = output.getvalue() print(exstr) QMessageBox.warning(self, 'Unknown exception occured!', exstr+'\nPlease report the problem to:\n\ \nhttps://github.com/cnapy-org/CNApy/issues') return else: self.eng.eval("[reaction, mcs, value] = find(mcs);", nargout=0, stdout=self.out, stderr=self.err) reactions = self.eng.workspace['reaction'] mcs = self.eng.workspace['mcs'] values = self.eng.workspace['value'] elif self.appdata.is_octave_ready(): reac_id = self.eng.pull('reac_id') reac_id = reac_id[0] try: self.eng.eval("[mcs] = cnapy_compute_mcs(cnap, genes, maxSolutions, maxSize, milp_time_limit, gKOs, advanced_on, solver, mcs_search_mode, dg_T,dg_D);", nargout=0) except Exception: output = io.StringIO() traceback.print_exc(file=output) exstr = output.getvalue() print(exstr) QMessageBox.warning(self, 'Unknown exception occured!', exstr+'\nPlease report the problem to:\n\ \nhttps://github.com/cnapy-org/CNApy/issues') return else: self.eng.eval("[reaction, mcs, value] = find(mcs);", nargout=0, stdout=self.out, stderr=self.err) reactions = self.eng.pull('reaction') mcs = self.eng.pull('mcs') values = self.eng.pull('value') if len(mcs) == 0: QMessageBox.information(self, 'No cut sets', 'Cut sets have not been calculated or do not exist.') else: last_mcs = 1 omcs = [] current_mcs = {} for i in range(0, len(reactions)): reacid = int(reactions[i][0]) reaction = reac_id[reacid-1] c_mcs = int(mcs[i][0]) c_value = int(values[i][0]) if c_value == -1: # -1 stands for removed which is 0 in the ui c_value = 0 if c_mcs > last_mcs: omcs.append(current_mcs) last_mcs = c_mcs current_mcs = {} current_mcs[reaction] = c_value omcs.append(current_mcs) self.appdata.project.modes = omcs self.centralwidget.mode_navigator.current = 0 QMessageBox.information(self, 'Cut sets found', str(len(omcs))+' Cut sets have been calculated.') self.centralwidget.update_mode() self.centralwidget.mode_navigator.title.setText("MCS Navigation") self.setCursor(Qt.ArrowCursor) def compute_optlang(self): self.setCursor(Qt.BusyCursor) max_mcs_num = float(self.max_solu.text()) max_mcs_size = int(self.max_size.text()) timeout = float(self.time_limit.text()) if timeout is float('inf'): timeout = None # if self.gen_kos.isChecked(): # self.eng.eval("gKOs = 1;", nargout=0) # else: # self.eng.eval("gKOs = 0;", nargout=0) # if self.advanced.isChecked(): # self.eng.eval("advanced_on = 1;", nargout=0) # else: # self.eng.eval("advanced_on = 0;", nargout=0) if self.smalles_mcs_first.isChecked(): enum_method = 1 elif self.mcs_by_cardinality.isChecked(): enum_method = 2 elif self.any_mcs.isChecked(): enum_method = 3 with self.appdata.project.cobra_py_model as model: if self.consider_scenario.isChecked(): # integrate scenario into model bounds for r in self.appdata.project.scen_values.keys(): model.reactions.get_by_id( r).bounds = self.appdata.project.scen_values[r] reac_id = model.reactions.list_attr("id") reac_id_symbols = cMCS_enumerator.get_reac_id_symbols(reac_id) rows = self.target_list.rowCount() targets = dict() for i in range(0, rows): p1 = self.target_list.cellWidget(i, 0).text() p2 = self.target_list.cellWidget(i, 1).text() if len(p1) > 0 and len(p2) > 0: if self.target_list.cellWidget(i, 2).currentText() == '≤': p3 = "<=" else: p3 = ">=" p4 = float(self.target_list.cellWidget(i, 3).text()) targets.setdefault(p1, []).append((p2, p3, p4)) targets = list(targets.values()) targets = [cMCS_enumerator.relations2leq_matrix(cMCS_enumerator.parse_relations( t, reac_id_symbols=reac_id_symbols), reac_id) for t in targets] rows = self.desired_list.rowCount() desired = dict() for i in range(0, rows): p1 = self.desired_list.cellWidget(i, 0).text() p2 = self.desired_list.cellWidget(i, 1).text() if len(p1) > 0 and len(p2) > 0: if self.desired_list.cellWidget(i, 2).currentText() == '≤': p3 = "<=" else: p3 = ">=" p4 = float(self.desired_list.cellWidget(i, 3).text()) desired.setdefault(p1, []).append((p2, p3, p4)) desired = list(desired.values()) desired = [cMCS_enumerator.relations2leq_matrix(cMCS_enumerator.parse_relations( d, reac_id_symbols=reac_id_symbols), reac_id) for d in desired] try: mcs = cMCS_enumerator.compute_mcs(model, targets=targets, desired=desired, enum_method=enum_method, max_mcs_size=max_mcs_size, max_mcs_num=max_mcs_num, timeout=timeout, exclude_boundary_reactions_as_cuts=self.exclude_boundary.isChecked()) except cMCS_enumerator.InfeasibleRegion as e: QMessageBox.warning(self, 'Cannot calculate MCS', str(e)) return targets, desired except Exception: output = io.StringIO() traceback.print_exc(file=output) exstr = output.getvalue() print(exstr) QMessageBox.warning(self, 'An exception has occured!', exstr+'\nPlease report the problem to:\n\ \nhttps://github.com/cnapy-org/CNApy/issues') return targets, desired finally: self.setCursor(Qt.ArrowCursor) if len(mcs) == 0: QMessageBox.information(self, 'No cut sets', 'Cut sets have not been calculated or do not exist.') return targets, desired omcs = [{reac_id[i]: 0 for i in m} for m in mcs] self.appdata.project.modes = omcs self.centralwidget.mode_navigator.current = 0 QMessageBox.information(self, 'Cut sets found', str(len(omcs))+' Cut sets have been calculated.') self.centralwidget.update_mode() self.centralwidget.mode_navigator.title.setText("MCS Navigation")