Exemple #1
0
    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)
Exemple #3
0
 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)
Exemple #5
0
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()))
Exemple #6
0
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)
Exemple #7
0
    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()
Exemple #8
0
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
Exemple #9
0
    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()
Exemple #10
0
    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")
Exemple #11
0
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)
Exemple #12
0
    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)
Exemple #13
0
    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)
Exemple #14
0
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
Exemple #15
0
    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)
Exemple #16
0
    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()
Exemple #17
0
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()
Exemple #18
0
    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)
Exemple #19
0
    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)
Exemple #20
0
    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)
Exemple #21
0
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,
        )
Exemple #22
0
    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")
Exemple #23
0
    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)
Exemple #24
0
    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)
Exemple #25
0
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)
Exemple #26
0
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()
Exemple #27
0
    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)
Exemple #28
0
    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)
Exemple #29
0
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()
Exemple #30
0
    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)
Exemple #31
0
    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)
Exemple #32
0
    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)
Exemple #33
0
    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)
Exemple #35
0
    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)
Exemple #36
0
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()
Exemple #37
0
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()
    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()
Exemple #39
0
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)