def __init__(self, layer): super().__init__() self.layer = layer layer.events.blending.connect(self._on_blending_change) layer.events.opacity.connect(self._on_opacity_change) self.setObjectName('layer') self.setMouseTracking(True) self.grid_layout = QGridLayout() self.grid_layout.setContentsMargins(0, 0, 0, 0) self.grid_layout.setSpacing(2) self.setLayout(self.grid_layout) sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(100) sld.setSingleStep(1) sld.setValue(self.layer.opacity * 100) sld.valueChanged[int].connect( lambda value=sld: self.changeOpacity(value)) self.opacitySilder = sld blend_comboBox = QComboBox() for blend in Blending: blend_comboBox.addItem(str(blend)) index = blend_comboBox.findText(self.layer.blending, Qt.MatchFixedString) blend_comboBox.setCurrentIndex(index) blend_comboBox.activated[str].connect( lambda text=blend_comboBox: self.changeBlending(text)) self.blendComboBox = blend_comboBox
def __init__(self, layer): super().__init__() self.layer = layer layer.events.blending.connect(self._on_blending_change) layer.events.opacity.connect(self._on_opacity_change) self.setObjectName('layer') self.setMouseTracking(True) self.grid_layout = QGridLayout(self) self.grid_layout.setContentsMargins(0, 0, 0, 0) self.grid_layout.setSpacing(2) self.grid_layout.setColumnMinimumWidth(0, 86) self.grid_layout.setColumnStretch(1, 1) self.setLayout(self.grid_layout) sld = QSlider(Qt.Horizontal, parent=self) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(100) sld.setSingleStep(1) sld.valueChanged.connect(self.changeOpacity) self.opacitySlider = sld self._on_opacity_change() blend_comboBox = QComboBox(self) blend_comboBox.addItems(Blending.keys()) index = blend_comboBox.findText( self.layer.blending, Qt.MatchFixedString ) blend_comboBox.setCurrentIndex(index) blend_comboBox.activated[str].connect(self.changeBlending) self.blendComboBox = blend_comboBox
class LabeledSlider(QWidget): def __init__(self, text, *args, minimum=1, maximum=10, parent=None, **kwargs): super(LabeledSlider, self).__init__(parent) self.label = QLabel(text) self.slider = QSlider(*args, **kwargs) self.slider.setMinimum(minimum) self.slider.setMaximum(maximum) self.slider.setTickInterval(5) self.valuebox = QSpinBox() self.valuebox.setRange(minimum, maximum) self.valuebox.setValue(self.slider.value()) self.slider.valueChanged.connect(self.valuebox.setValue) self.valuebox.valueChanged.connect(self.slider.setValue) layout = QHBoxLayout() layout.addWidget(self.label) layout.addWidget(self.slider) layout.addWidget(self.valuebox) self.setLayout(layout)
class Viewer(QWidget): def __init__(self, video): super().__init__() self.framenum = 0 self.video = video self.label = QLabel(self) self.vidlength = video.shape[0]-1 lisbon = Lisbon() p = self.palette() p.setColor(self.backgroundRole(), Qt.cyan) self.setPalette(p) self.setAutoFillBackground(True) self.initSlider() self.initUI() def initUI(self): #initialise UI self.update() self.slider.setGeometry(10, self.pixmap.height()+10, self.pixmap.width(), 50) self.label.setMargin(10) self.setGeometry(0, 0, self.pixmap.width()+20, (self.pixmap.height()+self.slider.height()*2)) self.slider.valueChanged.connect(self.valuechange) def update(self): #Update displayed image lisbon = Lisbon() self.npimage = self.video[self.framenum] self.image = self.np2qt(self.npimage) self.pixmap = QPixmap(QPixmap.fromImage(self.image)) self.label.setPixmap(self.pixmap) def wheelEvent(self, event): #scroll through slices and go to beginning if reached max self.framenum = self.framenum + int(event.angleDelta().y()/120) if self.framenum > self.vidlength: self.framenum = 0 if self.framenum < 0: self.framenum = self.vidlength self.slider.setValue(self.framenum) self.update() def np2qt(self, image): #transform np cv2 image to qt format height, width, channel = image.shape bytesPerLine = 3 * width return QImage(image.data, width, height, bytesPerLine, QImage.Format_RGB888) def initSlider(self): self.slider = QSlider(Qt.Horizontal, self) self.slider.setMinimum(0) self.slider.setMaximum(self.vidlength) def valuechange(self): self.framenum = self.slider.value() self.update()
def __init__(self, layer): super().__init__(layer) 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) sld = QSlider(Qt.Horizontal, self) sld.setFocusPolicy(Qt.NoFocus) sld.setFixedWidth(75) 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 self.grid_layout.addWidget(QLabel('width:'), 3, 0) self.grid_layout.addWidget(sld, 3, 1) face_comboBox = QComboBox() colors = self.layer._colors for c in colors: face_comboBox.addItem(c) index = face_comboBox.findText( self.layer.face_color, Qt.MatchFixedString) if index >= 0: face_comboBox.setCurrentIndex(index) face_comboBox.activated[str].connect(lambda text=face_comboBox: self.changeFaceColor(text)) self.faceComboBox = face_comboBox self.grid_layout.addWidget(QLabel('face_color:'), 4, 0) self.grid_layout.addWidget(face_comboBox, 4, 1) edge_comboBox = QComboBox() colors = self.layer._colors for c in colors: edge_comboBox.addItem(c) index = edge_comboBox.findText( self.layer.edge_color, Qt.MatchFixedString) if index >= 0: edge_comboBox.setCurrentIndex(index) edge_comboBox.activated[str].connect(lambda text=edge_comboBox: self.changeEdgeColor(text)) self.edgeComboBox = edge_comboBox self.grid_layout.addWidget(QLabel('edge_color:'), 5, 0) self.grid_layout.addWidget(edge_comboBox, 5, 1) self.setExpanded(False)
class QLabeledSlider(QWidget): """ A labeled slider widget """ range = None integer = None def __init__(self, parent=None): super(QLabeledSlider, self).__init__(parent) self._range = range self._slider = QSlider() self._slider.setMinimum(0) self._slider.setMaximum(100) self._slider.setOrientation(Qt.Horizontal) self._label = QLabel('') self._layout = QHBoxLayout() self._layout.setContentsMargins(2, 2, 2, 2) self._layout.addWidget(self._slider) self._layout.addWidget(self._label) self._slider.valueChanged.connect(self._update_label) self.setLayout(self._layout) def _update_label(self, *args): self._label.setText(str(self.value())) @property def valueChanged(self): return self._slider.valueChanged def value(self, layer=None, view=None): value = self._slider.value() / 100. * (self.range[1] - self.range[0]) + self.range[0] if self.integer: return int(value) else: return (value) _in_set_value = False def setValue(self, value): if self._in_set_value: return self._in_set_value = True value = int(100 * (value - self.range[0]) / (self.range[1] - self.range[0])) self._slider.setValue(value) self._in_set_value = False
class QTiffStackView(QWidget): #the view which the user of the videoviewer sees. #This class contains relevant 'client side' attributes e.g. buttons to get a frame, a slide bar and a timer. These attributes submit requests to the QTiffStackController to give the next frame etc. The controller returns either the requested frame or an error message def __init__(self): super(QTiffStackView, self).__init__() #add the image display. This is a subclass of QLabel, where paintEvent is overriden. self.frame_view = FrameView() #self.frame_view.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding) #add the slide bar which allows the user to manual flick through self.slideBar = QSlider(Qt.Horizontal) self.slideBar.setTickPosition(QSlider.TicksAbove) self.slideBar.setTracking(True) self.slideBar.setTickInterval(100) #add a counter which displays the frame which is currently displayed self.counter = QSpinBox() self.counter.setSingleStep(1) self.counter.setRange(self.slideBar.minimum(), self.slideBar.maximum()) #self explanatory self.play = QPushButton('Play') #when play button is pressed the timer takes control of the displaying of frames self.frametimer = QTimer() frame_rate = 30 self.frametimer.setInterval(frame_rate) #Add a sublayout to align the slidebar and frame counter next to eachother slidelyt = QHBoxLayout() slidelyt.addWidget(self.slideBar) slidelyt.addWidget(self.counter) #Add the main layout for the widget lyt = QVBoxLayout() lyt.addWidget(self.frame_view) lyt.addLayout(slidelyt) lyt.addWidget(self.play) self.setLayout(lyt) def updateRanges(self, maximum): assert type(maximum) == int self.slideBar.setMaximum(maximum) self.counter.setRange(self.slideBar.minimum(), self.slideBar.maximum())
class QLabeledSlider(QWidget): """ A labeled slider widget """ range = None integer = None def __init__(self, parent=None): super(QLabeledSlider, self).__init__(parent) self._range = range self._slider = QSlider() self._slider.setMinimum(0) self._slider.setMaximum(100) self._slider.setOrientation(Qt.Horizontal) self._label = QLabel('') self._layout = QHBoxLayout() self._layout.setContentsMargins(2, 2, 2, 2) self._layout.addWidget(self._slider) self._layout.addWidget(self._label) self._slider.valueChanged.connect(self._update_label) self.setLayout(self._layout) def _update_label(self, *args): self._label.setText(str(self.value())) @property def valueChanged(self): return self._slider.valueChanged def value(self, layer=None, view=None): value = self._slider.value() / 100. * (self.range[1] - self.range[0]) + self.range[0] if self.integer: return int(value) else: return(value) _in_set_value = False def setValue(self, value): if self._in_set_value: return self._in_set_value = True value = int(100 * (value - self.range[0]) / (self.range[1] - self.range[0])) self._slider.setValue(value) self._in_set_value = False
def __init__(self, layer): super().__init__(layer) self.layer.events.colormap.connect(self._on_colormap_change) self.layer.events.gamma.connect(self._on_gamma_change) self.layer.events.contrast_limits.connect( self._on_contrast_limits_change ) comboBox = QtColormapComboBox(self) comboBox.setObjectName("colormapComboBox") comboBox._allitems = set(self.layer.colormaps) for name, cm in AVAILABLE_COLORMAPS.items(): if name in self.layer.colormaps: comboBox.addItem(cm._display_name, name) comboBox.activated[str].connect(self.changeColor) self.colormapComboBox = comboBox # Create contrast_limits slider self.contrastLimitsSlider = QHRangeSlider( self.layer.contrast_limits, self.layer.contrast_limits_range, parent=self, ) self.contrastLimitsSlider.mousePressEvent = self._clim_mousepress set_clim = partial(setattr, self.layer, 'contrast_limits') set_climrange = partial(setattr, self.layer, 'contrast_limits_range') self.contrastLimitsSlider.valuesChanged.connect(set_clim) self.contrastLimitsSlider.rangeChanged.connect(set_climrange) # gamma slider sld = QSlider(Qt.Horizontal, parent=self) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(2) sld.setMaximum(200) sld.setSingleStep(2) sld.setValue(100) sld.valueChanged.connect(self.gamma_slider_changed) self.gammaSlider = sld self._on_gamma_change() self.colorbarLabel = QLabel(parent=self) self.colorbarLabel.setObjectName('colorbar') self.colorbarLabel.setToolTip(trans._('Colorbar')) self._on_colormap_change()
def __init__(self, vpoints: Sequence[VPoint], path: Sequence[Sequence[Tuple[float, float]]], monochrome: bool, parent: QWidget): super(AnimateDialog, self).__init__(parent) self.setWindowTitle("Animation") self.setWindowFlags(self.windowFlags() | Qt.WindowMaximizeButtonHint) self.setMinimumSize(800, 600) self.setModal(True) layout = QVBoxLayout(self) canvas = _DynamicCanvas(vpoints, path, self) canvas.set_monochrome_mode(monochrome) layout.addWidget(canvas) slider = QSlider(Qt.Horizontal, self) slider.setMaximum(max(len(p) for p in path)) slider.valueChanged.connect(canvas.set_index) layout.addWidget(slider)
def __init__(self, layer): super().__init__(layer) self.layer.events.colormap.connect(self._on_colormap_change) self.layer.events.contrast_limits.connect( lambda e: self.contrast_limits_slider_update() ) self.layer.events.gamma.connect(lambda e: self.gamma_slider_update()) comboBox = QComboBox() for cmap in self.layer.colormaps: comboBox.addItem(cmap) comboBox._allitems = set(self.layer.colormaps) comboBox.activated[str].connect( lambda text=comboBox: self.changeColor(text) ) self.colormapComboBox = comboBox # Create contrast_limits slider self.contrastLimitsSlider = QHRangeSlider( slider_range=[0, 1, 0.0001], values=[0, 1] ) self.contrastLimitsSlider.setEmitWhileMoving(True) self.contrastLimitsSlider.collapsable = False self.contrastLimitsSlider.setEnabled(True) self.contrastLimitsSlider.rangeChanged.connect( self.contrast_limits_slider_changed ) self.contrast_limits_slider_update() # gamma slider sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(2) sld.setMaximum(200) sld.setSingleStep(2) sld.setValue(100) sld.valueChanged[int].connect(self.gamma_slider_changed) self.gammaSlider = sld self.gamma_slider_update() self.colorbarLabel = QLabel() self.colorbarLabel.setObjectName('colorbar') self.colorbarLabel.setToolTip('Colorbar') self._on_colormap_change(None)
def __init__(self, layer): super().__init__(layer) self.layer.events.colormap.connect(self._on_colormap_change) self.layer.events.gamma.connect(self.gamma_slider_update) self.layer.events.contrast_limits.connect(self._on_clims_change) comboBox = QComboBox() comboBox.addItems(self.layer.colormaps) comboBox._allitems = set(self.layer.colormaps) comboBox.activated[str].connect(self.changeColor) self.colormapComboBox = comboBox # Create contrast_limits slider self.contrastLimitsSlider = QHRangeSlider( self.layer.contrast_limits, self.layer.contrast_limits_range ) self.contrastLimitsSlider.mousePressEvent = self._clim_mousepress set_clim = partial(setattr, self.layer, 'contrast_limits') set_climrange = partial(setattr, self.layer, 'contrast_limits_range') self.contrastLimitsSlider.valuesChanged.connect(set_clim) self.contrastLimitsSlider.rangeChanged.connect(set_climrange) # gamma slider sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(2) sld.setMaximum(200) sld.setSingleStep(2) sld.setValue(100) sld.valueChanged.connect(self.gamma_slider_changed) self.gammaSlider = sld self.gamma_slider_update() self.colorbarLabel = QLabel() self.colorbarLabel.setObjectName('colorbar') self.colorbarLabel.setToolTip('Colorbar') self._on_colormap_change()
def __init__(self, layer): super().__init__() self.layer = layer self.layer.events.blending.connect(self._on_blending_change) self.layer.events.opacity.connect(self._on_opacity_change) self.setAttribute(Qt.WA_DeleteOnClose) self.setObjectName('layer') self.setMouseTracking(True) self.grid_layout = QGridLayout(self) self.grid_layout.setContentsMargins(0, 0, 0, 0) self.grid_layout.setSpacing(2) self.grid_layout.setColumnMinimumWidth(0, 86) self.grid_layout.setColumnStretch(1, 1) self.setLayout(self.grid_layout) sld = QSlider(Qt.Horizontal, parent=self) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(100) sld.setSingleStep(1) sld.valueChanged.connect(self.changeOpacity) self.opacitySlider = sld self._on_opacity_change() blend_comboBox = QComboBox(self) for index, (data, text) in enumerate(BLENDING_TRANSLATIONS.items()): data = data.value blend_comboBox.addItem(text, data) if data == self.layer.blending: blend_comboBox.setCurrentIndex(index) blend_comboBox.activated[str].connect(self.changeBlending) self.blendComboBox = blend_comboBox
def __init__(self, layer): super().__init__(layer) self.layer.events.n_dimensional.connect(self._on_n_dim_change) self.layer.events.symbol.connect(self._on_symbol_change) self.layer.events.size.connect(self._on_size_change) self.layer.events.edge_color.connect(self._on_edge_color_change) self.layer.events.face_color.connect(self._on_face_color_change) sld = QSlider(Qt.Horizontal, self) sld.setFocusPolicy(Qt.NoFocus) sld.setFixedWidth(110) sld.setMinimum(0) sld.setMaximum(100) sld.setSingleStep(1) value = self.layer.size 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.changeSize(value)) self.sizeSlider = sld self.grid_layout.addWidget(QLabel('size:'), 3, 0) self.grid_layout.addWidget(sld, 3, 1) face_comboBox = QComboBox() colors = self.layer._colors for c in colors: face_comboBox.addItem(c) index = face_comboBox.findText(self.layer.face_color, Qt.MatchFixedString) face_comboBox.setCurrentIndex(index) face_comboBox.activated[str].connect( lambda text=face_comboBox: self.changeFaceColor(text)) self.faceComboBox = face_comboBox self.grid_layout.addWidget(QLabel('face_color:'), 4, 0) self.grid_layout.addWidget(face_comboBox, 4, 1) edge_comboBox = QComboBox() colors = self.layer._colors for c in colors: edge_comboBox.addItem(c) index = edge_comboBox.findText(self.layer.edge_color, Qt.MatchFixedString) edge_comboBox.setCurrentIndex(index) edge_comboBox.activated[str].connect( lambda text=edge_comboBox: self.changeEdgeColor(text)) self.edgeComboBox = edge_comboBox self.grid_layout.addWidget(QLabel('edge_color:'), 5, 0) self.grid_layout.addWidget(edge_comboBox, 5, 1) symbol_comboBox = QComboBox() for s in Symbol: symbol_comboBox.addItem(str(s)) index = symbol_comboBox.findText(self.layer.symbol, Qt.MatchFixedString) symbol_comboBox.setCurrentIndex(index) symbol_comboBox.activated[str].connect( lambda text=symbol_comboBox: self.changeSymbol(text)) self.symbolComboBox = symbol_comboBox self.grid_layout.addWidget(QLabel('symbol:'), 6, 0) self.grid_layout.addWidget(symbol_comboBox, 6, 1) ndim_cb = QCheckBox() ndim_cb.setToolTip('N-dimensional markers') ndim_cb.setChecked(self.layer.n_dimensional) ndim_cb.stateChanged.connect( lambda state=ndim_cb: self.change_ndim(state)) self.ndimCheckBox = ndim_cb self.grid_layout.addWidget(QLabel('n-dim:'), 7, 0) self.grid_layout.addWidget(ndim_cb, 7, 1) self.setExpanded(False)
class QtTracksControls(QtLayerControls): """Qt view and controls for the Tracks layer. Parameters ---------- layer : napari.layers.Tracks An instance of a Tracks layer. Attributes ---------- grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : layers.Tracks An instance of a Tracks layer. """ layer: 'napari.layers.Tracks' def __init__(self, layer): super().__init__(layer) # NOTE(arl): there are no events fired for changing checkboxes self.layer.events.tail_width.connect(self._on_tail_width_change) self.layer.events.tail_length.connect(self._on_tail_length_change) self.layer.events.head_length.connect(self._on_head_length_change) self.layer.events.properties.connect(self._on_properties_change) self.layer.events.colormap.connect(self._on_colormap_change) self.layer.events.color_by.connect(self._on_color_by_change) # combo box for track coloring, we can get these from the properties # keys self.color_by_combobox = QComboBox() self.color_by_combobox.addItems(self.layer.properties_to_color_by) self.colormap_combobox = QComboBox() for name, colormap in AVAILABLE_COLORMAPS.items(): display_name = colormap._display_name self.colormap_combobox.addItem(display_name, name) # slider for track head length self.head_length_slider = QSlider(Qt.Horizontal) self.head_length_slider.setFocusPolicy(Qt.NoFocus) self.head_length_slider.setMinimum(0) self.head_length_slider.setMaximum(self.layer._max_length) self.head_length_slider.setSingleStep(1) # slider for track tail length self.tail_length_slider = QSlider(Qt.Horizontal) self.tail_length_slider.setFocusPolicy(Qt.NoFocus) self.tail_length_slider.setMinimum(1) self.tail_length_slider.setMaximum(self.layer._max_length) self.tail_length_slider.setSingleStep(1) # slider for track edge width self.tail_width_slider = QSlider(Qt.Horizontal) self.tail_width_slider.setFocusPolicy(Qt.NoFocus) self.tail_width_slider.setMinimum(1) self.tail_width_slider.setMaximum(int(2 * self.layer._max_width)) self.tail_width_slider.setSingleStep(1) # checkboxes for display self.id_checkbox = QCheckBox() self.tail_checkbox = QCheckBox() self.tail_checkbox.setChecked(True) self.graph_checkbox = QCheckBox() self.graph_checkbox.setChecked(True) self.tail_width_slider.valueChanged.connect(self.change_tail_width) self.tail_length_slider.valueChanged.connect(self.change_tail_length) self.head_length_slider.valueChanged.connect(self.change_head_length) self.tail_checkbox.stateChanged.connect(self.change_display_tail) self.id_checkbox.stateChanged.connect(self.change_display_id) self.graph_checkbox.stateChanged.connect(self.change_display_graph) self.color_by_combobox.currentTextChanged.connect(self.change_color_by) self.colormap_combobox.currentTextChanged.connect(self.change_colormap) self.layout().addRow(trans._('color by:'), self.color_by_combobox) self.layout().addRow(trans._('colormap:'), self.colormap_combobox) self.layout().addRow(trans._('blending:'), self.blendComboBox) self.layout().addRow(trans._('opacity:'), self.opacitySlider) self.layout().addRow(trans._('tail width:'), self.tail_width_slider) self.layout().addRow(trans._('tail length:'), self.tail_length_slider) self.layout().addRow(trans._('head length:'), self.head_length_slider) self.layout().addRow(trans._('tail:'), self.tail_checkbox) self.layout().addRow(trans._('show ID:'), self.id_checkbox) self.layout().addRow(trans._('graph:'), self.graph_checkbox) self._on_tail_length_change() self._on_tail_width_change() self._on_colormap_change() self._on_color_by_change() def _on_tail_width_change(self): """Receive layer model track line width change event and update slider.""" with self.layer.events.tail_width.blocker(): value = int(2 * self.layer.tail_width) self.tail_width_slider.setValue(value) def _on_tail_length_change(self): """Receive layer model track line width change event and update slider.""" with self.layer.events.tail_length.blocker(): value = self.layer.tail_length self.tail_length_slider.setValue(value) def _on_head_length_change(self): """Receive layer model track line width change event and update slider.""" with self.layer.events.head_length.blocker(): value = self.layer.head_length self.head_length_slider.setValue(value) def _on_properties_change(self): """Change the properties that can be used to color the tracks.""" with self.layer.events.properties.blocker(): with qt_signals_blocked(self.color_by_combobox): self.color_by_combobox.clear() self.color_by_combobox.addItems(self.layer.properties_to_color_by) def _on_colormap_change(self): """Receive layer model colormap change event and update combobox.""" with self.layer.events.colormap.blocker(): self.colormap_combobox.setCurrentIndex( self.colormap_combobox.findData(self.layer.colormap)) def _on_color_by_change(self): """Receive layer model color_by change event and update combobox.""" with self.layer.events.color_by.blocker(): color_by = self.layer.color_by idx = self.color_by_combobox.findText(color_by, Qt.MatchFixedString) self.color_by_combobox.setCurrentIndex(idx) def change_tail_width(self, value): """Change track line width of shapes on the layer model. Parameters ---------- value : float Line width of track tails. """ self.layer.tail_width = float(value) / 2.0 def change_tail_length(self, value): """Change edge line backward length of shapes on the layer model. Parameters ---------- value : int Line length of track tails. """ self.layer.tail_length = value def change_head_length(self, value): """Change edge line forward length of shapes on the layer model. Parameters ---------- value : int Line length of track tails. """ self.layer.head_length = value def change_display_tail(self, state): self.layer.display_tail = self.tail_checkbox.isChecked() def change_display_id(self, state): self.layer.display_id = self.id_checkbox.isChecked() def change_display_graph(self, state): self.layer.display_graph = self.graph_checkbox.isChecked() def change_color_by(self, value: str): self.layer.color_by = value def change_colormap(self, colormap: str): self.layer.colormap = self.colormap_combobox.currentData()
def __init__(self, layer): super().__init__(layer) self.layer.events.colormap.connect(self._on_colormap_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.colormap_update = QPushButton('click') self.colormap_update.setObjectName('shuffle') self.colormap_update.clicked.connect(self.changeColor) self.colormap_update.setFixedWidth(112) self.colormap_update.setFixedHeight(25) shuffle_label = QLabel('shuffle colors:') shuffle_label.setObjectName('shuffle-label') row = self.grid_layout.rowCount() self.grid_layout.addWidget(shuffle_label, row, self.name_column) self.grid_layout.addWidget( self.colormap_update, row, self.property_column ) # selection spinbox self.selection_spinbox = QSpinBox() self.selection_spinbox.setSingleStep(1) self.selection_spinbox.setMinimum(0) self.selection_spinbox.setMaximum(2147483647) self.selection_spinbox.setValue(self.layer.selected_label) self.selection_spinbox.setFixedWidth(75) self.selection_spinbox.valueChanged.connect(self.changeSelection) row = self.grid_layout.rowCount() self.grid_layout.addWidget(QLabel('label:'), row, self.name_column) self.grid_layout.addWidget( self.selection_spinbox, row, self.property_column ) sld = QSlider(Qt.Horizontal, self) sld.setFocusPolicy(Qt.NoFocus) sld.setFixedWidth(110) sld.setMinimum(1) sld.setMaximum(40) sld.setSingleStep(1) value = self.layer.brush_size if isinstance(value, Iterable): if isinstance(value, list): value = np.asarray(value) value = value[:2].mean() sld.setValue(int(value)) sld.valueChanged[int].connect(lambda value=sld: self.changeSize(value)) self.brush_size_slider = sld row = self.grid_layout.rowCount() self.grid_layout.addWidget( QLabel('brush size:'), row, self.name_column ) self.grid_layout.addWidget(sld, row, self.property_column) contig_cb = QCheckBox() contig_cb.setToolTip('contiguous editing') contig_cb.setChecked(self.layer.contiguous) contig_cb.stateChanged.connect( lambda state=contig_cb: self.change_contig(state) ) self.contig_checkbox = contig_cb row = self.grid_layout.rowCount() self.grid_layout.addWidget( QLabel('contiguous:'), row, self.name_column ) self.grid_layout.addWidget(contig_cb, row, self.property_column) ndim_cb = QCheckBox() ndim_cb.setToolTip('n-dimensional editing') ndim_cb.setChecked(self.layer.n_dimensional) ndim_cb.stateChanged.connect( lambda state=ndim_cb: self.change_ndim(state) ) self.ndim_checkbox = ndim_cb row = self.grid_layout.rowCount() self.grid_layout.addWidget(QLabel('n-dim:'), row, self.name_column) self.grid_layout.addWidget(ndim_cb, row, self.property_column) self.setExpanded(False)
class AnimateDialog(QDialog): def __init__(self, vpoints: Sequence[VPoint], vlinks: Sequence[VLink], path: _Paths, slider_path: _SliderPaths, monochrome: bool, parent: QWidget): super(AnimateDialog, self).__init__(parent) self.setWindowTitle("Vector Animation") self.setWindowFlags(self.windowFlags() | Qt.WindowMaximizeButtonHint & ~Qt.WindowContextHelpButtonHint) self.setMinimumSize(800, 600) self.setModal(True) main_layout = QVBoxLayout(self) self.canvas = _DynamicCanvas(vpoints, vlinks, path, slider_path, self) self.canvas.set_monochrome_mode(monochrome) self.canvas.update_pos.connect(self.__set_pos) layout = QHBoxLayout(self) pt_option = QComboBox(self) pt_option.addItems([f"P{p}" for p in range(len(vpoints))]) layout.addWidget(pt_option) value_label = QLabel(self) @Slot(int) def show_values(ind: int): vel, vel_deg = self.canvas.get_vel(ind) acc, acc_deg = self.canvas.get_acc(ind) value_label.setText( f"Velocity: {vel:.04f} ({vel_deg:.04f}deg) | " f"Acceleration: {acc:.04f} ({acc_deg:.04f}deg)") pt_option.currentIndexChanged.connect(show_values) layout.addWidget(value_label) self.pos_label = QLabel(self) layout.addItem( QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)) layout.addWidget(self.pos_label) main_layout.addLayout(layout) main_layout.addWidget(self.canvas) layout = QHBoxLayout(self) self.play = QPushButton(QIcon(QPixmap(":/icons/play.png")), "", self) self.play.setCheckable(True) self.play.clicked.connect(self.__play) layout.addWidget(self.play) self.slider = QSlider(Qt.Horizontal, self) self.slider.setMaximum(max(len(p) for p in path) - 1) self.slider.valueChanged.connect(self.canvas.set_index) layout.addWidget(self.slider) layout.addWidget(QLabel("Total times:", self)) factor = QDoubleSpinBox(self) factor.valueChanged.connect(self.canvas.set_factor) factor.setSuffix('s') factor.setRange(0.01, 999999) factor.setValue(10) layout.addWidget(factor) main_layout.addLayout(layout) self.timer = QTimer() self.timer.setInterval(10) self.timer.timeout.connect(self.__move_ind) @Slot() def __move_ind(self): """Move indicator.""" value = self.slider.value() + 1 self.slider.setValue(value) if value > self.slider.maximum(): self.slider.setValue(0) @Slot(float, float) def __set_pos(self, x: float, y: float) -> None: """Set mouse position.""" self.pos_label.setText(f"({x:.04f}, {y:.04f})") @Slot() def __play(self): """Start playing.""" if self.play.isChecked(): self.timer.start() else: self.timer.stop()
class PyDMSlider(QFrame, TextFormatter, PyDMWritableWidget): """ A QSlider with support for Channels and more from PyDM. Parameters ---------- parent : QWidget The parent widget for the Label init_channel : str, optional The channel to be used by the widget. """ actionTriggered = Signal(int) rangeChanged = Signal(float, float) sliderMoved = Signal(float) sliderPressed = Signal() sliderReleased = Signal() valueChanged = Signal(float) def __init__(self, parent=None, init_channel=None): QFrame.__init__(self, parent) PyDMWritableWidget.__init__(self, init_channel=init_channel) self.alarmSensitiveContent = True self.alarmSensitiveBorder = False # Internal values for properties self._show_limit_labels = True self._show_value_label = True self._user_defined_limits = False self._needs_limit_info = True self._minimum = None self._maximum = None self._user_minimum = -10.0 self._user_maximum = 10.0 self._num_steps = 101 self._orientation = Qt.Horizontal # Set up all the internal widgets that make up a PyDMSlider. # We'll add all these things to layouts when we call setup_widgets_for_orientation label_size_policy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed) self.low_lim_label = QLabel(self) self.low_lim_label.setObjectName("lowLimLabel") self.low_lim_label.setSizePolicy(label_size_policy) self.low_lim_label.setAlignment(Qt.AlignLeft | Qt.AlignTrailing | Qt.AlignVCenter) self.value_label = QLabel(self) self.value_label.setObjectName("valueLabel") self.value_label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.high_lim_label = QLabel(self) self.high_lim_label.setObjectName("highLimLabel") self.high_lim_label.setSizePolicy(label_size_policy) self.high_lim_label.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) self._slider = QSlider(parent=self) self._slider.setOrientation(Qt.Horizontal) self._slider.sliderMoved.connect(self.internal_slider_moved) self._slider.sliderPressed.connect(self.internal_slider_pressed) self._slider.sliderReleased.connect(self.internal_slider_released) self._slider.valueChanged.connect(self.internal_slider_value_changed) # self.vertical_layout.addWidget(self._slider) # Other internal variables and final setup steps self._slider_position_to_value_map = None self._mute_internal_slider_changes = False self.setup_widgets_for_orientation(self._orientation) self.reset_slider_limits() def init_for_designer(self): """ Method called after the constructor to tweak configurations for when using the widget with the Qt Designer """ self.value = 0.0 @Property(Qt.Orientation) def orientation(self): """ The slider orientation (Horizontal or Vertical) Returns ------- int Qt.Horizontal or Qt.Vertical """ return self._orientation @orientation.setter def orientation(self, new_orientation): """ The slider orientation (Horizontal or Vertical) Parameters ---------- new_orientation : int Qt.Horizontal or Qt.Vertical """ self._orientation = new_orientation self.setup_widgets_for_orientation(new_orientation) def setup_widgets_for_orientation(self, new_orientation): """ Reconstruct the widget given the orientation. Parameters ---------- new_orientation : int Qt.Horizontal or Qt.Vertical """ if new_orientation not in (Qt.Horizontal, Qt.Vertical): logger.error("Invalid orientation '{0}'. The existing layout will not change.".format(new_orientation)) return layout = None if new_orientation == Qt.Horizontal: layout = QVBoxLayout() layout.setContentsMargins(4, 0, 4, 4) label_layout = QHBoxLayout() label_layout.addWidget(self.low_lim_label) label_layout.addStretch(0) label_layout.addWidget(self.value_label) label_layout.addStretch(0) label_layout.addWidget(self.high_lim_label) layout.addLayout(label_layout) self._slider.setOrientation(new_orientation) layout.addWidget(self._slider) elif new_orientation == Qt.Vertical: layout = QHBoxLayout() layout.setContentsMargins(0, 4, 4, 4) label_layout = QVBoxLayout() label_layout.addWidget(self.high_lim_label) label_layout.addStretch(0) label_layout.addWidget(self.value_label) label_layout.addStretch(0) label_layout.addWidget(self.low_lim_label) layout.addLayout(label_layout) self._slider.setOrientation(new_orientation) layout.addWidget(self._slider) if self.layout() is not None: # Trick to remove the existing layout by re-parenting it in an empty widget. QWidget().setLayout(self.layout()) self.setLayout(layout) def update_labels(self): """ Update the limits and value labels with the correct values. """ def set_label(value, label_widget): if value is None: label_widget.setText("") else: label_widget.setText(self.format_string.format(value)) set_label(self.minimum, self.low_lim_label) set_label(self.maximum, self.high_lim_label) set_label(self.value, self.value_label) def reset_slider_limits(self): """ Reset the limits and adjust the labels properly for the slider. """ if self.minimum is None or self.maximum is None: self._needs_limit_info = True self.set_enable_state() return self._needs_limit_info = False self._slider.setMinimum(0) self._slider.setMaximum(self._num_steps - 1) self._slider.setSingleStep(1) self._slider.setPageStep(10) self._slider_position_to_value_map = np.linspace(self.minimum, self.maximum, num=self._num_steps) self.update_labels() self.set_slider_to_closest_value(self.value) self.rangeChanged.emit(self.minimum, self.maximum) self.set_enable_state() def find_closest_slider_position_to_value(self, val): """ Find and returns the index for the closest position on the slider for a given value. Parameters ---------- val : float Returns ------- int """ diff = abs(self._slider_position_to_value_map - float(val)) return np.argmin(diff) def set_slider_to_closest_value(self, val): """ Set the value for the slider according to a given value. Parameters ---------- val : float """ if val is None or self._needs_limit_info: return # When we set the slider to the closest value, it may end up at a slightly # different position than val (if val is not in self._slider_position_to_value_map) # We don't want that slight difference to get broacast out and put the channel # somewhere new. For example, if the slider can only be at 0.4 or 0.5, but a # new value comes in of 0.45, its more important to keep the 0.45 than to change # it to where the slider gets set. Therefore, we mute the internal slider changes # so that its valueChanged signal doesn't cause us to emit a signal to PyDM to change # the value of the channel. self._mute_internal_slider_changes = True self._slider.setValue(self.find_closest_slider_position_to_value(val)) self._mute_internal_slider_changes = False def value_changed(self, new_val): """ Callback invoked when the Channel value is changed. Parameters ---------- new_val : int or float The new value from the channel. """ PyDMWritableWidget.value_changed(self, new_val) if hasattr(self, "value_label"): self.value_label.setText(self.format_string.format(self.value)) if not self._slider.isSliderDown(): self.set_slider_to_closest_value(self.value) def ctrl_limit_changed(self, which, new_limit): """ Callback invoked when the Channel receives new control limit values. Parameters ---------- which : str Which control limit was changed. "UPPER" or "LOWER" new_limit : float New value for the control limit """ PyDMWritableWidget.ctrl_limit_changed(self, which, new_limit) if not self.userDefinedLimits: self.reset_slider_limits() def update_format_string(self): """ Reconstruct the format string to be used when representing the output value. Returns ------- format_string : str The format string to be used including or not the precision and unit """ fs = super(PyDMSlider, self).update_format_string() self.update_labels() return fs def set_enable_state(self): """ Determines wether or not the widget must be enabled or not depending on the write access, connection state and presence of limits information """ # Even though by documentation disabling parent QFrame (self), should disable internal # slider, in practice it still remains interactive (PyQt 5.12.1). Disabling explicitly, solves # the problem. should_enable = self._write_access and self._connected and not self._needs_limit_info self.setEnabled(should_enable) self._slider.setEnabled(should_enable) @Slot(int) def internal_slider_action_triggered(self, action): self.actionTriggered.emit(action) @Slot(int) def internal_slider_moved(self, val): """ Method invoked when the slider is moved. Parameters ---------- val : float """ # Avoid potential crash if limits are undefined if self._slider_position_to_value_map is None: return # The user has moved the slider, we need to update our value. # Only update the underlying value, not the self.value property, # because we don't need to reset the slider position. If we change # self.value, we can get into a loop where the position changes, which # updates the value, which changes the position again, etc etc. self.value = self._slider_position_to_value_map[val] self.sliderMoved.emit(self.value) @Slot() def internal_slider_pressed(self): """ Method invoked when the slider is pressed """ self.sliderPressed.emit() @Slot() def internal_slider_released(self): """ Method invoked when the slider is released """ self.sliderReleased.emit() @Slot(int) def internal_slider_value_changed(self, val): """ Method invoked when a new value is selected on the slider. This will cause the new value to be emitted to the signal unless `mute_internal_slider_changes` is True. Parameters ---------- val : int """ # At this point, our local copy of the value reflects the position of the # slider, now all we need to do is emit a signal to PyDM so that the data # plugin will send a put to the channel. Don't update self.value or self._value # in here, it is pointless at best, and could cause an infinite loop at worst. if not self._mute_internal_slider_changes: self.send_value_signal[float].emit(self.value) @Property(bool) def showLimitLabels(self): """ Whether or not the high and low limits should be displayed on the slider. Returns ------- bool """ return self._show_limit_labels @showLimitLabels.setter def showLimitLabels(self, checked): """ Whether or not the high and low limits should be displayed on the slider. Parameters ---------- checked : bool """ self._show_limit_labels = checked if checked: self.low_lim_label.show() self.high_lim_label.show() else: self.low_lim_label.hide() self.high_lim_label.hide() @Property(bool) def showValueLabel(self): """ Whether or not the current value should be displayed on the slider. Returns ------- bool """ return self._show_value_label @showValueLabel.setter def showValueLabel(self, checked): """ Whether or not the current value should be displayed on the slider. Parameters ---------- checked : bool """ self._show_value_label = checked if checked: self.value_label.show() else: self.value_label.hide() @Property(QSlider.TickPosition) def tickPosition(self): """ Where to draw tick marks for the slider. Returns ------- QSlider.TickPosition """ return self._slider.tickPosition() @tickPosition.setter def tickPosition(self, position): """ Where to draw tick marks for the slider. Parameter --------- position : QSlider.TickPosition """ self._slider.setTickPosition(position) @Property(bool) def userDefinedLimits(self): """ Wether or not to use limits defined by the user and not from the channel Returns ------- bool """ return self._user_defined_limits @userDefinedLimits.setter def userDefinedLimits(self, user_defined_limits): """ Wether or not to use limits defined by the user and not from the channel Parameters ---------- user_defined_limits : bool """ self._user_defined_limits = user_defined_limits self.reset_slider_limits() @Property(float) def userMinimum(self): """ Lower user defined limit value Returns ------- float """ return self._user_minimum @userMinimum.setter def userMinimum(self, new_min): """ Lower user defined limit value Parameters ---------- new_min : float """ self._user_minimum = float(new_min) if new_min is not None else None if self.userDefinedLimits: self.reset_slider_limits() @Property(float) def userMaximum(self): """ Upper user defined limit value Returns ------- float """ return self._user_maximum @userMaximum.setter def userMaximum(self, new_max): """ Upper user defined limit value Parameters ---------- new_max : float """ self._user_maximum = float(new_max) if new_max is not None else None if self.userDefinedLimits: self.reset_slider_limits() @property def minimum(self): """ The current value being used for the lower limit Returns ------- float """ if self.userDefinedLimits: return self._user_minimum return self._lower_ctrl_limit @property def maximum(self): """ The current value being used for the upper limit Returns ------- float """ if self.userDefinedLimits: return self._user_maximum return self._upper_ctrl_limit @Property(int) def num_steps(self): """ The number of steps on the slider Returns ------- int """ return self._num_steps @num_steps.setter def num_steps(self, new_steps): """ The number of steps on the slider Parameters ---------- new_steps : int """ self._num_steps = int(new_steps) self.reset_slider_limits()
class QtSizeSliderPreviewWidget(QWidget): """ Widget displaying a description, textedit and slider to adjust font size with preview. Parameters ---------- parent : qtpy.QtWidgets.QWidget, optional Default is None. description : str, optional Default is "". preview_text : str, optional Default is "". value : int, optional Default is None. min_value : int, optional Default is 1. max_value : int, optional Default is 50. unit : str, optional Default is "px". """ valueChanged = Signal(int) def __init__( self, parent: QWidget = None, description: str = None, preview_text: str = None, value: int = None, min_value: int = 1, max_value: int = 50, unit: str = "px", ): super().__init__(parent) description = description or "" preview_text = preview_text or "" self._value = value if value else self.fontMetrics().height() self._min_value = min_value self._max_value = max_value # Widget self._lineedit = QLineEdit() self._description_label = QLabel(self) self._unit_label = QLabel(self) self._slider = QSlider(Qt.Horizontal, self) self._slider_min_label = QLabel(self) self._slider_max_label = QLabel(self) self._preview = QtFontSizePreview(self) self._preview_label = QLabel(self) self._validator = None # Widgets setup self._description_label.setText(description) self._description_label.setWordWrap(True) self._unit_label.setText(unit) self._lineedit.setAlignment(Qt.AlignRight) self._slider_min_label.setText(str(min_value)) self._slider_max_label.setText(str(max_value)) self._slider.setMinimum(min_value) self._slider.setMaximum(max_value) self._preview.setText(preview_text) self._preview_label.setText(trans._("preview")) self._preview_label.setAlignment(Qt.AlignHCenter) self.setFocusProxy(self._lineedit) # Layout left_bottom_layout = QHBoxLayout() left_bottom_layout.addWidget(self._lineedit) left_bottom_layout.addWidget(self._unit_label) left_bottom_layout.addWidget(self._slider_min_label) left_bottom_layout.addWidget(self._slider) left_bottom_layout.addWidget(self._slider_max_label) left_layout = QVBoxLayout() left_layout.addWidget(self._description_label) left_layout.addLayout(left_bottom_layout) right_layout = QVBoxLayout() right_layout.addWidget(self._preview) right_layout.addWidget(self._preview_label) layout = QHBoxLayout() layout.addLayout(left_layout, 2) layout.addLayout(right_layout, 1) self.setLayout(layout) # Signals self._slider.valueChanged.connect(self._update_value) self._lineedit.textChanged.connect(self._update_value) self._update_line_width() self._update_validator() self._update_value(self._value) def _update_validator(self): self._validator = QIntValidator(self._min_value, self._max_value, self) self._lineedit.setValidator(self._validator) def _update_line_width(self): """Update width ofg line text edit.""" size = self._lineedit.fontMetrics().horizontalAdvance( "m" * (1 + len(str(self._max_value)))) self._lineedit.setMaximumWidth(size) self._lineedit.setMinimumWidth(size) def _update_value(self, value: int): """Update internal value and emit if changed.""" if value == "": value = int(self._value) value = int(value) if value > self._max_value: value = self._max_value elif value < self._min_value: value = self._min_value if value != self._value: self.valueChanged.emit(value) self._value = value self._refresh(self._value) def _refresh(self, value: int = None): """Refresh the value on all subwidgets.""" value = value if value else self._value self.blockSignals(True) self._lineedit.setText(str(value)) self._slider.setValue(value) font = QFont() font.setPixelSize(value) self._preview.setFont(font) font = QFont() font.setPixelSize(self.fontMetrics().height() - 4) self._preview_label.setFont(font) self.blockSignals(False) def description(self) -> str: """Return the current widget description. Returns ------- str The description text. """ return self._description_label.text() def setDescription(self, text: str): """Set the current widget description. Parameters ---------- text : str The description text. """ self._description_label.setText(text) def previewText(self) -> str: """Return the current preview text. Returns ------- str The current preview text. """ return self._preview.text() def setPreviewText(self, text: str): """Set the current preview text. Parameters ---------- text : str The current preview text. """ self._preview.setText(text) def unit(self) -> str: """Return the current unit text. Returns ------- str The current unit text. """ return self._unit_label.text() def setUnit(self, text: str): """Set the current unit text. Parameters ---------- text : str The current preview text. """ self._unit_label.setText(text) def minimum(self) -> int: """Return the current minimum value for the slider and value in textbox. Returns ------- int The minimum value for the slider. """ return self._min_value def setMinimum(self, value: int): """Set the current minimum value for the slider and value in textbox. Parameters ---------- value : int The minimum value for the slider. """ value = int(value) if value < self._max_value: self._min_value = value self._value = (self._min_value if self._value < self._min_value else self._value) self._slider_min_label.setText(str(value)) self._slider.setMinimum(value) self._update_validator() self._refresh() else: raise ValueError( trans._("Minimum value must be smaller than {}".format( self._max_value))) def maximum(self) -> int: """Return the maximum value for the slider and value in textbox. Returns ------- int The maximum value for the slider. """ return self._max_value def setMaximum(self, value: int): """Set the maximum value for the slider and value in textbox. Parameters ---------- value : int The maximum value for the slider. """ value = int(value) if value > self._min_value: self._max_value = value self._value = (self._max_value if self._value > self._max_value else self._value) self._slider_max_label.setText(str(value)) self._slider.setMaximum(value) self._update_validator() self._update_line_width() self._refresh() else: raise ValueError( trans._("Maximum value must be larger than {}".format( self._min_value))) def value(self) -> int: """Return the current widget value. Returns ------- int The current value. """ return self._value def setValue(self, value: int): """Set the current widget value. Parameters ---------- value : int The current value. """ self._update_value(value)
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 create_wwwc_tab(apply_adjust): Conf = namedtuple("Conf", "min max step default") ww_conf = Conf(0, 2000, 1, 350) wc_conf = Conf(-2000, 2000, 1, 50) tab = QWidget() tab.layout = QFormLayout() # BUG: 只有一个负号的时候会变成0,应该保留负号 ww_edit = QLineEdit() ww_edit.setValidator(QtGui.QIntValidator(ww_conf.min, ww_conf.max)) ww_edit.setFixedWidth(50) wc_edit = QLineEdit() wc_edit.setValidator(QtGui.QIntValidator(wc_conf.min, wc_conf.max)) wc_edit.setFixedWidth(50) ww_slider = QSlider(Qt.Horizontal) # 窗宽滑动条 ww_slider.setMinimum(ww_conf.min) ww_slider.setMaximum(ww_conf.max) ww_slider.setSingleStep(ww_conf.step) ww_slider.setValue(ww_conf.default) wc_slider = QSlider(Qt.Horizontal) # 窗位滑动条 wc_slider.setMinimum(wc_conf.min) wc_slider.setMaximum(wc_conf.max) wc_slider.setSingleStep(wc_conf.step) wc_slider.setValue(wc_conf.default) ww_label = QLabel() ww_label.setText("窗宽: ") wc_label = QLabel() wc_label.setText("窗位: ") ww_combo = QHBoxLayout() ww_combo.addWidget(ww_edit) ww_combo.addWidget(ww_slider) wc_combo = QHBoxLayout() wc_combo.addWidget(wc_edit) wc_combo.addWidget(wc_slider) tab.layout.addRow(ww_label, ww_combo) tab.layout.addRow(wc_label, wc_combo) tab.setLayout(tab.layout) def slider_change(slider, editor): def func(): editor.setText(str(slider.value())) apply_adjust( functools.partial(wwwc, ww_slider.value(), wc_slider.value())) return func def edit_changed(editor, slider): def func(): value = editor.text() if value == "" or value == "-": value = 0 value = int(value) slider.setValue(value) return func ww_slider.valueChanged.connect(slider_change(ww_slider, ww_edit)) wc_slider.valueChanged.connect(slider_change(wc_slider, wc_edit)) ww_edit.textChanged.connect(edit_changed(ww_edit, ww_slider)) # wc_edit.textChanged.connect(wc_edit_change) return tab, "WW/WC"
class MiscellaneousLauncher(QWidget): def __init__(self, parent): super().__init__() # set title self.setWindowTitle(config.thisTranslation["cp4"]) # set up variables self.parent = parent # setup interface self.setupUI() def setupUI(self): mainLayout = QGridLayout() leftLayout = QVBoxLayout() rightLayout = QVBoxLayout() self.highLightLauncher = HighlightLauncher(self) leftLayout.addWidget(self.highLightLauncher) rightLayout.addWidget(self.noteEditor()) rightLayout.addWidget(self.textToSpeechUtility()) rightLayout.addWidget(self.utilities()) rightLayout.addStretch() mainLayout.addLayout(leftLayout, 0, 0, 1, 2) mainLayout.addLayout(rightLayout, 0, 2, 1, 1) self.setLayout(mainLayout) def noteEditor(self): box = QGroupBox(config.thisTranslation["note_editor"]) subLayout = QHBoxLayout() button = QPushButton(config.thisTranslation["menu7_create"]) button.setToolTip(config.thisTranslation["menu7_create"]) button.clicked.connect(self.parent.parent.createNewNoteFile) subLayout.addWidget(button) button = QPushButton(config.thisTranslation["menu7_open"]) button.setToolTip(config.thisTranslation["menu7_open"]) button.clicked.connect(self.parent.parent.openTextFileDialog) subLayout.addWidget(button) box.setLayout(subLayout) return box def utilities(self): box = QGroupBox(config.thisTranslation["utilities"]) subLayout = QHBoxLayout() button = QPushButton(config.thisTranslation["presentation"]) button.setToolTip(config.thisTranslation["presentation"]) button.clicked.connect( lambda: self.parent.parent.runPlugin("Presentation")) subLayout.addWidget(button) if config.isYoutubeDownloaderInstalled: button = QPushButton(config.thisTranslation["youtube_utility"]) button.setToolTip(config.thisTranslation["youtube_utility"]) button.clicked.connect(self.parent.parent.openYouTube) subLayout.addWidget(button) box.setLayout(subLayout) return box def textToSpeechUtility(self): box = QGroupBox(config.thisTranslation["tts_utility"]) layout = QVBoxLayout() subLayout = QHBoxLayout() self.ttsEdit = QLineEdit() self.ttsEdit.setClearButtonEnabled(True) self.ttsEdit.setToolTip(config.thisTranslation["enter_text_here"]) self.ttsEdit.returnPressed.connect(self.speakText) subLayout.addWidget(self.ttsEdit) layout.addLayout(subLayout) self.ttsSlider = QSlider(Qt.Horizontal) self.ttsSlider.setToolTip(config.thisTranslation["adjustSpeed"]) self.ttsSlider.setMinimum(10) self.ttsSlider.setMaximum(310) self.ttsSlider.setValue(config.espeakSpeed if config.espeak else ( 160 + config.qttsSpeed * 150)) self.ttsSlider.valueChanged.connect( self.changeEspeakSpeed if config.espeak else self.changeQttsSpeed) layout.addWidget(self.ttsSlider) subLayout = QHBoxLayout() self.languageCombo = QComboBox() subLayout.addWidget(self.languageCombo) if config.espeak: languages = TtsLanguages().isoLang2epeakLang else: languages = TtsLanguages().isoLang2qlocaleLang self.languageCodes = list(languages.keys()) for code in self.languageCodes: self.languageCombo.addItem(languages[code][1]) # Check if selected tts engine has the language user specify. if not (config.ttsDefaultLangauge in self.languageCodes): config.ttsDefaultLangauge = "en" # Set initial index # It is essential. Otherwise, default tts language is changed by defaultTtsLanguageChanged method. ttsLanguageIndex = self.languageCodes.index(config.ttsDefaultLangauge) self.languageCombo.setCurrentIndex(ttsLanguageIndex) # Change default tts language as users select a new language self.languageCombo.currentIndexChanged.connect( self.defaultTtsLanguageChanged) button = QPushButton(config.thisTranslation["speak"]) button.setToolTip(config.thisTranslation["speak"]) button.clicked.connect(self.speakText) subLayout.addWidget(button) button = QPushButton(config.thisTranslation["stop"]) button.setToolTip(config.thisTranslation["stop"]) button.clicked.connect( self.parent.parent.textCommandParser.stopTtsAudio) subLayout.addWidget(button) layout.addLayout(subLayout) box.setLayout(layout) return box def defaultTtsLanguageChanged(self, index): config.ttsDefaultLangauge = self.languageCodes[index] def speakText(self): text = self.ttsEdit.text() if text: if config.isTtsInstalled: if ":::" in text: text = text.split(":::")[-1] command = "SPEAK:::{0}:::{1}".format( self.languageCodes[self.languageCombo.currentIndex()], text) self.parent.isRefreshing = True self.parent.runTextCommand(command) self.parent.isRefreshing = False else: self.parent.displayMessage( config.thisTranslation["message_noSupport"]) else: self.parent.displayMessage( config.thisTranslation["enterTextFirst"]) def changeQttsSpeed(self, value): config.qttsSpeed = (value * (1.5 / 300) - .5) def changeEspeakSpeed(self, value): config.espeakSpeed = value def refresh(self): ttsLanguageIndex = self.languageCodes.index(config.ttsDefaultLangauge) self.languageCombo.setCurrentIndex(ttsLanguageIndex) self.highLightLauncher.refresh()
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 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) self.faceColorEdit = QColorSwatchEdit( initial_color=self.layer.current_face_color, tooltip='click to set current face color', ) self._on_current_face_color_change() self.edgeColorEdit = QColorSwatchEdit( initial_color=self.layer.current_edge_color, tooltip='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('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('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.addWidget(self.faceColorEdit, 4, 1) self.grid_layout.addWidget(QLabel('edge color:'), 5, 0) self.grid_layout.addWidget(self.edgeColorEdit, 5, 1) self.grid_layout.addWidget(QLabel('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 __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)
class QtTracksControls(QtLayerControls): """Qt view and controls for the Tracks layer. Parameters ---------- layer : napari.layers.Tracks An instance of a Tracks layer. Attributes ---------- grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : layers.Tracks An instance of a Tracks layer. """ def __init__(self, layer): super().__init__(layer) # NOTE(arl): there are no events fired for changing checkboxes self.layer.events.tail_width.connect(self._on_tail_width_change) self.layer.events.tail_length.connect(self._on_tail_length_change) self.layer.events.properties.connect(self._on_properties_change) self.layer.events.colormap.connect(self._on_colormap_change) self.layer.events.color_by.connect(self._on_color_by_change) # combo box for track coloring, we can get these from the properties # keys self.color_by_combobox = QComboBox() self.color_by_combobox.addItems(self.layer.properties_to_color_by) self.colormap_combobox = QComboBox() for name, colormap in AVAILABLE_COLORMAPS.items(): display_name = colormap._display_name self.colormap_combobox.addItem(display_name, name) # slider for track tail length self.tail_length_slider = QSlider(Qt.Horizontal) self.tail_length_slider.setFocusPolicy(Qt.NoFocus) self.tail_length_slider.setMinimum(1) self.tail_length_slider.setMaximum(MAX_TAIL_LENGTH) self.tail_length_slider.setSingleStep(1) # slider for track edge width self.tail_width_slider = QSlider(Qt.Horizontal) self.tail_width_slider.setFocusPolicy(Qt.NoFocus) self.tail_width_slider.setMinimum(1) self.tail_width_slider.setMaximum(MAX_TAIL_WIDTH) self.tail_width_slider.setSingleStep(1) # checkboxes for display self.id_checkbox = QCheckBox() self.tail_checkbox = QCheckBox() self.tail_checkbox.setChecked(True) self.graph_checkbox = QCheckBox() self.graph_checkbox.setChecked(True) self.tail_width_slider.valueChanged.connect(self.change_tail_width) self.tail_length_slider.valueChanged.connect(self.change_tail_length) self.tail_checkbox.stateChanged.connect(self.change_display_tail) self.id_checkbox.stateChanged.connect(self.change_display_id) self.graph_checkbox.stateChanged.connect(self.change_display_graph) self.color_by_combobox.currentTextChanged.connect(self.change_color_by) self.colormap_combobox.currentTextChanged.connect(self.change_colormap) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addWidget(QLabel(trans._('color by:')), 0, 0) self.grid_layout.addWidget(self.color_by_combobox, 0, 1) self.grid_layout.addWidget(QLabel(trans._('colormap:')), 1, 0) self.grid_layout.addWidget(self.colormap_combobox, 1, 1) self.grid_layout.addWidget(QLabel(trans._('blending:')), 2, 0) self.grid_layout.addWidget(self.blendComboBox, 2, 1) self.grid_layout.addWidget(QLabel(trans._('opacity:')), 3, 0) self.grid_layout.addWidget(self.opacitySlider, 3, 1) self.grid_layout.addWidget(QLabel(trans._('tail width:')), 4, 0) self.grid_layout.addWidget(self.tail_width_slider, 4, 1) self.grid_layout.addWidget(QLabel(trans._('tail length:')), 5, 0) self.grid_layout.addWidget(self.tail_length_slider, 5, 1) self.grid_layout.addWidget(QLabel(trans._('tail:')), 6, 0) self.grid_layout.addWidget(self.tail_checkbox, 6, 1) self.grid_layout.addWidget(QLabel(trans._('show ID:')), 7, 0) self.grid_layout.addWidget(self.id_checkbox, 7, 1) self.grid_layout.addWidget(QLabel(trans._('graph:')), 8, 0) self.grid_layout.addWidget(self.graph_checkbox, 8, 1) self.grid_layout.setRowStretch(9, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) self._on_tail_length_change() self._on_tail_width_change() self._on_colormap_change() self._on_color_by_change() def _on_tail_width_change(self, event=None): """Receive layer model track 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.tail_width.blocker(): value = self.layer.tail_width value = np.clip(int(2 * value), 1, MAX_TAIL_WIDTH) self.tail_width_slider.setValue(value) def _on_tail_length_change(self, event=None): """Receive layer model track 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.tail_length.blocker(): value = self.layer.tail_length value = np.clip(value, 1, MAX_TAIL_LENGTH) self.tail_length_slider.setValue(value) def _on_properties_change(self, event=None): """Change the properties that can be used to color the tracks.""" with self.layer.events.properties.blocker(): with qt_signals_blocked(self.color_by_combobox): self.color_by_combobox.clear() self.color_by_combobox.addItems(self.layer.properties_to_color_by) def _on_colormap_change(self, event=None): """Receive layer model colormap change event and update combobox. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context, by default None. """ with self.layer.events.colormap.blocker(): self.colormap_combobox.setCurrentIndex( self.colormap_combobox.findData(self.layer.colormap)) def _on_color_by_change(self, event=None): """Receive layer model color_by change event and update combobox. Parameters ---------- event : qtpy.QtCore.QEvent, optional. Event from the Qt context, by default None. """ with self.layer.events.color_by.blocker(): color_by = self.layer.color_by idx = self.color_by_combobox.findText(color_by, Qt.MatchFixedString) self.color_by_combobox.setCurrentIndex(idx) def change_tail_width(self, value): """Change track line width of shapes on the layer model. Parameters ---------- value : float Line width of track tails. """ self.layer.tail_width = float(value) / 2.0 def change_tail_length(self, value): """Change edge line width of shapes on the layer model. Parameters ---------- value : int Line length of track tails. """ self.layer.tail_length = value def change_display_tail(self, state): self.layer.display_tail = self.tail_checkbox.isChecked() def change_display_id(self, state): self.layer.display_id = self.id_checkbox.isChecked() def change_display_graph(self, state): self.layer.display_graph = self.graph_checkbox.isChecked() def change_color_by(self, value: str): self.layer.color_by = value def change_colormap(self, colormap: str): self.layer.colormap = self.colormap_combobox.currentData()
class PyDMSlider(QFrame, PyDMWritableWidget): """ A QSlider with support for Channels and more from PyDM. Parameters ---------- parent : QWidget The parent widget for the Label init_channel : str, optional The channel to be used by the widget. """ actionTriggered = Signal(int) rangeChanged = Signal(float, float) sliderMoved = Signal(float) sliderPressed = Signal() sliderReleased = Signal() valueChanged = Signal(float) def __init__(self, parent=None, init_channel=None): QFrame.__init__(self, parent) PyDMWritableWidget.__init__(self, init_channel=init_channel) self.alarmSensitiveContent = True self.alarmSensitiveBorder = False # Internal values for properties self.set_enable_state() self._show_limit_labels = True self._show_value_label = True self._user_defined_limits = False self._needs_limit_info = True self._minimum = None self._maximum = None self._user_minimum = -10.0 self._user_maximum = 10.0 self._num_steps = 101 self._orientation = Qt.Horizontal # Set up all the internal widgets that make up a PyDMSlider. # We'll add all these things to layouts when we call setup_widgets_for_orientation label_size_policy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed) self.low_lim_label = QLabel(self) self.low_lim_label.setObjectName("lowLimLabel") self.low_lim_label.setSizePolicy(label_size_policy) self.low_lim_label.setAlignment(Qt.AlignLeft | Qt.AlignTrailing | Qt.AlignVCenter) self.value_label = QLabel(self) self.value_label.setObjectName("valueLabel") self.value_label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.high_lim_label = QLabel(self) self.high_lim_label.setObjectName("highLimLabel") self.high_lim_label.setSizePolicy(label_size_policy) self.high_lim_label.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) self._slider = QSlider(parent=self) self._slider.setOrientation(Qt.Horizontal) self._slider.sliderMoved.connect(self.internal_slider_moved) self._slider.sliderPressed.connect(self.internal_slider_pressed) self._slider.sliderReleased.connect(self.internal_slider_released) self._slider.valueChanged.connect(self.internal_slider_value_changed) # self.vertical_layout.addWidget(self._slider) # Other internal variables and final setup steps self._slider_position_to_value_map = None self._mute_internal_slider_changes = False self.setup_widgets_for_orientation(self._orientation) self.reset_slider_limits() def init_for_designer(self): """ Method called after the constructor to tweak configurations for when using the widget with the Qt Designer """ self.value = 0.0 @Property(Qt.Orientation) def orientation(self): """ The slider orientation (Horizontal or Vertical) Returns ------- int Qt.Horizontal or Qt.Vertical """ return self._orientation @orientation.setter def orientation(self, new_orientation): """ The slider orientation (Horizontal or Vertical) Parameters ---------- new_orientation : int Qt.Horizontal or Qt.Vertical """ self._orientation = new_orientation self.setup_widgets_for_orientation(new_orientation) def setup_widgets_for_orientation(self, new_orientation): """ Reconstruct the widget given the orientation. Parameters ---------- new_orientation : int Qt.Horizontal or Qt.Vertical """ if new_orientation not in (Qt.Horizontal, Qt.Vertical): logger.error( "Invalid orientation '{0}'. The existing layout will not change." .format(new_orientation)) return layout = None if new_orientation == Qt.Horizontal: layout = QVBoxLayout() layout.setContentsMargins(4, 0, 4, 4) label_layout = QHBoxLayout() label_layout.addWidget(self.low_lim_label) label_layout.addStretch(0) label_layout.addWidget(self.value_label) label_layout.addStretch(0) label_layout.addWidget(self.high_lim_label) layout.addLayout(label_layout) self._slider.setOrientation(new_orientation) layout.addWidget(self._slider) elif new_orientation == Qt.Vertical: layout = QHBoxLayout() layout.setContentsMargins(0, 4, 4, 4) label_layout = QVBoxLayout() label_layout.addWidget(self.high_lim_label) label_layout.addStretch(0) label_layout.addWidget(self.value_label) label_layout.addStretch(0) label_layout.addWidget(self.low_lim_label) layout.addLayout(label_layout) self._slider.setOrientation(new_orientation) layout.addWidget(self._slider) if self.layout() is not None: # Trick to remove the existing layout by re-parenting it in an empty widget. QWidget().setLayout(self.layout()) self.setLayout(layout) def update_labels(self): """ Update the limits and value labels with the correct values. """ def set_label(value, label_widget): if value is None: label_widget.setText("") else: label_widget.setText(self.format_string.format(value)) set_label(self.minimum, self.low_lim_label) set_label(self.maximum, self.high_lim_label) set_label(self.value, self.value_label) def reset_slider_limits(self): """ Reset the limits and adjust the labels properly for the slider. """ if self.minimum is None or self.maximum is None: self._needs_limit_info = True self.set_enable_state() return self._needs_limit_info = False self._slider.setMinimum(0) self._slider.setMaximum(self._num_steps - 1) self._slider.setSingleStep(1) self._slider.setPageStep(10) self._slider_position_to_value_map = np.linspace(self.minimum, self.maximum, num=self._num_steps) self.update_labels() self.set_slider_to_closest_value(self.value) self.rangeChanged.emit(self.minimum, self.maximum) self.set_enable_state() def find_closest_slider_position_to_value(self, val): """ Find and returns the index for the closest position on the slider for a given value. Parameters ---------- val : float Returns ------- int """ diff = abs(self._slider_position_to_value_map - float(val)) return np.argmin(diff) def set_slider_to_closest_value(self, val): """ Set the value for the slider according to a given value. Parameters ---------- val : float """ if val is None or self._needs_limit_info: return # When we set the slider to the closest value, it may end up at a slightly # different position than val (if val is not in self._slider_position_to_value_map) # We don't want that slight difference to get broacast out and put the channel # somewhere new. For example, if the slider can only be at 0.4 or 0.5, but a # new value comes in of 0.45, its more important to keep the 0.45 than to change # it to where the slider gets set. Therefore, we mute the internal slider changes # so that its valueChanged signal doesn't cause us to emit a signal to PyDM to change # the value of the channel. self._mute_internal_slider_changes = True self._slider.setValue(self.find_closest_slider_position_to_value(val)) self._mute_internal_slider_changes = False def value_changed(self, new_val): """ Callback invoked when the Channel value is changed. Parameters ---------- new_val : int or float The new value from the channel. """ PyDMWritableWidget.value_changed(self, new_val) if hasattr(self, "value_label"): self.value_label.setText(self.format_string.format(self.value)) if not self._slider.isSliderDown(): self.set_slider_to_closest_value(self.value) def ctrl_limit_changed(self, which, new_limit): """ Callback invoked when the Channel receives new control limit values. Parameters ---------- which : str Which control limit was changed. "UPPER" or "LOWER" new_limit : float New value for the control limit """ PyDMWritableWidget.ctrl_limit_changed(self, which, new_limit) if not self.userDefinedLimits: self.reset_slider_limits() def update_format_string(self): """ Reconstruct the format string to be used when representing the output value. Returns ------- format_string : str The format string to be used including or not the precision and unit """ fs = PyDMWritableWidget.update_format_string(self) self.update_labels() return fs def set_enable_state(self): """ Determines wether or not the widget must be enabled or not depending on the write access, connection state and presence of limits information """ self.setEnabled(self._write_access and self._connected and not self._needs_limit_info) @Slot(int) def internal_slider_action_triggered(self, action): self.actionTriggered.emit(action) @Slot(int) def internal_slider_moved(self, val): """ Method invoked when the slider is moved. Parameters ---------- val : float """ # The user has moved the slider, we need to update our value. # Only update the underlying value, not the self.value property, # because we don't need to reset the slider position. If we change # self.value, we can get into a loop where the position changes, which # updates the value, which changes the position again, etc etc. self.value = self._slider_position_to_value_map[val] self.sliderMoved.emit(self.value) @Slot() def internal_slider_pressed(self): """ Method invoked when the slider is pressed """ self.sliderPressed.emit() @Slot() def internal_slider_released(self): """ Method invoked when the slider is released """ self.sliderReleased.emit() @Slot(int) def internal_slider_value_changed(self, val): """ Method invoked when a new value is selected on the slider. This will cause the new value to be emitted to the signal unless `mute_internal_slider_changes` is True. Parameters ---------- val : int """ # At this point, our local copy of the value reflects the position of the # slider, now all we need to do is emit a signal to PyDM so that the data # plugin will send a put to the channel. Don't update self.value or self._value # in here, it is pointless at best, and could cause an infinite loop at worst. if not self._mute_internal_slider_changes: self.send_value_signal[float].emit(self.value) @Property(bool) def showLimitLabels(self): """ Whether or not the high and low limits should be displayed on the slider. Returns ------- bool """ return self._show_limit_labels @showLimitLabels.setter def showLimitLabels(self, checked): """ Whether or not the high and low limits should be displayed on the slider. Parameters ---------- checked : bool """ self._show_limit_labels = checked if checked: self.low_lim_label.show() self.high_lim_label.show() else: self.low_lim_label.hide() self.high_lim_label.hide() @Property(bool) def showValueLabel(self): """ Whether or not the current value should be displayed on the slider. Returns ------- bool """ return self._show_value_label @showValueLabel.setter def showValueLabel(self, checked): """ Whether or not the current value should be displayed on the slider. Parameters ---------- checked : bool """ self._show_value_label = checked if checked: self.value_label.show() else: self.value_label.hide() @Property(QSlider.TickPosition) def tickPosition(self): """ Where to draw tick marks for the slider. Returns ------- QSlider.TickPosition """ return self._slider.tickPosition() @tickPosition.setter def tickPosition(self, position): """ Where to draw tick marks for the slider. Parameter --------- position : QSlider.TickPosition """ self._slider.setTickPosition(position) @Property(bool) def userDefinedLimits(self): """ Wether or not to use limits defined by the user and not from the channel Returns ------- bool """ return self._user_defined_limits @userDefinedLimits.setter def userDefinedLimits(self, user_defined_limits): """ Wether or not to use limits defined by the user and not from the channel Parameters ---------- user_defined_limits : bool """ self._user_defined_limits = user_defined_limits self.reset_slider_limits() @Property(float) def userMinimum(self): """ Lower user defined limit value Returns ------- float """ return self._user_minimum @userMinimum.setter def userMinimum(self, new_min): """ Lower user defined limit value Parameters ---------- new_min : float """ self._user_minimum = float(new_min) if new_min is not None else None if self.userDefinedLimits: self.reset_slider_limits() @Property(float) def userMaximum(self): """ Upper user defined limit value Returns ------- float """ return self._user_maximum @userMaximum.setter def userMaximum(self, new_max): """ Upper user defined limit value Parameters ---------- new_max : float """ self._user_maximum = float(new_max) if new_max is not None else None if self.userDefinedLimits: self.reset_slider_limits() @property def minimum(self): """ The current value being used for the lower limit Returns ------- float """ if self.userDefinedLimits: return self._user_minimum return self._lower_ctrl_limit @property def maximum(self): """ The current value being used for the upper limit Returns ------- float """ if self.userDefinedLimits: return self._user_maximum return self._upper_ctrl_limit @Property(int) def num_steps(self): """ The number of steps on the slider Returns ------- int """ return self._num_steps @num_steps.setter def num_steps(self, new_steps): """ The number of steps on the slider Parameters ---------- new_steps : int """ self._num_steps = int(new_steps) self.reset_slider_limits()
def __init__(self, layer): super().__init__(layer) self.layer.events.interpolation.connect(self._on_interpolation_change) self.layer.events.rendering.connect(self._on_rendering_change) self.layer.events.iso_threshold.connect(self._on_iso_threshold_change) self.layer.events.attenuation.connect(self._on_attenuation_change) self.layer.dims.events.ndisplay.connect(self._on_ndisplay_change) self.interpComboBox = QComboBox(self) self.interpComboBox.activated[str].connect(self.changeInterpolation) self.interpLabel = QLabel('interpolation:') renderComboBox = QComboBox(self) renderComboBox.addItems(Rendering.keys()) index = renderComboBox.findText( self.layer.rendering, Qt.MatchFixedString ) renderComboBox.setCurrentIndex(index) renderComboBox.activated[str].connect(self.changeRendering) self.renderComboBox = renderComboBox self.renderLabel = QLabel('rendering:') sld = QSlider(Qt.Horizontal, parent=self) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(100) sld.setSingleStep(1) sld.setValue(int(self.layer.iso_threshold * 100)) sld.valueChanged.connect(self.changeIsoThreshold) self.isoThresholdSlider = sld self.isoThresholdLabel = QLabel('iso threshold:') sld = QSlider(Qt.Horizontal, parent=self) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(100) sld.setSingleStep(1) sld.setValue(int(self.layer.attenuation * 200)) sld.valueChanged.connect(self.changeAttenuation) self.attenuationSlider = sld self.attenuationLabel = QLabel('attenuation:') self._on_ndisplay_change() colormap_layout = QHBoxLayout() colormap_layout.addWidget(self.colorbarLabel) colormap_layout.addWidget(self.colormapComboBox) colormap_layout.addStretch(1) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addWidget(QLabel('opacity:'), 0, 0) self.grid_layout.addWidget(self.opacitySlider, 0, 1) self.grid_layout.addWidget(QLabel('contrast limits:'), 1, 0) self.grid_layout.addWidget(self.contrastLimitsSlider, 1, 1) self.grid_layout.addWidget(QLabel('gamma:'), 2, 0) self.grid_layout.addWidget(self.gammaSlider, 2, 1) self.grid_layout.addWidget(QLabel('colormap:'), 3, 0) self.grid_layout.addLayout(colormap_layout, 3, 1) self.grid_layout.addWidget(QLabel('blending:'), 4, 0) self.grid_layout.addWidget(self.blendComboBox, 4, 1) self.grid_layout.addWidget(self.interpLabel, 5, 0) self.grid_layout.addWidget(self.interpComboBox, 5, 1) self.grid_layout.addWidget(self.renderLabel, 6, 0) self.grid_layout.addWidget(self.renderComboBox, 6, 1) self.grid_layout.addWidget(self.isoThresholdLabel, 7, 0) self.grid_layout.addWidget(self.isoThresholdSlider, 7, 1) self.grid_layout.addWidget(self.attenuationLabel, 8, 0) self.grid_layout.addWidget(self.attenuationSlider, 8, 1) self.grid_layout.setRowStretch(9, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4)
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)
class _ImageRaster2DSpectralWidget(_DatumWidget): MODE_SUM = 'sum' MODE_MAX = 'max' MODE_SINGLE = 'single' MODE_RANGE = 'range' def __init__(self, controller, datum=None, parent=None): self._datum = datum _DatumWidget.__init__(self, ImageRaster2DSpectral, controller, datum, parent) def _init_ui(self): # Widgets self._rdb_sum = QRadioButton("Sum") self._rdb_sum.setChecked(True) self._rdb_max = QRadioButton("Maximum") self._rdb_single = QRadioButton("Single") self._rdb_range = QRadioButton("Range") self._sld_start = QSlider(Qt.Horizontal) self._sld_start.setTickPosition(QSlider.TicksBelow) self._sld_start.setEnabled(False) self._sld_end = QSlider(Qt.Horizontal) self._sld_end.setTickPosition(QSlider.TicksBelow) self._sld_end.setEnabled(False) self._wdg_imageraster2d = self._create_imageraster2d_widget() self._wdg_analysis = self._create_analysis1d_widget() # Layouts layout = _DatumWidget._init_ui(self) sublayout = QHBoxLayout() sublayout.addWidget(self._rdb_sum) sublayout.addWidget(self._rdb_max) sublayout.addWidget(self._rdb_single) sublayout.addWidget(self._rdb_range) layout.addLayout(sublayout) sublayout = QFormLayout() sublayout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow) # Fix for Mac OS sublayout.addRow('Channels (Start)', self._sld_start) sublayout.addRow('Channels (End)', self._sld_end) layout.addLayout(sublayout) splitter = QSplitter() splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) splitter.addWidget(self._wdg_imageraster2d) splitter.addWidget(self._wdg_analysis) layout.addWidget(splitter) # Signals self._rdb_sum.toggled.connect(self._on_mode_sum) self._rdb_max.toggled.connect(self._on_mode_max) self._rdb_single.toggled.connect(self._on_mode_single) self._rdb_range.toggled.connect(self._on_mode_range) self._sld_start.valueChanged.connect(self._on_slide_start) self._sld_end.valueChanged.connect(self._on_slide_end) self._wdg_imageraster2d.valueSelected.connect(self._on_value_selected) # Defaults self.setMode(self.MODE_SUM) return layout def _create_analysis1d_widget(self): raise NotImplementedError def _create_imageraster2d_widget(self): raise NotImplementedError def _on_mode_sum(self, checked): if checked: self.setMode(self.MODE_SUM) def _on_mode_max(self, checked): if checked: self.setMode(self.MODE_MAX) def _on_mode_single(self, checked): if checked: self.setMode(self.MODE_SINGLE) def _on_mode_range(self, checked): if checked: self.setMode(self.MODE_RANGE) def _update_data(self, mode=None): if mode is None: mode = self.mode() if mode == self.MODE_SUM: self._update_mode_sum() elif mode == self.MODE_MAX: self._update_mode_max() elif mode == self.MODE_SINGLE: self._update_mode_single() elif mode == self.MODE_RANGE: self._update_mode_range() def _update_mode_sum(self): if self._datum is None: return subdatum = np.sum(self._datum, 2) self._wdg_imageraster2d.setDatum(subdatum) def _update_mode_max(self): if self._datum is None: return subdatum = np.amax(self._datum, 2) self._wdg_imageraster2d.setDatum(subdatum) def _update_mode_single(self): if self._datum is None: return channel = self._sld_start.value() subdatum = self._datum[:, :, channel] self._wdg_imageraster2d.setDatum(subdatum) def _update_mode_range(self): if self._datum is None: return start = self._sld_start.value() end = self._sld_end.value() start2 = min(start, end) end2 = max(start, end) subdatum = np.sum(self._datum[:, :, start2:end2 + 1], 2) self._wdg_imageraster2d.setDatum(subdatum) def _on_slide_start(self, channel): if self._rdb_single.isChecked(): self._update_mode_single() elif self._rdb_range.isChecked(): self._update_mode_range() def _on_slide_end(self, channel): self._update_mode_range() def _on_value_selected(self, x, y): if self._datum is None: return subdatum = self._datum[x, y] self._wdg_analysis.setDatum(subdatum.view(Analysis1D)) def setDatum(self, datum): _DatumWidget.setDatum(self, datum) self._datum = datum maximum = datum.channels - 1 if datum is not None else 0 self._sld_start.setMaximum(maximum) self._sld_end.setMaximum(maximum) self._update_data() def setMode(self, mode): rsum = rmax = rsingle = rrange = False sstart = send = False if mode == self.MODE_SUM: rsum = True elif mode == self.MODE_MAX: rmax = True elif mode == self.MODE_SINGLE: rsingle = True sstart = True elif mode == self.MODE_RANGE: rrange = True sstart = send = True else: raise ValueError('Unknown mode') self._rdb_sum.setChecked(rsum) self._rdb_max.setChecked(rmax) self._rdb_single.setChecked(rsingle) self._rdb_range.setChecked(rrange) self._sld_start.setEnabled(sstart) self._sld_end.setEnabled(send) self._update_data(mode) def mode(self): if self._rdb_sum.isChecked(): return self.MODE_SUM elif self._rdb_max.isChecked(): return self.MODE_MAX elif self._rdb_single.isChecked(): return self.MODE_SINGLE elif self._rdb_range.isChecked(): return self.MODE_RANGE else: raise ValueError('Unknown mode')
class livestream(QWidget): i = 0 def __init__(self,qnd,images = None,annotations_on = True,annotate_coords = None,threshold_switch = False): QWidget.__init__(self) self.threshold_switch = threshold_switch self.video = images #frames buffer self.videobox = Label() if annotations_on and annotate_coords is not None: self.coords = annotate_coords self.videobox.switch = annotations_on self.videobox.activecoord = self.coords[0] if self.video is not None: self.videobox.activeframe = self.video[0] self.videobox.maxintens = self.video.shape[0] else: self.videobox.activeframe = np.loadtxt(os.getcwd() + '/defaultimage.txt') print(self.videobox.activeframe.shape) self.videobox.maxintens = np.max(self.videobox.activeframe) self.videobox.setGeometry(QtCore.QRect(70, 80, 310, 310)) self.videobox.h = 310 self.videobox.w = 310 self.lyt = QVBoxLayout() self.lyt.addWidget(self.videobox,5) self.setLayout(self.lyt) self.sl = QSlider(Qt.Horizontal) self.sl.setMinimum(0.0) if self.video is not None: self.sl.setMaximum(self.video.shape[0]) self.sl.valueChanged.connect(self.whenslidechanges) self.sl.setTickPosition(QSlider.TicksAbove) self.sl.setTracking(True) self.sl.setTickInterval(100) self.frame_counter = QDoubleSpinBox() if images is not None: self.frame = images[0] self.frame_counter.valueChanged.connect(self.video_time_update) self.frame_counter.setSingleStep(1) self.frame_counter.setRange(self.sl.minimum(),self.sl.maximum()) self.frame_counter.valueChanged.connect(self.sl.setValue) self.video_time = QDoubleSpinBox() self.video_time.setSingleStep(30) self.video_time.setRange(self.sl.minimum(),30*self.sl.maximum()) self.frameratetimer = QTimer() self.frameratetimer.setInterval(30) if self.video is not None: self.frameratetimer.timeout.connect(self.update_display) self.play_button = QPushButton('Play Video') self.play_button.clicked.connect(self.frameratetimer.start) self.stop_button = QPushButton('Stop Video') self.stop_button.clicked.connect(self.frameratetimer.stop) if self.video is not None: self.sl.valueChanged.connect(self.whenslidechanges) self.lyt.addWidget(self.play_button,0) self.lyt.addWidget(self.stop_button,1) self.lyt.addWidget(self.sl,2) self.lyt.addWidget(self.frame_counter,3) self.lyt.addWidget(self.video_time,4) self.show() def assign_images(self,images,centres = None): '''#first disconnect signals from eachother so nothing should change whilst video data is being updated self.sl.valueChanged.disconnect(self.video_time_update) self.frameratetimer.timeout.disconnect(self.update_display) self.frame_counter.valueChanged.disconnect(self.whenslidechanges) ''' self.video = images self.coords = centres self.videobox.activeframe = self.video[0] if self.coords is not None: self.videobox.activecoord = self.coords[0] #readjust slider and ticker values to dimensions of video self.sl.setMaximum(len(self.video)-1) self.frame_counter.setRange(self.sl.minimum(),self.sl.maximum()) self.video_time.setRange(self.sl.minimum(),30*self.sl.maximum()) #connect slider and timer etc. self.sl.valueChanged.connect(self.whenslidechanges) self.frameratetimer.timeout.connect(self.update_display) self.frame_counter.valueChanged.connect(self.video_time_update) self.videobox.maxintens = np.max(self.video) self.videobox.update() def update_display(self): if self.threshold_switch: frame = self.video[livestream.i] threshold = threshold_otsu(frame) mask = np.zeros_like(frame) mask[frame > threshold] = 1 self.videobox.maxintens = 1 self.videobox.activeframe = mask else: #if threshold switch is off display usual video, so change active frame source and reset maximum intensity for passing to qimage2ndarray self.videobox.activeframe = self.video[livestream.i] self.videobox.maxintens = np.max(self.video) try: self.videobox.activecoord = self.coords[livestream.i] if not self.videobox.switch: self.videobox.switch = True except: self.videobox.activecoord = None self.videobox.switch = False self.videobox.update() self.frame_counter.setValue(float(livestream.i)) livestream.i+=1 def whenslidechanges(self): if self.frameratetimer.isActive(): self.frameratetimer.stop() livestream.i = self.sl.value() self.update_display() livestream.i -=1 self.frameratetimer.start() else: livestream.i = self.sl.value() self.update_display() livestream.i -=1 def video_time_update(self): self.video_time.setValue(30*self.frame_counter.value()) def turn_on_threshold(self,threshold_switch): self.threshold_switch = threshold_switch self.update_display()
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 __init__(self, layer): super().__init__() self.layer = layer layer.events.select.connect(lambda v: self.setSelected(True)) layer.events.deselect.connect(lambda v: self.setSelected(False)) layer.events.name.connect(self._on_layer_name_change) layer.events.blending.connect(self._on_blending_change) layer.events.opacity.connect(self._on_opacity_change) layer.events.visible.connect(self._on_visible_change) layer.events.thumbnail.connect(self._on_thumbnail_change) self.setObjectName('layer') self.vbox_layout = QVBoxLayout() self.top = QFrame() self.top_layout = QHBoxLayout() self.grid = QFrame() self.grid_layout = QGridLayout() self.vbox_layout.addWidget(self.top) self.vbox_layout.addWidget(self.grid) self.vbox_layout.setSpacing(0) self.top.setFixedHeight(38) self.top_layout.setContentsMargins(0, 0, 0, 0) self.grid_layout.setContentsMargins(0, 0, 0, 0) self.top_layout.setAlignment(Qt.AlignCenter) self.top.setLayout(self.top_layout) self.grid.setLayout(self.grid_layout) self.setLayout(self.vbox_layout) self.name_column = 0 self.property_column = 1 cb = QCheckBox(self) cb.setObjectName('visibility') cb.setToolTip('Layer visibility') cb.setChecked(self.layer.visible) cb.setProperty('mode', 'visibility') cb.stateChanged.connect(lambda state=cb: self.changeVisible(state)) self.visibleCheckBox = cb self.top_layout.addWidget(cb) tb = QLabel(self) tb.setObjectName('thumbmnail') tb.setToolTip('Layer thumbmnail') self.thumbnail_label = tb self._on_thumbnail_change(None) self.top_layout.addWidget(tb) textbox = QLineEdit(self) textbox.setText(layer.name) textbox.home(False) textbox.setToolTip('Layer name') textbox.setAcceptDrops(False) textbox.setEnabled(True) textbox.editingFinished.connect(self.changeText) self.nameTextBox = textbox self.top_layout.addWidget(textbox) pb = QPushButton(self) pb.setToolTip('Expand properties') pb.clicked.connect(self.changeExpanded) pb.setObjectName('expand') self.expand_button = pb self.top_layout.addWidget(pb) row = self.grid_layout.rowCount() sld = QSlider(Qt.Horizontal, self) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(100) sld.setSingleStep(1) sld.setValue(self.layer.opacity * 100) sld.valueChanged[int].connect( lambda value=sld: self.changeOpacity(value)) self.opacitySilder = sld row = self.grid_layout.rowCount() self.grid_layout.addWidget(QLabel('opacity:'), row, self.name_column) self.grid_layout.addWidget(sld, row, self.property_column) row = self.grid_layout.rowCount() blend_comboBox = QComboBox() for blend in Blending: blend_comboBox.addItem(str(blend)) index = blend_comboBox.findText(self.layer.blending, Qt.MatchFixedString) blend_comboBox.setCurrentIndex(index) blend_comboBox.activated[str].connect( lambda text=blend_comboBox: self.changeBlending(text)) self.blendComboBox = blend_comboBox self.grid_layout.addWidget(QLabel('blending:'), row, self.name_column) self.grid_layout.addWidget(blend_comboBox, row, self.property_column) msg = 'Click to select\nDrag to rearrange\nDouble click to expand' self.setToolTip(msg) self.setExpanded(False) self.setFixedWidth(250) self.grid_layout.setColumnMinimumWidth(0, 100) self.grid_layout.setColumnMinimumWidth(1, 100) self.setSelected(self.layer.selected)
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)
class SliderSpinBox(QWidget): valueChanged = Signal(float) def __init__(self, parent=None, value_range=(0, 100), slider_steps=100, spinbox_steps=1000, decimals=None): QWidget.__init__(self, parent) layout = QHBoxLayout(self) self.setLayout(layout) self.horizontalSlider = QSlider(Qt.Horizontal, self) self.doubleSpinBox = QDoubleSpinBox(self) self.decimals = decimals layout.setMargin(0) layout.addWidget(self.doubleSpinBox) layout.addWidget(self.horizontalSlider) self.doubleSpinBox.valueChanged.connect(self.spinbox_changed) self.horizontalSlider.valueChanged.connect(self.slider_changed) self.wt = [] self.changer = None self.slider_steps = slider_steps self.horizontalSlider.setMaximum(slider_steps) self.spinbox_steps = spinbox_steps self.set_range(value_range) def set_value(self, value): self.doubleSpinBox.setValue(value) value = property(lambda self: self.doubleSpinBox.value(), set_value) def set_range(self, value_range): self.changer = "set_range" spinbox_step_decimal = np.ceil(np.log10(1. / (float(value_range[1] - value_range[0]) / self.spinbox_steps))) self.slider_decimals = int(np.ceil(np.log10(self.slider_steps) - np.log10(value_range[1] - value_range[0]))) if self.decimals is None: self.doubleSpinBox.setDecimals(spinbox_step_decimal + 1) else: self.doubleSpinBox.setDecimals(self.decimals) spinbox_step_decimal = min(self.decimals, spinbox_step_decimal) self.slider_decimals = min(self.decimals, self.slider_decimals) self.doubleSpinBox.setSingleStep(10 ** -spinbox_step_decimal) self.value_range = value_range self.doubleSpinBox.setMinimum(value_range[0]) self.doubleSpinBox.setMaximum(value_range[1]) self.horizontalSlider.setValue(np.floor(value_range[0])) self.changer = None self.doubleSpinBox.setValue(value_range[0]) def range(self): return self.doubleSpinBox.minimum(), self.doubleSpinBox.maximum() def slider_changed(self, value): if self.changer is None: self.changer = 'slider' value = np.round(value * (self.value_range[1] - self.value_range[0]) / self.slider_steps + self.value_range[0], self.slider_decimals) self.doubleSpinBox.setValue(value) self.changer = None self.value_changed(value) def spinbox_changed(self, value): if self.changer is None: self.changer = 'spinbox' slider_value = .5 + (value - self.value_range[0]) * self.slider_steps / (self.value_range[1] - self.value_range[0]) self.horizontalSlider.setValue(slider_value) self.changer = None self.value_changed(value) @postpone_until_last_call_finishes def value_changed(self, value): self.valueChanged.emit(value)