示例#1
0
class ObjectExplorer(BaseDialog, SpyderConfigurationAccessor):
    """Object explorer main widget window."""
    CONF_SECTION = 'variable_explorer'

    def __init__(self,
                 obj,
                 name='',
                 expanded=False,
                 resize_to_contents=True,
                 parent=None,
                 attribute_columns=DEFAULT_ATTR_COLS,
                 attribute_details=DEFAULT_ATTR_DETAILS,
                 readonly=None,
                 reset=False):
        """
        Constructor

        :param name: name of the object as it will appear in the root node
        :param expanded: show the first visible root element expanded
        :param resize_to_contents: resize columns to contents ignoring width
            of the attributes
        :param obj: any Python object or variable
        :param attribute_columns: list of AttributeColumn objects that
            define which columns are present in the table and their defaults
        :param attribute_details: list of AttributeDetails objects that define
            which attributes can be selected in the details pane.
        :param reset: If true the persistent settings, such as column widths,
            are reset.
        """
        QDialog.__init__(self, parent=parent)
        self.setAttribute(Qt.WA_DeleteOnClose)

        # Options
        show_callable_attributes = self.get_conf('show_callable_attributes')
        show_special_attributes = self.get_conf('show_special_attributes')

        # Model
        self._attr_cols = attribute_columns
        self._attr_details = attribute_details
        self.readonly = readonly

        self.btn_save_and_close = None
        self.btn_close = None

        self._tree_model = TreeModel(obj,
                                     obj_name=name,
                                     attr_cols=self._attr_cols)

        self._proxy_tree_model = TreeProxyModel(
            show_callable_attributes=show_callable_attributes,
            show_special_attributes=show_special_attributes)

        self._proxy_tree_model.setSourceModel(self._tree_model)
        # self._proxy_tree_model.setSortRole(RegistryTableModel.SORT_ROLE)
        self._proxy_tree_model.setDynamicSortFilter(True)
        # self._proxy_tree_model.setSortCaseSensitivity(Qt.CaseInsensitive)

        # Tree widget
        self.obj_tree = ToggleColumnTreeView()
        self.obj_tree.setAlternatingRowColors(True)
        self.obj_tree.setModel(self._proxy_tree_model)
        self.obj_tree.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.obj_tree.setUniformRowHeights(True)
        self.obj_tree.add_header_context_menu()

        # Views
        self._setup_actions()
        self._setup_menu(show_callable_attributes=show_callable_attributes,
                         show_special_attributes=show_special_attributes)
        self._setup_views()
        if name:
            name = "{} -".format(name)
        self.setWindowTitle("{} {}".format(name, EDITOR_NAME))
        self.setWindowFlags(Qt.Window)

        self._resize_to_contents = resize_to_contents
        self._readViewSettings(reset=reset)

        # Update views with model
        self.toggle_show_special_attribute_action.setChecked(
            show_special_attributes)
        self.toggle_show_callable_action.setChecked(show_callable_attributes)

        # Select first row so that a hidden root node will not be selected.
        first_row_index = self._proxy_tree_model.firstItemIndex()
        self.obj_tree.setCurrentIndex(first_row_index)
        if self._tree_model.inspectedNodeIsVisible or expanded:
            self.obj_tree.expand(first_row_index)

    def get_value(self):
        """Get editor current object state."""
        return self._tree_model.inspectedItem.obj

    def _make_show_column_function(self, column_idx):
        """Creates a function that shows or hides a column."""
        show_column = lambda checked: self.obj_tree.setColumnHidden(
            column_idx, not checked)
        return show_column

    def _setup_actions(self):
        """Creates the main window actions."""
        # Show/hide callable objects
        self.toggle_show_callable_action = QAction(
            _("Show callable attributes"),
            self,
            checkable=True,
            shortcut=QKeySequence("Alt+C"),
            statusTip=_("Shows/hides attributes that are callable "
                        "(functions, methods, etc)"))
        self.toggle_show_callable_action.toggled.connect(
            self._proxy_tree_model.setShowCallables)
        self.toggle_show_callable_action.toggled.connect(
            self.obj_tree.resize_columns_to_contents)

        # Show/hide special attributes
        self.toggle_show_special_attribute_action = QAction(
            _("Show __special__ attributes"),
            self,
            checkable=True,
            shortcut=QKeySequence("Alt+S"),
            statusTip=_("Shows or hides __special__ attributes"))
        self.toggle_show_special_attribute_action.toggled.connect(
            self._proxy_tree_model.setShowSpecialAttributes)
        self.toggle_show_special_attribute_action.toggled.connect(
            self.obj_tree.resize_columns_to_contents)

    def _setup_menu(self,
                    show_callable_attributes=False,
                    show_special_attributes=False):
        """Sets up the main menu."""
        self.tools_layout = QHBoxLayout()

        callable_attributes = create_toolbutton(
            self,
            text=_("Show callable attributes"),
            icon=ima.icon("class"),
            toggled=self._toggle_show_callable_attributes_action)
        callable_attributes.setCheckable(True)
        callable_attributes.setChecked(show_callable_attributes)
        self.tools_layout.addWidget(callable_attributes)

        special_attributes = create_toolbutton(
            self,
            text=_("Show __special__ attributes"),
            icon=ima.icon("private2"),
            toggled=self._toggle_show_special_attributes_action)
        special_attributes.setCheckable(True)
        special_attributes.setChecked(show_special_attributes)
        self.tools_layout.addWidget(special_attributes)

        self.tools_layout.addStretch()

        self.options_button = create_toolbutton(self,
                                                text=_('Options'),
                                                icon=ima.icon('tooloptions'))
        self.options_button.setPopupMode(QToolButton.InstantPopup)

        self.show_cols_submenu = QMenu(self)
        self.options_button.setMenu(self.show_cols_submenu)
        # Don't show menu arrow and remove padding
        if is_dark_interface():
            self.options_button.setStyleSheet(
                ("QToolButton::menu-indicator{image: none;}\n"
                 "QToolButton{padding: 3px;}"))
        else:
            self.options_button.setStyleSheet(
                "QToolButton::menu-indicator{image: none;}")
        self.tools_layout.addWidget(self.options_button)

    @Slot()
    def _toggle_show_callable_attributes_action(self):
        """Toggle show callable atributes action."""
        action_checked = not self.toggle_show_callable_action.isChecked()
        self.toggle_show_callable_action.setChecked(action_checked)
        self.set_conf('show_callable_attributes', action_checked)

    @Slot()
    def _toggle_show_special_attributes_action(self):
        """Toggle show special attributes action."""
        action_checked = (
            not self.toggle_show_special_attribute_action.isChecked())
        self.toggle_show_special_attribute_action.setChecked(action_checked)
        self.set_conf('show_special_attributes', action_checked)

    def _setup_views(self):
        """Creates the UI widgets."""
        self.central_splitter = QSplitter(self, orientation=Qt.Vertical)
        layout = create_plugin_layout(self.tools_layout, self.central_splitter)
        self.setLayout(layout)

        # Stretch last column?
        # It doesn't play nice when columns are hidden and then shown again.
        obj_tree_header = self.obj_tree.header()
        obj_tree_header.setSectionsMovable(True)
        obj_tree_header.setStretchLastSection(False)
        add_actions(self.show_cols_submenu,
                    self.obj_tree.toggle_column_actions_group.actions())

        self.central_splitter.addWidget(self.obj_tree)

        # Bottom pane
        bottom_pane_widget = QWidget()
        bottom_layout = QHBoxLayout()
        bottom_layout.setSpacing(0)
        bottom_layout.setContentsMargins(5, 5, 5, 5)  # left top right bottom
        bottom_pane_widget.setLayout(bottom_layout)
        self.central_splitter.addWidget(bottom_pane_widget)

        group_box = QGroupBox(_("Details"))
        bottom_layout.addWidget(group_box)

        v_group_layout = QVBoxLayout()
        h_group_layout = QHBoxLayout()
        h_group_layout.setContentsMargins(2, 2, 2, 2)  # left top right bottom
        group_box.setLayout(v_group_layout)
        v_group_layout.addLayout(h_group_layout)

        # Radio buttons
        radio_widget = QWidget()
        radio_layout = QVBoxLayout()
        radio_layout.setContentsMargins(0, 0, 0, 0)  # left top right bottom
        radio_widget.setLayout(radio_layout)

        self.button_group = QButtonGroup(self)
        for button_id, attr_detail in enumerate(self._attr_details):
            radio_button = QRadioButton(attr_detail.name)
            radio_layout.addWidget(radio_button)
            self.button_group.addButton(radio_button, button_id)

        self.button_group.buttonClicked[int].connect(
            self._change_details_field)
        self.button_group.button(0).setChecked(True)

        radio_layout.addStretch(1)
        h_group_layout.addWidget(radio_widget)

        # Editor widget
        self.editor = SimpleCodeEditor(self)
        self.editor.setReadOnly(True)
        h_group_layout.addWidget(self.editor)

        # Save and close buttons
        btn_layout = QHBoxLayout()
        btn_layout.addStretch()

        if not self.readonly:
            self.btn_save_and_close = QPushButton(_('Save and Close'))
            self.btn_save_and_close.setDisabled(True)
            self.btn_save_and_close.clicked.connect(self.accept)
            btn_layout.addWidget(self.btn_save_and_close)

        self.btn_close = QPushButton(_('Close'))
        self.btn_close.setAutoDefault(True)
        self.btn_close.setDefault(True)
        self.btn_close.clicked.connect(self.reject)
        btn_layout.addWidget(self.btn_close)
        v_group_layout.addLayout(btn_layout)

        # Splitter parameters
        self.central_splitter.setCollapsible(0, False)
        self.central_splitter.setCollapsible(1, True)
        self.central_splitter.setSizes([500, 320])

        # Connect signals
        # Keep a temporary reference of the selection_model to prevent
        # segfault in PySide.
        # See http://permalink.gmane.org/gmane.comp.lib.qt.pyside.devel/222
        selection_model = self.obj_tree.selectionModel()
        selection_model.currentChanged.connect(self._update_details)

        # Check if the values of the model have been changed
        self._proxy_tree_model.sig_setting_data.connect(
            self.save_and_close_enable)

        self._proxy_tree_model.sig_update_details.connect(
            self._update_details_for_item)

    # End of setup_methods
    def _readViewSettings(self, reset=False):
        """
        Reads the persistent program settings.

        :param reset: If True, the program resets to its default settings.
        """
        pos = QPoint(20, 20)
        window_size = QSize(825, 650)
        details_button_idx = 0

        header = self.obj_tree.header()
        header_restored = False

        if reset:
            logger.debug("Resetting persistent view settings")
        else:
            pos = pos
            window_size = window_size
            details_button_idx = details_button_idx
            #            splitter_state = settings.value("central_splitter/state")
            splitter_state = None
            if splitter_state:
                self.central_splitter.restoreState(splitter_state)
#            header_restored = self.obj_tree.read_view_settings(
#                'table/header_state',
#                settings, reset)
            header_restored = False

        if not header_restored:
            column_sizes = [col.width for col in self._attr_cols]
            column_visible = [col.col_visible for col in self._attr_cols]

            for idx, size in enumerate(column_sizes):
                if not self._resize_to_contents and size > 0:  # Just in case
                    header.resizeSection(idx, size)
                else:
                    header.resizeSections(QHeaderView.ResizeToContents)
                    break

            for idx, visible in enumerate(column_visible):
                elem = self.obj_tree.toggle_column_actions_group.actions()[idx]
                elem.setChecked(visible)

        self.resize(window_size)

        button = self.button_group.button(details_button_idx)
        if button is not None:
            button.setChecked(True)

    @Slot()
    def save_and_close_enable(self):
        """Handle the data change event to enable the save and close button."""
        if self.btn_save_and_close:
            self.btn_save_and_close.setEnabled(True)
            self.btn_save_and_close.setAutoDefault(True)
            self.btn_save_and_close.setDefault(True)

    @Slot(QModelIndex, QModelIndex)
    def _update_details(self, current_index, _previous_index):
        """Shows the object details in the editor given an index."""
        tree_item = self._proxy_tree_model.treeItem(current_index)
        self._update_details_for_item(tree_item)

    def _change_details_field(self, _button_id=None):
        """Changes the field that is displayed in the details pane."""
        # logger.debug("_change_details_field: {}".format(_button_id))
        current_index = self.obj_tree.selectionModel().currentIndex()
        tree_item = self._proxy_tree_model.treeItem(current_index)
        self._update_details_for_item(tree_item)

    @Slot(TreeItem)
    def _update_details_for_item(self, tree_item):
        """Shows the object details in the editor given an tree_item."""
        try:
            # obj = tree_item.obj
            button_id = self.button_group.checkedId()
            assert button_id >= 0, ("No radio button selected. "
                                    "Please report this bug.")
            attr_details = self._attr_details[button_id]
            data = attr_details.data_fn(tree_item)
            self.editor.setPlainText(data)
            self.editor.setWordWrapMode(attr_details.line_wrap)
            self.editor.setup_editor(
                font=get_font(font_size_delta=DEFAULT_SMALL_DELTA),
                show_blanks=False,
                color_scheme=CONF.get('appearance', 'selected'),
                scroll_past_end=False,
            )
            self.editor.set_text(data)

            if attr_details.name == 'Source code':
                self.editor.set_language('Python')
            else:
                self.editor.set_language('Rst')

        except Exception as ex:
            self.editor.setStyleSheet("color: red;")
            stack_trace = traceback.format_exc()
            self.editor.setPlainText("{}\n\n{}".format(ex, stack_trace))
            self.editor.setWordWrapMode(
                QTextOption.WrapAtWordBoundaryOrAnywhere)

    @classmethod
    def create_explorer(cls, *args, **kwargs):
        """
        Creates and shows and ObjectExplorer window.

        The *args and **kwargs will be passed to the ObjectExplorer constructor

        A (class attribute) reference to the browser window is kept to prevent
        it from being garbage-collected.
        """
        object_explorer = cls(*args, **kwargs)
        object_explorer.exec_()
        return object_explorer
示例#2
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()
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)
示例#4
0
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)
示例#5
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()
示例#6
0
class NappingDialog(QDialog):
    class SelectionMode(IntEnum):
        FILE = 0
        DIR = 1

    class MatchingStrategy(Enum):
        ALPHABETICAL = 'Alphabetical order'
        FILENAME = 'Filename (without extension)'
        REGEX = 'Python regular expression (RegEx)'

    class TransformType(Enum):
        EUCLIDEAN = 'Euclidean (rotation, translation)'
        SIMILARITY = 'Similarity (Euclidean transform + uniform scaling)'
        AFFINE = 'Affine (Similarity transform + non-uniform scaling + shear)'

    SELECTION_MODE_SETTING = 'registrationDialog/selectionMode'
    MATCHING_STRATEGY_SETTING = 'registrationDialog/matchingStrategy'
    SOURCE_IMAGES_SETTING = 'registrationDialog/sourceImages'
    SOURCE_REGEX_SETTING = 'registrationDialog/sourceRegex'
    TARGET_IMAGES_SETTING = 'registrationDialog/targetImages'
    TARGET_REGEX_SETTING = 'registrationDialog/targetRegex'
    CONTROL_POINTS_DEST_SETTING = 'registrationDialog/controlPointsDest'
    JOINT_TRANSFORM_DEST_SETTING = 'registrationDialog/jointTransformDest'
    TRANSFORM_TYPE_SETTING = 'registrationDialog/transformType'
    SOURCE_COORDS_SETTING = 'registrationDialog/sourceCoords'
    SOURCE_COORDS_REGEX_SETTING = 'registrationDialog/sourceCoordsRegex'
    TRANSFORMED_COORDS_DEST_SETTING = 'registrationDialog/transformedCoordsDest'
    PRE_TRANSFORM_SETTING = 'registrationDialog/preTransformFile'
    POST_TRANSFORM_SETTING = 'registrationDialog/postTransformFile'

    DEFAULT_SELECTION_MODE = SelectionMode.FILE
    DEFAULT_MATCHING_STRATEGY = MatchingStrategy.FILENAME
    DEFAULT_SOURCE_IMAGES = ''
    DEFAULT_SOURCE_REGEX = ''
    DEFAULT_TARGET_IMAGES = ''
    DEFAULT_TARGET_REGEX = ''
    DEFAULT_CONTROL_POINTS_DEST = ''
    DEFAULT_JOINT_TRANSFORM_DEST = ''
    DEFAULT_TRANSFORM_TYPE = TransformType.SIMILARITY
    DEFAULT_SOURCE_COORDS = ''
    DEFAULT_SOURCE_COORDS_REGEX = ''
    DEFAULT_TRANSFORMED_COORDS_DEST = ''
    DEFAULT_PRE_TRANSFORM = ''
    DEFAULT_POST_TRANSFORM = ''

    def __init__(self, settings: QSettings, parent: Optional[QObject] = None):
        # noinspection PyArgumentList
        super(NappingDialog, self).__init__(parent)

        checked_selection_mode = NappingDialog.SelectionMode(
            int(
                settings.value(
                    self.SELECTION_MODE_SETTING,
                    defaultValue=self.DEFAULT_SELECTION_MODE.value)))
        self._file_selection_mode_button = QRadioButton(
            'Single file pair', self)
        self._file_selection_mode_button.setChecked(
            checked_selection_mode == NappingDialog.SelectionMode.FILE)
        self._dir_selection_mode_button = QRadioButton(
            'Directories (multiple file pairs)', self)
        self._dir_selection_mode_button.setChecked(
            checked_selection_mode == NappingDialog.SelectionMode.DIR)
        self._selection_mode_buttons_group = QButtonGroup(self)
        self._selection_mode_buttons_group.addButton(
            self._file_selection_mode_button, NappingDialog.SelectionMode.FILE)
        self._selection_mode_buttons_group.addButton(
            self._dir_selection_mode_button, NappingDialog.SelectionMode.DIR)
        # noinspection PyUnresolvedReferences
        self._selection_mode_buttons_group.buttonClicked.connect(
            lambda _: self.refresh())

        matching_strategy_combo_box_current_text = str(
            settings.value(self.MATCHING_STRATEGY_SETTING,
                           defaultValue=self.DEFAULT_MATCHING_STRATEGY.value))
        self._matching_strategy_combo_box = QComboBox(self)
        self._matching_strategy_combo_box.addItems(
            [x.value for x in NappingDialog.MatchingStrategy])
        self._matching_strategy_combo_box.setCurrentText(
            matching_strategy_combo_box_current_text)
        # noinspection PyUnresolvedReferences
        self._matching_strategy_combo_box.currentIndexChanged.connect(
            lambda _: self.refresh())

        source_images_file_line_edit_text = str(
            settings.value(self.SOURCE_IMAGES_SETTING,
                           defaultValue=self.DEFAULT_SOURCE_IMAGES))
        self._source_images_file_line_edit = FileLineEdit(check_exists=True,
                                                          parent=self)
        self._source_images_file_line_edit.file_dialog.setWindowTitle(
            'Select source image(s)')
        self._source_images_file_line_edit.setText(
            source_images_file_line_edit_text)
        # noinspection PyUnresolvedReferences
        self._source_images_file_line_edit.textChanged.connect(
            lambda text: self.refresh(text))

        source_regex_line_edit_text = str(
            settings.value(self.SOURCE_REGEX_SETTING,
                           defaultValue=self.DEFAULT_SOURCE_REGEX))
        self._source_regex_label = QLabel('        RegEx:')
        self._source_regex_line_edit = QLineEdit(self)
        self._source_regex_line_edit.setText(source_regex_line_edit_text)
        # noinspection PyUnresolvedReferences
        self._source_regex_line_edit.textChanged.connect(
            lambda _: self.refresh())

        target_images_file_line_edit_text = str(
            settings.value(self.TARGET_IMAGES_SETTING,
                           defaultValue=self.DEFAULT_TARGET_IMAGES))
        self._target_images_file_line_edit = FileLineEdit(check_exists=True,
                                                          parent=self)
        self._target_images_file_line_edit.file_dialog.setWindowTitle(
            'Select target image(s)')
        self._target_images_file_line_edit.setText(
            target_images_file_line_edit_text)
        # noinspection PyUnresolvedReferences
        self._target_images_file_line_edit.textChanged.connect(
            lambda text: self.refresh(text))

        target_regex_line_edit_text = str(
            settings.value(self.TARGET_REGEX_SETTING,
                           defaultValue=self.DEFAULT_TARGET_REGEX))
        self._target_regex_label = QLabel('        RegEx:')
        self._target_regex_line_edit = QLineEdit(self)
        self._target_regex_line_edit.setText(target_regex_line_edit_text)
        # noinspection PyUnresolvedReferences
        self._target_regex_line_edit.textChanged.connect(
            lambda _: self.refresh())

        control_points_dest_file_line_edit_text = str(
            settings.value(self.CONTROL_POINTS_DEST_SETTING,
                           defaultValue=self.DEFAULT_CONTROL_POINTS_DEST))
        self._control_points_dest_file_line_edit = FileLineEdit(parent=self)
        self._control_points_dest_file_line_edit.file_dialog.setWindowTitle(
            'Select control points destination')
        self._control_points_dest_file_line_edit.setText(
            control_points_dest_file_line_edit_text)
        # noinspection PyUnresolvedReferences
        self._control_points_dest_file_line_edit.textChanged.connect(
            lambda text: self.refresh(text))

        joint_transform_dest_file_line_edit_text = str(
            settings.value(self.JOINT_TRANSFORM_DEST_SETTING,
                           defaultValue=self.DEFAULT_JOINT_TRANSFORM_DEST))
        self._joint_transform_dest_file_line_edit = FileLineEdit(parent=self)
        self._joint_transform_dest_file_line_edit.file_dialog.setWindowTitle(
            'Select joint transform destination')
        self._joint_transform_dest_file_line_edit.setText(
            joint_transform_dest_file_line_edit_text)
        # noinspection PyUnresolvedReferences
        self._joint_transform_dest_file_line_edit.textChanged.connect(
            lambda text: self.refresh(text))

        transform_type_combo_box_current_text = str(
            settings.value(self.TRANSFORM_TYPE_SETTING,
                           defaultValue=self.DEFAULT_TRANSFORM_TYPE))
        self._transform_type_combo_box = QComboBox(self)
        self._transform_type_combo_box.addItems(
            [x.value for x in NappingDialog.TransformType])
        self._transform_type_combo_box.setCurrentText(
            transform_type_combo_box_current_text)
        # noinspection PyUnresolvedReferences
        self._transform_type_combo_box.currentIndexChanged.connect(
            lambda _: self.refresh())

        source_coords_file_line_edit_text = str(
            settings.value(self.SOURCE_COORDS_SETTING,
                           defaultValue=self.DEFAULT_SOURCE_COORDS))
        self._source_coords_file_line_edit = FileLineEdit(check_exists=True,
                                                          parent=self)
        self._source_coords_file_line_edit.file_dialog.setWindowTitle(
            'Select source coordinates')
        self._source_coords_file_line_edit.setText(
            source_coords_file_line_edit_text)
        # noinspection PyUnresolvedReferences
        self._source_coords_file_line_edit.textChanged.connect(
            lambda text: self.refresh(text))

        source_coords_regex_line_edit_text = str(
            settings.value(self.SOURCE_COORDS_REGEX_SETTING,
                           defaultValue=self.DEFAULT_SOURCE_COORDS_REGEX))
        self._source_coords_regex_label = QLabel('        RegEx:')
        self._source_coords_regex_line_edit = QLineEdit(self)
        self._source_coords_regex_line_edit.setText(
            source_coords_regex_line_edit_text)
        # noinspection PyUnresolvedReferences
        self._source_coords_regex_line_edit.textChanged.connect(
            lambda _: self.refresh())

        transformed_coords_dest_file_line_edit_text = str(
            settings.value(self.TRANSFORMED_COORDS_DEST_SETTING,
                           defaultValue=self.DEFAULT_TRANSFORMED_COORDS_DEST))
        self._transformed_coords_dest_file_line_edit = FileLineEdit(
            parent=self)
        self._transformed_coords_dest_file_line_edit.file_dialog.setWindowTitle(
            'Select transformed coordinates destination')
        self._transformed_coords_dest_file_line_edit.setText(
            transformed_coords_dest_file_line_edit_text)
        # noinspection PyUnresolvedReferences
        self._transformed_coords_dest_file_line_edit.textChanged.connect(
            lambda text: self.refresh(text))

        pre_transform_file_line_edit_text = str(
            settings.value(self.PRE_TRANSFORM_SETTING,
                           defaultValue=self.DEFAULT_PRE_TRANSFORM))
        self._pre_transform_file_line_edit = FileLineEdit(parent=self)
        self._pre_transform_file_line_edit.file_dialog.setWindowTitle(
            'Select pre-transform')
        self._pre_transform_file_line_edit.setText(
            pre_transform_file_line_edit_text)
        self._pre_transform_file_line_edit.file_dialog.setFileMode(
            QFileDialog.ExistingFile)
        self._pre_transform_file_line_edit.file_dialog.setNameFilter(
            'Numpy files (*.npy)')
        # noinspection PyUnresolvedReferences
        self._pre_transform_file_line_edit.textChanged.connect(
            lambda text: self.refresh(text))

        post_transform_file_line_edit_text = str(
            settings.value(self.POST_TRANSFORM_SETTING,
                           defaultValue=self.DEFAULT_POST_TRANSFORM))
        self._post_transform_file_line_edit = FileLineEdit(parent=self)
        self._post_transform_file_line_edit.file_dialog.setWindowTitle(
            'Select post-transform')
        self._post_transform_file_line_edit.file_dialog.setFileMode(
            QFileDialog.ExistingFile)
        self._post_transform_file_line_edit.file_dialog.setNameFilter(
            'Numpy files (*.npy)')
        self._post_transform_file_line_edit.setText(
            post_transform_file_line_edit_text)
        # noinspection PyUnresolvedReferences
        self._post_transform_file_line_edit.textChanged.connect(
            lambda text: self.refresh(text))

        self._button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self)
        # noinspection PyUnresolvedReferences
        self._button_box.rejected.connect(self.reject)

        # noinspection PyUnresolvedReferences
        @self._button_box.accepted.connect
        def on_button_box_accepted():
            settings.setValue(self.SELECTION_MODE_SETTING,
                              self.selection_mode.value)
            settings.setValue(self.SOURCE_IMAGES_SETTING,
                              str(self.source_images_path))
            settings.setValue(self.SOURCE_REGEX_SETTING, self.source_regex)
            settings.setValue(self.TARGET_IMAGES_SETTING,
                              str(self.target_images_path))
            settings.setValue(self.TARGET_REGEX_SETTING, self.target_regex)
            settings.setValue(self.CONTROL_POINTS_DEST_SETTING,
                              str(self.control_points_dest_path))
            settings.setValue(self.JOINT_TRANSFORM_DEST_SETTING,
                              str(self.joint_transform_dest_path))
            settings.setValue(self.TRANSFORM_TYPE_SETTING,
                              self.transform_type.value)
            settings.setValue(self.MATCHING_STRATEGY_SETTING,
                              self.matching_strategy.value)
            settings.setValue(self.SOURCE_COORDS_SETTING,
                              str(self.source_coords_path or ''))
            settings.setValue(self.SOURCE_COORDS_REGEX_SETTING,
                              self.source_coords_regex)
            settings.setValue(self.TRANSFORMED_COORDS_DEST_SETTING,
                              str(self.transformed_coords_dest_path or ''))
            settings.setValue(self.PRE_TRANSFORM_SETTING,
                              str(self.pre_transform_path or ''))
            settings.setValue(self.POST_TRANSFORM_SETTING,
                              str(self.post_transform_path or ''))
            settings.sync()
            self.accept()

        required_group_box = QGroupBox(self)
        required_group_box_layout = QFormLayout()
        required_group_box_layout.setLabelAlignment(Qt.AlignLeft)
        required_group_box_layout.setRowWrapPolicy(QFormLayout.DontWrapRows)
        required_group_box_layout.setFieldGrowthPolicy(
            QFormLayout.AllNonFixedFieldsGrow)
        required_group_box_layout.addRow('Source image(s):',
                                         self._source_images_file_line_edit)
        required_group_box_layout.addRow(self._source_regex_label,
                                         self._source_regex_line_edit)
        required_group_box_layout.addRow('Target image(s):',
                                         self._target_images_file_line_edit)
        required_group_box_layout.addRow(self._target_regex_label,
                                         self._target_regex_line_edit)
        required_group_box_layout.addRow(
            'Control points dest.:', self._control_points_dest_file_line_edit)
        required_group_box_layout.addRow(
            'Joint transform dest.:',
            self._joint_transform_dest_file_line_edit)
        required_group_box_layout.addRow('Transform type:',
                                         self._transform_type_combo_box)
        required_group_box.setLayout(required_group_box_layout)

        optional_group_box = QGroupBox(self)
        optional_group_box_layout = QFormLayout()
        optional_group_box_layout.setLabelAlignment(Qt.AlignLeft)
        optional_group_box_layout.setRowWrapPolicy(QFormLayout.DontWrapRows)
        optional_group_box_layout.setFieldGrowthPolicy(
            QFormLayout.AllNonFixedFieldsGrow)
        optional_group_box_layout.addRow('Source coordinates:',
                                         self._source_coords_file_line_edit)
        optional_group_box_layout.addRow(self._source_coords_regex_label,
                                         self._source_coords_regex_line_edit)
        optional_group_box_layout.addRow(
            'Transformed coord. dest.:',
            self._transformed_coords_dest_file_line_edit)
        optional_group_box_layout.addRow('Pre-transform:',
                                         self._pre_transform_file_line_edit)
        optional_group_box_layout.addRow('Post-transform:',
                                         self._post_transform_file_line_edit)
        optional_group_box.setLayout(optional_group_box_layout)

        layout = QVBoxLayout()
        mode_layout = QHBoxLayout()
        # noinspection PyArgumentList
        mode_layout.addWidget(self._file_selection_mode_button)
        # noinspection PyArgumentList
        mode_layout.addWidget(self._dir_selection_mode_button)
        # noinspection PyArgumentList
        mode_layout.addWidget(self._matching_strategy_combo_box)
        mode_layout.addStretch()
        layout.addLayout(mode_layout)
        # noinspection PyArgumentList
        layout.addWidget(required_group_box)
        # noinspection PyArgumentList
        layout.addWidget(optional_group_box)
        # noinspection PyArgumentList
        layout.addWidget(self._button_box)
        self.setLayout(layout)

        self.setWindowTitle('Control point matching')
        self.setMinimumWidth(600)
        self.refresh()

    @property
    def selection_mode(self) -> Optional['NappingDialog.SelectionMode']:
        selection_mode_value = self._selection_mode_buttons_group.checkedId()
        if selection_mode_value >= 0:
            return NappingDialog.SelectionMode(selection_mode_value)
        return None

    @property
    def matching_strategy(self) -> 'NappingDialog.MatchingStrategy':
        return NappingDialog.MatchingStrategy(
            self._matching_strategy_combo_box.currentText())

    @property
    def source_images_path(self) -> Optional[Path]:
        return self._source_images_file_line_edit.path

    @property
    def source_regex(self) -> str:
        return self._source_regex_line_edit.text()

    @property
    def target_images_path(self) -> Optional[Path]:
        return self._target_images_file_line_edit.path

    @property
    def target_regex(self) -> str:
        return self._target_regex_line_edit.text()

    @property
    def control_points_dest_path(self) -> Optional[Path]:
        return self._control_points_dest_file_line_edit.path

    @property
    def joint_transform_dest_path(self) -> Optional[Path]:
        return self._joint_transform_dest_file_line_edit.path

    @property
    def transform_type(self) -> 'NappingDialog.TransformType':
        return NappingDialog.TransformType(
            self._transform_type_combo_box.currentText())

    @property
    def transform_class(self) -> Type[ProjectiveTransform]:
        return {
            NappingDialog.TransformType.EUCLIDEAN: EuclideanTransform,
            NappingDialog.TransformType.SIMILARITY: SimilarityTransform,
            NappingDialog.TransformType.AFFINE: AffineTransform
        }[self.transform_type]

    @property
    def source_coords_path(self) -> Optional[Path]:
        return self._source_coords_file_line_edit.path

    @property
    def source_coords_regex(self) -> str:
        return self._source_coords_regex_line_edit.text()

    @property
    def transformed_coords_dest_path(self) -> Optional[Path]:
        return self._transformed_coords_dest_file_line_edit.path

    @property
    def pre_transform_path(self) -> Optional[Path]:
        return self._pre_transform_file_line_edit.path

    @property
    def post_transform_path(self) -> Optional[Path]:
        return self._post_transform_file_line_edit.path

    @property
    def is_valid(self) -> bool:
        if self.selection_mode == NappingDialog.SelectionMode.FILE:
            if self.source_images_path is None or not self.source_images_path.is_file(
            ):
                return False
            if self.target_images_path is None or not self.target_images_path.is_file(
            ):
                return False
            if self.control_points_dest_path is None or self.control_points_dest_path.is_dir(
            ):
                return False
            if self.joint_transform_dest_path is None or self.joint_transform_dest_path.is_dir(
            ):
                return False
            if self.source_coords_path is not None and not self.source_coords_path.is_file(
            ):
                return False
            if self.transformed_coords_dest_path is not None and self.control_points_dest_path.is_dir(
            ):
                return False
        elif self.selection_mode == NappingDialog.SelectionMode.DIR:
            if self.source_images_path is None or not self.source_images_path.is_dir(
            ):
                return False
            if self.target_images_path is None or not self.target_images_path.is_dir(
            ):
                return False
            if self.control_points_dest_path is None or self.control_points_dest_path.is_file(
            ):
                return False
            if self.joint_transform_dest_path is None or self.joint_transform_dest_path.is_file(
            ):
                return False
            if self.source_coords_path is not None and not self.source_coords_path.is_dir(
            ):
                return False
            if self.transformed_coords_dest_path is not None and self.control_points_dest_path.is_file(
            ):
                return False
            if self.matching_strategy == NappingDialog.MatchingStrategy.REGEX:
                if not self.source_regex:
                    return False
                if not self.target_regex:
                    return False
                if self.source_coords_path is not None and not self.source_coords_regex:
                    return False
        else:
            return False
        if self.pre_transform_path is not None and not self.pre_transform_path.is_file(
        ):
            return False
        if self.post_transform_path is not None and not self.post_transform_path.is_file(
        ):
            return False
        if bool(self.source_coords_path) != bool(
                self.transformed_coords_dest_path):
            return False
        if (bool(self.pre_transform_path) or bool(self.post_transform_path)
            ) and not bool(self.source_coords_path):
            return False
        unique_paths = {
            self.source_images_path, self.target_images_path,
            self.control_points_dest_path, self.joint_transform_dest_path
        }
        if self.source_coords_path is not None and self.transformed_coords_dest_path is not None:
            unique_paths.update(
                {self.source_coords_path, self.transformed_coords_dest_path})
            if len(unique_paths) != 6:
                return False
        elif len(unique_paths) != 4:
            return False
        return True

    def refresh(self, last_path: Union[str, Path, None] = None):
        if last_path:
            directory = str(Path(last_path).parent)
            self._source_images_file_line_edit.file_dialog.setDirectory(
                directory)
            self._target_images_file_line_edit.file_dialog.setDirectory(
                directory)
            self._control_points_dest_file_line_edit.file_dialog.setDirectory(
                directory)
            self._joint_transform_dest_file_line_edit.file_dialog.setDirectory(
                directory)
            self._source_coords_file_line_edit.file_dialog.setDirectory(
                directory)
            self._transformed_coords_dest_file_line_edit.file_dialog.setDirectory(
                directory)
            self._pre_transform_file_line_edit.file_dialog.setDirectory(
                directory)
            self._post_transform_file_line_edit.file_dialog.setDirectory(
                directory)

        if self.selection_mode in (NappingDialog.SelectionMode.FILE,
                                   NappingDialog.SelectionMode.DIR):
            if self.selection_mode == NappingDialog.SelectionMode.FILE:
                any_file_mode = QFileDialog.AnyFile
                existing_file_mode = QFileDialog.ExistingFile
                control_points_name_filter = 'CSV files (*.csv)'
                control_points_default_suffix = '.csv'
                transform_name_filter = 'Numpy files (*.npy)'
                transform_default_suffix = '.npy'
                source_coords_name_filter = transformed_coords_name_filter = 'CSV files (*.csv)'
                source_coords_default_suffix = transformed_coords_default_suffix = '.csv'
                show_dirs_only = False
            else:
                any_file_mode = QFileDialog.Directory
                existing_file_mode = QFileDialog.Directory
                control_points_name_filter = None
                control_points_default_suffix = None
                transform_name_filter = None
                transform_default_suffix = None
                source_coords_name_filter = transformed_coords_name_filter = None
                source_coords_default_suffix = transformed_coords_default_suffix = None
                show_dirs_only = True

            self._source_images_file_line_edit.file_dialog.setFileMode(
                existing_file_mode)
            self._source_images_file_line_edit.file_dialog.setOption(
                QFileDialog.ShowDirsOnly, show_dirs_only)

            self._target_images_file_line_edit.file_dialog.setFileMode(
                existing_file_mode)
            self._target_images_file_line_edit.file_dialog.setOption(
                QFileDialog.ShowDirsOnly, show_dirs_only)

            self._control_points_dest_file_line_edit.file_dialog.setFileMode(
                any_file_mode)
            self._control_points_dest_file_line_edit.file_dialog.setNameFilter(
                control_points_name_filter)
            self._control_points_dest_file_line_edit.file_dialog.setDefaultSuffix(
                control_points_default_suffix)
            self._control_points_dest_file_line_edit.file_dialog.setOption(
                QFileDialog.ShowDirsOnly, show_dirs_only)

            self._joint_transform_dest_file_line_edit.file_dialog.setFileMode(
                any_file_mode)
            self._joint_transform_dest_file_line_edit.file_dialog.setNameFilter(
                transform_name_filter)
            self._joint_transform_dest_file_line_edit.file_dialog.setDefaultSuffix(
                transform_default_suffix)
            self._joint_transform_dest_file_line_edit.file_dialog.setOption(
                QFileDialog.ShowDirsOnly, show_dirs_only)

            self._source_coords_file_line_edit.file_dialog.setFileMode(
                existing_file_mode)
            self._source_coords_file_line_edit.file_dialog.setNameFilter(
                source_coords_name_filter)
            self._source_coords_file_line_edit.file_dialog.setDefaultSuffix(
                source_coords_default_suffix)
            self._source_coords_file_line_edit.file_dialog.setOption(
                QFileDialog.ShowDirsOnly, show_dirs_only)

            self._transformed_coords_dest_file_line_edit.file_dialog.setFileMode(
                any_file_mode)
            self._transformed_coords_dest_file_line_edit.file_dialog.setNameFilter(
                transformed_coords_name_filter)
            self._transformed_coords_dest_file_line_edit.file_dialog.setDefaultSuffix(
                transformed_coords_default_suffix)
            self._transformed_coords_dest_file_line_edit.file_dialog.setOption(
                QFileDialog.ShowDirsOnly, show_dirs_only)

        if self.selection_mode == NappingDialog.SelectionMode.DIR:
            self._matching_strategy_combo_box.setEnabled(True)
        else:
            self._matching_strategy_combo_box.setEnabled(False)

        dir_selection_mode = (
            self.selection_mode == NappingDialog.SelectionMode.DIR)
        regex_matching_strategy = (
            self.matching_strategy == NappingDialog.MatchingStrategy.REGEX)
        self._source_regex_label.setEnabled(dir_selection_mode
                                            and regex_matching_strategy)
        self._source_regex_line_edit.setEnabled(dir_selection_mode
                                                and regex_matching_strategy)
        self._target_regex_label.setEnabled(dir_selection_mode
                                            and regex_matching_strategy)
        self._target_regex_line_edit.setEnabled(dir_selection_mode
                                                and regex_matching_strategy)
        self._source_coords_regex_label.setEnabled(dir_selection_mode
                                                   and regex_matching_strategy)
        self._source_coords_regex_line_edit.setEnabled(
            dir_selection_mode and regex_matching_strategy)

        self._button_box.button(QDialogButtonBox.Ok).setEnabled(self.is_valid)