def __init__(self, parent=None): QWidget.__init__(self, parent) # Widgets, layouts and signals self._group = QButtonGroup() layout = QGridLayout() layout.setSpacing(0) ## Element for z, position in _ELEMENT_POSITIONS.items(): widget = ElementPushButton(z) widget.setCheckable(True) layout.addWidget(widget, *position) self._group.addButton(widget, z) ## Labels layout.addWidget(QLabel(''), 7, 0) # Dummy layout.addWidget(QLabel('*'), 5, 2, Qt.AlignRight) layout.addWidget(QLabel('*'), 8, 2, Qt.AlignRight) layout.addWidget(QLabel('**'), 6, 2, Qt.AlignRight) layout.addWidget(QLabel('**'), 9, 2, Qt.AlignRight) for row in [0, 1, 2, 3, 4, 5, 6, 8, 9]: layout.setRowStretch(row, 1) self.setLayout(layout) # Signals self._group.buttonClicked.connect(self.selectionChanged) # Default self.setColorFunction(_category_color_function)
def initialize_content(self): # Set some validators self._content.monint_low_edit.setValidator(QIntValidator(self._content.monint_low_edit)) self._content.monint_high_edit.setValidator(QIntValidator(self._content.monint_high_edit)) self._content.tof_start_edit.setValidator(QIntValidator(self._content.tof_start_edit)) self._content.tof_end_edit.setValidator(QIntValidator(self._content.tof_end_edit)) # Make group for incident beam normalisation radio buttons self.incident_beam_norm_grp = QButtonGroup() self.incident_beam_norm_grp.addButton(self._content.none_rb, 0) self.incident_beam_norm_grp.addButton(self._content.current_rb, 1) self.incident_beam_norm_grp.addButton(self._content.monitor1_rb, 2) self._monitor_intrange_widgets_state(self._content.monitor1_rb.isChecked()) self._content.monitor1_rb.toggled.connect(self._monitor_intrange_widgets_state) self._detvan_intrange_widgets_state(self._content.van_int_cb.isChecked()) self._content.van_int_cb.toggled.connect(self._detvan_intrange_widgets_state) self._content.use_procdetvan_cb.toggled.connect(self._detvan_widgets_opp_state) self._save_detvan_widgets_state(self._content.save_procdetvan_cb.isChecked()) self._content.save_procdetvan_cb.toggled.connect(self._save_detvan_widgets_state) # Connections self._content.van_input_browse.clicked.connect(self._detvan_browse) self._content.save_procdetvan_save.clicked.connect(self._save_procdetvan_save)
def __init__(self, parent=None, init_channel=None): QWidget.__init__(self, parent) PyDMWritableWidget.__init__(self, init_channel=init_channel) self._has_enums = False self.setLayout(QGridLayout(self)) self._btn_group = QButtonGroup() self._btn_group.setExclusive(True) self._btn_group.buttonClicked[int].connect(self.handle_button_clicked) self._widget_type = WidgetType.PushButton self._orientation = Qt.Vertical self._widgets = [] self.rebuild_widgets()
def create_layout(self): ok_cancel_box = QHBoxLayout() ok_cancel_box.addWidget(self.cancel_button) grid = QGridLayout() irow = 0 grid.addWidget(self.name, irow, 0) grid.addWidget(self.name_edit, irow, 1) irow += 1 grid.addWidget(self.color, irow, 0) grid.addWidget(self.color_edit, irow, 1) irow += 1 grid.addWidget(self.opacity, irow, 0) if self.use_slider: grid.addWidget(self.opacity_edit, irow, 2) grid.addWidget(self.opacity_slider_edit, irow, 1) else: grid.addWidget(self.opacity_edit, irow, 1) irow += 1 grid.addWidget(self.line_width, irow, 0) if self.use_slider: grid.addWidget(self.line_width_edit, irow, 2) grid.addWidget(self.line_width_slider_edit, irow, 1) else: grid.addWidget(self.line_width_edit, irow, 1) irow += 1 grid.addWidget(self.point_size, irow, 0) if self.use_slider: grid.addWidget(self.point_size_edit, irow, 2) grid.addWidget(self.point_size_slider_edit, irow, 1) else: grid.addWidget(self.point_size_edit, irow, 1) irow += 1 grid.addWidget(self.bar_scale, irow, 0) if self.use_slider and 0: grid.addWidget(self.bar_scale_edit, irow, 2) grid.addWidget(self.bar_scale_slider_edit, irow, 1) else: grid.addWidget(self.bar_scale_edit, irow, 1) irow += 1 wire_surf_checkboxes = QButtonGroup(self) wire_surf_checkboxes.addButton(self.checkbox_wire) wire_surf_checkboxes.addButton(self.checkbox_surf) checkboxs = QButtonGroup(self) checkboxs.addButton(self.checkbox_show) checkboxs.addButton(self.checkbox_hide) vbox = QVBoxLayout() vbox.addWidget(self.table, stretch=1) vbox.addLayout(grid) vbox1 = QVBoxLayout() vbox1.addWidget(self.checkbox_wire) vbox1.addWidget(self.checkbox_surf) vbox2 = QVBoxLayout() vbox2.addWidget(self.checkbox_show) vbox2.addWidget(self.checkbox_hide) #vbox.addLayout(vbox1) vbox.addLayout(vbox2) vbox.addStretch() #vbox.addWidget(self.check_apply) vbox.addLayout(ok_cancel_box) self.setLayout(vbox)
class StackedCanvasView(CanvasView): """ View that can display intents in their corresponding canvases. Currently, uses a stacked widget with several pages: one for tabview and others for different split views. Can be adapted as long as its internal widgets are CanvasDisplayWidgets. """ def __init__(self, parent=None, model=None): super(StackedCanvasView, self).__init__(parent) if model is not None: self.setModel(model) self.canvas_display_widgets = [ CanvasDisplayTabWidget(), SplitHorizontal(), SplitVertical(), SplitThreeView(), SplitGridView() ] ### Create stacked widget and fill pages with different canvas display widgets self.stackedwidget = QStackedWidget(self) # Create a visual layout section for the buttons that are used to switch the widgets self.buttonpanel = QHBoxLayout() self.buttonpanel.addStretch(10) # Create a logical button grouping that will: # - show the currently selected view (button will be checked/pressed) # - allow for switching the buttons/views in a mutually exclusive manner (only one can be pressed at a time) self.buttongroup = QButtonGroup() def add_canvas_display_widgets(): for i in range(len(self.canvas_display_widgets)): # Add the view to the stacked widget self.stackedwidget.addWidget(self.canvas_display_widgets[i]) # Create a button, using the view's recommended display icon button = QPushButton(self) button.setCheckable(True) button.setIcon(self.canvas_display_widgets[i].icon) button.setToolTip( getattr(self.canvas_display_widgets[i], "tool_tip", None)) button.setWhatsThis( getattr(self.canvas_display_widgets[i], "whats_this", None)) # Add the button to the logical button group self.buttongroup.addButton(button, i) # Add the button to the visual layout section self.buttonpanel.addWidget(button) add_canvas_display_widgets() def set_default_canvas_display_widget(): # The first button added to the buttongroup will be the currently selected button (and therefore view) self.buttongroup.button(0).setChecked(True) set_default_canvas_display_widget() # Whenever a button is switched, capture its id (corresponds to integer index in our case); # this will handle switching the view and displaying canvases. self.buttongroup.idToggled.connect(self.switch_view) # define outer layout & add stacked widget and button panel self.layout = QVBoxLayout() self.layout.addWidget(self.stackedwidget) self.layout.addLayout(self.buttonpanel) self.setLayout(self.layout) # INTERFACING WITH TOOLBAR (see XPCSToolbar) def view(self): # from xicam.gui.canvases import ImageIntentCanvas view = self.stackedwidget.currentWidget() return view.getView() # return None # DONE INTERFACING def switch_view(self, id, toggled): # when toggled==True, the the button is the new button that was switched to. # when False, the button is the previous button view = self.canvas_display_widgets[id] if not toggled: ... # TODO: is there anything we need to do here (re: cleanup)? else: self.stackedwidget.setCurrentIndex(id) self.show_canvases() def show_canvases(self): self.stackedwidget.currentWidget().clear_canvases() self.stackedwidget.currentWidget().show_canvases( self._canvas_manager.canvases(self.model()))
class QtSearchInput(QWidget): """ Qt view for SearchInput Parameters ---------- model: SearchInput """ def __init__(self, model, *args, **kwargs): self.model = model super().__init__(*args, **kwargs) self.setLayout(QFormLayout()) # Radiobuttons to quickly select default time period self.all_widget = QRadioButton("All") self.year_widget = QRadioButton("1 Year") self.month_widget = QRadioButton("30 Days") self.week_widget = QRadioButton("1 Week") self.today_widget = QRadioButton("24h") self.hour_widget = QRadioButton("1 Hour") self.radio_button_group = QButtonGroup() self.radio_button_group.addButton(self.all_widget) self.radio_button_group.addButton(self.year_widget) self.radio_button_group.addButton(self.month_widget) self.radio_button_group.addButton(self.week_widget) self.radio_button_group.addButton(self.today_widget) self.radio_button_group.addButton(self.hour_widget) default_period_layout = QGridLayout() default_period_layout.setHorizontalSpacing(85) default_period_layout.setVerticalSpacing(10) default_period_layout.addWidget(self.all_widget, 0, 0, 1, 2) default_period_layout.addWidget(self.year_widget, 1, 0, 1, 2) default_period_layout.addWidget(self.month_widget, 2, 0, 1, 2) default_period_layout.addWidget(self.week_widget, 0, 1, 1, 2) default_period_layout.addWidget(self.today_widget, 1, 1, 1, 2) default_period_layout.addWidget(self.hour_widget, 2, 1, 1, 2) self.layout().addRow("When:", default_period_layout) # TODO: rethink if restriction to acceptable timedelta values is required # from ..models.search.search_input import SearchInput # self.allowed = {timedelta(days=-1), timedelta(days=-30), timedelta(minutes=-60), timedelta(days=-7), # timedelta(days=-365)} # def time_validator(since=None, until=None): # """ # Enforce that since and until are values that a UI can represent. # This is an example similar to what will be used in the Qt UI. # """ # now = timedelta() # if isinstance(since, timedelta): # if not (until is None or until == now): # raise ValueError( # "This UI cannot express since=timedelta(...) unless until " # "is timedelta() or None." # ) # for item in allowed: # if since == item: # break # else: # # No matches # raise ValueError( # "This UI can only express since as a timedelta if it is " # f"one of {allowed}. The value {since} is not allowed" # ) # s = SearchInput() # s.time_validator = time_validator # "Since: <datetime picker>" self.since_widget = QDateTimeEdit() self.since_widget.setCalendarPopup(True) self.since_widget.setDisplayFormat("yyyy-MM-dd HH:mm") self.layout().addRow("Since:", self.since_widget) # "Until: <datetime picker>" self.until_widget = QDateTimeEdit() self.until_widget.setCalendarPopup(True) self.until_widget.setDisplayFormat("yyyy-MM-dd HH:mm") self.layout().addRow("Until:", self.until_widget) # Refresh Button self.refresh_button = QPushButton("Refresh") self.layout().addWidget(self.refresh_button) # Changes to the GUI update the model. self.since_widget.dateTimeChanged.connect(self.on_since_view_changed) self.until_widget.dateTimeChanged.connect(self.on_until_view_changed) self.refresh_button.clicked.connect(self.model.request_reload) self.model.events.reload.connect(self.on_reload) self.model.events.query.connect(self.on_reload) # Changes to the model update the GUI. self.model.events.since.connect(self.on_since_model_changed) self.model.events.until.connect(self.on_until_model_changed) # connect QRadioButtons and change date dropdowns (since/until widgets) accordingly self.hour_widget.toggled.connect(self.on_toggle_hour) self.today_widget.toggled.connect(self.on_toggle_24h) self.week_widget.toggled.connect(self.on_toggle_week) self.month_widget.toggled.connect(self.on_toggle_month) self.year_widget.toggled.connect(self.on_toggle_year) self.all_widget.toggled.connect(self.on_toggle_all) self.all_widget.setChecked(True) def on_reload(self, event): now = datetime.now(LOCAL_TIMEZONE) if isinstance(self.model.since, timedelta): with _blocked(self.since_widget): self.since_widget.setDateTime(as_qdatetime(now + self.model.since)) if isinstance(self.model.until, timedelta): with _blocked(self.until_widget): self.until_widget.setDateTime(as_qdatetime(now + self.model.until)) def on_since_view_changed(self, qdatetime): # When GUI is updated self.model.since = QDateTime.toPython(qdatetime) def on_since_model_changed(self, event): # When model is updated (e.g. from console or by clicking a QRadioButton) now = datetime.now(LOCAL_TIMEZONE) if isinstance(event.date, timedelta): qdatetime = as_qdatetime(now + event.date) if event.date == timedelta(minutes=-60): self.hour_widget.setChecked(True) elif event.date == timedelta(days=-1): self.today_widget.setChecked(True) elif event.date == timedelta(days=-7): self.week_widget.setChecked(True) elif event.date == timedelta(days=-30): self.month_widget.setChecked(True) elif event.date == timedelta(days=-365): self.year_widget.setChecked(True) else: # No checkbox associated with this custom timedelta pass else: # Must be a datetime if event.date == ADA_LOVELACE_BIRTHDAY: self.all_widget.setChecked(True) else: self.uncheck_radiobuttons() qdatetime = as_qdatetime(event.date) with _blocked(self.since_widget): self.since_widget.setDateTime(qdatetime) with _blocked(self.until_widget): self.until_widget.setDateTime(as_qdatetime(now)) def on_until_view_changed(self, qdatetime): # When GUI is updated self.model.until = QDateTime.toPython(qdatetime) def on_until_model_changed(self, event): # When model is updated (e.g. from console or by clicking a QRadioButton) if not isinstance(event.date, timedelta): qdatetime = as_qdatetime(event.date) self.uncheck_radiobuttons() with _blocked(self.until_widget): self.until_widget.setDateTime(qdatetime) def on_toggle_24h(self): if self.today_widget.isChecked(): self.model.since = timedelta(days=-1) self.model.until = timedelta() def on_toggle_hour(self): if self.hour_widget.isChecked(): self.model.since = timedelta(minutes=-60) self.model.until = timedelta() def on_toggle_week(self): if self.week_widget.isChecked(): self.model.since = timedelta(days=-7) self.model.until = timedelta() def on_toggle_month(self): if self.month_widget.isChecked(): self.model.since = timedelta(days=-30) self.model.until = timedelta() def on_toggle_year(self): if self.year_widget.isChecked(): self.model.since = timedelta(days=-365) self.model.until = timedelta() def on_toggle_all(self): # Search for all catalogs since Ada Lovelace's Birthday. if self.all_widget.isChecked(): self.model.since = ADA_LOVELACE_BIRTHDAY self.model.until = timedelta() def uncheck_radiobuttons(self): self.radio_button_group.setExclusive(False) self.all_widget.setChecked(False) self.year_widget.setChecked(False) self.month_widget.setChecked(False) self.week_widget.setChecked(False) self.today_widget.setChecked(False) self.hour_widget.setChecked(False) self.radio_button_group.setExclusive(True)
def initialize_content(self): # Validators self._summary.detector_offset_edit.setValidator(QDoubleValidator(self._summary.detector_offset_edit)) self._summary.sample_dist_edit.setValidator(QDoubleValidator(self._summary.sample_dist_edit)) self._summary.n_q_bins_edit.setValidator(QIntValidator(self._summary.n_q_bins_edit)) # Event connections self._summary.detector_offset_chk.clicked.connect(self._det_offset_clicked) self._summary.sample_dist_chk.clicked.connect(self._sample_dist_clicked) self._summary.help_button.clicked.connect(self._show_help) self._summary.dark_current_check.clicked.connect(self._dark_clicked) self._summary.dark_browse_button.clicked.connect(self._dark_browse) self._summary.dark_plot_button.clicked.connect(self._dark_plot_clicked) # Output directory g2 = QButtonGroup(self) g2.addButton(self._summary.select_output_dir_radio) g2.addButton(self._summary.use_data_dir_radio) g2.setExclusive(True) self._summary.select_output_dir_radio.clicked.connect(self._output_dir_clicked) self._summary.use_data_dir_radio.clicked.connect(self._output_dir_clicked) self._summary.output_dir_browse_button.clicked.connect(self._output_dir_browse) self._output_dir_clicked() # Lin/log option g3 = QButtonGroup(self) g3.addButton(self._summary.log_binning_radio) g3.addButton(self._summary.lin_binning_radio) g3.setExclusive(True) # Q range self._summary.n_q_bins_edit.setText("100") self._summary.scale_edit.setText("1") self._summary.instr_name_label.hide() self._dark_clicked(self._summary.dark_current_check.isChecked()) # Mask Connections self._summary.mask_browse_button.clicked.connect(self._mask_browse_clicked) self._summary.mask_plot_button.clicked.connect(self._mask_plot_clicked) self._summary.mask_check.clicked.connect(self._mask_checked) # Absolute scale connections and validators self._summary.scale_edit.setValidator(QDoubleValidator(self._summary.scale_edit)) self._summary.scale_beam_radius_edit.setValidator(QDoubleValidator(self._summary.scale_beam_radius_edit)) self._summary.scale_att_trans_edit.setValidator(QDoubleValidator(self._summary.scale_att_trans_edit)) self._summary.scale_data_browse_button.clicked.connect(self._scale_data_browse) self._summary.scale_data_plot_button.clicked.connect(self._scale_data_plot_clicked) self._summary.beamstop_chk.clicked.connect(self._beamstop_clicked) self._summary.scale_chk.clicked.connect(self._scale_clicked) self._scale_clicked(self._summary.scale_chk.isChecked()) # TOF cut validator self._summary.low_tof_edit.setValidator(QDoubleValidator(self._summary.low_tof_edit)) self._summary.high_tof_edit.setValidator(QDoubleValidator(self._summary.high_tof_edit)) # TOF connections self._summary.tof_cut_chk.clicked.connect(self._tof_clicked) # Monitor normalization self._summary.beam_monitor_chk.clicked.connect(self._beam_monitor_clicked) self._summary.beam_monitor_browse_button.clicked.connect(self._beam_monitor_reference_browse) # Resolution validator self._summary.sample_apert_edit.setValidator(QDoubleValidator(self._summary.sample_apert_edit)) self._summary.resolution_chk.clicked.connect(self._resolution_clicked) # Since EQSANS does not currently use the absolute scale calculation, expose it in debug mode only for now if not self._settings.debug: self._summary.config_options_layout.deleteLater() self._summary.abs_scale_options_layout.deleteLater() self._summary.abs_scale_direct_beam_layout.deleteLater() self._summary.monitor_layout.deleteLater() self._summary.direct_beam_label.hide() self._summary.att_trans_label.hide() self._summary.beamstop_chk.hide() self._summary.scale_data_edit.hide() self._summary.scale_data_plot_button.hide() self._summary.scale_data_browse_button.hide() self._summary.scale_att_trans_edit.hide() self._summary.scale_beam_radius_edit.hide() self._summary.scale_chk.hide() self._summary.beam_monitor_chk.hide() self._summary.tof_correction_chk.hide() self._summary.beam_monitor_edit.hide() self._summary.beam_monitor_browse_button.hide() # Same thing for sample-detector distance and offset: not yet hooked in self._summary.geometry_options_groupbox.hide() # Hide expert options #self._summary.config_mask_chk.hide() self._summary.tof_cut_chk.hide() self._summary.low_tof_edit.hide() self._summary.high_tof_edit.hide() self._summary.low_tof_label.hide() self._summary.high_tof_label.hide() if not self._settings.advanced: self._summary.att_scale_factor_label.hide() self._summary.scale_edit.hide() self._summary.mask_groupbox.hide() self._summary.solid_angle_chk.hide() self._summary.resolution_chk.hide() self._summary.sample_apert_edit.hide() self._summary.sample_apert_label.hide() # We need the EQSANS data proxy for a quick load of a file for masking purposes, but # we don't want to show the plot button. Turn this off for the moment. if True or not self._has_instrument_view: self._summary.dark_plot_button.hide() self._summary.scale_data_plot_button.hide()
class QtReaderDialog(QDialog): """Dialog for user to select a reader plugin for a given file extension or folder""" def __init__( self, pth: str = '', parent: QWidget = None, readers: Dict[str, str] = {}, error_message: str = '', ): super().__init__(parent) self.setObjectName('Choose reader') self.setWindowTitle(trans._('Choose reader')) self._current_file = pth if os.path.isdir(pth) and str(pth).endswith('/'): pth = os.path.dirname(pth) self._extension = os.path.splitext(pth)[1] self._reader_buttons = [] self.setup_ui(error_message, readers) def setup_ui(self, error_message, readers): """Build UI using given error_messsage and readers dict""" # add instruction label layout = QVBoxLayout() if error_message: error_message += "\n" label = QLabel( f"{error_message}Choose reader for {self._current_file}:" ) layout.addWidget(label) # add radio button for each reader plugin self.reader_btn_group = QButtonGroup(self) self.add_reader_buttons(layout, readers) if self.reader_btn_group.buttons(): self.reader_btn_group.buttons()[0].toggle() # OK & cancel buttons for the dialog btns = QDialogButtonBox.Ok | QDialogButtonBox.Cancel self.btn_box = QDialogButtonBox(btns) self.btn_box.accepted.connect(self.accept) self.btn_box.rejected.connect(self.reject) # checkbox to remember the choice (doesn't pop up for folders with no extension) if self._extension: self.persist_checkbox = QCheckBox( f'Remember this choice for files with a {self._extension} extension' ) self.persist_checkbox.toggle() layout.addWidget(self.persist_checkbox) layout.addWidget(self.btn_box) self.setLayout(layout) def add_reader_buttons(self, layout, readers): """Add radio button to layout for each reader in readers""" for display_name in sorted(readers.values()): button = QRadioButton(f"{display_name}") self.reader_btn_group.addButton(button) layout.addWidget(button) def _get_plugin_choice(self): """Get user's plugin choice based on the checked button""" checked_btn = self.reader_btn_group.checkedButton() if checked_btn: return checked_btn.text() def _get_persist_choice(self): """Get persistence checkbox choice""" return ( hasattr(self, 'persist_checkbox') and self.persist_checkbox.isChecked() ) def get_user_choices(self) -> Tuple[str, bool]: """Execute dialog and get user choices""" display_name = '' persist_choice = False dialog_result = self.exec_() # user pressed cancel if dialog_result: # grab the selected radio button text display_name = self._get_plugin_choice() # grab the persistence checkbox choice persist_choice = self._get_persist_choice() return display_name, persist_choice
def __init__(self): super().__init__() self.resize(400, 200) self.setWindowTitle("Load Run From Database") self._id_uid = None self._mode_id_uid = "id" label = QLabel("Enter run ID or UID:") self.le_id_uid = LineEditExtended() self.le_id_uid.textChanged.connect(self.le_id_uid_text_changed) self.le_id_uid.editingFinished.connect(self.le_id_uid_editing_finished) set_tooltip(self.le_id_uid, "Enter <b>Run ID</b> or <b>Run UID</b>.") self._validator_id = IntValidatorStrict() # Short UID example: "04c9afa7" self._validator_uid_short = QRegExpValidator(QRegExp(r"[0-9a-f]{8}")) # Full UID example: "04c9afa7-a43a-4af1-8e55-2034384d4a77" self._validator_uid_full = QRegExpValidator( QRegExp( r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" )) self.rb_id = QRadioButton("Run ID") set_tooltip( self.rb_id, "The value in the line edit box is <b>Run ID</b> (e.g. <b>34235</b> or <b>-1</b>)" ) self.rb_id.setChecked(self._mode_id_uid == "id") self.rb_uid = QRadioButton("Run UID") self.rb_uid.setChecked(self._mode_id_uid == "uid") set_tooltip( self.rb_uid, "The value in the line edit box is <b>Run UID</b> " "(e.g. <b>04c9afb7-a43a-4af1-8e55-2034384d4a77</b> or <b>04c9afb7</b>)", ) self.btn_group = QButtonGroup() self.btn_group.addButton(self.rb_id) self.btn_group.addButton(self.rb_uid) self.btn_group.buttonToggled.connect(self.btn_group_button_toggled) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) self.pb_ok = button_box.button(QDialogButtonBox.Ok) vbox = QVBoxLayout() vbox.addStretch(1) hbox = QHBoxLayout() hbox.addWidget(label) hbox.addStretch(1) vbox.addLayout(hbox) vbox.addWidget(self.le_id_uid) hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.rb_id) hbox.addStretch(1) hbox.addWidget(self.rb_uid) hbox.addStretch(1) vbox.addLayout(hbox) vbox.addStretch(1) vbox.addWidget(button_box) self.setLayout(vbox) # This is how the button from QDialogButtonBox can be disabled # button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.le_id_uid.setText("") self._validate_id_uid()
def create_general_widgets(self): """creates the menu objects""" # -------------------------------------------------------------- # Name self.name = QLabel("Name:") self.name_edit = QLineEdit(str(self._default_name)) self.colormap = QLabel("Color:") self.colormap_edit = QPushButtonColor(self._color_int, 'Select a color', parent=self) self.colormap_button = QPushButton("Advanced") self.colormap_button.setEnabled(False) # -------------------------------------------------------------- # TODO: the way these sliders work is they have some defined range # if you go outside the range, they "reset" to 50% and # are redefined from value-delta to value+delta, where # delta=nominal/2 num_u = 5 num_w = 10 self.tesselation_u = QLabel("Num_U") self.tesselation_u_edit = QJumpSlider(QtCore.Qt.Horizontal) self.tesselation_u_edit.setTickPosition(QSlider.TicksBelow) self.tesselation_u_edit.setRange(1, 10) self.tesselation_u_edit.setValue(num_u) self.tesselation_u_button = QLineEdit('') def int_func(val): return str(int(val)) self.tesselation_u_edit.set_forward_connection( self.tesselation_u_button, int_func) self.tesselation_w = QLabel("Num_W") self.tesselation_w_edit = QJumpSlider(QtCore.Qt.Horizontal) self.tesselation_w_edit.setTickPosition(QSlider.TicksBelow) self.tesselation_w_edit.setRange(1, 10) self.tesselation_w_edit.setValue(num_w) self.tesselation_w_button = QLineEdit('') # -------------------------------------------------------------- density = 1.0 self.density = QLabel("Density") self.density_edit = QLineEdit(str(density)) self.density_button = QCheckBox('Thin Shell') priority = 3 priority_max = 50 self.priority = QLabel('Priority') msg = ( 'Components compete for volume during the mass properties.\n' 'A lower priority (e.g., 0) takes precendence. Additionally, \n' 'when combined with a density of 0.0, this can be used to \n' 'create voids (empty regions).') self.priority.setToolTip(msg) self.priority_edit = QSpinBox(self) self.priority_edit.setRange(0, priority_max) self.priority_edit.setSingleStep(1) self.priority_edit.setValue(priority) # -------------------------------------------------------------- self.negative_volume_button = QCheckBox('Negative Volume') # -------------------------------------------------------------- #for key in self.colormap_keys: #self.colormap_edit.addItem(key) #self._colormap = 'grey' #self.colormap_edit.setCurrentIndex(self.colormap_keys.index(self._colormap)) # -------------------------------------------------------------- # the header self.grid2_title = QLabel("Color Scale:") # on / off self.show_radio = QRadioButton("Show") self.hide_radio = QRadioButton("Hide") widget = QWidget(self) show_hide_group = QButtonGroup(widget) # -------------------------------------------------------------- # closing self.apply_button = QPushButton("Apply") self.ok_button = QPushButton("OK") self.cancel_button = QPushButton("Cancel")
class DialogSelectScan(QDialog): def __init__(self): super().__init__() self.resize(400, 200) self.setWindowTitle("Load Run From Database") self._id_uid = None self._mode_id_uid = "id" label = QLabel("Enter run ID or UID:") self.le_id_uid = LineEditExtended() self.le_id_uid.textChanged.connect(self.le_id_uid_text_changed) self.le_id_uid.editingFinished.connect(self.le_id_uid_editing_finished) set_tooltip(self.le_id_uid, "Enter <b>Run ID</b> or <b>Run UID</b>.") self._validator_id = IntValidatorStrict() # Short UID example: "04c9afa7" self._validator_uid_short = QRegExpValidator(QRegExp(r"[0-9a-f]{8}")) # Full UID example: "04c9afa7-a43a-4af1-8e55-2034384d4a77" self._validator_uid_full = QRegExpValidator( QRegExp( r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" )) self.rb_id = QRadioButton("Run ID") set_tooltip( self.rb_id, "The value in the line edit box is <b>Run ID</b> (e.g. <b>34235</b> or <b>-1</b>)" ) self.rb_id.setChecked(self._mode_id_uid == "id") self.rb_uid = QRadioButton("Run UID") self.rb_uid.setChecked(self._mode_id_uid == "uid") set_tooltip( self.rb_uid, "The value in the line edit box is <b>Run UID</b> " "(e.g. <b>04c9afb7-a43a-4af1-8e55-2034384d4a77</b> or <b>04c9afb7</b>)", ) self.btn_group = QButtonGroup() self.btn_group.addButton(self.rb_id) self.btn_group.addButton(self.rb_uid) self.btn_group.buttonToggled.connect(self.btn_group_button_toggled) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) self.pb_ok = button_box.button(QDialogButtonBox.Ok) vbox = QVBoxLayout() vbox.addStretch(1) hbox = QHBoxLayout() hbox.addWidget(label) hbox.addStretch(1) vbox.addLayout(hbox) vbox.addWidget(self.le_id_uid) hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.rb_id) hbox.addStretch(1) hbox.addWidget(self.rb_uid) hbox.addStretch(1) vbox.addLayout(hbox) vbox.addStretch(1) vbox.addWidget(button_box) self.setLayout(vbox) # This is how the button from QDialogButtonBox can be disabled # button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.le_id_uid.setText("") self._validate_id_uid() def btn_group_button_toggled(self, button, state): if state: if button == self.rb_id: self._mode_id_uid = "id" elif button == self.rb_uid: self._mode_id_uid = "uid" text = self.le_id_uid.text() if self._validate_id_uid(text): self._read_id_uid(text) def le_id_uid_text_changed(self, text): self._validate_id_uid(text) def le_id_uid_editing_finished(self): text = self.le_id_uid.text() if self._validate_id_uid(text): self._read_id_uid(text) def get_id_uid(self): """ Read the selected scan ID or UID Returns ------- (str, int) or (str, str) keyword "id" or "uid" depending on whether the second element is scan ID or UID, scan ID (int), scan UID (str) or None if no scan ID or UID is selected """ return self._mode_id_uid, self._id_uid def _validate_id_uid(self, text=None): valid = False if text is None: text = self.le_id_uid.text() if self._mode_id_uid == "id": if self._validator_id.validate(text, 0)[0] == QIntValidator.Acceptable: valid = True elif self._mode_id_uid == "uid": if (self._validator_uid_short.validate( text, 0)[0] == QIntValidator.Acceptable or self._validator_uid_full.validate( text, 0)[0] == QIntValidator.Acceptable): valid = True self.le_id_uid.setValid(valid) self.pb_ok.setEnabled(valid) return valid def _read_id_uid(self, text): # It is assumed that the entered text is valid for the selected mode if text is None: text = self.le_id_uid.text() if self._mode_id_uid == "id": self._id_uid = int(text) elif self._mode_id_uid == "uid": self._id_uid = text def _set_mode(self, mode): if mode == "id": self._mode_id_uid = mode self.rb_id.setChecked(True) elif mode == "uid": self._mode_id_uid = mode self.rb_uid.setChecked(True)
def setup_page(self): newcb = self.create_checkbox # --- Advanced # Remove disabled languages language_codes = LANGUAGE_CODES.copy() for lang in DISABLED_LANGUAGES: language_codes.pop(lang) languages = language_codes.items() language_choices = sorted([(val, key) for key, val in languages]) language_combo = self.create_combobox(_('Language:'), language_choices, 'interface_language', restart=True) opengl_options = ['Automatic', 'Desktop', 'Software', 'GLES'] opengl_choices = list(zip(opengl_options, [c.lower() for c in opengl_options])) opengl_combo = self.create_combobox(_('Rendering engine:'), opengl_choices, 'opengl', restart=True) single_instance_box = newcb(_("Use a single instance"), 'single_instance', tip=_("Set this to open external<br> " "Python files in an already running " "instance (Requires a restart)")) prompt_box = newcb(_("Prompt when exiting"), 'prompt_on_exit') popup_console_box = newcb(_("Show internal Spyder errors to report " "them to Github"), 'show_internal_errors') check_updates = newcb(_("Check for updates on startup"), 'check_updates_on_startup') # Decide if it's possible to activate or not single instance mode if running_in_mac_app(): self.set_option("single_instance", True) single_instance_box.setEnabled(False) comboboxes_advanced_layout = QHBoxLayout() cbs_adv_grid = QGridLayout() cbs_adv_grid.addWidget(language_combo.label, 0, 0) cbs_adv_grid.addWidget(language_combo.combobox, 0, 1) cbs_adv_grid.addWidget(opengl_combo.label, 1, 0) cbs_adv_grid.addWidget(opengl_combo.combobox, 1, 1) comboboxes_advanced_layout.addLayout(cbs_adv_grid) comboboxes_advanced_layout.addStretch(1) advanced_layout = QVBoxLayout() advanced_layout.addLayout(comboboxes_advanced_layout) advanced_layout.addWidget(single_instance_box) advanced_layout.addWidget(prompt_box) advanced_layout.addWidget(popup_console_box) advanced_layout.addWidget(check_updates) advanced_widget = QWidget() advanced_widget.setLayout(advanced_layout) # --- Panes interface_group = QGroupBox(_("Panes")) verttabs_box = newcb(_("Vertical tabs in panes"), 'vertical_tabs') margin_box = newcb(_("Custom margin for panes:"), 'use_custom_margin') margin_spin = self.create_spinbox("", _("pixels"), 'custom_margin', default=0, min_=0, max_=30) margin_box.toggled.connect(margin_spin.spinbox.setEnabled) margin_box.toggled.connect(margin_spin.slabel.setEnabled) margin_spin.spinbox.setEnabled(self.get_option('use_custom_margin')) margin_spin.slabel.setEnabled(self.get_option('use_custom_margin')) cursor_box = newcb(_("Cursor blinking:"), 'use_custom_cursor_blinking') cursor_spin = self.create_spinbox( "", _("ms"), 'custom_cursor_blinking', default=QApplication.cursorFlashTime(), min_=0, max_=5000, step=100) cursor_box.toggled.connect(cursor_spin.spinbox.setEnabled) cursor_box.toggled.connect(cursor_spin.slabel.setEnabled) cursor_spin.spinbox.setEnabled( self.get_option('use_custom_cursor_blinking')) cursor_spin.slabel.setEnabled( self.get_option('use_custom_cursor_blinking')) margins_cursor_layout = QGridLayout() margins_cursor_layout.addWidget(margin_box, 0, 0) margins_cursor_layout.addWidget(margin_spin.spinbox, 0, 1) margins_cursor_layout.addWidget(margin_spin.slabel, 0, 2) margins_cursor_layout.addWidget(cursor_box, 1, 0) margins_cursor_layout.addWidget(cursor_spin.spinbox, 1, 1) margins_cursor_layout.addWidget(cursor_spin.slabel, 1, 2) margins_cursor_layout.setColumnStretch(2, 100) # Layout interface interface_layout = QVBoxLayout() interface_layout.addWidget(verttabs_box) interface_layout.addLayout(margins_cursor_layout) interface_group.setLayout(interface_layout) if sys.platform == "darwin": def set_open_file(state): if state: register_app_launchservices() else: restore_launchservices() macOS_group = QGroupBox(_("macOS integration")) mac_open_file_box = newcb( _("Open files from Finder with Spyder"), 'mac_open_file', tip=_("Register Spyder with the Launch Services")) mac_open_file_box.toggled.connect(set_open_file) macOS_layout = QVBoxLayout() macOS_layout.addWidget(mac_open_file_box) if als.get_bundle_identifier() is None: # Disable setting mac_open_file_box.setDisabled(True) macOS_layout.addWidget(QLabel( _('Launch Spyder with <code>python.app</code> to enable' ' Apple event integrations.'))) macOS_group.setLayout(macOS_layout) # --- Screen resolution Group (hidpi) screen_resolution_group = QGroupBox(_("Screen resolution")) screen_resolution_bg = QButtonGroup(screen_resolution_group) screen_resolution_label = QLabel(_("Configuration for high DPI " "screens<br><br>" "Please see " "<a href=\"{0}\">{0}</a><> " "for more information about " "these options (in " "English).").format(HDPI_QT_PAGE)) screen_resolution_label.setWordWrap(True) screen_resolution_label.setOpenExternalLinks(True) normal_radio = self.create_radiobutton( _("Normal"), 'normal_screen_resolution', button_group=screen_resolution_bg) auto_scale_radio = self.create_radiobutton( _("Enable auto high DPI scaling"), 'high_dpi_scaling', button_group=screen_resolution_bg, tip=_("Set this for high DPI displays"), restart=True) custom_scaling_radio = self.create_radiobutton( _("Set a custom high DPI scaling"), 'high_dpi_custom_scale_factor', button_group=screen_resolution_bg, tip=_("Set this for high DPI displays when " "auto scaling does not work"), restart=True) self.custom_scaling_edit = self.create_lineedit( "", 'high_dpi_custom_scale_factors', tip=_("Enter values for different screens " "separated by semicolons ';'.\n" "Float values are supported"), alignment=Qt.Horizontal, regex=r"[0-9]+(?:\.[0-9]*)(;[0-9]+(?:\.[0-9]*))*", restart=True) normal_radio.toggled.connect(self.custom_scaling_edit.setDisabled) auto_scale_radio.toggled.connect(self.custom_scaling_edit.setDisabled) custom_scaling_radio.toggled.connect( self.custom_scaling_edit.setEnabled) # Layout Screen resolution screen_resolution_layout = QVBoxLayout() screen_resolution_layout.addWidget(screen_resolution_label) screen_resolution_inner_layout = QGridLayout() screen_resolution_inner_layout.addWidget(normal_radio, 0, 0) screen_resolution_inner_layout.addWidget(auto_scale_radio, 1, 0) screen_resolution_inner_layout.addWidget(custom_scaling_radio, 2, 0) screen_resolution_inner_layout.addWidget( self.custom_scaling_edit, 2, 1) screen_resolution_layout.addLayout(screen_resolution_inner_layout) screen_resolution_group.setLayout(screen_resolution_layout) if sys.platform == "darwin": interface_tab = self.create_tab(screen_resolution_group, interface_group, macOS_group) else: interface_tab = self.create_tab(screen_resolution_group, interface_group) self.tabs = QTabWidget() self.tabs.addTab(interface_tab, _("Interface")) self.tabs.addTab(self.create_tab(advanced_widget), _("Advanced settings")) vlayout = QVBoxLayout() vlayout.addWidget(self.tabs) self.setLayout(vlayout)
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._edge.events.current_color.connect( self._on_current_edge_color_change) self.layer.events.current_face_color.connect( self._on_current_face_color_change) self.layer._face.events.current_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=trans._('click to set current face color'), ) self.edgeColorEdit = QColorSwatchEdit( initial_color=self.layer.current_edge_color, tooltip=trans._('click to set current edge color'), ) self.faceColorEdit.color_changed.connect(self.changeFaceColor) self.edgeColorEdit.color_changed.connect(self.changeEdgeColor) symbol_comboBox = QComboBox() current_index = 0 for index, (data, text) in enumerate(SYMBOL_TRANSLATION.items()): data = data.value symbol_comboBox.addItem(text, data) if data == self.layer.symbol: current_index = index symbol_comboBox.setCurrentIndex(current_index) symbol_comboBox.activated[str].connect(self.changeSymbol) self.symbolComboBox = symbol_comboBox ndim_cb = QCheckBox() ndim_cb.setToolTip(trans._('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=trans._('Select points (S)'), ) self.addition_button = QtModeRadioButton( layer, 'add_points', Mode.ADD, tooltip=trans._('Add points (P)')) self.panzoom_button = QtModeRadioButton( layer, 'pan_zoom', Mode.PAN_ZOOM, tooltip=trans._('Pan/zoom (Z)'), checked=True, ) self.delete_button = QtModePushButton( layer, 'delete_shape', slot=self.layer.remove_selected, tooltip=trans._( "Delete selected points ({shortcut})", shortcut=Shortcut('Backspace').platform, ), ) 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 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(trans._('opacity:')), 1, 0) self.grid_layout.addWidget(self.opacitySlider, 1, 1) self.grid_layout.addWidget(QLabel(trans._('point size:')), 2, 0) self.grid_layout.addWidget(self.sizeSlider, 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._('symbol:')), 4, 0) self.grid_layout.addWidget(self.symbolComboBox, 4, 1) self.grid_layout.addWidget(QLabel(trans._('face color:')), 5, 0) self.grid_layout.addWidget(self.faceColorEdit, 5, 1) self.grid_layout.addWidget(QLabel(trans._('edge color:')), 6, 0) self.grid_layout.addWidget(self.edgeColorEdit, 6, 1) self.grid_layout.addWidget(QLabel(trans._('display text:')), 7, 0) self.grid_layout.addWidget(self.textDispCheckBox, 7, 1) self.grid_layout.addWidget(QLabel(trans._('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)
class ObjectExplorer(BaseDialog): """Object explorer main widget window.""" # TODO: Use signal to trigger update of configs sig_option_changed = Signal(str, object) def __init__(self, obj, name='', expanded=False, resize_to_contents=True, parent=None, attribute_columns=DEFAULT_ATTR_COLS, attribute_details=DEFAULT_ATTR_DETAILS, show_callable_attributes=False, show_special_attributes=False, dataframe_format=None, readonly=None, reset=False): """ Constructor :param name: name of the object as it will appear in the root node :param expanded: show the first visible root element expanded :param resize_to_contents: resize columns to contents ignoring width of the attributes :param obj: any Python object or variable :param attribute_columns: list of AttributeColumn objects that define which columns are present in the table and their defaults :param attribute_details: list of AttributeDetails objects that define which attributes can be selected in the details pane. :param show_callable_attributes: if True rows where the 'is attribute' and 'is callable' columns are both True, are displayed. Otherwise they are hidden. :param show_special_attributes: if True rows where the 'is attribute' is True and the object name starts and ends with two underscores, are displayed. Otherwise they are hidden. :param dataframe_format: Format for the values in the Dataframe Editor. :param reset: If true the persistent settings, such as column widths, are reset. """ QDialog.__init__(self, parent=parent) self.setAttribute(Qt.WA_DeleteOnClose) # Model self._attr_cols = attribute_columns self._attr_details = attribute_details self._dataframe_format = dataframe_format self.readonly = readonly self.btn_save_and_close = None self.btn_close = None self._tree_model = TreeModel(obj, obj_name=name, attr_cols=self._attr_cols) self._proxy_tree_model = TreeProxyModel( show_callable_attributes=show_callable_attributes, show_special_attributes=show_special_attributes, dataframe_format=dataframe_format) self._proxy_tree_model.setSourceModel(self._tree_model) # self._proxy_tree_model.setSortRole(RegistryTableModel.SORT_ROLE) self._proxy_tree_model.setDynamicSortFilter(True) # self._proxy_tree_model.setSortCaseSensitivity(Qt.CaseInsensitive) # Tree widget self.obj_tree = ToggleColumnTreeView( dataframe_format=self._dataframe_format) self.obj_tree.setAlternatingRowColors(True) self.obj_tree.setModel(self._proxy_tree_model) self.obj_tree.setSelectionBehavior(QAbstractItemView.SelectRows) self.obj_tree.setUniformRowHeights(True) self.obj_tree.add_header_context_menu() self.obj_tree.sig_option_changed.connect(self.sig_option_changed.emit) # Views self._setup_actions() self._setup_menu(show_callable_attributes=show_callable_attributes, show_special_attributes=show_special_attributes) self._setup_views() if name: name = "{} -".format(name) self.setWindowTitle("{} {}".format(name, EDITOR_NAME)) self.setWindowFlags(Qt.Window) self._resize_to_contents = resize_to_contents self._readViewSettings(reset=reset) # Update views with model self.toggle_show_special_attribute_action.setChecked( show_special_attributes) self.toggle_show_callable_action.setChecked(show_callable_attributes) # Select first row so that a hidden root node will not be selected. first_row_index = self._proxy_tree_model.firstItemIndex() self.obj_tree.setCurrentIndex(first_row_index) if self._tree_model.inspectedNodeIsVisible or expanded: self.obj_tree.expand(first_row_index) def get_value(self): """Get editor current object state.""" return self._tree_model.inspectedItem.obj def _make_show_column_function(self, column_idx): """Creates a function that shows or hides a column.""" show_column = lambda checked: self.obj_tree.setColumnHidden( column_idx, not checked) return show_column def _setup_actions(self): """Creates the main window actions.""" # Show/hide callable objects self.toggle_show_callable_action = \ QAction(_("Show callable attributes"), self, checkable=True, shortcut=QKeySequence("Alt+C"), statusTip=_("Shows/hides attributes " "that are callable (functions, methods, etc)")) self.toggle_show_callable_action.toggled.connect( self._proxy_tree_model.setShowCallables) self.toggle_show_callable_action.toggled.connect( self.obj_tree.resize_columns_to_contents) # Show/hide special attributes self.toggle_show_special_attribute_action = \ QAction(_("Show __special__ attributes"), self, checkable=True, shortcut=QKeySequence("Alt+S"), statusTip=_("Shows or hides __special__ attributes")) self.toggle_show_special_attribute_action.toggled.connect( self._proxy_tree_model.setShowSpecialAttributes) self.toggle_show_special_attribute_action.toggled.connect( self.obj_tree.resize_columns_to_contents) def _setup_menu(self, show_callable_attributes=False, show_special_attributes=False): """Sets up the main menu.""" self.tools_layout = QHBoxLayout() callable_attributes = create_toolbutton( self, text=_("Show callable attributes"), icon=ima.icon("class"), toggled=self._toggle_show_callable_attributes_action) callable_attributes.setCheckable(True) callable_attributes.setChecked(show_callable_attributes) self.tools_layout.addWidget(callable_attributes) special_attributes = create_toolbutton( self, text=_("Show __special__ attributes"), icon=ima.icon("private2"), toggled=self._toggle_show_special_attributes_action) special_attributes.setCheckable(True) special_attributes.setChecked(show_special_attributes) self.tools_layout.addWidget(special_attributes) self.tools_layout.addStretch() self.options_button = create_toolbutton(self, text=_('Options'), icon=ima.icon('tooloptions')) self.options_button.setPopupMode(QToolButton.InstantPopup) self.show_cols_submenu = QMenu(self) self.options_button.setMenu(self.show_cols_submenu) # Don't show menu arrow and remove padding if is_dark_interface(): self.options_button.setStyleSheet( ("QToolButton::menu-indicator{image: none;}\n" "QToolButton{padding: 3px;}")) else: self.options_button.setStyleSheet( "QToolButton::menu-indicator{image: none;}") self.tools_layout.addWidget(self.options_button) @Slot() def _toggle_show_callable_attributes_action(self): """Toggle show callable atributes action.""" action_checked = not self.toggle_show_callable_action.isChecked() self.toggle_show_callable_action.setChecked(action_checked) self.sig_option_changed.emit('show_callable_attributes', action_checked) @Slot() def _toggle_show_special_attributes_action(self): """Toggle show special attributes action.""" action_checked = ( not self.toggle_show_special_attribute_action.isChecked()) self.toggle_show_special_attribute_action.setChecked(action_checked) self.sig_option_changed.emit('show_special_attributes', action_checked) @Slot(str) def _set_dataframe_format(self, new_format): """ Set format to use in DataframeEditor. Args: new_format (string): e.g. "%.3f" """ self.sig_option_changed.emit('dataframe_format', new_format) self._tree_model.dataframe_format = new_format def _setup_views(self): """Creates the UI widgets.""" self.central_splitter = QSplitter(self, orientation=Qt.Vertical) layout = create_plugin_layout(self.tools_layout, self.central_splitter) self.setLayout(layout) # Stretch last column? # It doesn't play nice when columns are hidden and then shown again. obj_tree_header = self.obj_tree.header() obj_tree_header.setSectionsMovable(True) obj_tree_header.setStretchLastSection(False) add_actions(self.show_cols_submenu, self.obj_tree.toggle_column_actions_group.actions()) self.central_splitter.addWidget(self.obj_tree) # Bottom pane bottom_pane_widget = QWidget() bottom_layout = QHBoxLayout() bottom_layout.setSpacing(0) bottom_layout.setContentsMargins(5, 5, 5, 5) # left top right bottom bottom_pane_widget.setLayout(bottom_layout) self.central_splitter.addWidget(bottom_pane_widget) group_box = QGroupBox(_("Details")) bottom_layout.addWidget(group_box) v_group_layout = QVBoxLayout() h_group_layout = QHBoxLayout() h_group_layout.setContentsMargins(2, 2, 2, 2) # left top right bottom group_box.setLayout(v_group_layout) v_group_layout.addLayout(h_group_layout) # Radio buttons radio_widget = QWidget() radio_layout = QVBoxLayout() radio_layout.setContentsMargins(0, 0, 0, 0) # left top right bottom radio_widget.setLayout(radio_layout) self.button_group = QButtonGroup(self) for button_id, attr_detail in enumerate(self._attr_details): radio_button = QRadioButton(attr_detail.name) radio_layout.addWidget(radio_button) self.button_group.addButton(radio_button, button_id) self.button_group.buttonClicked[int].connect( self._change_details_field) self.button_group.button(0).setChecked(True) radio_layout.addStretch(1) h_group_layout.addWidget(radio_widget) # Editor widget self.editor = SimpleCodeEditor(self) self.editor.setReadOnly(True) h_group_layout.addWidget(self.editor) # Save and close buttons btn_layout = QHBoxLayout() btn_layout.addStretch() if not self.readonly: self.btn_save_and_close = QPushButton(_('Save and Close')) self.btn_save_and_close.setDisabled(True) self.btn_save_and_close.clicked.connect(self.accept) btn_layout.addWidget(self.btn_save_and_close) self.btn_close = QPushButton(_('Close')) self.btn_close.setAutoDefault(True) self.btn_close.setDefault(True) self.btn_close.clicked.connect(self.reject) btn_layout.addWidget(self.btn_close) v_group_layout.addLayout(btn_layout) # Splitter parameters self.central_splitter.setCollapsible(0, False) self.central_splitter.setCollapsible(1, True) self.central_splitter.setSizes([500, 320]) # Connect signals # Keep a temporary reference of the selection_model to prevent # segfault in PySide. # See http://permalink.gmane.org/gmane.comp.lib.qt.pyside.devel/222 selection_model = self.obj_tree.selectionModel() selection_model.currentChanged.connect(self._update_details) # Check if the values of the model have been changed self._proxy_tree_model.sig_setting_data.connect( self.save_and_close_enable) self._proxy_tree_model.sig_update_details.connect( self._update_details_for_item) # End of setup_methods def _readViewSettings(self, reset=False): """ Reads the persistent program settings. :param reset: If True, the program resets to its default settings. """ pos = QPoint(20, 20) window_size = QSize(825, 650) details_button_idx = 0 header = self.obj_tree.header() header_restored = False if reset: logger.debug("Resetting persistent view settings") else: pos = pos window_size = window_size details_button_idx = details_button_idx # splitter_state = settings.value("central_splitter/state") splitter_state = None if splitter_state: self.central_splitter.restoreState(splitter_state) # header_restored = self.obj_tree.read_view_settings( # 'table/header_state', # settings, reset) header_restored = False if not header_restored: column_sizes = [col.width for col in self._attr_cols] column_visible = [col.col_visible for col in self._attr_cols] for idx, size in enumerate(column_sizes): if not self._resize_to_contents and size > 0: # Just in case header.resizeSection(idx, size) else: header.resizeSections(QHeaderView.ResizeToContents) break for idx, visible in enumerate(column_visible): elem = self.obj_tree.toggle_column_actions_group.actions()[idx] elem.setChecked(visible) self.resize(window_size) button = self.button_group.button(details_button_idx) if button is not None: button.setChecked(True) @Slot() def save_and_close_enable(self): """Handle the data change event to enable the save and close button.""" if self.btn_save_and_close: self.btn_save_and_close.setEnabled(True) self.btn_save_and_close.setAutoDefault(True) self.btn_save_and_close.setDefault(True) @Slot(QModelIndex, QModelIndex) def _update_details(self, current_index, _previous_index): """Shows the object details in the editor given an index.""" tree_item = self._proxy_tree_model.treeItem(current_index) self._update_details_for_item(tree_item) def _change_details_field(self, _button_id=None): """Changes the field that is displayed in the details pane.""" # logger.debug("_change_details_field: {}".format(_button_id)) current_index = self.obj_tree.selectionModel().currentIndex() tree_item = self._proxy_tree_model.treeItem(current_index) self._update_details_for_item(tree_item) @Slot(TreeItem) def _update_details_for_item(self, tree_item): """Shows the object details in the editor given an tree_item.""" try: # obj = tree_item.obj button_id = self.button_group.checkedId() assert button_id >= 0, ("No radio button selected. " "Please report this bug.") attr_details = self._attr_details[button_id] data = attr_details.data_fn(tree_item) self.editor.setPlainText(data) self.editor.setWordWrapMode(attr_details.line_wrap) self.editor.setup_editor( font=get_font(font_size_delta=DEFAULT_SMALL_DELTA), show_blanks=False, color_scheme=CONF.get('appearance', 'selected'), scroll_past_end=False, ) self.editor.set_text(data) if attr_details.name == 'Source code': self.editor.set_language('Python') else: self.editor.set_language('Rst') except Exception as ex: self.editor.setStyleSheet("color: red;") stack_trace = traceback.format_exc() self.editor.setPlainText("{}\n\n{}".format(ex, stack_trace)) self.editor.setWordWrapMode( QTextOption.WrapAtWordBoundaryOrAnywhere) @classmethod def create_explorer(cls, *args, **kwargs): """ Creates and shows and ObjectExplorer window. The *args and **kwargs will be passed to the ObjectExplorer constructor A (class attribute) reference to the browser window is kept to prevent it from being garbage-collected. """ object_explorer = cls(*args, **kwargs) object_explorer.exec_() return object_explorer
def setup_page(self): self.ICON = ima.icon('genprefs') newcb = self.create_checkbox # --- Interface general_group = QGroupBox(_("General")) languages = LANGUAGE_CODES.items() language_choices = sorted([(val, key) for key, val in languages]) language_combo = self.create_combobox(_('Language:'), language_choices, 'interface_language', restart=True) opengl_options = ['Automatic', 'Desktop', 'Software', 'GLES'] opengl_choices = list( zip(opengl_options, [c.lower() for c in opengl_options])) opengl_combo = self.create_combobox(_('Rendering engine:'), opengl_choices, 'opengl', restart=True) single_instance_box = newcb(_("Use a single instance"), 'single_instance', tip=_("Set this to open external<br> " "Python files in an already running " "instance (Requires a restart)")) prompt_box = newcb(_("Prompt when exiting"), 'prompt_on_exit') popup_console_box = newcb( _("Show internal Spyder errors to report " "them to Github"), 'show_internal_errors') check_updates = newcb(_("Check for updates on startup"), 'check_updates_on_startup') # Decide if it's possible to activate or not single instance mode if running_in_mac_app(check_file=True): self.set_option("single_instance", True) single_instance_box.setEnabled(False) comboboxes_advanced_layout = QHBoxLayout() cbs_adv_grid = QGridLayout() cbs_adv_grid.addWidget(language_combo.label, 0, 0) cbs_adv_grid.addWidget(language_combo.combobox, 0, 1) cbs_adv_grid.addWidget(opengl_combo.label, 1, 0) cbs_adv_grid.addWidget(opengl_combo.combobox, 1, 1) comboboxes_advanced_layout.addLayout(cbs_adv_grid) comboboxes_advanced_layout.addStretch(1) general_layout = QVBoxLayout() general_layout.addLayout(comboboxes_advanced_layout) general_layout.addWidget(single_instance_box) general_layout.addWidget(prompt_box) general_layout.addWidget(popup_console_box) general_layout.addWidget(check_updates) general_group.setLayout(general_layout) # --- Theme interface_group = QGroupBox(_("Interface")) vertdock_box = newcb(_("Vertical title bars in panes"), 'vertical_dockwidget_titlebars') verttabs_box = newcb(_("Vertical tabs in panes"), 'vertical_tabs') animated_box = newcb(_("Animated toolbars and panes"), 'animated_docks') tear_off_box = newcb(_("Tear off menus"), 'tear_off_menus', tip=_("Set this to detach any<br> " "menu from the main window")) margin_box = newcb(_("Custom margin for panes:"), 'use_custom_margin') margin_spin = self.create_spinbox("", _("pixels"), 'custom_margin', default=0, min_=0, max_=30) margin_box.toggled.connect(margin_spin.spinbox.setEnabled) margin_box.toggled.connect(margin_spin.slabel.setEnabled) margin_spin.spinbox.setEnabled(self.get_option('use_custom_margin')) margin_spin.slabel.setEnabled(self.get_option('use_custom_margin')) cursor_box = newcb(_("Cursor blinking:"), 'use_custom_cursor_blinking') cursor_spin = self.create_spinbox( "", _("ms"), 'custom_cursor_blinking', default=QApplication.cursorFlashTime(), min_=0, max_=5000, step=100) cursor_box.toggled.connect(cursor_spin.spinbox.setEnabled) cursor_box.toggled.connect(cursor_spin.slabel.setEnabled) cursor_spin.spinbox.setEnabled( self.get_option('use_custom_cursor_blinking')) cursor_spin.slabel.setEnabled( self.get_option('use_custom_cursor_blinking')) margins_cursor_layout = QGridLayout() margins_cursor_layout.addWidget(margin_box, 0, 0) margins_cursor_layout.addWidget(margin_spin.spinbox, 0, 1) margins_cursor_layout.addWidget(margin_spin.slabel, 0, 2) margins_cursor_layout.addWidget(cursor_box, 1, 0) margins_cursor_layout.addWidget(cursor_spin.spinbox, 1, 1) margins_cursor_layout.addWidget(cursor_spin.slabel, 1, 2) margins_cursor_layout.setColumnStretch(2, 100) # Layout interface interface_layout = QVBoxLayout() interface_layout.addWidget(vertdock_box) interface_layout.addWidget(verttabs_box) interface_layout.addWidget(animated_box) interface_layout.addWidget(tear_off_box) interface_layout.addLayout(margins_cursor_layout) interface_group.setLayout(interface_layout) # --- Status bar sbar_group = QGroupBox(_("Status bar")) show_status_bar = newcb(_("Show status bar"), 'show_status_bar') memory_box = newcb(_("Show memory usage every"), 'memory_usage/enable', tip=self.main.mem_status.toolTip()) memory_spin = self.create_spinbox("", _(" ms"), 'memory_usage/timeout', min_=100, max_=1000000, step=100) memory_box.toggled.connect(memory_spin.setEnabled) memory_spin.setEnabled(self.get_option('memory_usage/enable')) memory_box.setEnabled(self.main.mem_status.is_supported()) memory_spin.setEnabled(self.main.mem_status.is_supported()) cpu_box = newcb(_("Show CPU usage every"), 'cpu_usage/enable', tip=self.main.cpu_status.toolTip()) cpu_spin = self.create_spinbox("", _(" ms"), 'cpu_usage/timeout', min_=100, max_=1000000, step=100) cpu_box.toggled.connect(cpu_spin.setEnabled) cpu_spin.setEnabled(self.get_option('cpu_usage/enable')) cpu_box.setEnabled(self.main.cpu_status.is_supported()) cpu_spin.setEnabled(self.main.cpu_status.is_supported()) status_bar_o = self.get_option('show_status_bar') show_status_bar.toggled.connect(memory_box.setEnabled) show_status_bar.toggled.connect(memory_spin.setEnabled) show_status_bar.toggled.connect(cpu_box.setEnabled) show_status_bar.toggled.connect(cpu_spin.setEnabled) memory_box.setEnabled(status_bar_o) memory_spin.setEnabled(status_bar_o) cpu_box.setEnabled(status_bar_o) cpu_spin.setEnabled(status_bar_o) # Layout status bar cpu_memory_layout = QGridLayout() cpu_memory_layout.addWidget(memory_box, 0, 0) cpu_memory_layout.addWidget(memory_spin, 0, 1) cpu_memory_layout.addWidget(cpu_box, 1, 0) cpu_memory_layout.addWidget(cpu_spin, 1, 1) sbar_layout = QVBoxLayout() sbar_layout.addWidget(show_status_bar) sbar_layout.addLayout(cpu_memory_layout) sbar_group.setLayout(sbar_layout) # --- Screen resolution Group (hidpi) screen_resolution_group = QGroupBox(_("Screen resolution")) screen_resolution_bg = QButtonGroup(screen_resolution_group) screen_resolution_label = QLabel( _("Configuration for high DPI " "screens<br><br>" "Please see " "<a href=\"{0}\">{0}</a><> " "for more information about " "these options (in " "English).").format(HDPI_QT_PAGE)) screen_resolution_label.setWordWrap(True) screen_resolution_label.setOpenExternalLinks(True) normal_radio = self.create_radiobutton( _("Normal"), 'normal_screen_resolution', button_group=screen_resolution_bg) auto_scale_radio = self.create_radiobutton( _("Enable auto high DPI scaling"), 'high_dpi_scaling', button_group=screen_resolution_bg, tip=_("Set this for high DPI displays"), restart=True) custom_scaling_radio = self.create_radiobutton( _("Set a custom high DPI scaling"), 'high_dpi_custom_scale_factor', button_group=screen_resolution_bg, tip=_("Set this for high DPI displays when " "auto scaling does not work"), restart=True) custom_scaling_edit = self.create_lineedit( "", 'high_dpi_custom_scale_factors', tip=_("Enter values for different screens " "separated by semicolons ';'.\n" "Float values are supported"), alignment=Qt.Horizontal, regex=r"[0-9]+(?:\.[0-9]*)(;[0-9]+(?:\.[0-9]*))*", restart=True) normal_radio.toggled.connect(custom_scaling_edit.setDisabled) auto_scale_radio.toggled.connect(custom_scaling_edit.setDisabled) custom_scaling_radio.toggled.connect(custom_scaling_edit.setEnabled) # Layout Screen resolution screen_resolution_layout = QVBoxLayout() screen_resolution_layout.addWidget(screen_resolution_label) screen_resolution_inner_layout = QGridLayout() screen_resolution_inner_layout.addWidget(normal_radio, 0, 0) screen_resolution_inner_layout.addWidget(auto_scale_radio, 1, 0) screen_resolution_inner_layout.addWidget(custom_scaling_radio, 2, 0) screen_resolution_inner_layout.addWidget(custom_scaling_edit, 2, 1) screen_resolution_layout.addLayout(screen_resolution_inner_layout) screen_resolution_group.setLayout(screen_resolution_layout) tabs = QTabWidget() tabs.addTab(self.create_tab(screen_resolution_group, interface_group), _("Interface")) tabs.addTab(self.create_tab(general_group, sbar_group), _("Advanced settings")) vlayout = QVBoxLayout() vlayout.addWidget(tabs) self.setLayout(vlayout)
def initialize_content(self): """ Declare the validators and event connections for the widgets loaded through the .ui file. """ # Sample data # Validators self._content.transmission_edit.setValidator(QDoubleValidator(self._content.transmission_edit)) self._content.dtransmission_edit.setValidator(QDoubleValidator(self._content.dtransmission_edit)) self._content.beam_radius_edit.setValidator(QDoubleValidator(self._content.beam_radius_edit)) self._content.sample_thickness_edit.setValidator(QDoubleValidator(self._content.sample_thickness_edit)) # Connections self._content.data_file_browse_button.clicked.connect(self._data_file_browse) self._content.calculate_radio.clicked.connect(self._calculate_clicked) self._content.fix_trans_radio.clicked.connect(self._calculate_clicked) self._content.empty_button.clicked.connect(self._empty_browse) self._content.sample_button.clicked.connect(self._sample_browse) self._content.data_file_plot_button.clicked.connect(self._data_file_plot) self._content.empty_plot_button.clicked.connect(self._empty_plot) self._content.sample_plot_button.clicked.connect(self._sample_plot) # Calculate/Fix radio button g1 = QButtonGroup(self) g1.addButton(self._content.calculate_radio) g1.addButton(self._content.fix_trans_radio) g1.setExclusive(True) if not self._settings.debug: self._content.fix_transmission_layout.deleteLater() self._content.calculate_radio.hide() self._content.fix_trans_radio.hide() self._content.plus_minus_label.hide() self._content.transmission_edit.hide() self._content.dtransmission_edit.hide() if not self._has_instrument_view: self._content.data_file_plot_button.hide() self._content.empty_plot_button.hide() self._content.sample_plot_button.hide() # Background ########## # Validators self._content.bck_transmission_edit.setValidator(QDoubleValidator(self._content.bck_transmission_edit)) self._content.bck_dtransmission_edit.setValidator(QDoubleValidator(self._content.bck_dtransmission_edit)) self._content.bck_beam_radius_edit.setValidator(QDoubleValidator(self._content.beam_radius_edit)) #self._content.bck_thickness_edit.setValidator(QDoubleValidator(self._content.bck_thickness_edit)) # Connections self._content.background_chk.clicked.connect(self._background_clicked) self._content.background_browse.clicked.connect(self._background_browse) self._content.bck_calculate_radio.clicked.connect(self._bck_calculate_clicked) self._content.bck_fix_trans_radio.clicked.connect(self._bck_calculate_clicked) self._content.bck_empty_button.clicked.connect(self._bck_empty_browse) self._content.bck_sample_button.clicked.connect(self._bck_sample_browse) self._content.background_plot_button.clicked.connect(self._background_plot_clicked) self._content.bck_empty_plot_button.clicked.connect(self._bck_empty_plot) self._content.bck_sample_plot_button.clicked.connect(self._bck_sample_plot) # Calculate/Fix radio button g2 = QButtonGroup(self) g2.addButton(self._content.bck_calculate_radio) g2.addButton(self._content.bck_fix_trans_radio) g2.setExclusive(True) if not self._settings.debug: self._content.bck_fix_transmission_layout.deleteLater() self._content.bck_calculate_radio.hide() self._content.bck_fix_trans_radio.hide() self._content.bck_plus_minus_label.hide() self._content.bck_transmission_edit.hide() self._content.bck_dtransmission_edit.hide() if not self._settings.advanced: self._content.theta_dep_chk.hide() self._content.bck_theta_dep_chk.hide() self._content.sample_thickness_label.hide() self._content.sample_thickness_edit.hide() #self._content.bck_thickness_label.hide() #self._content.bck_thickness_edit.hide() if not self._has_instrument_view: self._content.background_plot_button.hide() self._content.bck_empty_plot_button.hide() self._content.bck_sample_plot_button.hide()
class QtLabelsControls(QtLayerControls): """Qt view and controls for the napari Labels layer. Parameters ---------- layer : napari.layers.Labels An instance of a napari Labels layer. Attributes ---------- button_group : qtpy.QtWidgets.QButtonGroup Button group of labels layer modes: PAN_ZOOM, PICKER, PAINT, ERASE, or FILL. colormapUpdate : qtpy.QtWidgets.QPushButton Button to update colormap of label layer. contigCheckBox : qtpy.QtWidgets.QCheckBox Checkbox to control if label layer is contiguous. fill_button : qtpy.QtWidgets.QtModeRadioButton Button to select FILL mode on Labels layer. grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : napari.layers.Labels An instance of a napari Labels layer. ndimSpinBox : qtpy.QtWidgets.QSpinBox Spinbox to control the number of editable dimensions of label layer. paint_button : qtpy.QtWidgets.QtModeRadioButton Button to select PAINT mode on Labels layer. panzoom_button : qtpy.QtWidgets.QtModeRadioButton Button to select PAN_ZOOM mode on Labels layer. pick_button : qtpy.QtWidgets.QtModeRadioButton Button to select PICKER mode on Labels layer. erase_button : qtpy.QtWidgets.QtModeRadioButton Button to select ERASE mode on Labels layer. selectionSpinBox : superqt.QLargeIntSpinBox Widget to select a specific label by its index. N.B. cannot represent labels > 2**53. Raises ------ ValueError Raise error if label mode is not PAN_ZOOM, PICKER, PAINT, ERASE, or FILL. """ def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self._on_mode_change) self.layer.events._ndisplay.connect(self._on_ndisplay_change) self.layer.events.rendering.connect(self._on_rendering_change) self.layer.events.selected_label.connect( self._on_selected_label_change) self.layer.events.brush_size.connect(self._on_brush_size_change) self.layer.events.contiguous.connect(self._on_contiguous_change) self.layer.events.n_edit_dimensions.connect( self._on_n_edit_dimensions_change) self.layer.events.contour.connect(self._on_contour_change) self.layer.events.editable.connect(self._on_editable_change) self.layer.events.preserve_labels.connect( self._on_preserve_labels_change) self.layer.events.color_mode.connect(self._on_color_mode_change) # selection spinbox self.selectionSpinBox = QLargeIntSpinBox() dtype_lims = get_dtype_limits(get_dtype(layer)) self.selectionSpinBox.setRange(*dtype_lims) self.selectionSpinBox.setKeyboardTracking(False) self.selectionSpinBox.valueChanged.connect(self.changeSelection) self.selectionSpinBox.setAlignment(Qt.AlignCenter) self._on_selected_label_change() sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(1) sld.setMaximum(40) sld.setSingleStep(1) sld.valueChanged.connect(self.changeSize) self.brushSizeSlider = sld self._on_brush_size_change() contig_cb = QCheckBox() contig_cb.setToolTip(trans._('contiguous editing')) contig_cb.stateChanged.connect(self.change_contig) self.contigCheckBox = contig_cb self._on_contiguous_change() ndim_sb = QSpinBox() self.ndimSpinBox = ndim_sb ndim_sb.setToolTip(trans._('number of dimensions for label editing')) ndim_sb.valueChanged.connect(self.change_n_edit_dim) ndim_sb.setMinimum(2) ndim_sb.setMaximum(self.layer.ndim) ndim_sb.setSingleStep(1) ndim_sb.setAlignment(Qt.AlignCenter) self._on_n_edit_dimensions_change() self.contourSpinBox = QLargeIntSpinBox() self.contourSpinBox.setRange(*dtype_lims) self.contourSpinBox.setToolTip(trans._('display contours of labels')) self.contourSpinBox.valueChanged.connect(self.change_contour) self.contourSpinBox.setKeyboardTracking(False) self.contourSpinBox.setAlignment(Qt.AlignCenter) self._on_contour_change() preserve_labels_cb = QCheckBox() preserve_labels_cb.setToolTip( trans._('preserve existing labels while painting')) preserve_labels_cb.stateChanged.connect(self.change_preserve_labels) self.preserveLabelsCheckBox = preserve_labels_cb self._on_preserve_labels_change() selectedColorCheckbox = QCheckBox() selectedColorCheckbox.setToolTip( trans._("Display only selected label")) selectedColorCheckbox.stateChanged.connect(self.toggle_selected_mode) self.selectedColorCheckbox = selectedColorCheckbox # shuffle colormap button self.colormapUpdate = QtModePushButton( None, 'shuffle', slot=self.changeColor, tooltip=trans._('shuffle colors'), ) self.panzoom_button = QtModeRadioButton( layer, 'zoom', Mode.PAN_ZOOM, checked=True, ) action_manager.bind_button('napari:activate_label_pan_zoom_mode', self.panzoom_button) self.pick_button = QtModeRadioButton(layer, 'picker', Mode.PICK) action_manager.bind_button('napari:activate_label_picker_mode', self.pick_button) self.paint_button = QtModeRadioButton(layer, 'paint', Mode.PAINT) action_manager.bind_button('napari:activate_paint_mode', self.paint_button) self.fill_button = QtModeRadioButton( layer, 'fill', Mode.FILL, ) action_manager.bind_button( 'napari:activate_fill_mode', self.fill_button, extra_tooltip_text=trans._( "Toggle with {shortcut}", shortcut=Shortcut("Control"), ), ) self.erase_button = QtModeRadioButton( layer, 'erase', Mode.ERASE, ) action_manager.bind_button( 'napari:activate_label_erase_mode', self.erase_button, extra_tooltip_text=trans._( "Toggle with {shortcut}", shortcut=Shortcut("Alt"), ), ) # don't bind with action manager as this would remove "Toggle with {shortcut}" 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.paint_button) button_row.addWidget(self.fill_button) button_row.addWidget(self.pick_button) button_row.addWidget(self.panzoom_button) button_row.setSpacing(4) button_row.setContentsMargins(0, 0, 0, 5) renderComboBox = QComboBox(self) rendering_options = [i.value for i in LabelsRendering] renderComboBox.addItems(rendering_options) index = renderComboBox.findText(self.layer.rendering, Qt.MatchFixedString) renderComboBox.setCurrentIndex(index) renderComboBox.activated[str].connect(self.changeRendering) self.renderComboBox = renderComboBox self.renderLabel = QLabel(trans._('rendering:')) self._on_ndisplay_change() color_mode_comboBox = QComboBox(self) for index, (data, text) in enumerate(LABEL_COLOR_MODE_TRANSLATIONS.items()): data = data.value color_mode_comboBox.addItem(text, data) if self.layer.color_mode == data: color_mode_comboBox.setCurrentIndex(index) color_mode_comboBox.activated.connect(self.change_color_mode) self.colorModeComboBox = color_mode_comboBox self._on_color_mode_change() color_layout = QHBoxLayout() self.colorBox = QtColorBox(layer) color_layout.addWidget(self.colorBox) color_layout.addWidget(self.selectionSpinBox) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_row, 0, 0, 1, 4) self.grid_layout.addWidget(QLabel(trans._('label:')), 1, 0, 1, 1) self.grid_layout.addLayout(color_layout, 1, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('opacity:')), 2, 0, 1, 1) self.grid_layout.addWidget(self.opacitySlider, 2, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('brush size:')), 3, 0, 1, 1) self.grid_layout.addWidget(self.brushSizeSlider, 3, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('blending:')), 5, 0, 1, 1) self.grid_layout.addWidget(self.blendComboBox, 5, 1, 1, 3) self.grid_layout.addWidget(self.renderLabel, 6, 0, 1, 1) self.grid_layout.addWidget(self.renderComboBox, 6, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('color mode:')), 7, 0, 1, 1) self.grid_layout.addWidget(self.colorModeComboBox, 7, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('contour:')), 8, 0, 1, 1) self.grid_layout.addWidget(self.contourSpinBox, 8, 1, 1, 1) self.grid_layout.addWidget(QLabel(trans._('n edit dim:')), 9, 0, 1, 1) self.grid_layout.addWidget(self.ndimSpinBox, 9, 1, 1, 1) self.grid_layout.addWidget(QLabel(trans._('contiguous:')), 10, 0, 1, 1) self.grid_layout.addWidget(self.contigCheckBox, 10, 1, 1, 1) self.grid_layout.addWidget(QLabel(trans._('preserve\nlabels:')), 11, 0, 1, 2) self.grid_layout.addWidget(self.preserveLabelsCheckBox, 11, 1, 1, 1) self.grid_layout.addWidget(QLabel(trans._('show\nselected:')), 11, 2, 1, 1) self.grid_layout.addWidget(self.selectedColorCheckbox, 11, 3, 1, 1) self.grid_layout.setRowStretch(12, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def _on_mode_change(self, event): """Receive layer model mode change event and update checkbox ticks. Parameters ---------- event : napari.utils.event.Event The napari event that triggered this method. Raises ------ ValueError Raise error if event.mode is not PAN_ZOOM, PICK, PAINT, ERASE, or FILL """ mode = event.mode if mode == Mode.PAN_ZOOM: self.panzoom_button.setChecked(True) elif mode == Mode.PICK: self.pick_button.setChecked(True) elif mode == Mode.PAINT: self.paint_button.setChecked(True) elif mode == Mode.FILL: self.fill_button.setChecked(True) elif mode == Mode.ERASE: self.erase_button.setChecked(True) elif mode == Mode.TRANSFORM: pass else: raise ValueError(trans._("Mode not recognized")) def changeRendering(self, text): """Change rendering mode for image display. Parameters ---------- text : str Rendering mode used by vispy. Selects a preset rendering mode in vispy that determines how volume is displayed: * translucent: voxel colors are blended along the view ray until the result is opaque. * iso_categorical: isosurface for categorical data (e.g., labels). Cast a ray until a value greater than zero is encountered. At that location, lighning calculations are performed to give the visual appearance of a surface. """ self.layer.rendering = text def changeColor(self): """Change colormap of the label layer.""" self.layer.new_colormap() def changeSelection(self, value): """Change currently selected label. Parameters ---------- value : int Index of label to select. """ self.layer.selected_label = value self.selectionSpinBox.clearFocus() self.setFocus() def toggle_selected_mode(self, state): if state == Qt.Checked: self.layer.show_selected_label = True else: self.layer.show_selected_label = False def changeSize(self, value): """Change paint brush size. Parameters ---------- value : float Size of the paint brush. """ self.layer.brush_size = value def change_contig(self, state): """Toggle contiguous state of label layer. Parameters ---------- state : QCheckBox Checkbox indicating if labels are contiguous. """ if state == Qt.Checked: self.layer.contiguous = True else: self.layer.contiguous = False def change_n_edit_dim(self, value): """Change the number of editable dimensions of label layer. Parameters ---------- value : int The number of editable dimensions to set. """ self.layer.n_edit_dimensions = value self.ndimSpinBox.clearFocus() self.setFocus() def change_contour(self, value): """Change contour thickness. Parameters ---------- value : int Thickness of contour. """ self.layer.contour = value self.contourSpinBox.clearFocus() self.setFocus() def change_preserve_labels(self, state): """Toggle preserve_labels state of label layer. Parameters ---------- state : QCheckBox Checkbox indicating if overwriting label is enabled. """ self.layer.preserve_labels = state == Qt.Checked def change_color_mode(self): """Change color mode of label layer""" self.layer.color_mode = self.colorModeComboBox.currentData() def _on_contour_change(self): """Receive layer model contour value change event and update spinbox.""" with self.layer.events.contour.blocker(): value = self.layer.contour self.contourSpinBox.setValue(value) def _on_selected_label_change(self): """Receive layer model label selection change event and update spinbox.""" with self.layer.events.selected_label.blocker(): value = self.layer.selected_label self.selectionSpinBox.setValue(value) def _on_brush_size_change(self): """Receive layer model brush size change event and update the slider.""" with self.layer.events.brush_size.blocker(): value = self.layer.brush_size value = np.maximum(1, int(value)) if value > self.brushSizeSlider.maximum(): self.brushSizeSlider.setMaximum(int(value)) self.brushSizeSlider.setValue(value) def _on_n_edit_dimensions_change(self): """Receive layer model n-dim mode change event and update the checkbox.""" with self.layer.events.n_edit_dimensions.blocker(): value = self.layer.n_edit_dimensions self.ndimSpinBox.setValue(int(value)) def _on_contiguous_change(self): """Receive layer model contiguous change event and update the checkbox.""" with self.layer.events.contiguous.blocker(): self.contigCheckBox.setChecked(self.layer.contiguous) def _on_preserve_labels_change(self): """Receive layer model preserve_labels event and update the checkbox.""" with self.layer.events.preserve_labels.blocker(): self.preserveLabelsCheckBox.setChecked(self.layer.preserve_labels) def _on_color_mode_change(self): """Receive layer model color.""" with self.layer.events.color_mode.blocker(): self.colorModeComboBox.setCurrentIndex( self.colorModeComboBox.findData(self.layer.color_mode)) def _on_editable_change(self): """Receive layer model editable change event & enable/disable buttons.""" # In 3D mode, we need to disable all buttons other than picking # (only picking works in 3D) widget_list = [ 'pick_button', 'fill_button', 'paint_button', 'erase_button', ] widgets_to_toggle = { (2, True): widget_list, (2, False): widget_list, (3, True): widget_list, (3, False): widget_list, } disable_with_opacity( self, widgets_to_toggle[(self.layer._ndisplay, self.layer.editable)], self.layer.editable, ) def _on_rendering_change(self): """Receive layer model rendering change event and update dropdown menu.""" with self.layer.events.rendering.blocker(): index = self.renderComboBox.findText(self.layer.rendering, Qt.MatchFixedString) self.renderComboBox.setCurrentIndex(index) def _on_ndisplay_change(self): """Toggle between 2D and 3D visualization modes.""" if self.layer._ndisplay == 2: self.renderComboBox.hide() self.renderLabel.hide() else: self.renderComboBox.show() self.renderLabel.show() self._on_editable_change() def deleteLater(self): disconnect_events(self.layer.events, self.colorBox) super().deleteLater()
def __init__(self, settings, plotWidget, parent=None): super().__init__(parent) self.mainwindow = parent self.plotWidget = plotWidget # xmin self.xminLabel = QLabel(self.tr('xmin:')) self.xminSpinBox = CoordSpinBox() self.xminLabel.setBuddy(self.xminSpinBox) self.xminSpinBox.editingFinished.connect(self.change_limits) # xmax self.xmaxLabel = QLabel(self.tr('xmax:')) self.xmaxSpinBox = CoordSpinBox() self.xmaxLabel.setBuddy(self.xmaxSpinBox) self.xmaxSpinBox.editingFinished.connect(self.change_limits) # ymin self.yminLabel = QLabel(self.tr('ymin:')) self.yminSpinBox = CoordSpinBox() self.yminLabel.setBuddy(self.yminSpinBox) self.yminSpinBox.editingFinished.connect(self.change_limits) # ymax self.ymaxLabel = QLabel(self.tr('ymax:')) self.ymaxSpinBox = CoordSpinBox() self.ymaxLabel.setBuddy(self.ymaxSpinBox) self.ymaxSpinBox.editingFinished.connect(self.change_limits) # Autoscale Radio Group self.autoscaleButtonGroup = QButtonGroup() # Autoscale Group Box self.autoscaleGroupBox = QGroupBox() # autoscale self.autoscaleRadioButton = QRadioButton(self.tr("autoscale")) self.autoscaleButtonGroup.addButton(self.autoscaleRadioButton) # autotrack self.autoscrollRadioButton = QRadioButton(self.tr("autoscroll")) self.autoscaleButtonGroup.addButton(self.autoscrollRadioButton) # no autoscale self.manualscaleRadioButton = QRadioButton(self.tr("manual")) self.autoscaleButtonGroup.addButton(self.manualscaleRadioButton) layout = QVBoxLayout() layout.addWidget(self.autoscaleRadioButton) layout.addWidget(self.autoscrollRadioButton) layout.addWidget(self.manualscaleRadioButton) self.autoscaleGroupBox.setLayout(layout) # Layout layout = QGridLayout() layout.addWidget(self.xminLabel, 1, 0) layout.addWidget(self.xminSpinBox, 1, 1) layout.addWidget(self.xmaxLabel, 2, 0) layout.addWidget(self.xmaxSpinBox, 2, 1) layout.addWidget(self.yminLabel, 3, 0) layout.addWidget(self.yminSpinBox, 3, 1) layout.addWidget(self.ymaxLabel, 4, 0) layout.addWidget(self.ymaxSpinBox, 4, 1) layout.addWidget(self.autoscaleGroupBox, 5, 0, 1, 2) layout.setRowStretch(6, 1) self.setLayout(layout) # settings self.settings = settings self.settings.add_handler(S_XMIN, self.xminSpinBox) self.settings.add_handler(S_XMAX, self.xmaxSpinBox) self.settings.add_handler(S_YMIN, self.yminSpinBox) self.settings.add_handler(S_YMAX, self.ymaxSpinBox) self.settings.add_handler(S_AUTOSCALE, self.autoscaleButtonGroup)
def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self._on_mode_change) self.layer.events._ndisplay.connect(self._on_ndisplay_change) self.layer.events.rendering.connect(self._on_rendering_change) self.layer.events.selected_label.connect( self._on_selected_label_change) self.layer.events.brush_size.connect(self._on_brush_size_change) self.layer.events.contiguous.connect(self._on_contiguous_change) self.layer.events.n_edit_dimensions.connect( self._on_n_edit_dimensions_change) self.layer.events.contour.connect(self._on_contour_change) self.layer.events.editable.connect(self._on_editable_change) self.layer.events.preserve_labels.connect( self._on_preserve_labels_change) self.layer.events.color_mode.connect(self._on_color_mode_change) # selection spinbox self.selectionSpinBox = QLargeIntSpinBox() dtype_lims = get_dtype_limits(get_dtype(layer)) self.selectionSpinBox.setRange(*dtype_lims) self.selectionSpinBox.setKeyboardTracking(False) self.selectionSpinBox.valueChanged.connect(self.changeSelection) self.selectionSpinBox.setAlignment(Qt.AlignCenter) self._on_selected_label_change() sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(1) sld.setMaximum(40) sld.setSingleStep(1) sld.valueChanged.connect(self.changeSize) self.brushSizeSlider = sld self._on_brush_size_change() contig_cb = QCheckBox() contig_cb.setToolTip(trans._('contiguous editing')) contig_cb.stateChanged.connect(self.change_contig) self.contigCheckBox = contig_cb self._on_contiguous_change() ndim_sb = QSpinBox() self.ndimSpinBox = ndim_sb ndim_sb.setToolTip(trans._('number of dimensions for label editing')) ndim_sb.valueChanged.connect(self.change_n_edit_dim) ndim_sb.setMinimum(2) ndim_sb.setMaximum(self.layer.ndim) ndim_sb.setSingleStep(1) ndim_sb.setAlignment(Qt.AlignCenter) self._on_n_edit_dimensions_change() self.contourSpinBox = QLargeIntSpinBox() self.contourSpinBox.setRange(*dtype_lims) self.contourSpinBox.setToolTip(trans._('display contours of labels')) self.contourSpinBox.valueChanged.connect(self.change_contour) self.contourSpinBox.setKeyboardTracking(False) self.contourSpinBox.setAlignment(Qt.AlignCenter) self._on_contour_change() preserve_labels_cb = QCheckBox() preserve_labels_cb.setToolTip( trans._('preserve existing labels while painting')) preserve_labels_cb.stateChanged.connect(self.change_preserve_labels) self.preserveLabelsCheckBox = preserve_labels_cb self._on_preserve_labels_change() selectedColorCheckbox = QCheckBox() selectedColorCheckbox.setToolTip( trans._("Display only selected label")) selectedColorCheckbox.stateChanged.connect(self.toggle_selected_mode) self.selectedColorCheckbox = selectedColorCheckbox # shuffle colormap button self.colormapUpdate = QtModePushButton( None, 'shuffle', slot=self.changeColor, tooltip=trans._('shuffle colors'), ) self.panzoom_button = QtModeRadioButton( layer, 'zoom', Mode.PAN_ZOOM, checked=True, ) action_manager.bind_button('napari:activate_label_pan_zoom_mode', self.panzoom_button) self.pick_button = QtModeRadioButton(layer, 'picker', Mode.PICK) action_manager.bind_button('napari:activate_label_picker_mode', self.pick_button) self.paint_button = QtModeRadioButton(layer, 'paint', Mode.PAINT) action_manager.bind_button('napari:activate_paint_mode', self.paint_button) self.fill_button = QtModeRadioButton( layer, 'fill', Mode.FILL, ) action_manager.bind_button( 'napari:activate_fill_mode', self.fill_button, extra_tooltip_text=trans._( "Toggle with {shortcut}", shortcut=Shortcut("Control"), ), ) self.erase_button = QtModeRadioButton( layer, 'erase', Mode.ERASE, ) action_manager.bind_button( 'napari:activate_label_erase_mode', self.erase_button, extra_tooltip_text=trans._( "Toggle with {shortcut}", shortcut=Shortcut("Alt"), ), ) # don't bind with action manager as this would remove "Toggle with {shortcut}" 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.paint_button) button_row.addWidget(self.fill_button) button_row.addWidget(self.pick_button) button_row.addWidget(self.panzoom_button) button_row.setSpacing(4) button_row.setContentsMargins(0, 0, 0, 5) renderComboBox = QComboBox(self) rendering_options = [i.value for i in LabelsRendering] renderComboBox.addItems(rendering_options) index = renderComboBox.findText(self.layer.rendering, Qt.MatchFixedString) renderComboBox.setCurrentIndex(index) renderComboBox.activated[str].connect(self.changeRendering) self.renderComboBox = renderComboBox self.renderLabel = QLabel(trans._('rendering:')) self._on_ndisplay_change() color_mode_comboBox = QComboBox(self) for index, (data, text) in enumerate(LABEL_COLOR_MODE_TRANSLATIONS.items()): data = data.value color_mode_comboBox.addItem(text, data) if self.layer.color_mode == data: color_mode_comboBox.setCurrentIndex(index) color_mode_comboBox.activated.connect(self.change_color_mode) self.colorModeComboBox = color_mode_comboBox self._on_color_mode_change() color_layout = QHBoxLayout() self.colorBox = QtColorBox(layer) color_layout.addWidget(self.colorBox) color_layout.addWidget(self.selectionSpinBox) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_row, 0, 0, 1, 4) self.grid_layout.addWidget(QLabel(trans._('label:')), 1, 0, 1, 1) self.grid_layout.addLayout(color_layout, 1, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('opacity:')), 2, 0, 1, 1) self.grid_layout.addWidget(self.opacitySlider, 2, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('brush size:')), 3, 0, 1, 1) self.grid_layout.addWidget(self.brushSizeSlider, 3, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('blending:')), 5, 0, 1, 1) self.grid_layout.addWidget(self.blendComboBox, 5, 1, 1, 3) self.grid_layout.addWidget(self.renderLabel, 6, 0, 1, 1) self.grid_layout.addWidget(self.renderComboBox, 6, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('color mode:')), 7, 0, 1, 1) self.grid_layout.addWidget(self.colorModeComboBox, 7, 1, 1, 3) self.grid_layout.addWidget(QLabel(trans._('contour:')), 8, 0, 1, 1) self.grid_layout.addWidget(self.contourSpinBox, 8, 1, 1, 1) self.grid_layout.addWidget(QLabel(trans._('n edit dim:')), 9, 0, 1, 1) self.grid_layout.addWidget(self.ndimSpinBox, 9, 1, 1, 1) self.grid_layout.addWidget(QLabel(trans._('contiguous:')), 10, 0, 1, 1) self.grid_layout.addWidget(self.contigCheckBox, 10, 1, 1, 1) self.grid_layout.addWidget(QLabel(trans._('preserve\nlabels:')), 11, 0, 1, 2) self.grid_layout.addWidget(self.preserveLabelsCheckBox, 11, 1, 1, 1) self.grid_layout.addWidget(QLabel(trans._('show\nselected:')), 11, 2, 1, 1) self.grid_layout.addWidget(self.selectedColorCheckbox, 11, 3, 1, 1) self.grid_layout.setRowStretch(12, 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.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, "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 self.layout().addRow(button_grid) self.layout().addRow(trans._('opacity:'), self.opacitySlider) self.layout().addRow(trans._('edge width:'), self.widthSlider) self.layout().addRow(trans._('blending:'), self.blendComboBox) self.layout().addRow(trans._('face color:'), self.faceColorEdit) self.layout().addRow(trans._('edge color:'), self.edgeColorEdit) self.layout().addRow(trans._('display text:'), self.textDispCheckBox)
class QtLabelsControls(QtLayerControls): """Qt view and controls for the napari Labels layer. Parameters ---------- layer : napari.layers.Labels An instance of a napari Labels layer. Attributes ---------- button_group : qtpy.QtWidgets.QButtonGroup Button group of labels layer modes: PAN_ZOOM, PICKER, PAINT, ERASE, or FILL. colormapUpdate : qtpy.QtWidgets.QPushButton Button to update colormap of label layer. contigCheckBox : qtpy.QtWidgets.QCheckBox Checkbox to control if label layer is contiguous. fill_button : qtpy.QtWidgets.QtModeRadioButton Button to select FILL mode on Labels layer. grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : napari.layers.Labels An instance of a napari Labels layer. ndimCheckBox : qtpy.QtWidgets.QCheckBox Checkbox to control if label layer is n-dimensional. paint_button : qtpy.QtWidgets.QtModeRadioButton Button to select PAINT mode on Labels layer. panzoom_button : qtpy.QtWidgets.QtModeRadioButton Button to select PAN_ZOOM mode on Labels layer. pick_button : qtpy.QtWidgets.QtModeRadioButton Button to select PICKER mode on Labels layer. erase_button : qtpy.QtWidgets.QtModeRadioButton Button to select ERASE mode on Labels layer. selectionSpinBox : qtpy.QtWidgets.QSpinBox Widget to select a specific label by its index. Raises ------ ValueError Raise error if label mode is not PAN_ZOOM, PICKER, PAINT, ERASE, or FILL. """ def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self._on_mode_change) self.layer.events.selected_label.connect(self._on_selection_change) self.layer.events.brush_size.connect(self._on_brush_size_change) self.layer.events.contiguous.connect(self._on_contig_change) self.layer.events.n_dimensional.connect(self._on_n_dim_change) self.layer.events.editable.connect(self._on_editable_change) self.layer.events.preserve_labels.connect( self._on_preserve_labels_change) self.layer.events.color_mode.connect(self._on_color_mode_change) # selection spinbox self.selectionSpinBox = QSpinBox() self.selectionSpinBox.setKeyboardTracking(False) self.selectionSpinBox.setSingleStep(1) self.selectionSpinBox.setMinimum(0) self.selectionSpinBox.setMaximum(2147483647) self.selectionSpinBox.valueChanged.connect(self.changeSelection) self.selectionSpinBox.setAlignment(Qt.AlignCenter) self._on_selection_change() sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(1) sld.setMaximum(40) sld.setSingleStep(1) sld.valueChanged.connect(self.changeSize) self.brushSizeSlider = sld self._on_brush_size_change() contig_cb = QCheckBox() contig_cb.setToolTip('contiguous editing') contig_cb.stateChanged.connect(self.change_contig) self.contigCheckBox = contig_cb self._on_contig_change() ndim_cb = QCheckBox() ndim_cb.setToolTip('n-dimensional editing') ndim_cb.stateChanged.connect(self.change_ndim) self.ndimCheckBox = ndim_cb self._on_n_dim_change() preserve_labels_cb = QCheckBox() preserve_labels_cb.setToolTip( 'preserve existing labels while painting') preserve_labels_cb.stateChanged.connect(self.change_preserve_labels) self.preserveLabelsCheckBox = preserve_labels_cb self._on_preserve_labels_change() selectedColorCheckbox = QCheckBox() selectedColorCheckbox.setToolTip("Display only selected label") selectedColorCheckbox.stateChanged.connect(self.toggle_selected_mode) self.selectedColorCheckbox = selectedColorCheckbox # shuffle colormap button self.colormapUpdate = QtModePushButton( None, 'shuffle', slot=self.changeColor, tooltip='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) brush_shape_comboBox = QComboBox(self) brush_shape_comboBox.addItems(LabelBrushShape.keys()) index = brush_shape_comboBox.findText(self.layer.brush_shape, Qt.MatchFixedString) brush_shape_comboBox.setCurrentIndex(index) brush_shape_comboBox.activated[str].connect(self.change_brush_shape) self.brushShapeComboBox = brush_shape_comboBox self._on_brush_shape_change() color_mode_comboBox = QComboBox(self) 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('brush shape:'), 4, 0, 1, 1) self.grid_layout.addWidget(self.brushShapeComboBox, 4, 1, 1, 3) self.grid_layout.addWidget(QLabel('blending:'), 5, 0, 1, 1) self.grid_layout.addWidget(self.blendComboBox, 5, 1, 1, 3) self.grid_layout.addWidget(QLabel('color mode:'), 6, 0, 1, 1) self.grid_layout.addWidget(self.colorModeComboBox, 6, 1, 1, 3) self.grid_layout.addWidget(QLabel('contiguous:'), 7, 0, 1, 1) self.grid_layout.addWidget(self.contigCheckBox, 7, 1, 1, 1) self.grid_layout.addWidget(QLabel('n-dim:'), 7, 2, 1, 1) self.grid_layout.addWidget(self.ndimCheckBox, 7, 3, 1, 1) self.grid_layout.addWidget(QLabel('preserve labels:'), 8, 0, 1, 2) self.grid_layout.addWidget(self.preserveLabelsCheckBox, 8, 1, 1, 1) self.grid_layout.addWidget(QLabel('show selected:'), 8, 2, 1, 1) self.grid_layout.addWidget(self.selectedColorCheckbox, 8, 3, 1, 1) self.grid_layout.setRowStretch(9, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def mouseMoveEvent(self, event): """On mouse move, set layer status equal to the current selected mode. Available mode options are: PAN_ZOOM, PICKER, PAINT, ERASE or FILL Parameters ---------- event : qtpy.QtCore.QEvent Event from the Qt context. """ self.layer.status = str(self.layer.mode) def _on_mode_change(self, event): """Receive layer model mode change event and update checkbox ticks. Parameters ---------- event : napari.utils.event.Event The napari event that triggered this method. Raises ------ ValueError Raise error if event.mode is not PAN_ZOOM, PICK, PAINT, ERASE, or FILL """ mode = event.mode if mode == Mode.PAN_ZOOM: self.panzoom_button.setChecked(True) elif mode == Mode.PICK: self.pick_button.setChecked(True) elif mode == Mode.PAINT: self.paint_button.setChecked(True) elif mode == Mode.FILL: self.fill_button.setChecked(True) elif mode == Mode.ERASE: self.erase_button.setChecked(True) else: raise ValueError("Mode not recognized") def changeColor(self): """Change colormap of the label layer.""" self.layer.new_colormap() def changeSelection(self, value): """Change currently selected label. Parameters ---------- value : int Index of label to select. """ self.layer.selected_label = value self.selectionSpinBox.clearFocus() self.setFocus() def toggle_selected_mode(self, state): if state == Qt.Checked: self.layer.show_selected_label = True else: self.layer.show_selected_label = False def changeSize(self, value): """Change paint brush size. Parameters ---------- value : float Size of the paint brush. """ self.layer.brush_size = value def change_contig(self, state): """Toggle contiguous state of label layer. Parameters ---------- state : QCheckBox Checkbox indicating if labels are contiguous. """ if state == Qt.Checked: self.layer.contiguous = True else: self.layer.contiguous = False def change_ndim(self, state): """Toggle n-dimensional state of label layer. Parameters ---------- state : QCheckBox Checkbox indicating if label layer is n-dimensional. """ if state == Qt.Checked: self.layer.n_dimensional = True else: self.layer.n_dimensional = False def change_preserve_labels(self, state): """Toggle preserve_labels state of label layer. Parameters ---------- state : QCheckBox Checkbox indicating if overwriting label is enabled. """ if state == Qt.Checked: self.layer.preserve_labels = True else: self.layer.preserve_labels = False def change_color_mode(self, new_mode): """Change color mode of label layer. Parameters ---------- new_mode : str AUTO (default) allows color to be set via a hash function with a seed. DIRECT allows color of each label to be set directly by a color dictionary. """ self.layer.color_mode = new_mode def change_brush_shape(self, brush_shape): """Change paintbrush shape of label layer. Parameters ---------- brush_shape : str CIRCLE (default) uses circle paintbrush (case insensitive). SQUARE uses square paintbrush (case insensitive). """ self.layer.brush_shape = brush_shape def _on_selection_change(self, event=None): """Receive layer model label selection change event and update spinbox. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method. """ with self.layer.events.selected_label.blocker(): value = self.layer.selected_label self.selectionSpinBox.setValue(int(value)) def _on_brush_size_change(self, event=None): """Receive layer model brush size change event and update the slider. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method. """ with self.layer.events.brush_size.blocker(): value = self.layer.brush_size value = np.clip(int(value), 1, 40) self.brushSizeSlider.setValue(value) def _on_n_dim_change(self, event=None): """Receive layer model n-dim mode change event and update the checkbox. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method. """ with self.layer.events.n_dimensional.blocker(): self.ndimCheckBox.setChecked(self.layer.n_dimensional) def _on_contig_change(self, event=None): """Receive layer model contiguous change event and update the checkbox. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method. """ with self.layer.events.contiguous.blocker(): self.contigCheckBox.setChecked(self.layer.contiguous) def _on_preserve_labels_change(self, event=None): """Receive layer model preserve_labels event and update the checkbox. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method. """ with self.layer.events.preserve_labels.blocker(): self.preserveLabelsCheckBox.setChecked(self.layer.preserve_labels) def _on_color_mode_change(self, event=None): """Receive layer model color. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method. """ with self.layer.events.color_mode.blocker(): index = self.colorModeComboBox.findText(self.layer.color_mode, Qt.MatchFixedString) self.colorModeComboBox.setCurrentIndex(index) def _on_brush_shape_change(self, event=None): """Receive brush shape change event and update dropdown menu. Parameters ---------- event : napari.utils.event.Event The napari event that triggered this method. """ with self.layer.events.brush_shape.blocker(): index = self.brushShapeComboBox.findText(self.layer.brush_shape, Qt.MatchFixedString) self.brushShapeComboBox.setCurrentIndex(index) def _on_editable_change(self, event=None): """Receive layer model editable change event & enable/disable buttons. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method. """ disable_with_opacity( self, ['pick_button', 'paint_button', 'fill_button'], self.layer.editable, )
def create_widgets(self, show_animation_button=True): """creates the menu objects""" # title self.title_label = QLabel("Title:") self.title_edit = QLineEdit(str(self._default_title)) self.title_button = QPushButton("Default") # Min self.min_label = QLabel("Min:") self.min_edit = QLineEdit(str(self._default_min)) self.min_button = QPushButton("Default") # Max self.max_label = QLabel("Max:") self.max_edit = QLineEdit(str(self._default_max)) self.max_button = QPushButton("Default") #--------------------------------------- # Format self.format_label = QLabel("Format (e.g. %.3f, %g, %.6e):") self.format_edit = QLineEdit(str(self._format)) self.format_button = QPushButton("Default") #--------------------------------------- # Scale self.scale_label = QLabel("True Scale:") self.scale_edit = QLineEdit(str(self._scale)) self.scale_button = QPushButton("Default") if self._icase_disp is None: self.scale_label.setVisible(False) self.scale_edit.setVisible(False) self.scale_button.setVisible(False) # Phase self.phase_label = QLabel("Phase (deg):") self.phase_edit = QLineEdit(str(self._phase)) self.phase_button = QPushButton("Default") if self._icase_disp is None or self._default_phase is None: self.phase_label.setVisible(False) self.phase_edit.setVisible(False) self.phase_button.setVisible(False) self.phase_edit.setText('0.0') #--------------------------------------- self.arrow_scale_label = QLabel("Arrow Scale:") self.arrow_scale_edit = QLineEdit(str(self._arrow_scale)) self.arrow_scale_button = QPushButton("Default") if self._icase_vector is None: self.arrow_scale_label.setVisible(False) self.arrow_scale_edit.setVisible(False) self.arrow_scale_button.setVisible(False) #tip = QtGui.QToolTip() #tip.setTe #self.format_edit.toolTip(tip) #--------------------------------------- # nlabels self.nlabels_label = QLabel("Number of Labels:") self.nlabels_edit = QLineEdit(str(self._nlabels)) self.nlabels_button = QPushButton("Default") self.labelsize_label = QLabel("Label Size:") self.labelsize_edit = QLineEdit(str(self._labelsize)) self.labelsize_button = QPushButton("Default") self.ncolors_label = QLabel("Number of Colors:") self.ncolors_edit = QLineEdit(str(self._ncolors)) self.ncolors_button = QPushButton("Default") self.colormap_label = QLabel("Color Map:") self.colormap_edit = QComboBox(self) self.colormap_button = QPushButton("Default") for key in colormap_keys: self.colormap_edit.addItem(key) self.colormap_edit.setCurrentIndex(colormap_keys.index(self._colormap)) # -------------------------------------------------------------- # the header self.grid2_title = QLabel("Color Scale:") # red/blue or blue/red self.low_to_high_radio = QRadioButton('Low -> High') self.high_to_low_radio = QRadioButton('High -> Low') widget = QWidget(self) low_to_high_group = QButtonGroup(widget) low_to_high_group.addButton(self.low_to_high_radio) low_to_high_group.addButton(self.high_to_low_radio) self.low_to_high_radio.setChecked(self._default_is_low_to_high) self.high_to_low_radio.setChecked(not self._default_is_low_to_high) # horizontal / vertical self.horizontal_radio = QRadioButton("Horizontal") self.vertical_radio = QRadioButton("Vertical") widget = QWidget(self) horizontal_vertical_group = QButtonGroup(widget) horizontal_vertical_group.addButton(self.horizontal_radio) horizontal_vertical_group.addButton(self.vertical_radio) self.horizontal_radio.setChecked(self._default_is_horizontal) self.vertical_radio.setChecked(not self._default_is_horizontal) # on / off self.show_radio = QRadioButton("Show") self.hide_radio = QRadioButton("Hide") widget = QWidget(self) show_hide_group = QButtonGroup(widget) show_hide_group.addButton(self.show_radio) show_hide_group.addButton(self.hide_radio) self.show_radio.setChecked(self._default_is_shown) self.hide_radio.setChecked(not self._default_is_shown) # -------------------------------------------------------------- if self._icase_fringe is None: self.title_label.setVisible(False) self.title_edit.setVisible(False) self.title_button.setVisible(False) if not self._is_fringe: self.max_label.hide() self.min_label.hide() self.max_edit.hide() self.min_edit.hide() self.max_button.hide() self.min_button.hide() self.format_label.hide() self.format_edit.hide() self.format_button.hide() self.nlabels_label.hide() self.nlabels_edit.hide() self.nlabels_button.hide() self.ncolors_label.hide() self.ncolors_edit.hide() self.ncolors_button.hide() self.grid2_title.hide() self.vertical_radio.hide() self.horizontal_radio.hide() self.show_radio.hide() self.hide_radio.hide() self.low_to_high_radio.hide() self.high_to_low_radio.hide() self.colormap_label.hide() self.colormap_edit.hide() self.colormap_button.hide() self.animate_button = QPushButton('Create Animation') self.animate_button.setVisible(show_animation_button) #self.advanced_button = QPushButton('Advanced') if self._default_icase_disp is None: # or self._default_icase_vector is None: self.animate_button.setEnabled(False) self.animate_button.setToolTip(ANIMATE_TOOLTIP_OFF) else: self.animate_button.setEnabled(True) self.animate_button.setToolTip(ANIMATE_TOOLTIP_ON) # closing self.apply_button = QPushButton("Apply") self.ok_button = QPushButton("OK") self.cancel_button = QPushButton("Cancel")
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() selectedColorCheckbox = QCheckBox() selectedColorCheckbox.setToolTip("Display only selected label") selectedColorCheckbox.stateChanged.connect(self.toggle_selected_mode) self.selectedColorCheckbox = selectedColorCheckbox # shuffle colormap button self.colormapUpdate = QtModePushButton( None, 'shuffle', slot=self.changeColor, tooltip='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) brush_shape_comboBox = QComboBox(self) brush_shape_comboBox.addItems(LabelBrushShape.keys()) index = brush_shape_comboBox.findText(self.layer.brush_shape, Qt.MatchFixedString) brush_shape_comboBox.setCurrentIndex(index) brush_shape_comboBox.activated[str].connect(self.change_brush_shape) self.brushShapeComboBox = brush_shape_comboBox self._on_brush_shape_change() color_mode_comboBox = QComboBox(self) 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('brush shape:'), 4, 0, 1, 1) self.grid_layout.addWidget(self.brushShapeComboBox, 4, 1, 1, 3) self.grid_layout.addWidget(QLabel('blending:'), 5, 0, 1, 1) self.grid_layout.addWidget(self.blendComboBox, 5, 1, 1, 3) self.grid_layout.addWidget(QLabel('color mode:'), 6, 0, 1, 1) self.grid_layout.addWidget(self.colorModeComboBox, 6, 1, 1, 3) self.grid_layout.addWidget(QLabel('contiguous:'), 7, 0, 1, 1) self.grid_layout.addWidget(self.contigCheckBox, 7, 1, 1, 1) self.grid_layout.addWidget(QLabel('n-dim:'), 7, 2, 1, 1) self.grid_layout.addWidget(self.ndimCheckBox, 7, 3, 1, 1) self.grid_layout.addWidget(QLabel('preserve labels:'), 8, 0, 1, 2) self.grid_layout.addWidget(self.preserveLabelsCheckBox, 8, 1, 1, 1) self.grid_layout.addWidget(QLabel('show selected:'), 8, 2, 1, 1) self.grid_layout.addWidget(self.selectedColorCheckbox, 8, 3, 1, 1) self.grid_layout.setRowStretch(9, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4)
def __init__(self, model, *args, **kwargs): self.model = model super().__init__(*args, **kwargs) self.setLayout(QFormLayout()) # Radiobuttons to quickly select default time period self.all_widget = QRadioButton("All") self.year_widget = QRadioButton("1 Year") self.month_widget = QRadioButton("30 Days") self.week_widget = QRadioButton("1 Week") self.today_widget = QRadioButton("24h") self.hour_widget = QRadioButton("1 Hour") self.radio_button_group = QButtonGroup() self.radio_button_group.addButton(self.all_widget) self.radio_button_group.addButton(self.year_widget) self.radio_button_group.addButton(self.month_widget) self.radio_button_group.addButton(self.week_widget) self.radio_button_group.addButton(self.today_widget) self.radio_button_group.addButton(self.hour_widget) default_period_layout = QGridLayout() default_period_layout.setHorizontalSpacing(85) default_period_layout.setVerticalSpacing(10) default_period_layout.addWidget(self.all_widget, 0, 0, 1, 2) default_period_layout.addWidget(self.year_widget, 1, 0, 1, 2) default_period_layout.addWidget(self.month_widget, 2, 0, 1, 2) default_period_layout.addWidget(self.week_widget, 0, 1, 1, 2) default_period_layout.addWidget(self.today_widget, 1, 1, 1, 2) default_period_layout.addWidget(self.hour_widget, 2, 1, 1, 2) self.layout().addRow("When:", default_period_layout) # TODO: rethink if restriction to acceptable timedelta values is required # from ..models.search.search_input import SearchInput # self.allowed = {timedelta(days=-1), timedelta(days=-30), timedelta(minutes=-60), timedelta(days=-7), # timedelta(days=-365)} # def time_validator(since=None, until=None): # """ # Enforce that since and until are values that a UI can represent. # This is an example similar to what will be used in the Qt UI. # """ # now = timedelta() # if isinstance(since, timedelta): # if not (until is None or until == now): # raise ValueError( # "This UI cannot express since=timedelta(...) unless until " # "is timedelta() or None." # ) # for item in allowed: # if since == item: # break # else: # # No matches # raise ValueError( # "This UI can only express since as a timedelta if it is " # f"one of {allowed}. The value {since} is not allowed" # ) # s = SearchInput() # s.time_validator = time_validator # "Since: <datetime picker>" self.since_widget = QDateTimeEdit() self.since_widget.setCalendarPopup(True) self.since_widget.setDisplayFormat("yyyy-MM-dd HH:mm") self.layout().addRow("Since:", self.since_widget) # "Until: <datetime picker>" self.until_widget = QDateTimeEdit() self.until_widget.setCalendarPopup(True) self.until_widget.setDisplayFormat("yyyy-MM-dd HH:mm") self.layout().addRow("Until:", self.until_widget) # Refresh Button self.refresh_button = QPushButton("Refresh") self.layout().addWidget(self.refresh_button) # Changes to the GUI update the model. self.since_widget.dateTimeChanged.connect(self.on_since_view_changed) self.until_widget.dateTimeChanged.connect(self.on_until_view_changed) self.refresh_button.clicked.connect(self.model.request_reload) self.model.events.reload.connect(self.on_reload) self.model.events.query.connect(self.on_reload) # Changes to the model update the GUI. self.model.events.since.connect(self.on_since_model_changed) self.model.events.until.connect(self.on_until_model_changed) # connect QRadioButtons and change date dropdowns (since/until widgets) accordingly self.hour_widget.toggled.connect(self.on_toggle_hour) self.today_widget.toggled.connect(self.on_toggle_24h) self.week_widget.toggled.connect(self.on_toggle_week) self.month_widget.toggled.connect(self.on_toggle_month) self.year_widget.toggled.connect(self.on_toggle_year) self.all_widget.toggled.connect(self.on_toggle_all) self.all_widget.setChecked(True)
class PyDMEnumButton(QWidget, PyDMWritableWidget, WidgetType): """ A QWidget that renders buttons for every option of Enum Items. For now, two types of buttons can be rendered: - Push Button - Radio Button Parameters ---------- parent : QWidget The parent widget for the Label init_channel : str, optional The channel to be used by the widget. Signals ------- send_value_signal : int, float, str, bool or np.ndarray Emitted when the user changes the value. """ Q_ENUMS(WidgetType) WidgetType = WidgetType def __init__(self, parent=None, init_channel=None): QWidget.__init__(self, parent) PyDMWritableWidget.__init__(self, init_channel=init_channel) self._has_enums = False self._checkable = True self.setLayout(QGridLayout(self)) self._layout_spacing_horizontal = 6 self._layout_spacing_vertical = 6 self._layout_margins = QMargins(9, 9, 9, 9) self._btn_group = QButtonGroup() self._btn_group.setExclusive(True) self._btn_group.buttonClicked[int].connect(self.handle_button_clicked) self._widget_type = WidgetType.PushButton self._orientation = Qt.Vertical self._widgets = [] self.rebuild_widgets() def minimumSizeHint(self): """ This property holds the recommended minimum size for the widget. Returns ------- QSize """ # This is totally arbitrary, I just want *some* visible nonzero size return QSize(50, 100) @Property("QStringList") def items(self): """ Items to be displayed in the button group. This property can be overridden by the items coming from the control system. Because C++ QStringList expects a list type, we need to make sure that None is never returned. Returns ------- List[str] """ return self.enum_strings or [] @items.setter def items(self, value): self.enum_strings_changed(value) @Property(WidgetType) def widgetType(self): """ The widget type to be used when composing the group. Returns ------- WidgetType """ return self._widget_type @widgetType.setter def widgetType(self, new_type): """ The widget type to be used when composing the group. Parameters ---------- new_type : WidgetType """ if new_type != self._widget_type: self._widget_type = new_type self.rebuild_widgets() @Property(Qt.Orientation) def orientation(self): """ Whether to lay out the bit indicators vertically or horizontally. Returns ------- int """ return self._orientation @orientation.setter def orientation(self, new_orientation): """ Whether to lay out the bit indicators vertically or horizontally. Parameters ------- new_orientation : Qt.Orientation, int """ if new_orientation != self._orientation: self._orientation = new_orientation self.rebuild_layout() @Property(int) def marginTop(self): """ The top margin of the QGridLayout of buttons. Returns ------- int """ return self._layout_margins.top() @marginTop.setter def marginTop(self, new_margin): """ Set the top margin of the QGridLayout of buttons. Parameters ------- int """ new_margin = max(0, int(new_margin)) self._layout_margins.setTop(new_margin) self.layout().setContentsMargins(self._layout_margins) @Property(int) def marginBottom(self): """ The bottom margin of the QGridLayout of buttons. Returns ------- int """ return self._layout_margins.bottom() @marginBottom.setter def marginBottom(self, new_margin): """ Set the bottom margin of the QGridLayout of buttons. Parameters ------- int """ new_margin = max(0, int(new_margin)) self._layout_margins.setBottom(new_margin) self.layout().setContentsMargins(self._layout_margins) @Property(int) def marginLeft(self): """ The left margin of the QGridLayout of buttons. Returns ------- int """ return self._layout_margins.left() @marginLeft.setter def marginLeft(self, new_margin): """ Set the left margin of the QGridLayout of buttons. Parameters ------- int """ new_margin = max(0, int(new_margin)) self._layout_margins.setLeft(new_margin) self.layout().setContentsMargins(self._layout_margins) @Property(int) def marginRight(self): """ The right margin of the QGridLayout of buttons. Returns ------- int """ return self._layout_margins.right() @marginRight.setter def marginRight(self, new_margin): """ Set the right margin of the QGridLayout of buttons. Parameters ------- int """ new_margin = max(0, int(new_margin)) self._layout_margins.setRight(new_margin) self.layout().setContentsMargins(self._layout_margins) @Property(int) def horizontalSpacing(self): """ The horizontal gap of the QGridLayout containing the QButtonGroup. Returns ------- int """ return self._layout_spacing_horizontal @horizontalSpacing.setter def horizontalSpacing(self, new_spacing): """ Set the layout horizontal gap between buttons. Parameters ------- new_spacing : int """ new_spacing = max(0, int(new_spacing)) if new_spacing != self._layout_spacing_horizontal: self._layout_spacing_horizontal = new_spacing self.layout().setHorizontalSpacing(new_spacing) @Property(int) def verticalSpacing(self): """ The vertical gap of the QGridLayout containing the QButtonGroup. Returns ------- int """ return self._layout_spacing_vertical @verticalSpacing.setter def verticalSpacing(self, new_spacing): """ Set the layout vertical gap between buttons. Parameters ------- new_spacing : int """ new_spacing = max(0, int(new_spacing)) if new_spacing != self._layout_spacing_vertical: self._layout_spacing_vertical = new_spacing self.layout().setVerticalSpacing(new_spacing) @Property(bool) def checkable(self): """ Whether or not the button should be checkable. Returns ------- bool """ return self._checkable @checkable.setter def checkable(self, value): if value != self._checkable: self._checkable = value for widget in self._widgets: widget.setCheckable(value) @Slot(int) def handle_button_clicked(self, id): """ Handles the event of a button being clicked. Parameters ---------- id : int The clicked button id. """ self.send_value_signal.emit(id) def clear(self): """ Remove all inner widgets from the layout """ for col in range(0, self.layout().columnCount()): for row in range(0, self.layout().rowCount()): item = self.layout().itemAtPosition(row, col) if item is not None: w = item.widget() if w is not None: self.layout().removeWidget(w) def rebuild_widgets(self): """ Rebuild the list of widgets based on a new enum or generates a default list of fake strings so we can see something at Designer. """ def generate_widgets(items): while len(self._widgets) != 0: w = self._widgets.pop(0) self._btn_group.removeButton(w) w.deleteLater() for idx, entry in enumerate(items): w = class_for_type[self._widget_type](parent=self) w.setCheckable(self.checkable) w.setText(entry) self._widgets.append(w) self._btn_group.addButton(w, idx) self.clear() if self._has_enums: generate_widgets(self.enum_strings) else: generate_widgets(["Item 1", "Item 2", "Item ..."]) self.rebuild_layout() def rebuild_layout(self): """ Method to reorganize the top-level widget and its contents according to the layout property values. """ self.clear() if self.orientation == Qt.Vertical: for i, widget in enumerate(self._widgets): self.layout().addWidget(widget, i, 0) elif self.orientation == Qt.Horizontal: for i, widget in enumerate(self._widgets): self.layout().addWidget(widget, 0, i) def check_enable_state(self): """ Checks whether or not the widget should be disable. This method also disables the widget and add a Tool Tip with the reason why it is disabled. """ status = self._write_access and self._connected and self._has_enums tooltip = "" if not self._connected: tooltip += "Channel is disconnected." elif not self._write_access: if data_plugins.is_read_only(): tooltip += "Running PyDM on Read-Only mode." else: tooltip += "Access denied by Channel Access Security." elif not self._has_enums: tooltip += "Enums not available." self.setToolTip(tooltip) self.setEnabled(status) def value_changed(self, new_val): """ Callback invoked when the Channel value is changed. Parameters ---------- new_val : int The new value from the channel. """ if new_val is not None and new_val != self.value: super(PyDMEnumButton, self).value_changed(new_val) btn = self._btn_group.button(new_val) if btn: btn.setChecked(True) def enum_strings_changed(self, new_enum_strings): """ Callback invoked when the Channel has new enum values. This callback also triggers a value_changed call so the new enum values to be broadcasted. Parameters ---------- new_enum_strings : tuple The new list of values """ if new_enum_strings is not None \ and new_enum_strings != self.enum_strings: super(PyDMEnumButton, self).enum_strings_changed(new_enum_strings) self._has_enums = True self.check_enable_state() self.rebuild_widgets() def paintEvent(self, _): """ Paint events are sent to widgets that need to update themselves, for instance when part of a widget is exposed because a covering widget was moved. At PyDMDrawing this method handles the alarm painting with parameters from the stylesheet, configures the brush, pen and calls ```draw_item``` so the specifics can be performed for each of the drawing classes. Parameters ---------- event : QPaintEvent """ painter = QPainter(self) opt = QStyleOption() opt.initFrom(self) self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self) painter.setRenderHint(QPainter.Antialiasing)
class PyDMEnumButton(QWidget, PyDMWritableWidget, WidgetType): """ A QWidget that renders buttons for every option of Enum Items. For now three types of buttons can be rendered: - Push Button - Radio Button Parameters ---------- parent : QWidget The parent widget for the Label init_channel : str, optional The channel to be used by the widget. Signals ------- send_value_signal : int, float, str, bool or np.ndarray Emitted when the user changes the value. """ Q_ENUMS(WidgetType) WidgetType = WidgetType def __init__(self, parent=None, init_channel=None): QWidget.__init__(self, parent) PyDMWritableWidget.__init__(self, init_channel=init_channel) self._has_enums = False self.setLayout(QGridLayout(self)) self._btn_group = QButtonGroup() self._btn_group.setExclusive(True) self._btn_group.buttonClicked[int].connect(self.handle_button_clicked) self._widget_type = WidgetType.PushButton self._orientation = Qt.Vertical self._widgets = [] self.rebuild_widgets() def minimumSizeHint(self): """ This property holds the recommended minimum size for the widget. Returns ------- QSize """ # This is totally arbitrary, I just want *some* visible nonzero size return QSize(50, 100) @Property(WidgetType) def widgetType(self): """ The widget type to be used when composing the group. Returns ------- WidgetType """ return self._widget_type @widgetType.setter def widgetType(self, new_type): """ The widget type to be used when composing the group. Parameters ---------- new_type : WidgetType """ if new_type != self._widget_type: self._widget_type = new_type self.rebuild_widgets() @Property(Qt.Orientation) def orientation(self): """ Whether to lay out the bit indicators vertically or horizontally. Returns ------- int """ return self._orientation @orientation.setter def orientation(self, new_orientation): """ Whether to lay out the bit indicators vertically or horizontally. Parameters ------- new_orientation : Qt.Orientation, int """ if new_orientation != self._orientation: self._orientation = new_orientation self.rebuild_layout() @Slot(int) def handle_button_clicked(self, id): """ Handles the event of a button being clicked. Parameters ---------- id : int The clicked button id. """ self.send_value_signal.emit(id) def clear(self): """ Remove all inner widgets from the layout """ for col in range(0, self.layout().columnCount()): for row in range(0, self.layout().rowCount()): item = self.layout().itemAtPosition(row, col) if item is not None: w = item.widget() if w is not None: self.layout().removeWidget(w) def rebuild_widgets(self): """ Rebuild the list of widgets based on a new enum or generates a default list of fake strings so we can see something at Designer. """ def generate_widgets(items): while len(self._widgets) != 0: w = self._widgets.pop(0) self._btn_group.removeButton(w) w.deleteLater() for idx, entry in enumerate(items): w = class_for_type[self._widget_type](parent=self) w.setCheckable(True) w.setText(entry) self._widgets.append(w) self._btn_group.addButton(w, idx) self.clear() if self._has_enums: generate_widgets(self.enum_strings) else: generate_widgets(["Item 1", "Item 2", "Item ..."]) self.rebuild_layout() def rebuild_layout(self): """ Method to reorganize the top-level widget and its contents according to the layout property values. """ self.clear() if self.orientation == Qt.Vertical: for i, widget in enumerate(self._widgets): self.layout().addWidget(widget, i, 0) elif self.orientation == Qt.Horizontal: for i, widget in enumerate(self._widgets): self.layout().addWidget(widget, 0, i) def check_enable_state(self): """ Checks whether or not the widget should be disable. This method also disables the widget and add a Tool Tip with the reason why it is disabled. """ status = self._write_access and self._connected and self._has_enums tooltip = "" if not self._connected: tooltip += "Channel is disconnected." elif not self._write_access: if data_plugins.is_read_only(): tooltip += "Running PyDM on Read-Only mode." else: tooltip += "Access denied by Channel Access Security." elif not self._has_enums: tooltip += "Enums not available." self.setToolTip(tooltip) self.setEnabled(status) def value_changed(self, new_val): """ Callback invoked when the Channel value is changed. Parameters ---------- new_val : int The new value from the channel. """ if new_val is not None and new_val != self.value: super(PyDMEnumButton, self).value_changed(new_val) btn = self._btn_group.button(new_val) if btn: btn.setChecked(True) def enum_strings_changed(self, new_enum_strings): """ Callback invoked when the Channel has new enum values. This callback also triggers a value_changed call so the new enum values to be broadcasted. Parameters ---------- new_enum_strings : tuple The new list of values """ if new_enum_strings is not None \ and new_enum_strings != self.enum_strings: super(PyDMEnumButton, self).enum_strings_changed(new_enum_strings) self._has_enums = True self.check_enable_state() self.rebuild_widgets()
def initialize_content(self): """ Initialize the content of the frame """ # Validators self._content.low_scale_edit.setValidator(QDoubleValidator(self._content.low_scale_edit)) self._content.medium_scale_edit.setValidator(QDoubleValidator(self._content.medium_scale_edit)) self._content.high_scale_edit.setValidator(QDoubleValidator(self._content.high_scale_edit)) self._content.low_min_edit.setValidator(QDoubleValidator(self._content.low_min_edit)) self._content.low_max_edit.setValidator(QDoubleValidator(self._content.low_max_edit)) self._content.medium_min_edit.setValidator(QDoubleValidator(self._content.medium_min_edit)) self._content.medium_max_edit.setValidator(QDoubleValidator(self._content.medium_max_edit)) # Browse buttons self._content.low_q_browse_button.clicked.connect(self._low_q_browse) self._content.medium_q_browse_button.clicked.connect(self._medium_q_browse) self._content.high_q_browse_button.clicked.connect(self._high_q_browse) self._content.low_q_combo.activated.connect(self._update_low_q) self._content.medium_q_combo.activated.connect(self._update_medium_q) self._content.high_q_combo.activated.connect(self._update_high_q) # Radio buttons self._content.low_radio.clicked.connect(self._low_q_selected) self._content.medium_radio.clicked.connect(self._medium_q_selected) self._content.high_radio.clicked.connect(self._high_q_selected) # Selection buttons self._content.low_range_button.clicked.connect(self._low_range) self._content.medium_range_button.clicked.connect(self._medium_range) # Scale factors self._content.low_scale_edit.returnPressed.connect(self._update_low_scale) self._content.medium_scale_edit.returnPressed.connect(self._update_medium_scale) self._content.high_scale_edit.returnPressed.connect(self._update_high_scale) # Apply and save buttons self._content.apply_button.clicked.connect(self._apply) self._content.save_result_button.clicked.connect(self._save_result) # Create button group for data set selection g = QButtonGroup(self) g.addButton(self._content.low_radio) g.addButton(self._content.medium_radio) g.addButton(self._content.high_radio) g.setExclusive(True) self._content.low_radio.setChecked(True) self._content.low_q_combo.insertItem(0, "") self.populate_combobox(self._content.low_q_combo) self._content.low_q_combo.setEditable(True) self._content.medium_q_combo.insertItem(0, "") self.populate_combobox(self._content.medium_q_combo) self._content.medium_q_combo.setEditable(True) self._content.high_q_combo.insertItem(0, "") self.populate_combobox(self._content.high_q_combo) self._content.high_q_combo.setEditable(True) # pylint: disable = no-self-argument class ShowEventFilter(QObject): def eventFilter(obj_self, filteredObj, event): if event.type() == QEvent.HoverEnter: self.populate_combobox(filteredObj) filteredObj.update() elif event.type() == QEvent.KeyPress: if filteredObj == self._content.low_q_combo: self._low_q_modified = True elif filteredObj == self._content.medium_q_combo: self._medium_q_modified = True elif filteredObj == self._content.high_q_combo: self._high_q_modified = True if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: filteredObj.setItemText(0, filteredObj.lineEdit().text()) if filteredObj == self._content.low_q_combo: self._update_low_q() elif filteredObj == self._content.medium_q_combo: self._update_medium_q() elif filteredObj == self._content.high_q_combo: self._update_high_q() return True return QObject.eventFilter(obj_self, filteredObj, event) eventFilter = ShowEventFilter(self) self._content.low_q_combo.installEventFilter(eventFilter) self._content.medium_q_combo.installEventFilter(eventFilter) self._content.high_q_combo.installEventFilter(eventFilter)
def setup_page(self): model_group = QGroupBox(_('Half-space elastic model')) lbl_model = QLabel(_("Model")) lbl_vp = QLabel(_("Vp")) lbl_vs = QLabel(_("Vs")) lbl_ro = QLabel(_("Density")) lbl_upp = QLabel(_("Upper space")) self.le_upp_vp = QLineEdit() self.le_upp_vs = QLineEdit() self.le_upp_ro = QLineEdit() lbl_low = QLabel(_("Lower space")) self.le_low_vp = QLineEdit() self.le_low_vs = QLineEdit() self.le_low_ro = QLineEdit() model_layout = QGridLayout() model_layout.addWidget(lbl_model, 0, 0) model_layout.addWidget(lbl_vp, 0, 1) model_layout.addWidget(lbl_vs, 0, 2) model_layout.addWidget(lbl_ro, 0, 3) model_layout.addWidget(lbl_upp, 1, 0) model_layout.addWidget(self.le_upp_vp, 1, 1) model_layout.addWidget(self.le_upp_vs, 1, 2) model_layout.addWidget(self.le_upp_ro, 1, 3) model_layout.addWidget(lbl_low, 2, 0) model_layout.addWidget(self.le_low_vp, 2, 1) model_layout.addWidget(self.le_low_vs, 2, 2) model_layout.addWidget(self.le_low_ro, 2, 3) model_group.setLayout(model_layout) self.layout.addWidget(model_group) text = _("Incident angles") self.angles = self.create_lineedit(text) self.layout.addWidget(self.angles) lbl_reflection = QLabel(_('Reflection')) self.rb_pp = QRadioButton(_('PP')) self.rb_ps = QRadioButton(_('PS')) bg_reflection = QButtonGroup() bg_reflection.addButton(self.rb_pp) bg_reflection.addButton(self.rb_ps) hbox = QHBoxLayout() hbox.addWidget(lbl_reflection) hbox.addWidget(self.rb_pp) hbox.addWidget(self.rb_ps) self.layout.addLayout(hbox) lbl_complex = QLabel(_('Complex')) self.rb_amp = QRadioButton(_('amplitude')) self.rb_pha = QRadioButton(_('phase')) bg_complex = QButtonGroup() bg_complex.addButton(self.rb_amp) bg_complex.addButton(self.rb_pha) hbox = QHBoxLayout() hbox.addWidget(lbl_complex) hbox.addWidget(self.rb_amp) hbox.addWidget(self.rb_pha) self.layout.addLayout(hbox) lbl_equation = QLabel(_('Equation')) self.rb_linear = QRadioButton(_('Linear')) self.rb_quadratic = QRadioButton(_('Quadratic')) self.rb_zoeppritz = QRadioButton(_('Zoeppritz')) bg_equation = QButtonGroup() bg_equation.addButton(self.rb_linear) bg_equation.addButton(self.rb_quadratic) bg_equation.addButton(self.rb_zoeppritz) hbox = QHBoxLayout() hbox.addWidget(lbl_equation) hbox.addWidget(self.rb_linear) hbox.addWidget(self.rb_quadratic) hbox.addWidget(self.rb_zoeppritz) self.layout.addLayout(hbox) text = _("New point") self.new_point = self.create_lineedit(text) self.layout.addWidget(self.new_point) action = self.create_action() self.layout.addWidget(action) self.rb_linear.toggled.connect( lambda: self.set_equation(self.rb_linear)) self.rb_quadratic.toggled.connect( lambda: self.set_equation(self.rb_quadratic)) self.rb_zoeppritz.toggled.connect( lambda: self.set_equation(self.rb_zoeppritz)) self.rb_zoeppritz.setChecked(True) self.rb_pp.toggled.connect(lambda: self.set_reflection(self.rb_pp)) self.rb_ps.toggled.connect(lambda: self.set_reflection(self.rb_ps)) self.rb_pp.setChecked(True) self.rb_amp.toggled.connect(lambda: self.set_complex(self.rb_amp)) self.rb_pha.toggled.connect(lambda: self.set_complex(self.rb_pha)) self.rb_amp.setChecked(True)
class QtShapesControls(QtLayerControls): """Qt view and controls for the napari Shapes layer. Parameters ---------- layer : napari.layers.Shapes An instance of a napari Shapes layer. Attributes ---------- button_group : qtpy.QtWidgets.QButtonGroup Button group for shapes layer modes (SELECT, DIRECT, PAN_ZOOM, ADD_RECTANGLE, ADD_ELLIPSE, ADD_LINE, ADD_PATH, ADD_POLYGON, VERTEX_INSERT, VERTEX_REMOVE). delete_button : qtpy.QtWidgets.QtModePushButton Button to delete selected shapes direct_button : qtpy.QtWidgets.QtModeRadioButton Button to select individual vertices in shapes. edgeColorSwatch : qtpy.QtWidgets.QFrame Thumbnail display of points edge color. edgeComboBox : qtpy.QtWidgets.QComboBox Drop down list allowing user to set edge color of points. ellipse_button : qtpy.QtWidgets.QtModeRadioButton Button to add ellipses to shapes layer. faceColorSwatch : qtpy.QtWidgets.QFrame Thumbnail display of points face color. faceComboBox : qtpy.QtWidgets.QComboBox Drop down list allowing user to set face color of points. grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : napari.layers.Shapes An instance of a napari Shapes layer. line_button : qtpy.QtWidgets.QtModeRadioButton Button to add lines to shapes layer. move_back_button : qtpy.QtWidgets.QtModePushButton Button to move selected shape(s) to the back. move_front_button : qtpy.QtWidgets.QtModePushButton Button to move shape(s) to the front. panzoom_button : qtpy.QtWidgets.QtModeRadioButton Button to pan/zoom shapes layer. path_button : qtpy.QtWidgets.QtModeRadioButton Button to add paths to shapes layer. polygon_button : qtpy.QtWidgets.QtModeRadioButton Button to add polygons to shapes layer. rectangle_button : qtpy.QtWidgets.QtModeRadioButton Button to add rectangles to shapes layer. select_button : qtpy.QtWidgets.QtModeRadioButton Button to select shapes. vertex_insert_button : qtpy.QtWidgets.QtModeRadioButton Button to insert vertex into shape. vertex_remove_button : qtpy.QtWidgets.QtModeRadioButton Button to remove vertex from shapes. widthSlider : qtpy.QtWidgets.QSlider Slider controlling line edge width of shapes. Raises ------ ValueError Raise error if shapes mode is not recognized. """ def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self._on_mode_change) self.layer.events.edge_width.connect(self._on_edge_width_change) self.layer.events.current_edge_color.connect( self._on_current_edge_color_change) self.layer.events.current_face_color.connect( self._on_current_face_color_change) self.layer.events.editable.connect(self._on_editable_change) self.layer.text.events.visible.connect(self._on_text_visibility_change) sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(40) sld.setSingleStep(1) value = self.layer.current_edge_width if isinstance(value, Iterable): if isinstance(value, list): value = np.asarray(value) value = value.mean() sld.setValue(int(value)) sld.valueChanged.connect(self.changeWidth) self.widthSlider = sld self.select_button = QtModeRadioButton( layer, 'select', Mode.SELECT, tooltip=trans._('Select shapes (S)')) self.direct_button = QtModeRadioButton( layer, 'direct', Mode.DIRECT, tooltip=trans._('Select vertices (D)'), ) self.panzoom_button = QtModeRadioButton( layer, 'zoom', Mode.PAN_ZOOM, tooltip=trans._('Pan/zoom (Space)'), checked=True, ) self.rectangle_button = QtModeRadioButton( layer, 'rectangle', Mode.ADD_RECTANGLE, tooltip=trans._('Add rectangles (R)'), ) self.ellipse_button = QtModeRadioButton( layer, 'ellipse', Mode.ADD_ELLIPSE, tooltip=trans._('Add ellipses (E)'), ) self.line_button = QtModeRadioButton(layer, 'line', Mode.ADD_LINE, tooltip=trans._('Add lines (L)')) self.path_button = QtModeRadioButton(layer, 'path', Mode.ADD_PATH, tooltip=trans._('Add paths (T)')) self.polygon_button = QtModeRadioButton( layer, 'polygon', Mode.ADD_POLYGON, tooltip=trans._('Add polygons (P)'), ) self.vertex_insert_button = QtModeRadioButton( layer, 'vertex_insert', Mode.VERTEX_INSERT, tooltip=trans._('Insert vertex (I)'), ) self.vertex_remove_button = QtModeRadioButton( layer, 'vertex_remove', Mode.VERTEX_REMOVE, tooltip=trans._('Remove vertex (X)'), ) self.move_front_button = QtModePushButton( layer, 'move_front', slot=self.layer.move_to_front, tooltip=trans._('Move to front'), ) self.move_back_button = QtModePushButton( layer, 'move_back', slot=self.layer.move_to_back, tooltip=trans._('Move to back'), ) self.delete_button = QtModePushButton( layer, 'delete_shape', slot=self.layer.remove_selected, tooltip=trans._("Delete selected shapes ({key})", key=KEY_SYMBOLS['Backspace']), ) self.button_group = QButtonGroup(self) self.button_group.addButton(self.select_button) self.button_group.addButton(self.direct_button) self.button_group.addButton(self.panzoom_button) self.button_group.addButton(self.rectangle_button) self.button_group.addButton(self.ellipse_button) self.button_group.addButton(self.line_button) self.button_group.addButton(self.path_button) self.button_group.addButton(self.polygon_button) self.button_group.addButton(self.vertex_insert_button) self.button_group.addButton(self.vertex_remove_button) button_grid = QGridLayout() button_grid.addWidget(self.vertex_remove_button, 0, 2) button_grid.addWidget(self.vertex_insert_button, 0, 3) button_grid.addWidget(self.delete_button, 0, 4) button_grid.addWidget(self.direct_button, 0, 5) button_grid.addWidget(self.select_button, 0, 6) button_grid.addWidget(self.panzoom_button, 0, 7) button_grid.addWidget(self.move_back_button, 1, 1) button_grid.addWidget(self.move_front_button, 1, 2) button_grid.addWidget(self.ellipse_button, 1, 3) button_grid.addWidget(self.rectangle_button, 1, 4) button_grid.addWidget(self.polygon_button, 1, 5) button_grid.addWidget(self.line_button, 1, 6) button_grid.addWidget(self.path_button, 1, 7) button_grid.setContentsMargins(5, 0, 0, 5) button_grid.setColumnStretch(0, 1) button_grid.setSpacing(4) self.faceColorEdit = QColorSwatchEdit( initial_color=self.layer.current_face_color, tooltip=trans._('click to set current face color'), ) self._on_current_face_color_change() self.edgeColorEdit = QColorSwatchEdit( initial_color=self.layer.current_edge_color, tooltip=trans._('click to set current edge color'), ) self._on_current_edge_color_change() self.faceColorEdit.color_changed.connect(self.changeFaceColor) self.edgeColorEdit.color_changed.connect(self.changeEdgeColor) text_disp_cb = QCheckBox() text_disp_cb.setToolTip(trans._('toggle text visibility')) text_disp_cb.setChecked(self.layer.text.visible) text_disp_cb.stateChanged.connect(self.change_text_visibility) self.textDispCheckBox = text_disp_cb # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_grid, 0, 0, 1, 2) self.grid_layout.addWidget(QLabel(trans._('opacity:')), 1, 0) self.grid_layout.addWidget(self.opacitySlider, 1, 1) self.grid_layout.addWidget(QLabel(trans._('edge width:')), 2, 0) self.grid_layout.addWidget(self.widthSlider, 2, 1) self.grid_layout.addWidget(QLabel(trans._('blending:')), 3, 0) self.grid_layout.addWidget(self.blendComboBox, 3, 1) self.grid_layout.addWidget(QLabel(trans._('face color:')), 4, 0) self.grid_layout.addWidget(self.faceColorEdit, 4, 1) self.grid_layout.addWidget(QLabel(trans._('edge color:')), 5, 0) self.grid_layout.addWidget(self.edgeColorEdit, 5, 1) self.grid_layout.addWidget(QLabel(trans._('display text:')), 6, 0) self.grid_layout.addWidget(self.textDispCheckBox, 6, 1) self.grid_layout.setRowStretch(7, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4) def _on_mode_change(self, event): """Update ticks in checkbox widgets when shapes layer mode changed. Available modes for shapes layer are: * SELECT * DIRECT * PAN_ZOOM * ADD_RECTANGLE * ADD_ELLIPSE * ADD_LINE * ADD_PATH * ADD_POLYGON * VERTEX_INSERT * VERTEX_REMOVE Parameters ---------- event : napari.utils.event.Event The napari event that triggered this method. Raises ------ ValueError Raise error if event.mode is not ADD, PAN_ZOOM, or SELECT. """ mode_buttons = { Mode.SELECT: self.select_button, Mode.DIRECT: self.direct_button, Mode.PAN_ZOOM: self.panzoom_button, Mode.ADD_RECTANGLE: self.rectangle_button, Mode.ADD_ELLIPSE: self.ellipse_button, Mode.ADD_LINE: self.line_button, Mode.ADD_PATH: self.path_button, Mode.ADD_POLYGON: self.polygon_button, Mode.VERTEX_INSERT: self.vertex_insert_button, Mode.VERTEX_REMOVE: self.vertex_remove_button, } if event.mode in mode_buttons: mode_buttons[event.mode].setChecked(True) else: raise ValueError( trans._("Mode '{mode}'not recognized", mode=event.mode)) def changeFaceColor(self, color: np.ndarray): """Change face color of shapes. Parameters ---------- color : np.ndarray Face color for shapes, color name or hex string. Eg: 'white', 'red', 'blue', '#00ff00', etc. """ with self.layer.events.current_face_color.blocker(): self.layer.current_face_color = color def changeEdgeColor(self, color: np.ndarray): """Change edge color of shapes. Parameters ---------- color : np.ndarray Edge color for shapes, color name or hex string. Eg: 'white', 'red', 'blue', '#00ff00', etc. """ with self.layer.events.current_edge_color.blocker(): self.layer.current_edge_color = color def changeWidth(self, value): """Change edge line width of shapes on the layer model. Parameters ---------- value : float Line width of shapes. """ self.layer.current_edge_width = float(value) / 2 def changeOpacity(self, value): """Change opacity value of shapes on the layer model. Parameters ---------- value : float Opacity value for shapes. Input range 0 - 100 (transparent to fully opaque). """ with self.layer.events.blocker(self._on_opacity_change): self.layer.opacity = value / 100 def change_text_visibility(self, state): """Toggle the visibiltiy of the text. Parameters ---------- state : QCheckBox Checkbox indicating if text is visible. """ if state == Qt.Checked: self.layer.text.visible = True else: self.layer.text.visible = False def _on_text_visibility_change(self, event): """Receive layer model text visibiltiy change change event and update checkbox. Parameters ---------- event : qtpy.QtCore.QEvent Event from the Qt context. """ with self.layer.text.events.visible.blocker(): self.textDispCheckBox.setChecked(self.layer.text.visible) def _on_edge_width_change(self, event=None): """Receive layer model edge line width change event and update slider. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ with self.layer.events.edge_width.blocker(): value = self.layer.current_edge_width value = np.clip(int(2 * value), 0, 40) self.widthSlider.setValue(value) def _on_current_edge_color_change(self, event=None): """Receive layer model edge color change event and update color swatch. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ with qt_signals_blocked(self.edgeColorEdit): self.edgeColorEdit.setColor(self.layer.current_edge_color) def _on_current_face_color_change(self, event=None): """Receive layer model face color change event and update color swatch. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ with qt_signals_blocked(self.faceColorEdit): self.faceColorEdit.setColor(self.layer.current_face_color) def _on_editable_change(self, event=None): """Receive layer model editable change event & enable/disable buttons. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ disable_with_opacity( self, [ 'select_button', 'direct_button', 'rectangle_button', 'ellipse_button', 'line_button', 'path_button', 'polygon_button', 'vertex_remove_button', 'vertex_insert_button', 'delete_button', 'move_back_button', 'move_front_button', ], self.layer.editable, ) def close(self): """Disconnect events when widget is closing.""" disconnect_events(self.layer.text.events, self) super().close()
def _setup_views(self): """Creates the UI widgets.""" self.central_splitter = QSplitter(self, orientation=Qt.Vertical) layout = create_plugin_layout(self.tools_layout, self.central_splitter) self.setLayout(layout) # Stretch last column? # It doesn't play nice when columns are hidden and then shown again. obj_tree_header = self.obj_tree.header() obj_tree_header.setSectionsMovable(True) obj_tree_header.setStretchLastSection(False) add_actions(self.show_cols_submenu, self.obj_tree.toggle_column_actions_group.actions()) self.central_splitter.addWidget(self.obj_tree) # Bottom pane bottom_pane_widget = QWidget() bottom_layout = QHBoxLayout() bottom_layout.setSpacing(0) bottom_layout.setContentsMargins(5, 5, 5, 5) # left top right bottom bottom_pane_widget.setLayout(bottom_layout) self.central_splitter.addWidget(bottom_pane_widget) group_box = QGroupBox(_("Details")) bottom_layout.addWidget(group_box) v_group_layout = QVBoxLayout() h_group_layout = QHBoxLayout() h_group_layout.setContentsMargins(2, 2, 2, 2) # left top right bottom group_box.setLayout(v_group_layout) v_group_layout.addLayout(h_group_layout) # Radio buttons radio_widget = QWidget() radio_layout = QVBoxLayout() radio_layout.setContentsMargins(0, 0, 0, 0) # left top right bottom radio_widget.setLayout(radio_layout) self.button_group = QButtonGroup(self) for button_id, attr_detail in enumerate(self._attr_details): radio_button = QRadioButton(attr_detail.name) radio_layout.addWidget(radio_button) self.button_group.addButton(radio_button, button_id) self.button_group.buttonClicked[int].connect( self._change_details_field) self.button_group.button(0).setChecked(True) radio_layout.addStretch(1) h_group_layout.addWidget(radio_widget) # Editor widget self.editor = SimpleCodeEditor(self) self.editor.setReadOnly(True) h_group_layout.addWidget(self.editor) # Save and close buttons btn_layout = QHBoxLayout() btn_layout.addStretch() if not self.readonly: self.btn_save_and_close = QPushButton(_('Save and Close')) self.btn_save_and_close.setDisabled(True) self.btn_save_and_close.clicked.connect(self.accept) btn_layout.addWidget(self.btn_save_and_close) self.btn_close = QPushButton(_('Close')) self.btn_close.setAutoDefault(True) self.btn_close.setDefault(True) self.btn_close.clicked.connect(self.reject) btn_layout.addWidget(self.btn_close) v_group_layout.addLayout(btn_layout) # Splitter parameters self.central_splitter.setCollapsible(0, False) self.central_splitter.setCollapsible(1, True) self.central_splitter.setSizes([500, 320]) # Connect signals # Keep a temporary reference of the selection_model to prevent # segfault in PySide. # See http://permalink.gmane.org/gmane.comp.lib.qt.pyside.devel/222 selection_model = self.obj_tree.selectionModel() selection_model.currentChanged.connect(self._update_details) # Check if the values of the model have been changed self._proxy_tree_model.sig_setting_data.connect( self.save_and_close_enable) self._proxy_tree_model.sig_update_details.connect( self._update_details_for_item)
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=trans._('Select shapes (S)')) self.direct_button = QtModeRadioButton( layer, 'direct', Mode.DIRECT, tooltip=trans._('Select vertices (D)'), ) self.panzoom_button = QtModeRadioButton( layer, 'zoom', Mode.PAN_ZOOM, tooltip=trans._('Pan/zoom (Space)'), checked=True, ) self.rectangle_button = QtModeRadioButton( layer, 'rectangle', Mode.ADD_RECTANGLE, tooltip=trans._('Add rectangles (R)'), ) self.ellipse_button = QtModeRadioButton( layer, 'ellipse', Mode.ADD_ELLIPSE, tooltip=trans._('Add ellipses (E)'), ) self.line_button = QtModeRadioButton(layer, 'line', Mode.ADD_LINE, tooltip=trans._('Add lines (L)')) self.path_button = QtModeRadioButton(layer, 'path', Mode.ADD_PATH, tooltip=trans._('Add paths (T)')) self.polygon_button = QtModeRadioButton( layer, 'polygon', Mode.ADD_POLYGON, tooltip=trans._('Add polygons (P)'), ) self.vertex_insert_button = QtModeRadioButton( layer, 'vertex_insert', Mode.VERTEX_INSERT, tooltip=trans._('Insert vertex (I)'), ) self.vertex_remove_button = QtModeRadioButton( layer, 'vertex_remove', Mode.VERTEX_REMOVE, tooltip=trans._('Remove vertex (X)'), ) self.move_front_button = QtModePushButton( layer, 'move_front', slot=self.layer.move_to_front, tooltip=trans._('Move to front'), ) self.move_back_button = QtModePushButton( layer, 'move_back', slot=self.layer.move_to_back, tooltip=trans._('Move to back'), ) self.delete_button = QtModePushButton( layer, 'delete_shape', slot=self.layer.remove_selected, tooltip=trans._("Delete selected shapes ({key})", key=KEY_SYMBOLS['Backspace']), ) self.button_group = QButtonGroup(self) self.button_group.addButton(self.select_button) self.button_group.addButton(self.direct_button) self.button_group.addButton(self.panzoom_button) self.button_group.addButton(self.rectangle_button) self.button_group.addButton(self.ellipse_button) self.button_group.addButton(self.line_button) self.button_group.addButton(self.path_button) self.button_group.addButton(self.polygon_button) self.button_group.addButton(self.vertex_insert_button) self.button_group.addButton(self.vertex_remove_button) button_grid = QGridLayout() button_grid.addWidget(self.vertex_remove_button, 0, 2) button_grid.addWidget(self.vertex_insert_button, 0, 3) button_grid.addWidget(self.delete_button, 0, 4) button_grid.addWidget(self.direct_button, 0, 5) button_grid.addWidget(self.select_button, 0, 6) button_grid.addWidget(self.panzoom_button, 0, 7) button_grid.addWidget(self.move_back_button, 1, 1) button_grid.addWidget(self.move_front_button, 1, 2) button_grid.addWidget(self.ellipse_button, 1, 3) button_grid.addWidget(self.rectangle_button, 1, 4) button_grid.addWidget(self.polygon_button, 1, 5) button_grid.addWidget(self.line_button, 1, 6) button_grid.addWidget(self.path_button, 1, 7) button_grid.setContentsMargins(5, 0, 0, 5) button_grid.setColumnStretch(0, 1) button_grid.setSpacing(4) self.faceColorEdit = QColorSwatchEdit( initial_color=self.layer.current_face_color, tooltip=trans._('click to set current face color'), ) self._on_current_face_color_change() self.edgeColorEdit = QColorSwatchEdit( initial_color=self.layer.current_edge_color, tooltip=trans._('click to set current edge color'), ) self._on_current_edge_color_change() self.faceColorEdit.color_changed.connect(self.changeFaceColor) self.edgeColorEdit.color_changed.connect(self.changeEdgeColor) text_disp_cb = QCheckBox() text_disp_cb.setToolTip(trans._('toggle text visibility')) text_disp_cb.setChecked(self.layer.text.visible) text_disp_cb.stateChanged.connect(self.change_text_visibility) self.textDispCheckBox = text_disp_cb # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_grid, 0, 0, 1, 2) self.grid_layout.addWidget(QLabel(trans._('opacity:')), 1, 0) self.grid_layout.addWidget(self.opacitySlider, 1, 1) self.grid_layout.addWidget(QLabel(trans._('edge width:')), 2, 0) self.grid_layout.addWidget(self.widthSlider, 2, 1) self.grid_layout.addWidget(QLabel(trans._('blending:')), 3, 0) self.grid_layout.addWidget(self.blendComboBox, 3, 1) self.grid_layout.addWidget(QLabel(trans._('face color:')), 4, 0) self.grid_layout.addWidget(self.faceColorEdit, 4, 1) self.grid_layout.addWidget(QLabel(trans._('edge color:')), 5, 0) self.grid_layout.addWidget(self.edgeColorEdit, 5, 1) self.grid_layout.addWidget(QLabel(trans._('display text:')), 6, 0) self.grid_layout.addWidget(self.textDispCheckBox, 6, 1) self.grid_layout.setRowStretch(7, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4)
def setup_page(self): about_label = QLabel( _("The <b>current working directory</b> is " "the working directory for IPython consoles " "and the current directory for the File Explorer.")) about_label.setWordWrap(True) startup_group = QGroupBox(_("Startup")) startup_bg = QButtonGroup(startup_group) startup_label = QLabel( _("At startup, the current working " "directory is:")) startup_label.setWordWrap(True) lastdir_radio = self.create_radiobutton( _("The current project directory " "or user home directory " "(if no project is active)"), 'startup/use_project_or_home_directory', True, _("At startup,"), # TODO button_group=startup_bg) thisdir_radio = self.create_radiobutton( _("the following directory:"), 'startup/use_fixed_directory', False, _("At startup, the current working " "directory will be the specified path"), button_group=startup_bg) thisdir_bd = self.create_browsedir("", 'startup/fixed_directory', getcwd()) thisdir_radio.toggled.connect(thisdir_bd.setEnabled) lastdir_radio.toggled.connect(thisdir_bd.setDisabled) thisdir_layout = QHBoxLayout() thisdir_layout.addWidget(thisdir_radio) thisdir_layout.addWidget(thisdir_bd) startup_layout = QVBoxLayout() startup_layout.addWidget(startup_label) startup_layout.addWidget(lastdir_radio) startup_layout.addLayout(thisdir_layout) startup_group.setLayout(startup_layout) # Console Directory console_group = QGroupBox(_("Console directory")) console_label = QLabel(_("The working directory for new consoles is:")) console_label.setWordWrap(True) console_bg = QButtonGroup(console_group) console_project_radio = self.create_radiobutton( _("The current project directory " "or user home directory " "(if no project is active)"), 'console/use_project_or_home_directory', True, button_group=console_bg) console_cwd_radio = self.create_radiobutton( _("The current working directory"), 'console/use_cwd', False, button_group=console_bg) console_dir_radio = self.create_radiobutton( _("the following directory:"), 'console/use_fixed_directory', False, _("The directory when a new console " "is open will be the specified path"), button_group=console_bg) console_dir_bd = self.create_browsedir("", 'console/fixed_directory', getcwd()) console_dir_radio.toggled.connect(console_dir_bd.setEnabled) console_project_radio.toggled.connect(console_dir_bd.setDisabled) console_cwd_radio.toggled.connect(console_dir_bd.setDisabled) console_dir_layout = QHBoxLayout() console_dir_layout.addWidget(console_dir_radio) console_dir_layout.addWidget(console_dir_bd) console_layout = QVBoxLayout() console_layout.addWidget(console_label) console_layout.addWidget(console_project_radio) console_layout.addWidget(console_cwd_radio) console_layout.addLayout(console_dir_layout) console_group.setLayout(console_layout) vlayout = QVBoxLayout() vlayout.addWidget(about_label) vlayout.addSpacing(10) vlayout.addWidget(startup_group) vlayout.addWidget(console_group) vlayout.addStretch(1) self.setLayout(vlayout)
def setup_page(self): newcb = self.create_checkbox # Python executable Group pyexec_group = QGroupBox(_("Python interpreter")) pyexec_bg = QButtonGroup(pyexec_group) pyexec_label = QLabel(_("Select the Python interpreter for all Spyder " "consoles")) self.def_exec_radio = self.create_radiobutton( _("Default (i.e. the same as Spyder's)"), 'default', button_group=pyexec_bg) self.cus_exec_radio = self.create_radiobutton( _("Use the following Python interpreter:"), 'custom', button_group=pyexec_bg) if os.name == 'nt': filters = _("Executables")+" (*.exe)" else: filters = None pyexec_layout = QVBoxLayout() pyexec_layout.addWidget(pyexec_label) pyexec_layout.addWidget(self.def_exec_radio) pyexec_layout.addWidget(self.cus_exec_radio) self.validate_custom_interpreters_list() self.cus_exec_combo = self.create_file_combobox( _('Recent custom interpreters'), self.get_option('custom_interpreters_list'), 'custom_interpreter', filters=filters, default_line_edit=True, adjust_to_contents=True ) self.def_exec_radio.toggled.connect(self.cus_exec_combo.setDisabled) self.cus_exec_radio.toggled.connect(self.cus_exec_combo.setEnabled) pyexec_layout.addWidget(self.cus_exec_combo) pyexec_group.setLayout(pyexec_layout) self.pyexec_edit = self.cus_exec_combo.combobox.lineEdit() # UMR Group umr_group = QGroupBox(_("User Module Reloader (UMR)")) umr_label = QLabel(_("UMR forces Python to reload modules which were " "imported when executing a file in a Python or " "IPython console with the <i>runfile</i> " "function.")) umr_label.setWordWrap(True) umr_enabled_box = newcb(_("Enable UMR"), 'umr/enabled', msg_if_enabled=True, msg_warning=_( "This option will enable the User Module Reloader (UMR) " "in Python/IPython consoles. UMR forces Python to " "reload deeply modules during import when running a " "Python script using the Spyder's builtin function " "<b>runfile</b>." "<br><br><b>1.</b> UMR may require to restart the " "console in which it will be called " "(otherwise only newly imported modules will be " "reloaded when executing files)." "<br><br><b>2.</b> If errors occur when re-running a " "PyQt-based program, please check that the Qt objects " "are properly destroyed (e.g. you may have to use the " "attribute <b>Qt.WA_DeleteOnClose</b> on your main " "window, using the <b>setAttribute</b> method)"), ) umr_verbose_box = newcb(_("Show reloaded modules list"), 'umr/verbose', msg_info=_( "Please note that these changes will " "be applied only to new consoles")) umr_namelist_btn = QPushButton( _("Set UMR excluded (not reloaded) modules")) umr_namelist_btn.clicked.connect(self.set_umr_namelist) umr_layout = QVBoxLayout() umr_layout.addWidget(umr_label) umr_layout.addWidget(umr_enabled_box) umr_layout.addWidget(umr_verbose_box) umr_layout.addWidget(umr_namelist_btn) umr_group.setLayout(umr_layout) vlayout = QVBoxLayout() vlayout.addWidget(pyexec_group) vlayout.addWidget(umr_group) vlayout.addStretch(1) self.setLayout(vlayout)
class DataCorrectionsWidget(BaseWidget): """ Widget that presents data correction options to the user. """ ## Widget name name = "Data Corrections" _old_backgnd_sub = None _old_norm_button = None incident_beam_norm_grp = None def __init__(self, parent=None, state=None, settings=None, data_type=None): super(DataCorrectionsWidget, self).__init__(parent, state, settings, data_type=data_type) class DataCorrsFrame(QFrame): def __init__(self, parent=None): QFrame.__init__(self, parent) self.ui = load_ui(__file__, '../../../ui/inelastic/dgs_data_corrections.ui', baseinstance=self) self._content = DataCorrsFrame(self) self._layout.addWidget(self._content) self.initialize_content() self._instrument_name = settings.instrument_name if state is not None: self.set_state(state) else: self.set_state(DataCorrectionsScript(self._instrument_name)) def initialize_content(self): # Set some validators self._content.monint_low_edit.setValidator(QIntValidator(self._content.monint_low_edit)) self._content.monint_high_edit.setValidator(QIntValidator(self._content.monint_high_edit)) self._content.tof_start_edit.setValidator(QIntValidator(self._content.tof_start_edit)) self._content.tof_end_edit.setValidator(QIntValidator(self._content.tof_end_edit)) # Make group for incident beam normalisation radio buttons self.incident_beam_norm_grp = QButtonGroup() self.incident_beam_norm_grp.addButton(self._content.none_rb, 0) self.incident_beam_norm_grp.addButton(self._content.current_rb, 1) self.incident_beam_norm_grp.addButton(self._content.monitor1_rb, 2) self._monitor_intrange_widgets_state(self._content.monitor1_rb.isChecked()) self._content.monitor1_rb.toggled.connect(self._monitor_intrange_widgets_state) self._detvan_intrange_widgets_state(self._content.van_int_cb.isChecked()) self._content.van_int_cb.toggled.connect(self._detvan_intrange_widgets_state) self._content.use_procdetvan_cb.toggled.connect(self._detvan_widgets_opp_state) self._save_detvan_widgets_state(self._content.save_procdetvan_cb.isChecked()) self._content.save_procdetvan_cb.toggled.connect(self._save_detvan_widgets_state) # Connections self._content.van_input_browse.clicked.connect(self._detvan_browse) self._content.save_procdetvan_save.clicked.connect(self._save_procdetvan_save) def _monitor_intrange_widgets_state(self, state=False): self._content.monint_label.setEnabled(state) self._content.monint_low_edit.setEnabled(state) self._content.monint_high_edit.setEnabled(state) def _detvan_intrange_widgets_state(self, state=False): self._content.van_int_range_label.setEnabled(state) self._content.van_int_range_low_edit.setEnabled(state) self._content.van_int_range_high_edit.setEnabled(state) self._content.van_int_range_units_cb.setEnabled(state) def _detvan_widgets_opp_state(self, state=False): self._content.van_int_cb.setEnabled(not state) if self._content.van_int_cb.isChecked(): self._detvan_intrange_widgets_state(not state) self._content.van_int_cb.setChecked(False) self._content.save_procdetvan_cb.setEnabled(not state) if self._content.save_procdetvan_cb.isChecked(): self._content.save_procdetvan_cb.setChecked(False) def _save_detvan_widgets_state(self, state=False): self._content.save_procdetvan_label.setEnabled(state) self._content.save_procdetvan_edit.setEnabled(state) self._content.save_procdetvan_save.setEnabled(state) def _detvan_browse(self): fname = self.data_browse_dialog() if fname: self._content.van_input_edit.setText(fname) def _save_procdetvan_save(self): fname = self.data_save_dialog("*.nxs") if fname: self._content.save_procdetvan_edit.setText(fname) def set_state(self, state): """ Populate the UI elements with the data from the given state. @param state: DataCorrectionsScript object """ self._content.filter_bad_pulses_chkbox.setChecked(state.filter_bad_pulses) button_index = DataCorrectionsScript.INCIDENT_BEAM_NORM_TYPES.index(state.incident_beam_norm) cbutton = self.incident_beam_norm_grp.button(button_index) cbutton.setChecked(True) self._content.monint_low_edit.setText(str(state.monitor_int_low)) self._content.monint_high_edit.setText(str(state.monitor_int_high)) self._content.background_sub_gb.setChecked(state.tib_subtraction) self._content.tof_start_edit.setText(str(state.tib_tof_start)) self._content.tof_end_edit.setText(str(state.tib_tof_end)) self._content.correct_kikf_cb.setChecked(state.correct_kikf) self._content.van_input_edit.setText(state.detector_vanadium) self._content.van_int_cb.setChecked(state.detvan_integration) self._content.van_int_range_low_edit.setText(str(state.detvan_int_range_low)) self._content.van_int_range_high_edit.setText(str(state.detvan_int_range_high)) entry_index = self._content.van_int_range_units_cb.findText(state.detvan_int_range_units) self._content.van_int_range_units_cb.setCurrentIndex(entry_index) self._content.save_procdetvan_cb.setChecked(state.save_proc_detvan) self._content.save_procdetvan_edit.setText(str(state.save_proc_detvan_file)) self._content.use_procdetvan_cb.setChecked(state.use_proc_detvan) def get_state(self): """ Returns an object with the state of the interface """ d = DataCorrectionsScript(self._instrument_name) d.filter_bad_pulses = self._content.filter_bad_pulses_chkbox.isChecked() d.incident_beam_norm = DataCorrectionsScript.INCIDENT_BEAM_NORM_TYPES[self.incident_beam_norm_grp.checkedId()] d.monitor_int_low = util._check_and_get_float_line_edit(self._content.monint_low_edit) d.monitor_int_high = util._check_and_get_float_line_edit(self._content.monint_high_edit) d.tib_subtraction = self._content.background_sub_gb.isChecked() d.tib_tof_start = util._check_and_get_float_line_edit(self._content.tof_start_edit) d.tib_tof_end = util._check_and_get_float_line_edit(self._content.tof_end_edit) d.correct_kikf = self._content.correct_kikf_cb.isChecked() d.detector_vanadium = self._content.van_input_edit.text() d.detvan_integration = self._content.van_int_cb.isChecked() d.detvan_int_range_low = util._check_and_get_float_line_edit(self._content.van_int_range_low_edit) d.detvan_int_range_high = util._check_and_get_float_line_edit(self._content.van_int_range_high_edit) d.detvan_int_range_units = self._content.van_int_range_units_cb.currentText() d.save_proc_detvan = self._content.save_procdetvan_cb.isChecked() d.save_proc_detvan_file = self._content.save_procdetvan_edit.text() d.use_proc_detvan = self._content.use_procdetvan_cb.isChecked() return d def live_button_toggled_actions(self,checked): if checked: self._old_norm_button = self.incident_beam_norm_grp.checkedId() self._old_backgnd_sub = self._content.background_sub_gb.isChecked() self._content.none_rb.setChecked(True) self._content.background_sub_gb.setChecked(False) else: try: self.incident_beam_norm_grp.button(self._old_norm_button).setChecked(True) self._content.background_sub_gb.setChecked(self._old_backgnd_sub) except: # This is for if the live button started out checked pass self._content.incident_beam_norm_gb.setEnabled(not checked) self._content.background_sub_gb.setEnabled(not checked)
def __init__(self, settings): BaseWidget.__init__(self, settings=settings) inf = float('inf') def set_spin(spin, minVal=-inf, maxVal=+inf, decimals=3): spin.setRange(minVal, maxVal) spin.setDecimals(decimals) spin.setSingleStep(0.01) def tip(widget, text): if text: widget.setToolTip(text) return widget def setEnabled(widget, *widgets): """enables widget, when value of all widgets evaluates to true""" def setEnabled(): widget.setEnabled(all(w.isChecked() for w in widgets)) for w in widgets: w.toggled.connect(setEnabled) return widget def DoubleEdit(): edit = SmallQLineEdit() edit.setValidator(QDoubleValidator()) return edit # ui data elements self.prefix = tip(QLineEdit(), self.TIP_prefix) self.dataDir = tip(QLineEdit(), self.TIP_dataDir) self.saveDir = tip(QLineEdit(), self.TIP_saveDir) self.vanRuns = tip(QLineEdit(), self.TIP_vanRuns) self.vanCmnt = tip(QLineEdit(), self.TIP_vanCmnt) self.vanTemp = tip(DoubleEdit(), self.TIP_vanTemp) self.ecRuns = tip(SmallQLineEdit(), self.TIP_ecRuns) self.ecTemp = tip(DoubleEdit(), self.TIP_ecTemp) self.ecFactor = tip(QDoubleSpinBox(), self.TIP_ecFactor) set_spin(self.ecFactor, 0, 1) self.binEon = tip(QCheckBox(), self.TIP_binEon) self.binEstart = setEnabled(tip(QDoubleSpinBox(), self.TIP_binEstart), self.binEon) self.binEstep = setEnabled(tip(QDoubleSpinBox(), self.TIP_binEstep), self.binEon) self.binEend = setEnabled(tip(QDoubleSpinBox(), self.TIP_binEend), self.binEon) set_spin(self.binEstart) set_spin(self.binEstep, decimals=4) set_spin(self.binEend) self.binQon = setEnabled(tip(QCheckBox(), self.TIP_binQon), self.binEon) self.binQstart = setEnabled(tip(QDoubleSpinBox(), self.TIP_binQstart), self.binEon, self.binQon) self.binQstep = setEnabled(tip(QDoubleSpinBox(), self.TIP_binQstep), self.binEon, self.binQon) self.binQend = setEnabled(tip(QDoubleSpinBox(), self.TIP_binQend), self.binEon, self.binQon) set_spin(self.binQstart) set_spin(self.binQstep) set_spin(self.binQend) self.maskDetectors = tip(QLineEdit(), self.TIP_maskDetectors) headers = ('Data runs', 'Comment', 'T (K)') self.dataRunsView = tip( DataTableView(self, headers, TOFTOFSetupWidget.TofTofDataTableModel), self.TIP_dataRunsView) self.dataRunsView.horizontalHeader().setStretchLastSection(True) self.dataRunsView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.runDataModel = self.dataRunsView.model() # ui controls self.btnDataDir = tip(QPushButton('Browse'), self.TIP_btnDataDir) self.btnSaveDir = tip(QPushButton('Browse'), self.TIP_btnSaveDir) self.chkSubtractECVan = tip( QCheckBox('Subtract empty can from vanadium'), self.TIP_chkSubtractECVan) self.vanEcFactor = setEnabled( tip(QDoubleSpinBox(), self.TIP_vanEcFactor), self.chkSubtractECVan) set_spin(self.vanEcFactor, 0, 1) self.chkReplaceNaNs = setEnabled( tip(QCheckBox(u'Replace special values in S(Q, ω) with 0'), self.TIP_chkReplaceNaNs), self.binEon) self.chkCreateDiff = setEnabled( tip(QCheckBox('Create diffractograms'), self.TIP_chkCreateDiff), self.binEon) self.chkKeepSteps = tip(QCheckBox('Keep intermediate steps'), self.TIP_chkKeepSteps) self.chkSofTWNxspe = setEnabled( tip(QCheckBox('NXSPE'), self.TIP_chkNxspe), self.binEon) self.chkSofTWNexus = tip(QCheckBox('NeXus'), self.TIP_chkNexus) self.chkSofTWAscii = tip(QCheckBox('Ascii'), self.TIP_chkAscii) self.chkSofQWNexus = setEnabled( tip(QCheckBox('NeXus'), self.TIP_chkNexus), self.binEon, self.binQon) self.chkSofQWAscii = setEnabled( tip(QCheckBox('Ascii'), self.TIP_chkAscii), self.binEon, self.binQon) self.rbtNormaliseNone = tip(QRadioButton('none'), self.TIP_rbtNormaliseNone) self.rbtNormaliseMonitor = tip(QRadioButton('to monitor'), self.TIP_rbtNormaliseMonitor) self.rbtNormaliseTime = tip(QRadioButton('to time'), self.TIP_rbtNormaliseTime) self.rbtCorrectTOFNone = tip(QRadioButton('none'), self.TIP_rbtCorrectTOFNone) self.rbtCorrectTOFVan = tip(QRadioButton('vanadium'), self.TIP_rbtCorrectTOFVan) self.rbtCorrectTOFSample = tip(QRadioButton('sample'), self.TIP_rbtCorrectTOFSample) # ui layout def _box(cls, widgets): box = cls() for wgt in widgets: if isinstance(wgt, QLayout): box.addLayout(wgt) elif isinstance(wgt, QWidget): box.addWidget(wgt) else: box.addStretch(wgt) return box def hbox(*widgets): return _box(QHBoxLayout, widgets) def vbox(*widgets): return _box(QVBoxLayout, widgets) def label(text, tip): label = QLabel(text) if tip: label.setToolTip(tip) return label self.gbSave = QGroupBox('Save reduced data') self.gbSave.setCheckable(True) gbDataDir = QGroupBox('Data search directory') gbPrefix = QGroupBox('Workspace prefix') gbOptions = QGroupBox('Options') gbInputs = QGroupBox('Inputs') gbBinning = QGroupBox('Binning') gbData = QGroupBox('Data') box = QVBoxLayout() self._layout.addLayout(box) box.addLayout( hbox(vbox(gbDataDir, gbInputs, gbBinning, gbOptions, 1), vbox(gbPrefix, gbData, self.gbSave))) gbDataDir.setLayout(hbox(self.dataDir, self.btnDataDir)) gbPrefix.setLayout(hbox(self.prefix, )) grid = QGridLayout() grid.addWidget(self.chkSubtractECVan, 0, 0, 1, 4) grid.addWidget(label('Normalise', 'tip'), 1, 0) grid.addWidget(self.rbtNormaliseNone, 1, 1) grid.addWidget(self.rbtNormaliseMonitor, 1, 2) grid.addWidget(self.rbtNormaliseTime, 1, 3) grid.addWidget(QLabel('Correct TOF'), 2, 0) grid.addWidget(self.rbtCorrectTOFNone, 2, 1) grid.addWidget(self.rbtCorrectTOFVan, 2, 2) grid.addWidget(self.rbtCorrectTOFSample, 2, 3) grid.addWidget(self.chkReplaceNaNs, 3, 0, 1, 4) grid.addWidget(self.chkCreateDiff, 4, 0, 1, 4) grid.addWidget(self.chkKeepSteps, 5, 0, 1, 4) grid.setColumnStretch(4, 1) gbOptions.setLayout(grid) btnGroup = QButtonGroup(self) btnGroup.addButton(self.rbtNormaliseNone) btnGroup.addButton(self.rbtNormaliseMonitor) btnGroup.addButton(self.rbtNormaliseTime) btnGroup = QButtonGroup(self) btnGroup.addButton(self.rbtCorrectTOFNone) btnGroup.addButton(self.rbtCorrectTOFVan) btnGroup.addButton(self.rbtCorrectTOFSample) grid = QGridLayout() grid.addWidget(QLabel('Vanadium runs'), 0, 0) grid.addWidget(self.vanRuns, 0, 1, 1, 3) grid.addWidget(QLabel('Van. comment'), 1, 0) grid.addWidget(self.vanCmnt, 1, 1, 1, 1) grid.addLayout(hbox(QLabel('EC factor'), self.vanEcFactor), 1, 2, 1, 1) grid.addLayout(hbox(QLabel('T (K)'), self.vanTemp), 1, 3) grid.addWidget(QLabel('Empty can runs'), 2, 0) grid.addWidget(self.ecRuns, 2, 1, 1, 1) grid.addLayout(hbox(QLabel('EC factor'), self.ecFactor), 2, 2, 1, 1) grid.addLayout(hbox(QLabel('T (K)'), self.ecTemp), 2, 3) grid.addWidget(QLabel('Mask detectors'), 3, 0) grid.addWidget(self.maskDetectors, 3, 1, 1, 3) gbInputs.setLayout(grid) grid = QGridLayout() grid.addWidget(QLabel('on'), 0, 1) grid.addWidget(QLabel('start'), 0, 2) grid.addWidget(QLabel('step'), 0, 3) grid.addWidget(QLabel('end'), 0, 4) grid.addWidget(QLabel('Energy'), 1, 0) grid.addWidget(self.binEon, 1, 1) grid.addWidget(self.binEstart, 1, 2) grid.addWidget(self.binEstep, 1, 3) grid.addWidget(self.binEend, 1, 4) grid.addWidget(QLabel('Q'), 2, 0) grid.addWidget(self.binQon, 2, 1) grid.addWidget(self.binQstart, 2, 2) grid.addWidget(self.binQstep, 2, 3) grid.addWidget(self.binQend, 2, 4) for col in (0, 2, 3, 4): grid.setColumnStretch(col, 1) gbBinning.setLayout(grid) gbData.setLayout(hbox(self.dataRunsView)) grid = QGridLayout() saveDirGroup = hbox(self.saveDir, self.btnSaveDir) grid.addWidget(QLabel('Directory'), 0, 0) grid.addLayout(saveDirGroup, 0, 1, 1, 4) grid.addWidget(setEnabled(QLabel(u'S(Q, ω):'), self.binEon), 1, 0) grid.addWidget(self.chkSofQWNexus, 1, 1) grid.addWidget(self.chkSofQWAscii, 1, 2) grid.addItem(QSpacerItem(5, 5, hPolicy=QSizePolicy.Expanding), 1, 4) grid.addWidget(QLabel(u'S(2θ, ω):'), 2, 0) grid.addWidget(self.chkSofTWNexus, 2, 1) grid.addWidget(self.chkSofTWAscii, 2, 2) grid.addWidget(self.chkSofTWNxspe, 2, 3) self.gbSave.setLayout(grid) # handle signals self.btnDataDir.clicked.connect(self._onDataDir) self.btnSaveDir.clicked.connect(self._onSaveDir) self.runDataModel.selectCell.connect(self._onSelectedCell)
class PlotSettingsWidget(QWidget): def __init__(self, settings, plotWidget, parent=None): super().__init__(parent) self.mainwindow = parent self.plotWidget = plotWidget # xmin self.xminLabel = QLabel(self.tr('xmin:')) self.xminSpinBox = CoordSpinBox() self.xminLabel.setBuddy(self.xminSpinBox) self.xminSpinBox.editingFinished.connect(self.change_limits) # xmax self.xmaxLabel = QLabel(self.tr('xmax:')) self.xmaxSpinBox = CoordSpinBox() self.xmaxLabel.setBuddy(self.xmaxSpinBox) self.xmaxSpinBox.editingFinished.connect(self.change_limits) # ymin self.yminLabel = QLabel(self.tr('ymin:')) self.yminSpinBox = CoordSpinBox() self.yminLabel.setBuddy(self.yminSpinBox) self.yminSpinBox.editingFinished.connect(self.change_limits) # ymax self.ymaxLabel = QLabel(self.tr('ymax:')) self.ymaxSpinBox = CoordSpinBox() self.ymaxLabel.setBuddy(self.ymaxSpinBox) self.ymaxSpinBox.editingFinished.connect(self.change_limits) # Autoscale Radio Group self.autoscaleButtonGroup = QButtonGroup() # Autoscale Group Box self.autoscaleGroupBox = QGroupBox() # autoscale self.autoscaleRadioButton = QRadioButton(self.tr("autoscale")) self.autoscaleButtonGroup.addButton(self.autoscaleRadioButton) # autotrack self.autoscrollRadioButton = QRadioButton(self.tr("autoscroll")) self.autoscaleButtonGroup.addButton(self.autoscrollRadioButton) # no autoscale self.manualscaleRadioButton = QRadioButton(self.tr("manual")) self.autoscaleButtonGroup.addButton(self.manualscaleRadioButton) layout = QVBoxLayout() layout.addWidget(self.autoscaleRadioButton) layout.addWidget(self.autoscrollRadioButton) layout.addWidget(self.manualscaleRadioButton) self.autoscaleGroupBox.setLayout(layout) # Layout layout = QGridLayout() layout.addWidget(self.xminLabel, 1, 0) layout.addWidget(self.xminSpinBox, 1, 1) layout.addWidget(self.xmaxLabel, 2, 0) layout.addWidget(self.xmaxSpinBox, 2, 1) layout.addWidget(self.yminLabel, 3, 0) layout.addWidget(self.yminSpinBox, 3, 1) layout.addWidget(self.ymaxLabel, 4, 0) layout.addWidget(self.ymaxSpinBox, 4, 1) layout.addWidget(self.autoscaleGroupBox, 5, 0, 1, 2) layout.setRowStretch(6, 1) self.setLayout(layout) # settings self.settings = settings self.settings.add_handler(S_XMIN, self.xminSpinBox) self.settings.add_handler(S_XMAX, self.xmaxSpinBox) self.settings.add_handler(S_YMIN, self.yminSpinBox) self.settings.add_handler(S_YMAX, self.ymaxSpinBox) self.settings.add_handler(S_AUTOSCALE, self.autoscaleButtonGroup) def refresh(self, state): pass def change_limits(self): if self.autoscale != AUTOSCALE_COMPLETE: self.plotWidget.xmin = self.xmin self.plotWidget.xmax = self.xmax self.plotWidget.ymin = self.ymin self.plotWidget.ymax = self.ymax self.plotWidget.draw() @property def xmin(self): return self.xminSpinBox.value() @property def xmax(self): return self.xmaxSpinBox.value() @property def ymin(self): return self.yminSpinBox.value() @property def ymax(self): return self.ymaxSpinBox.value() @property def autoscale(self): return self.autoscaleButtonGroup.checkedId()
class QtShapesControls(QtLayerControls): """Qt view and controls for the napari Shapes layer. Parameters ---------- layer : napari.layers.Shapes An instance of a napari Shapes layer. Attributes ---------- button_group : qtpy.QtWidgets.QButtonGroup Button group for shapes layer modes (SELECT, DIRECT, PAN_ZOOM, ADD_RECTANGLE, ADD_ELLIPSE, ADD_LINE, ADD_PATH, ADD_POLYGON, VERTEX_INSERT, VERTEX_REMOVE). delete_button : qtpy.QtWidgets.QtModePushButton Button to delete selected shapes direct_button : qtpy.QtWidgets.QtModeRadioButton Button to select individual vertices in shapes. edgeColorSwatch : qtpy.QtWidgets.QFrame Thumbnail display of points edge color. edgeComboBox : qtpy.QtWidgets.QComboBox Drop down list allowing user to set edge color of points. ellipse_button : qtpy.QtWidgets.QtModeRadioButton Button to add ellipses to shapes layer. faceColorSwatch : qtpy.QtWidgets.QFrame Thumbnail display of points face color. faceComboBox : qtpy.QtWidgets.QComboBox Drop down list allowing user to set face color of points. grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : napari.layers.Shapes An instance of a napari Shapes layer. line_button : qtpy.QtWidgets.QtModeRadioButton Button to add lines to shapes layer. move_back_button : qtpy.QtWidgets.QtModePushButton Button to move selected shape(s) to the back. move_front_button : qtpy.QtWidgets.QtModePushButton Button to move shape(s) to the front. panzoom_button : qtpy.QtWidgets.QtModeRadioButton Button to pan/zoom shapes layer. path_button : qtpy.QtWidgets.QtModeRadioButton Button to add paths to shapes layer. polygon_button : qtpy.QtWidgets.QtModeRadioButton Button to add polygons to shapes layer. rectangle_button : qtpy.QtWidgets.QtModeRadioButton Button to add rectangles to shapes layer. select_button : qtpy.QtWidgets.QtModeRadioButton Button to select shapes. vertex_insert_button : qtpy.QtWidgets.QtModeRadioButton Button to insert vertex into shape. vertex_remove_button : qtpy.QtWidgets.QtModeRadioButton Button to remove vertex from shapes. widthSlider : qtpy.QtWidgets.QSlider Slider controlling line edge width of shapes. Raises ------ ValueError Raise error if shapes mode is not recognized. """ layer: 'napari.layers.Shapes' 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, "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 self.layout().addRow(button_grid) self.layout().addRow(trans._('opacity:'), self.opacitySlider) self.layout().addRow(trans._('edge width:'), self.widthSlider) self.layout().addRow(trans._('blending:'), self.blendComboBox) self.layout().addRow(trans._('face color:'), self.faceColorEdit) self.layout().addRow(trans._('edge color:'), self.edgeColorEdit) self.layout().addRow(trans._('display text:'), self.textDispCheckBox) def _on_mode_change(self, event): """Update ticks in checkbox widgets when shapes layer mode changed. Available modes for shapes layer are: * SELECT * DIRECT * PAN_ZOOM * ADD_RECTANGLE * ADD_ELLIPSE * ADD_LINE * ADD_PATH * ADD_POLYGON * VERTEX_INSERT * VERTEX_REMOVE Parameters ---------- event : napari.utils.event.Event The napari event that triggered this method. Raises ------ ValueError Raise error if event.mode is not ADD, PAN_ZOOM, or SELECT. """ mode_buttons = { Mode.SELECT: self.select_button, Mode.DIRECT: self.direct_button, Mode.PAN_ZOOM: self.panzoom_button, Mode.ADD_RECTANGLE: self.rectangle_button, Mode.ADD_ELLIPSE: self.ellipse_button, Mode.ADD_LINE: self.line_button, Mode.ADD_PATH: self.path_button, Mode.ADD_POLYGON: self.polygon_button, Mode.VERTEX_INSERT: self.vertex_insert_button, Mode.VERTEX_REMOVE: self.vertex_remove_button, } if event.mode in mode_buttons: mode_buttons[event.mode].setChecked(True) elif event.mode == Mode.TRANSFORM: pass else: raise ValueError( trans._("Mode '{mode}'not recognized", mode=event.mode)) def changeFaceColor(self, color: np.ndarray): """Change face color of shapes. Parameters ---------- color : np.ndarray Face color for shapes, color name or hex string. Eg: 'white', 'red', 'blue', '#00ff00', etc. """ with self.layer.events.current_face_color.blocker(): self.layer.current_face_color = color def changeEdgeColor(self, color: np.ndarray): """Change edge color of shapes. Parameters ---------- color : np.ndarray Edge color for shapes, color name or hex string. Eg: 'white', 'red', 'blue', '#00ff00', etc. """ with self.layer.events.current_edge_color.blocker(): self.layer.current_edge_color = color def changeWidth(self, value): """Change edge line width of shapes on the layer model. Parameters ---------- value : float Line width of shapes. """ self.layer.current_edge_width = float(value) def change_text_visibility(self, state): """Toggle the visibility of the text. Parameters ---------- state : QCheckBox Checkbox indicating if text is visible. """ if state == Qt.Checked: self.layer.text.visible = True else: self.layer.text.visible = False def _on_text_visibility_change(self): """Receive layer model text visibiltiy change change event and update checkbox.""" with self.layer.text.events.visible.blocker(): self.textDispCheckBox.setChecked(self.layer.text.visible) def _on_edge_width_change(self): """Receive layer model edge line width change event and update slider.""" with self.layer.events.edge_width.blocker(): value = self.layer.current_edge_width value = np.clip(int(value), 0, 40) self.widthSlider.setValue(value) def _on_current_edge_color_change(self): """Receive layer model edge color change event and update color swatch.""" with qt_signals_blocked(self.edgeColorEdit): self.edgeColorEdit.setColor(self.layer.current_edge_color) def _on_current_face_color_change(self): """Receive layer model face color change event and update color swatch.""" with qt_signals_blocked(self.faceColorEdit): self.faceColorEdit.setColor(self.layer.current_face_color) def _on_editable_change(self): """Receive layer model editable change event & enable/disable buttons.""" disable_with_opacity( self, [ 'select_button', 'direct_button', 'rectangle_button', 'ellipse_button', 'line_button', 'path_button', 'polygon_button', 'vertex_remove_button', 'vertex_insert_button', 'delete_button', 'move_back_button', 'move_front_button', ], self.layer.editable, ) def close(self): """Disconnect events when widget is closing.""" disconnect_events(self.layer.text.events, self) super().close()
class PeriodicTableWidget(QWidget): selectionChanged = Signal() def __init__(self, parent=None): QWidget.__init__(self, parent) # Widgets, layouts and signals self._group = QButtonGroup() layout = QGridLayout() layout.setSpacing(0) ## Element for z, position in _ELEMENT_POSITIONS.items(): widget = ElementPushButton(z) widget.setCheckable(True) layout.addWidget(widget, *position) self._group.addButton(widget, z) ## Labels layout.addWidget(QLabel(''), 7, 0) # Dummy layout.addWidget(QLabel('*'), 5, 2, Qt.AlignRight) layout.addWidget(QLabel('*'), 8, 2, Qt.AlignRight) layout.addWidget(QLabel('**'), 6, 2, Qt.AlignRight) layout.addWidget(QLabel('**'), 9, 2, Qt.AlignRight) for row in [0, 1, 2, 3, 4, 5, 6, 8, 9]: layout.setRowStretch(row, 1) self.setLayout(layout) # Signals self._group.buttonClicked.connect(self.selectionChanged) # Default self.setColorFunction(_category_color_function) def setColorFunction(self, func): if not callable(func): raise ValueError('Not a function') self._color_function = func # Redraw for widget in self._group.buttons(): z = self._group.id(widget) bcolor = func(z) fcolor = 'white' if _calculate_brightness(bcolor) < 128 else 'black' sheet = 'background-color: %s; color: %s' % (bcolor.name(), fcolor) widget.setStyleSheet(sheet) def colorFunction(self): return self._color_function def setMultipleSelection(self, multiple): self._group.setExclusive(not multiple) def isMultipleSelection(self): return not self._group.exclusive() def setSelection(self, selection): def _uncheckedAll(): for widget in self._group.buttons(): widget.setChecked(False) if selection is None: _uncheckedAll() self.selectionChanged.emit() return if isinstance(selection, (int, six.string_types)): selection = [selection] if not self.isMultipleSelection() and len(selection) > 1: raise ValueError('Multiple selection mode is off. Cannot select more than one element') _uncheckedAll() for z in selection: if isinstance(z, six.string_types): z = get_atomic_number(z) self._group.button(z).setChecked(True) self.selectionChanged.emit() # def selection(self): selection = set() for widget in self._group.buttons(): if widget.isChecked(): selection.add(self._group.id(widget)) if self.isMultipleSelection(): return frozenset(selection) else: if len(selection) > 0: return list(selection)[0] else: return None def selectionSymbol(self): selection = self.selection() if self.isMultipleSelection(): return frozenset(map(get_symbol, selection)) else: if selection is None: return None else: return get_symbol(selection)