コード例 #1
0
    def createIntegerLineEdit(self, minimum=None, maximum=None, placeholder=""):
        line_edit = ClearableLineEdit(placeholder=placeholder)
        validator = QIntValidator()

        if minimum is not None:
            validator.setBottom(minimum)

        if maximum is not None:
            validator.setTop(maximum)

        line_edit.setValidator(validator)
        return line_edit
コード例 #2
0
ファイル: dlg_load_mask.py プロジェクト: mrakitin/PyXRF
class DialogLoadMask(QDialog):
    """
    Dialog box for selecting spatial ROI and mask.

    Typical use:

    default_dir = "/home/user/data"
    n_rows, n_columns = 15, 20  # Some values

    # Values that are changed by the dialog box
    roi = (2, 3, 11, 9)
    use_roi = True
    mask_f_path = ""
    use_mask = False

    dlg = DialogLoadMask()
    dlg.set_image_size(n_rows=n_rows, n_columns=n_columns)
    dlg.set_roi(row_start=roi[0], column_start=roi[1], row_end=roi[2], column_end=roi[3])
    dlg.set_roi_active(use_roi)

    dlg.set_default_directory(default_dir)
    dlg.set_mask_file_path(mask_f_path)
    dlg.set_mask_file_active(use_mask)

    if dlg.exec() == QDialog.Accepted:
        # If success, then read the values back. Discard changes if rejected.
        roi = dlg.get_roi()
        use_roi = dlg.get_roi_active()
        mask_f_path = dlg.get_mask_file_path()
        use_mask = dlg.get_mask_file_active()
    """
    def __init__(self):

        super().__init__()

        self._validation_enabled = False

        self.resize(500, 300)
        self.setWindowTitle("Load Mask or Select ROI")

        # Initialize variables used with ROI selection group
        self._n_rows = 0
        self._n_columns = 0
        self._roi_active = False
        self._row_start = -1
        self._column_start = -1
        self._row_end = -1
        self._column_end = -1
        # ... with Mask group
        self._mask_active = False
        self._mask_file_path = ""
        self._default_directory = ""

        # Fields for entering spatial ROI coordinates
        self.validator_rows = QIntValidator()
        self.validator_rows.setBottom(1)
        self.validator_cols = QIntValidator()
        self.validator_cols.setBottom(1)

        self.le_roi_start_row = LineEditExtended()
        self.le_roi_start_row.setValidator(self.validator_rows)
        self.le_roi_start_row.editingFinished.connect(
            self.le_roi_start_row_editing_finished)
        self.le_roi_start_row.textChanged.connect(
            self.le_roi_start_row_text_changed)
        self.le_roi_start_col = LineEditExtended()
        self.le_roi_start_col.setValidator(self.validator_cols)
        self.le_roi_start_col.editingFinished.connect(
            self.le_roi_start_col_editing_finished)
        self.le_roi_start_col.textChanged.connect(
            self.le_roi_start_col_text_changed)
        self.le_roi_end_row = LineEditExtended()
        self.le_roi_end_row.setValidator(self.validator_rows)
        self.le_roi_end_row.editingFinished.connect(
            self.le_roi_end_row_editing_finished)
        self.le_roi_end_row.textChanged.connect(
            self.le_roi_end_row_text_changed)
        self.le_roi_end_col = LineEditExtended()
        self.le_roi_end_col.setValidator(self.validator_cols)
        self.le_roi_end_col.editingFinished.connect(
            self.le_roi_end_col_editing_finished)
        self.le_roi_end_col.textChanged.connect(
            self.le_roi_end_col_text_changed)
        self._text_map_size_base = "   * Map size: "
        self.label_map_size = QLabel(self._text_map_size_base + "not set")

        # Group box for spatial ROI selection
        self.gb_roi = QGroupBox("Select ROI (in pixels)")
        set_tooltip(
            self.gb_roi,
            "Select rectangular <b>spatial ROI</b>. If <b>mask</b> is "
            "loaded, then ROI is applied to the masked data.",
        )
        self.gb_roi.setCheckable(True)
        self.gb_roi.toggled.connect(self.gb_roi_toggled)
        self.gb_roi.setChecked(self._roi_active)
        vbox = QVBoxLayout()
        grid = QGridLayout()
        grid.addWidget(QLabel("Start position(*):"), 0, 0)
        grid.addWidget(QLabel("row"), 0, 1)
        grid.addWidget(self.le_roi_start_row, 0, 2)
        grid.addWidget(QLabel("column"), 0, 3)
        grid.addWidget(self.le_roi_start_col, 0, 4)
        grid.addWidget(QLabel("End position(*):"), 1, 0)
        grid.addWidget(QLabel("row"), 1, 1)
        grid.addWidget(self.le_roi_end_row, 1, 2)
        grid.addWidget(QLabel("column"), 1, 3)
        grid.addWidget(self.le_roi_end_col, 1, 4)
        vbox.addLayout(grid)
        vbox.addWidget(self.label_map_size)
        self.gb_roi.setLayout(vbox)

        # Widgets for loading mask
        self.pb_load_mask = QPushButton("Load Mask ...")
        self.pb_load_mask.clicked.connect(self.pb_load_mask_clicked)
        self._le_load_mask_default_text = "select 'mask' file"
        self.le_load_mask = LineEditReadOnly(self._le_load_mask_default_text)

        # Group box for setting mask
        self.gb_mask = QGroupBox("Set mask")
        set_tooltip(
            self.gb_mask,
            "Load <b>mask</b> from file. Active pixels in the mask are "
            "represented by positive integers. If <b>spatial ROI</b> is "
            "selected, then it is applied to the masked data.",
        )
        self.gb_mask.setCheckable(True)
        self.gb_mask.toggled.connect(self.gb_mask_toggled)
        self.gb_mask.setChecked(self._mask_active)
        hbox = QHBoxLayout()
        hbox.addWidget(self.pb_load_mask)
        hbox.addWidget(self.le_load_mask)
        self.gb_mask.setLayout(hbox)

        # Yes/No button box
        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                           | QDialogButtonBox.Cancel)
        self.button_ok = self.button_box.button(QDialogButtonBox.Ok)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)

        vbox = QVBoxLayout()
        vbox.addWidget(self.gb_roi)
        vbox.addStretch(1)
        vbox.addWidget(self.gb_mask)
        vbox.addStretch(2)
        vbox.addWidget(self.button_box)
        self.setLayout(vbox)

        self._validation_enabled = True
        self._validate_all_widgets()

    def _compute_home_directory(self):
        dir_name = "."
        if self._default_directory:
            dir_name = self._default_directory
        if self._mask_file_path:
            d, _ = os.path.split(self._mask_file_path)
            dir_name = d if d else dir_name
        return dir_name

    def pb_load_mask_clicked(self):
        dir_name = self._compute_home_directory()
        file_name = QFileDialog.getOpenFileName(self, "Load Mask From File",
                                                dir_name, "All (*)")
        file_name = file_name[0]
        if file_name:
            self._mask_file_path = file_name
            self._show_mask_file_path()
            logger.debug(f"Mask file is selected: '{file_name}'")

    def gb_roi_toggled(self, state):
        self._roi_activate(state)

    def _read_le_roi_value(self, line_edit, v_default):
        """
        Attempt to read value from line edit box as int, return `v_default` if not successful.

        Parameters
        ----------
        line_edit: QLineEdit
            reference to QLineEdit object
        v_default: int
            default value returned if the value read from edit box is incorrect
        """
        try:
            val = int(line_edit.text())
            if val < 1:
                raise Exception()
        except Exception:
            val = v_default
        return val

    def le_roi_start_row_editing_finished(self):
        self._row_start = self._read_le_roi_value(self.le_roi_start_row,
                                                  self._row_start + 1) - 1

    def le_roi_end_row_editing_finished(self):
        self._row_end = self._read_le_roi_value(self.le_roi_end_row,
                                                self._row_end)

    def le_roi_start_col_editing_finished(self):
        self._column_start = self._read_le_roi_value(
            self.le_roi_start_col, self._column_start + 1) - 1

    def le_roi_end_col_editing_finished(self):
        self._column_end = self._read_le_roi_value(self.le_roi_end_col,
                                                   self._column_end)

    def le_roi_start_row_text_changed(self):
        self._validate_all_widgets()

    def le_roi_end_row_text_changed(self):
        self._validate_all_widgets()

    def le_roi_start_col_text_changed(self):
        self._validate_all_widgets()

    def le_roi_end_col_text_changed(self):
        self._validate_all_widgets()

    def gb_mask_toggled(self, state):
        self._mask_file_activate(state)

    def _validate_all_widgets(self):
        """
        Validate the values and state of all widgets, update the 'valid' state
        of the widgets and enable/disable Ok button.
        """

        if not self._validation_enabled:
            return

        # Check if all fields have valid input values
        def _check_valid_input(line_edit, flag_valid):
            val = self._read_le_roi_value(line_edit, -1)
            state = val > 0
            line_edit.setValid(state)
            flag_valid = flag_valid if state else False
            return val, flag_valid

        # Set all line edits to 'valid' state
        self.le_roi_start_row.setValid(True)
        self.le_roi_end_row.setValid(True)
        self.le_roi_start_col.setValid(True)
        self.le_roi_end_col.setValid(True)
        self.le_load_mask.setValid(True)

        flag_valid = True

        if self._roi_active:
            # Perform the following checks only if ROI group is active
            rs, flag_valid = _check_valid_input(self.le_roi_start_row,
                                                flag_valid)
            re, flag_valid = _check_valid_input(self.le_roi_end_row,
                                                flag_valid)
            cs, flag_valid = _check_valid_input(self.le_roi_start_col,
                                                flag_valid)
            ce, flag_valid = _check_valid_input(self.le_roi_end_col,
                                                flag_valid)

            # Check if start
            if (rs > 0) and (re > 0) and (rs > re):
                self.le_roi_start_row.setValid(False)
                self.le_roi_end_row.setValid(False)
                flag_valid = False
            if (cs > 0) and (ce > 0) and (cs > ce):
                self.le_roi_start_col.setValid(False)
                self.le_roi_end_col.setValid(False)
                flag_valid = False

        if self._mask_active:
            if not self._mask_file_path:
                self.le_load_mask.setValid(False)
                flag_valid = False

        self.button_ok.setEnabled(flag_valid)

    def set_image_size(self, *, n_rows, n_columns):
        """
        Set the image size. Image size is used to for input validation. When image
        size is set, the selection is reset to cover the whole image.

        Parameters
        ----------
        n_rows: int
            The number of rows in the image: (1..)
        n_columns: int
            The number of columns in the image: (1..)
        """
        if n_rows < 1 or n_columns < 1:
            raise ValueError(
                "DialogLoadMask: image size have zero rows or zero columns: "
                f"n_rows={n_rows} n_columns={n_columns}. "
                "Report the error to the development team.")

        self._n_rows = n_rows
        self._n_columns = n_columns

        self._row_start = 0
        self._row_end = n_rows
        self._column_start = 0
        self._column_end = n_columns

        self._show_selection(True)
        self._validate_all_widgets()

        self.validator_rows.setTop(n_rows)
        self.validator_cols.setTop(n_columns)
        # Set label
        self.label_map_size.setText(
            f"{self._text_map_size_base}{self._n_rows} rows, {self._n_columns} columns."
        )

    def _show_selection(self, visible):
        """visible: True - show values, False - hide values"""
        def _show_number(l_edit, value):
            if value > 0:
                l_edit.setText(f"{value}")
            else:
                # This would typically mean incorrect initialization of the dialog box
                l_edit.setText("")

        _show_number(self.le_roi_start_row, self._row_start + 1)
        _show_number(self.le_roi_start_col, self._column_start + 1)
        _show_number(self.le_roi_end_row, self._row_end)
        _show_number(self.le_roi_end_col, self._column_end)

    def set_roi(self, *, row_start, column_start, row_end, column_end):
        """
        Set the values of fields that define selection of the image region. First set
        the image size (`set_image_size()`) and then set the selection.

        Parameters
        ----------
        row_start: int
            The row number of the first pixel in the selection (0..n_rows-1).
            Negative (-1) - resets value to 0.
        column_start: int
            The column number of the first pixel in the selection (0..n_columns-1).
            Negative (-1) - resets value to 0.
        row_end: int
            The row number following the last pixel in the selection (1..n_rows).
            This row is not included in the selection. Negative (-1) - resets value to n_rows.
        column_end: int
            The column number following the last pixel in the selection (1..n_columns).
            This column is not included in the selection. Negative (-1) - resets value to n_columns.
        """
        # The variables holding the selected region are following Python conventions for the
        #   selections: row_start, column_start are in the range 0..n_rows-1, 0..n_columns-1
        #   and row_end, column_end are in the range 1..n_rows, 1..n_columns.
        #   The values displayed in the dialog box are just pixels numbers in the range
        #   1..n_rows, 1..n_columns that define the rectangle in the way intuitive to the user.
        self._row_start = int(
            np.clip(row_start, a_min=0, a_max=self._n_rows - 1))
        self._column_start = int(
            np.clip(column_start, a_min=0, a_max=self._n_columns - 1))

        def _adjust_last_index(index, n_elements):
            if index < 0:
                index = n_elements
            index = int(np.clip(index, 1, n_elements))
            return index

        self._row_end = _adjust_last_index(row_end, self._n_rows)
        self._column_end = _adjust_last_index(column_end, self._n_columns)
        self._show_selection(self._roi_active)
        self._validate_all_widgets()

    def _roi_activate(self, state):
        self._roi_active = state
        self._show_selection(state)
        self._validate_all_widgets()

    def set_roi_active(self, state):
        self._roi_activate(state)
        self.gb_roi.setChecked(self._roi_active)

    def get_roi(self):
        return self._row_start, self._column_start, self._row_end, self._column_end

    def get_roi_active(self):
        return self._roi_active

    def _show_mask_file_path(self):
        fpath = self._mask_file_path if self._mask_file_path else self._le_load_mask_default_text
        self.le_load_mask.setText(fpath)
        self._validate_all_widgets()

    def _mask_file_activate(self, state):
        self._mask_active = state
        self._show_mask_file_path()
        self._validate_all_widgets()

    def set_mask_file_active(self, state):
        self._mask_file_activate(state)
        self.gb_mask.setChecked(self._mask_active)

    def get_mask_file_active(self):
        return self._mask_active

    def set_mask_file_path(self, fpath):
        self._mask_file_path = fpath
        self._show_mask_file_path()

    def get_mask_file_path(self):
        return self._mask_file_path

    def set_default_directory(self, dir_name):
        self._default_directory = dir_name

    def get_default_directory(self):
        return self._default_directory
コード例 #3
0
    def __init__(self, appdata: CnaData):
        QDialog.__init__(self)
        self.setWindowTitle("Configure CNApy")

        cross_icon = QIcon(":/icons/cross.png")
        cross = cross_icon.pixmap(QSize(32, 32))

        self.appdata = appdata
        self.oeng = appdata.octave_engine
        self.meng = appdata.matlab_engine
        self.layout = QVBoxLayout()
        self.cna_ok = False

        descr = QLabel("\
            Some functionalities in CNApy need a working CNA installation.\n \
            To use CNA you need either Matlab >= R2019 or Octave >= 5 .\n \
            Below you can choose a Matlab directory or the Octave executable.\n \
            Only if one of the engines is green your CNA directory can be validated."
                       )
        self.layout.addWidget(descr)
        ml = QHBoxLayout()
        label = QLabel("Matlab")
        label.setFixedWidth(100)
        ml.addWidget(label)
        self.ml_label = QLabel()
        self.ml_label.setPixmap(cross)
        self.ml_label.setFixedWidth(100)
        ml.addWidget(self.ml_label)
        self.choose_ml_path_btn = QPushButton("Choose Matlab folder")
        self.choose_ml_path_btn.setFixedWidth(300)
        ml.addWidget(self.choose_ml_path_btn)
        label2 = QLabel("")
        label2.setFixedWidth(30)
        ml.addWidget(label2)
        self.matlab_path = QLabel()
        self.matlab_path.setMinimumWidth(200)
        self.matlab_path.setText(self.appdata.matlab_path)
        ml.addWidget(self.matlab_path)

        self.layout.addItem(ml)

        oc = QHBoxLayout()
        label = QLabel("Octave")
        label.setFixedWidth(100)
        oc.addWidget(label)
        self.oc_label = QLabel()
        self.oc_label.setPixmap(cross)
        self.oc_label.setFixedWidth(100)
        oc.addWidget(self.oc_label)

        self.choose_oc_exe_btn = QPushButton("Choose Octave octave-cli(.exe)")
        self.choose_oc_exe_btn.setFixedWidth(300)
        oc.addWidget(self.choose_oc_exe_btn)

        label2 = QLabel("")
        label2.setFixedWidth(30)
        oc.addWidget(label2)

        self.oc_exe = QLabel()
        self.oc_exe.setText(self.appdata.octave_executable)
        self.oc_exe.setMinimumWidth(200)
        oc.addWidget(self.oc_exe)

        self.layout.addItem(oc)

        h9 = QHBoxLayout()
        label = QLabel("Selected engine:")
        h9.addWidget(label)
        label2 = QLabel("")
        label2.setFixedWidth(30)
        h9.addWidget(label2)
        self.selected_engine = QComboBox()
        self.selected_engine.addItem("None")
        h9.addWidget(self.selected_engine)
        label2 = QLabel("")
        label2.setMinimumWidth(200)
        h9.addWidget(label2)
        self.layout.addItem(h9)

        cna_l = QHBoxLayout()
        label = QLabel("CNA")
        label.setFixedWidth(100)
        cna_l.addWidget(label)
        self.cna_label = QLabel()
        self.cna_label.setPixmap(cross)
        self.cna_label.setFixedWidth(100)
        cna_l.addWidget(self.cna_label)
        self.choose_cna_path_btn = QPushButton("Choose CNA directory")
        self.choose_cna_path_btn.setFixedWidth(300)
        cna_l.addWidget(self.choose_cna_path_btn)
        label2 = QLabel("")
        label2.setFixedWidth(30)
        cna_l.addWidget(label2)

        self.cna_path = QLabel()
        self.cna_path.setMinimumWidth(200)
        self.cna_path.setText(self.appdata.cna_path)
        cna_l.addWidget(self.cna_path)
        self.layout.addItem(cna_l)

        h2 = QHBoxLayout()
        label = QLabel("Default color for values in a scenario:")
        h2.addWidget(label)
        self.scen_color_btn = QPushButton()
        self.scen_color_btn.setFixedWidth(100)
        palette = self.scen_color_btn.palette()
        palette.setColor(QPalette.Button, self.appdata.scen_color)
        self.scen_color_btn.setPalette(palette)
        h2.addWidget(self.scen_color_btn)
        self.layout.addItem(h2)

        h3 = QHBoxLayout()
        label = QLabel(
            "Default color for computed values not part of the scenario:")
        h3.addWidget(label)
        self.comp_color_btn = QPushButton()
        self.comp_color_btn.setFixedWidth(100)
        palette = self.comp_color_btn.palette()
        palette.setColor(QPalette.Button, self.appdata.comp_color)
        self.comp_color_btn.setPalette(palette)
        h3.addWidget(self.comp_color_btn)
        self.layout.addItem(h3)

        h4 = QHBoxLayout()
        label = QLabel("Special Color used for non equal flux bounds:")
        h4.addWidget(label)
        self.spec1_color_btn = QPushButton()
        self.spec1_color_btn.setFixedWidth(100)
        palette = self.spec1_color_btn.palette()
        palette.setColor(QPalette.Button, self.appdata.special_color_1)
        self.spec1_color_btn.setPalette(palette)
        h4.addWidget(self.spec1_color_btn)
        self.layout.addItem(h4)

        h5 = QHBoxLayout()
        label = QLabel(
            "Special Color 2 used for non equal flux bounds that exclude 0:")
        h5.addWidget(label)
        self.spec2_color_btn = QPushButton()
        self.spec2_color_btn.setFixedWidth(100)
        palette = self.spec2_color_btn.palette()
        palette.setColor(QPalette.Button, self.appdata.special_color_2)
        self.spec2_color_btn.setPalette(palette)
        h5.addWidget(self.spec2_color_btn)
        self.layout.addItem(h5)

        h6 = QHBoxLayout()
        label = QLabel("Color used for empty reaction boxes:")
        h6.addWidget(label)
        self.default_color_btn = QPushButton()
        self.default_color_btn.setFixedWidth(100)
        palette = self.default_color_btn.palette()
        palette.setColor(QPalette.Button, self.appdata.default_color)
        self.default_color_btn.setPalette(palette)
        h6.addWidget(self.default_color_btn)
        self.layout.addItem(h6)

        h = QHBoxLayout()
        label = QLabel("Work directory:")
        h.addWidget(label)
        self.work_directory = QPushButton()
        self.work_directory.setText(self.appdata.work_directory)
        h.addWidget(self.work_directory)
        self.layout.addItem(h)

        h7 = QHBoxLayout()
        label = QLabel("Shown number of digits after the decimal point:")
        h7.addWidget(label)
        self.rounding = QLineEdit()
        self.rounding.setFixedWidth(100)
        self.rounding.setText(str(self.appdata.rounding))
        validator = QIntValidator(0, 20, self)
        self.rounding.setValidator(validator)
        h7.addWidget(self.rounding)
        self.layout.addItem(h7)

        h8 = QHBoxLayout()
        label = QLabel(
            "Absolute tolerance used to compare float values in the UI:")
        h8.addWidget(label)
        self.abs_tol = QLineEdit()
        self.abs_tol.setFixedWidth(100)
        self.abs_tol.setText(str(self.appdata.abs_tol))
        validator = QDoubleValidator(self)
        validator.setTop(1)
        self.abs_tol.setValidator(validator)
        h8.addWidget(self.abs_tol)
        self.layout.addItem(h8)

        l2 = QHBoxLayout()
        self.button = QPushButton("Apply Changes")
        self.cancel = QPushButton("Close")
        l2.addWidget(self.button)
        l2.addWidget(self.cancel)
        self.layout.addItem(l2)
        self.setLayout(self.layout)

        # Connecting the signal
        self.choose_ml_path_btn.clicked.connect(self.choose_ml_path)
        self.choose_oc_exe_btn.clicked.connect(self.choose_oc_exe)
        self.choose_cna_path_btn.clicked.connect(self.choose_cna_path)
        self.work_directory.clicked.connect(self.choose_work_directory)
        self.scen_color_btn.clicked.connect(self.choose_scen_color)
        self.comp_color_btn.clicked.connect(self.choose_comp_color)
        self.spec1_color_btn.clicked.connect(self.choose_spec1_color)
        self.spec2_color_btn.clicked.connect(self.choose_spec2_color)
        self.default_color_btn.clicked.connect(self.choose_default_color)
        self.selected_engine.currentTextChanged.connect(self.update)
        self.cancel.clicked.connect(self.reject)
        self.button.clicked.connect(self.apply)

        self.check_all()

        if self.meng is not None:
            self.selected_engine.insertItem(1, "Matlab")
            if self.appdata.selected_engine == "matlab":
                self.selected_engine.setCurrentIndex(1)
        if self.oeng is not None:
            self.selected_engine.insertItem(1, "Octave")
            if self.appdata.selected_engine == "octave":
                self.selected_engine.setCurrentIndex(1)

        self.update()