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 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_selected_label_change) self.layer.events.brush_size.connect(self._on_brush_size_change) self.layer.events.contiguous.connect(self._on_contiguous_change) self.layer.events.n_dimensional.connect(self._on_n_dimensional_change) self.layer.events.contour.connect(self._on_contour_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(1024) self.selectionSpinBox.valueChanged.connect(self.changeSelection) self.selectionSpinBox.setAlignment(Qt.AlignCenter) self._on_selected_label_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(trans._('contiguous editing')) contig_cb.stateChanged.connect(self.change_contig) self.contigCheckBox = contig_cb self._on_contiguous_change() ndim_cb = QCheckBox() ndim_cb.setToolTip(trans._('edit all dimensions')) ndim_cb.stateChanged.connect(self.change_ndim) self.ndimCheckBox = ndim_cb self._on_n_dimensional_change() contour_sb = QSpinBox() contour_sb.setToolTip(trans._('display contours of labels')) contour_sb.valueChanged.connect(self.change_contour) self.contourSpinBox = contour_sb self.contourSpinBox.setKeyboardTracking(False) self.contourSpinBox.setSingleStep(1) self.contourSpinBox.setMinimum(0) self.contourSpinBox.setMaximum(2147483647) self.contourSpinBox.setAlignment(Qt.AlignCenter) self._on_contour_change() preserve_labels_cb = QCheckBox() preserve_labels_cb.setToolTip( trans._('preserve existing labels while painting')) preserve_labels_cb.stateChanged.connect(self.change_preserve_labels) self.preserveLabelsCheckBox = preserve_labels_cb self._on_preserve_labels_change() selectedColorCheckbox = QCheckBox() selectedColorCheckbox.setToolTip( trans._("Display only selected label")) selectedColorCheckbox.stateChanged.connect(self.toggle_selected_mode) self.selectedColorCheckbox = selectedColorCheckbox # shuffle colormap button self.colormapUpdate = QtModePushButton( None, 'shuffle', slot=self.changeColor, tooltip=trans._('shuffle colors'), ) self.panzoom_button = QtModeRadioButton( layer, 'zoom', Mode.PAN_ZOOM, tooltip=trans._('Pan/zoom mode (Space)'), checked=True, ) self.pick_button = QtModeRadioButton(layer, 'picker', Mode.PICK, tooltip=trans._('Pick mode (L)')) self.paint_button = QtModeRadioButton( layer, 'paint', Mode.PAINT, tooltip=trans._('Paint mode (P)')) self.fill_button = QtModeRadioButton( layer, 'fill', Mode.FILL, tooltip=trans._("Fill mode (F) \nToggle with {key}".format( key=KEY_SYMBOLS['Control'])), ) self.erase_button = QtModeRadioButton( layer, 'erase', Mode.ERASE, tooltip=trans._("Erase mode (E) \nToggle with {key}".format( key=KEY_SYMBOLS['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) brush_shape_comboBox = QComboBox(self) for index, (data, text) in enumerate(LABEL_BRUSH_SHAPE_TRANSLATIONS.items()): data = data.value brush_shape_comboBox.addItem(text, data) if self.layer.brush_shape == data: brush_shape_comboBox.setCurrentIndex(index) brush_shape_comboBox.activated[str].connect(self.change_brush_shape) self.brushShapeComboBox = brush_shape_comboBox self._on_brush_shape_change() color_mode_comboBox = QComboBox(self) for index, (data, text) in enumerate(LABEL_COLOR_MODE_TRANSLATIONS.items()): data = data.value color_mode_comboBox.addItem(text, data) if self.layer.color_mode == data: 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() self.colorBox = QtColorBox(layer) color_layout.addWidget(self.colorBox) 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(trans._('label:')), 1, 0, 1, 1) self.grid_layout.addLayout(color_layout, 1, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('opacity:')), 2, 0, 1, 1) self.grid_layout.addWidget(self.opacitySlider, 2, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('brush size:')), 3, 0, 1, 1) self.grid_layout.addWidget(self.brushSizeSlider, 3, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('brush shape:')), 4, 0, 1, 1) self.grid_layout.addWidget(self.brushShapeComboBox, 4, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('blending:')), 5, 0, 1, 1) self.grid_layout.addWidget(self.blendComboBox, 5, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('color mode:')), 6, 0, 1, 1) self.grid_layout.addWidget(self.colorModeComboBox, 6, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('contour:')), 7, 0, 1, 1) self.grid_layout.addWidget(self.contourSpinBox, 7, 1, 1, 1) self.grid_layout.addWidget(QLabel(trans._('contiguous:')), 8, 0, 1, 1) self.grid_layout.addWidget(self.contigCheckBox, 8, 1, 1, 1) self.grid_layout.addWidget(QLabel(trans._('n-dim:')), 8, 2, 1, 1) self.grid_layout.addWidget(self.ndimCheckBox, 8, 3, 1, 1) self.grid_layout.addWidget(QLabel(trans._('preserve labels:')), 9, 0, 1, 2) self.grid_layout.addWidget(self.preserveLabelsCheckBox, 9, 1, 1, 1) self.grid_layout.addWidget(QLabel(trans._('show selected:')), 9, 2, 1, 1) self.grid_layout.addWidget(self.selectedColorCheckbox, 9, 3, 1, 1) self.grid_layout.setRowStretch(9, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def _on_mode_change(self, event): """Receive layer model mode change event and update checkbox ticks. Parameters ---------- event : napari.utils.event.Event The napari event that triggered this method. 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(trans._("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 toggle_selected_mode(self, state): if state == Qt.Checked: self.layer.show_selected_label = True else: self.layer.show_selected_label = False 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_contour(self, value): """Change contour thickness. Parameters ---------- value : int Thickness of contour. """ self.layer.contour = value self.contourSpinBox.clearFocus() self.setFocus() 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 = self.colorModeComboBox.currentData() def change_brush_shape(self, brush_shape): """Change paintbrush shape of label layer. Parameters ---------- brush_shape : str CIRCLE (default) uses circle paintbrush (case insensitive). SQUARE uses square paintbrush (case insensitive). """ self.layer.brush_shape = self.brushShapeComboBox.currentData() def _on_contour_change(self, event=None): """Receive layer model contour value change event and update spinbox. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method. """ with self.layer.events.contour.blocker(): value = self.layer.contour self.contourSpinBox.setValue(int(value)) def _on_selected_label_change(self, event=None): """Receive layer model label selection change event and update spinbox. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method. """ 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 : napari.utils.event.Event, optional The napari event that triggered this method. """ 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_dimensional_change(self, event=None): """Receive layer model n-dim mode change event and update the checkbox. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method. """ with self.layer.events.n_dimensional.blocker(): self.ndimCheckBox.setChecked(self.layer.n_dimensional) def _on_contiguous_change(self, event=None): """Receive layer model contiguous change event and update the checkbox. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method. """ 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 : napari.utils.event.Event, optional The napari event that triggered this method. """ 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 : napari.utils.event.Event, optional The napari event that triggered this method. """ with self.layer.events.color_mode.blocker(): # `self.colorModeComboBox.findData` is not returning the correct index. for index in range(self.colorModeComboBox.count()): if self.layer.color_mode == self.colorModeComboBox.itemData( index): self.colorModeComboBox.setCurrentIndex(index) break def _on_brush_shape_change(self, event=None): """Receive brush shape change event and update dropdown menu. Parameters ---------- event : napari.utils.event.Event The napari event that triggered this method. """ with self.layer.events.brush_shape.blocker(): # `self.brushShapeComboBox.findData` is not returning the correct index. for index in range(self.brushShapeComboBox.count()): if self.layer.brush_shape == self.brushShapeComboBox.itemData( index): self.brushShapeComboBox.setCurrentIndex(index) break 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. """ disable_with_opacity( self, ['pick_button', 'paint_button', 'fill_button'], self.layer.editable, )
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 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, 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. selectionSpinBox : qtpy.QtWidgets.QSpinBox Widget to select a specfic label by its index. Raises ------ ValueError Raise error if label mode is not PAN_ZOOM, PICKER, PAINT, 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) # shuffle colormap button self.colormapUpdate = QPushButton('shuffle colors') self.colormapUpdate.setObjectName('shuffleButton') self.colormapUpdate.clicked.connect(self.changeColor) # 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() self.panzoom_button = QtModeRadioButton(layer, 'zoom', Mode.PAN_ZOOM, tooltip='Pan/zoom mode', checked=True) self.pick_button = QtModeRadioButton(layer, 'picker', Mode.PICKER, tooltip='Pick mode') self.paint_button = QtModeRadioButton(layer, 'paint', Mode.PAINT, tooltip='Paint mode') self.fill_button = QtModeRadioButton(layer, 'fill', Mode.FILL, tooltip='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._on_editable_change() button_row = QHBoxLayout() button_row.addStretch(1) 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.setSpacing(4) button_row.setContentsMargins(0, 0, 0, 5) 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, 1) self.grid_layout.addWidget(self.colormapUpdate, 0, 0) self.grid_layout.addWidget(QLabel('label:'), 1, 0) self.grid_layout.addLayout(color_layout, 1, 1) self.grid_layout.addWidget(QLabel('opacity:'), 2, 0) self.grid_layout.addWidget(self.opacitySlider, 2, 1) self.grid_layout.addWidget(QLabel('brush size:'), 3, 0) self.grid_layout.addWidget(self.brushSizeSlider, 3, 1) self.grid_layout.addWidget(QLabel('blending:'), 4, 0) self.grid_layout.addWidget(self.blendComboBox, 4, 1) 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): """On mouse move, set layer status equal to the current selected mode. Available mode options are: PAN_ZOOM, PICKER, PAINT, 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, PICKER, PAINT, or FILL """ 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 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 _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_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, )