Example #1
0
class DialogPlotEscapePeak(QDialog):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Escape Peak Settings")

        self.grp_show_escape_peak = QGroupBox("Show Escape Peak")
        self.grp_show_escape_peak.setCheckable(True)
        self.grp_show_escape_peak.setChecked(False)  # Set based on data !!!

        self.le_incident_energy = LineEditReadOnly()
        set_tooltip(
            self.le_incident_energy,
            "<b>Incident energy</b>. Use <b>General...</b> button of <b>Model</b> tab "
            "to change the value if needed.",
        )
        self.combo_detector_type = QComboBox()
        set_tooltip(self.combo_detector_type, "Select <b>detector</b> material. The typical choice is <b>Si</b>")

        self._detector_types = ["Si", "Ge"]
        self.combo_detector_type.addItems(self._detector_types)

        grid = QGridLayout()
        grid.addWidget(QLabel("Incident energy, kev:"), 0, 0)
        grid.addWidget(self.le_incident_energy, 0, 1)
        grid.addWidget(QLabel("Detectory type:"), 1, 0)
        grid.addWidget(self.combo_detector_type, 1, 1)
        self.grp_show_escape_peak.setLayout(grid)

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

        vbox = QVBoxLayout()
        vbox.addWidget(self.grp_show_escape_peak)
        vbox.addWidget(button_box)
        self.setLayout(vbox)

    def set_parameters(self, plot_escape_peak, incident_energy, detector_material):
        index_material = -1
        if detector_material in self._detector_types:
            index_material = self._detector_types.index(detector_material)
        self.combo_detector_type.setCurrentIndex(index_material)

        self.le_incident_energy.setText(f"{incident_energy:.10g}")

        self.grp_show_escape_peak.setChecked(plot_escape_peak)

    def get_parameters(self):
        plot_escape_peak = self.grp_show_escape_peak.isChecked()

        index = self.combo_detector_type.currentIndex()
        if index >= 0:
            detector_material = self._detector_types[self.combo_detector_type.currentIndex()]
        else:
            detector_material = ""

        return plot_escape_peak, detector_material
Example #2
0
def create_groupbox(parent,
                    title=None,
                    toggled=None,
                    checked=None,
                    flat=False,
                    layout=None):
    """Create a QGroupBox"""
    if title is None:
        group = QGroupBox(parent)
    else:
        group = QGroupBox(title, parent)
    group.setFlat(flat)
    if toggled is not None:
        group.setCheckable(True)
        if checked is not None:
            group.setChecked(checked)
        group.toggled.connect(toggled)
    if layout is not None:
        group.setLayout(layout)
    return group
Example #3
0
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
Example #4
0
class ImageRotationDialog(ExToolWindow):
    def __init__(self, signal, axes, parent, plugin):
        super(ImageRotationDialog, self).__init__(parent)
        self.ui = parent
        self.create_controls()
        self.accepted.connect(self.ok)
        self.rejected.connect(self.close_new)
        self.signal = signal
        self.plugin = plugin
        self.new_out = None
        self._connected_updates = False
        if isinstance(axes, str):
            axm = signal.signal.axes_manager
            if axes.startswith("nav"):
                axes = (axm._axes.index(axm.navigation_axes[0]),
                        axm._axes.index(axm.navigation_axes[1]))
            elif axes.startswith("sig"):
                axes = (axm._axes.index(axm.signal_axes[0]),
                        axm._axes.index(axm.signal_axes[1]))
        self.axes = axes
        self.setWindowTitle(tr("Rotate"))

        # TODO: TAG: Functionality check
        if not hasattr(signal.signal, 'events'):
            self.gbo_preview.setVisible(False)

        # TODO: Add dynamic rotation, e.g. one that rotates when source
        # signal's data_changed event triggers

    def connect(self):
        # TODO: Don't have to con/dis those in gbo
        self.opt_new.toggled.connect(self.close_new)
        self.num_angle.valueChanged.connect(self.update)
        self.chk_grid.toggled.connect(self.update)
        self.num_grid.valueChanged.connect(self.update)
        self.chk_reshape.toggled.connect(self.update)
        self.opt_new.toggled.connect(self.update)
        self.opt_replace.toggled.connect(self.update)

    def disconnect(self):
        self.num_angle.valueChanged.disconnect(self.update)
        self.chk_grid.toggled.disconnect(self.update)
        self.num_grid.valueChanged.disconnect(self.update)
        self.chk_reshape.toggled.disconnect(self.update)
        self.opt_new.toggled.disconnect(self.update)
        self.opt_replace.toggled.disconnect(self.update)

    def ok(self):
        # Draw figure if not already done
        # TODO: TAG: Functionality check
        if not hasattr(self.signal.signal, 'events') or \
                not self.gbo_preview.isChecked():
            self.update()
        angle = self.num_angle.value()
        reshape = self.chk_reshape.isChecked()
        self.plugin.record_code(
            r"<p>.rotate_signal({0}, reshape={1}, axes={2})".format(
                angle, reshape, self.axes))
        # Clean up event connections
        if self.new_out is not None:
            self.connect_update_plot(self.new_out.signal, disconnect=True)

    def close_new(self, value=False):
        if self.new_out is not None and not value:
            self.new_out.close()
            self.new_out = None
            self._connected_updates = False

    def set_preview(self, value):
        if not hasattr(self.signal.signal, 'events'):
            return
        if value:
            self.connect()
            self.update()
        else:
            self.disconnect()
            self.close_new()

    def _axes_in_nav(self):
        axm = self.signal.signal.axes_manager
        navidx = [axm._axes.index(ax) for ax in axm.navigation_axes]
        if self.axes[0] in navidx:
            return True
        return False

    def connect_update_plot(self, signal, disconnect=False):
        if self._connected_updates != disconnect:
            return  # Nothing to do, prevent double connections
        if self._axes_in_nav():
            f = signal._plot.navigator_plot.update
        else:
            f = signal._plot.signal_plot.update

        # TODO: TAG: Functionality check
        if hasattr(signal, 'events') and hasattr(signal.events,
                                                 'data_changed'):
            if disconnect:
                signal.events.data_changed.disconnect(f)
            else:
                signal.events.data_changed.connect(f, [])
        self._connected_updates = not disconnect

    def update(self):
        angle = self.num_angle.value()
        reshape = self.chk_reshape.isChecked()
        if self.opt_new.isChecked():
            if self.new_out is None:
                out = None
            else:
                out = self.new_out.signal
        elif self.opt_replace.isChecked():
            out = self.signal.signal
        else:
            return  # Indeterminate state, do nothing

        s = self.plugin.rotate_signal(angle,
                                      self.signal.signal,
                                      record=False,
                                      reshape=reshape,
                                      out=out,
                                      axes=self.axes)

        if out is None:
            s.metadata.General.title = self.signal.name + "[Rotated]"
            s.plot()
            self.connect_update_plot(s)
            if (self.gbo_preview.isChecked() and self.opt_new.isChecked()
                    and self.new_out is None):
                self.new_out = self.ui.lut_signalwrapper[s]
        else:
            s = out

        if self.chk_grid.isChecked() is True:
            pass  # TODO: Draw grid

    def create_controls(self):
        """
        Create UI controls.
        """
        vbox = QVBoxLayout()

        form = QFormLayout()
        self.num_angle = QDoubleSpinBox()
        self.num_angle.setValue(0.0)
        self.num_angle.setMinimum(-360)
        self.num_angle.setMaximum(360)
        form.addRow(tr("Angle:"), self.num_angle)
        vbox.addLayout(form)

        self.gbo_preview = QGroupBox(tr("Preview"))
        self.gbo_preview.setCheckable(True)
        self.gbo_preview.setChecked(False)
        gbo_vbox = QVBoxLayout()
        self.chk_grid = QCheckBox(tr("Grid"))
        self.chk_grid.setChecked(False)
        self.num_grid = QSpinBox()
        self.num_grid.setValue(4)
        self.num_grid.setMinimum(1)
        self.num_grid.setEnabled(False)
        self.chk_grid.toggled[bool].connect(self.num_grid.setEnabled)
        gbo_vbox.addWidget(self.chk_grid)
        gbo_vbox.addWidget(self.num_grid)
        self.gbo_preview.setLayout(gbo_vbox)
        vbox.addWidget(self.gbo_preview)

        self.gbo_preview.toggled[bool].connect(self.set_preview)

        self.gbo_output = QGroupBox(tr("Output"))
        self.opt_new = QRadioButton(tr("New signal"))
        self.opt_replace = QRadioButton(tr("In place"))
        self.opt_new.setChecked(True)
        gbo_vbox2 = QVBoxLayout()
        gbo_vbox2.addWidget(self.opt_new)
        gbo_vbox2.addWidget(self.opt_replace)
        self.gbo_output.setLayout(gbo_vbox2)
        vbox.addWidget(self.gbo_output)

        self.chk_reshape = QCheckBox(tr("Resize to fit"))
        self.chk_reshape.setChecked(False)
        vbox.addWidget(self.chk_reshape)

        self.btn_ok = QPushButton(tr("&OK"))
        self.btn_ok.setDefault(True)
        self.btn_ok.clicked.connect(self.accept)
        self.btn_cancel = QPushButton(tr("&Cancel"))
        self.btn_cancel.clicked.connect(self.reject)
        hbox = QHBoxLayout()
        hbox.addWidget(self.btn_ok)
        hbox.addWidget(self.btn_cancel)
        vbox.addLayout(hbox)

        vbox.addStretch(1)
        self.setLayout(vbox)
Example #5
0
class KernelConnectionDialog(QDialog):
    """Dialog to connect to existing kernels (either local or remote)."""
    def __init__(self, parent=None):
        super(KernelConnectionDialog, self).__init__(parent)
        self.setWindowTitle(_('Connect to an existing kernel'))

        main_label = QLabel(
            _("<p>Please select the JSON connection file (<i>e.g.</i> "
              "<tt>kernel-1234.json</tt>) of the existing kernel, and enter "
              "the SSH information if connecting to a remote machine. "
              "To learn more about starting external kernels and connecting "
              "to them, see <a href=\"https://docs.spyder-ide.org/"
              "ipythonconsole.html#connect-to-an-external-kernel\">"
              "our documentation</a>.</p>"))
        main_label.setWordWrap(True)
        main_label.setAlignment(Qt.AlignJustify)
        main_label.setOpenExternalLinks(True)

        # Connection file
        cf_label = QLabel(_('Connection file:'))
        self.cf = QLineEdit()
        self.cf.setPlaceholderText(_('Kernel connection file path'))
        self.cf.setMinimumWidth(350)
        cf_open_btn = QPushButton(_('Browse'))
        cf_open_btn.clicked.connect(self.select_connection_file)

        cf_layout = QHBoxLayout()
        cf_layout.addWidget(cf_label)
        cf_layout.addWidget(self.cf)
        cf_layout.addWidget(cf_open_btn)

        # Remote kernel groupbox
        self.rm_group = QGroupBox(_("This is a remote kernel (via SSH)"))

        # SSH connection
        hn_label = QLabel(_('Hostname:'))
        self.hn = QLineEdit()
        pn_label = QLabel(_('Port:'))
        self.pn = QLineEdit()
        self.pn.setMaximumWidth(75)
        self.pn.setText('22')
        un_label = QLabel(_('Username:'******'Password:'******'SSH keyfile:'))

        self.pw = QLineEdit()
        self.pw.setEchoMode(QLineEdit.Password)
        self.pw_radio.toggled.connect(self.pw.setEnabled)
        self.kf_radio.toggled.connect(self.pw.setDisabled)

        self.kf = QLineEdit()
        kf_open_btn = QPushButton(_('Browse'))
        kf_open_btn.clicked.connect(self.select_ssh_key)
        kf_layout = QHBoxLayout()
        kf_layout.addWidget(self.kf)
        kf_layout.addWidget(kf_open_btn)

        kfp_label = QLabel(_('Passphase:'))
        self.kfp = QLineEdit()
        self.kfp.setPlaceholderText(_('Optional'))
        self.kfp.setEchoMode(QLineEdit.Password)

        self.kf_radio.toggled.connect(self.kf.setEnabled)
        self.kf_radio.toggled.connect(self.kfp.setEnabled)
        self.kf_radio.toggled.connect(kf_open_btn.setEnabled)
        self.kf_radio.toggled.connect(kfp_label.setEnabled)
        self.pw_radio.toggled.connect(self.kf.setDisabled)
        self.pw_radio.toggled.connect(self.kfp.setDisabled)
        self.pw_radio.toggled.connect(kf_open_btn.setDisabled)
        self.pw_radio.toggled.connect(kfp_label.setDisabled)

        # SSH layout
        ssh_layout = QGridLayout()
        ssh_layout.addWidget(hn_label, 0, 0, 1, 2)
        ssh_layout.addWidget(self.hn, 0, 2)
        ssh_layout.addWidget(pn_label, 0, 3)
        ssh_layout.addWidget(self.pn, 0, 4)
        ssh_layout.addWidget(un_label, 1, 0, 1, 2)
        ssh_layout.addWidget(self.un, 1, 2, 1, 3)

        # SSH authentication layout
        auth_layout = QGridLayout()
        auth_layout.addWidget(self.pw_radio, 1, 0)
        auth_layout.addWidget(pw_label, 1, 1)
        auth_layout.addWidget(self.pw, 1, 2)
        auth_layout.addWidget(self.kf_radio, 2, 0)
        auth_layout.addWidget(kf_label, 2, 1)
        auth_layout.addLayout(kf_layout, 2, 2)
        auth_layout.addWidget(kfp_label, 3, 1)
        auth_layout.addWidget(self.kfp, 3, 2)
        auth_group.setLayout(auth_layout)

        # Remote kernel layout
        rm_layout = QVBoxLayout()
        rm_layout.addLayout(ssh_layout)
        rm_layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 8)))
        rm_layout.addWidget(auth_group)
        self.rm_group.setLayout(rm_layout)
        self.rm_group.setCheckable(True)
        self.rm_group.setChecked(False)
        self.rm_group.toggled.connect(self.pw_radio.setChecked)

        # Ok and Cancel buttons
        self.accept_btns = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)

        self.accept_btns.accepted.connect(self.accept)
        self.accept_btns.rejected.connect(self.reject)

        # Dialog layout
        layout = QVBoxLayout(self)
        layout.addWidget(main_label)
        layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 8)))
        layout.addLayout(cf_layout)
        layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 12)))
        layout.addWidget(self.rm_group)
        layout.addWidget(self.accept_btns)

    def select_connection_file(self):
        cf = getopenfilename(self, _('Select kernel connection file'),
                             jupyter_runtime_dir(), '*.json;;*.*')[0]
        self.cf.setText(cf)

    def select_ssh_key(self):
        kf = getopenfilename(self, _('Select SSH keyfile'), get_home_dir(),
                             '*.pem;;*')[0]
        self.kf.setText(kf)

    @staticmethod
    def get_connection_parameters(parent=None, dialog=None):
        if not dialog:
            dialog = KernelConnectionDialog(parent)
        result = dialog.exec_()
        is_remote = bool(dialog.rm_group.isChecked())
        accepted = result == QDialog.Accepted
        if is_remote:

            def falsy_to_none(arg):
                return arg if arg else None

            if dialog.hn.text() and dialog.un.text():
                port = dialog.pn.text() if dialog.pn.text() else '22'
                hostname = "{0}@{1}:{2}".format(dialog.un.text(),
                                                dialog.hn.text(), port)
            else:
                hostname = None
            if dialog.pw_radio.isChecked():
                password = falsy_to_none(dialog.pw.text())
                keyfile = None
            elif dialog.kf_radio.isChecked():
                keyfile = falsy_to_none(dialog.kf.text())
                password = falsy_to_none(dialog.kfp.text())
            else:  # imposible?
                keyfile = None
                password = None
            return (dialog.cf.text(), hostname, keyfile, password, accepted)
        else:
            path = dialog.cf.text()
            _dir, filename = osp.dirname(path), osp.basename(path)
            if _dir == '' and not filename.endswith('.json'):
                path = osp.join(jupyter_runtime_dir(),
                                'kernel-' + path + '.json')
            return (path, None, None, None, accepted)
Example #6
0
class KernelConnectionDialog(QDialog):
    """Dialog to connect to existing kernels (either local or remote)."""

    def __init__(self, parent=None):
        super(KernelConnectionDialog, self).__init__(parent)
        self.setWindowTitle(_('Connect to an existing kernel'))

        main_label = QLabel(_(
            "<p>Please select the JSON connection file (<i>e.g.</i> "
            "<tt>kernel-1234.json</tt>) of the existing kernel, and enter "
            "the SSH information if connecting to a remote machine. "
            "To learn more about starting external kernels and connecting "
            "to them, see <a href=\"https://docs.spyder-ide.org/"
            "ipythonconsole.html#connect-to-an-external-kernel\">"
            "our documentation</a>.</p>"))
        main_label.setWordWrap(True)
        main_label.setAlignment(Qt.AlignJustify)
        main_label.setOpenExternalLinks(True)

        # Connection file
        cf_label = QLabel(_('Connection file:'))
        self.cf = QLineEdit()
        self.cf.setPlaceholderText(_('Kernel connection file path'))
        self.cf.setMinimumWidth(350)
        cf_open_btn = QPushButton(_('Browse'))
        cf_open_btn.clicked.connect(self.select_connection_file)

        cf_layout = QHBoxLayout()
        cf_layout.addWidget(cf_label)
        cf_layout.addWidget(self.cf)
        cf_layout.addWidget(cf_open_btn)

        # Remote kernel groupbox
        self.rm_group = QGroupBox(_("This is a remote kernel (via SSH)"))

        # SSH connection
        hn_label = QLabel(_('Hostname:'))
        self.hn = QLineEdit()
        pn_label = QLabel(_('Port:'))
        self.pn = QLineEdit()
        self.pn.setMaximumWidth(75)
        self.pn.setText('22')
        un_label = QLabel(_('Username:'******'Password:'******'SSH keyfile:'))

        self.pw = QLineEdit()
        self.pw.setEchoMode(QLineEdit.Password)
        self.pw_radio.toggled.connect(self.pw.setEnabled)
        self.kf_radio.toggled.connect(self.pw.setDisabled)

        self.kf = QLineEdit()
        kf_open_btn = QPushButton(_('Browse'))
        kf_open_btn.clicked.connect(self.select_ssh_key)
        kf_layout = QHBoxLayout()
        kf_layout.addWidget(self.kf)
        kf_layout.addWidget(kf_open_btn)

        kfp_label = QLabel(_('Passphase:'))
        self.kfp = QLineEdit()
        self.kfp.setPlaceholderText(_('Optional'))
        self.kfp.setEchoMode(QLineEdit.Password)

        self.kf_radio.toggled.connect(self.kf.setEnabled)
        self.kf_radio.toggled.connect(self.kfp.setEnabled)
        self.kf_radio.toggled.connect(kf_open_btn.setEnabled)
        self.kf_radio.toggled.connect(kfp_label.setEnabled)
        self.pw_radio.toggled.connect(self.kf.setDisabled)
        self.pw_radio.toggled.connect(self.kfp.setDisabled)
        self.pw_radio.toggled.connect(kf_open_btn.setDisabled)
        self.pw_radio.toggled.connect(kfp_label.setDisabled)

        # SSH layout
        ssh_layout = QGridLayout()
        ssh_layout.addWidget(hn_label, 0, 0, 1, 2)
        ssh_layout.addWidget(self.hn, 0, 2)
        ssh_layout.addWidget(pn_label, 0, 3)
        ssh_layout.addWidget(self.pn, 0, 4)
        ssh_layout.addWidget(un_label, 1, 0, 1, 2)
        ssh_layout.addWidget(self.un, 1, 2, 1, 3)

        # SSH authentication layout
        auth_layout = QGridLayout()
        auth_layout.addWidget(self.pw_radio, 1, 0)
        auth_layout.addWidget(pw_label, 1, 1)
        auth_layout.addWidget(self.pw, 1, 2)
        auth_layout.addWidget(self.kf_radio, 2, 0)
        auth_layout.addWidget(kf_label, 2, 1)
        auth_layout.addLayout(kf_layout, 2, 2)
        auth_layout.addWidget(kfp_label, 3, 1)
        auth_layout.addWidget(self.kfp, 3, 2)
        auth_group.setLayout(auth_layout)

        # Remote kernel layout
        rm_layout = QVBoxLayout()
        rm_layout.addLayout(ssh_layout)
        rm_layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 8)))
        rm_layout.addWidget(auth_group)
        self.rm_group.setLayout(rm_layout)
        self.rm_group.setCheckable(True)
        self.rm_group.setChecked(False)
        self.rm_group.toggled.connect(self.pw_radio.setChecked)

        # Ok and Cancel buttons
        self.accept_btns = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
            Qt.Horizontal, self)

        self.accept_btns.accepted.connect(self.accept)
        self.accept_btns.rejected.connect(self.reject)

        # Dialog layout
        layout = QVBoxLayout(self)
        layout.addWidget(main_label)
        layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 8)))
        layout.addLayout(cf_layout)
        layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 12)))
        layout.addWidget(self.rm_group)
        layout.addWidget(self.accept_btns)

    def select_connection_file(self):
        cf = getopenfilename(self, _('Select kernel connection file'),
                             jupyter_runtime_dir(), '*.json;;*.*')[0]
        self.cf.setText(cf)

    def select_ssh_key(self):
        kf = getopenfilename(self, _('Select SSH keyfile'),
                             get_home_dir(), '*.pem;;*')[0]
        self.kf.setText(kf)

    @staticmethod
    def get_connection_parameters(parent=None, dialog=None):
        if not dialog:
            dialog = KernelConnectionDialog(parent)
        result = dialog.exec_()
        is_remote = bool(dialog.rm_group.isChecked())
        accepted = result == QDialog.Accepted
        if is_remote:
            def falsy_to_none(arg):
                return arg if arg else None
            if dialog.hn.text() and dialog.un.text():
                port = dialog.pn.text() if dialog.pn.text() else '22'
                hostname = "{0}@{1}:{2}".format(dialog.un.text(),
                                                dialog.hn.text(),
                                                port)
            else:
                hostname = None
            if dialog.pw_radio.isChecked():
                password = falsy_to_none(dialog.pw.text())
                keyfile = None
            elif dialog.kf_radio.isChecked():
                keyfile = falsy_to_none(dialog.kf.text())
                password = falsy_to_none(dialog.kfp.text())
            else:  # imposible?
                keyfile = None
                password = None
            return (dialog.cf.text(), hostname, keyfile, password, accepted)
        else:
            path = dialog.cf.text()
            _dir, filename = osp.dirname(path), osp.basename(path)
            if _dir == '' and not filename.endswith('.json'):
                path = osp.join(jupyter_runtime_dir(), 'kernel-'+path+'.json')
            return (path, None, None, None, accepted)
Example #7
0
    def uicreate_groupbox(self, idx):
        """."""
        grpbx = QGroupBox(self.line_names[idx], self)
        grpbx.setCheckable(True)
        grpbx.setChecked(not idx)
        grpbx.toggled.connect(self.updater[idx].set_visible)
        vbl = QVBoxLayout(grpbx)
        gdl = QGridLayout()
        gdl.setSpacing(4)
        vbl.addLayout(gdl)

        if self.is_orb:
            lbl_orb = self.uicreate_label('Show', grpbx)
            lbl_ref = self.uicreate_label('as diff to:', grpbx)
            cbx_ref = self.uicreate_combobox(grpbx, 'ref', idx)
            cbx_orb = self.uicreate_combobox(grpbx, 'val', idx)
            gdl.addWidget(lbl_orb, 0, 0)
            gdl.addWidget(lbl_ref, 1, 0)
            gdl.addWidget(cbx_orb, 0, 1)
            gdl.addWidget(cbx_ref, 1, 1)

            pb_save = QPushButton('', grpbx)
            pb_save.clicked.connect(_part(self._save_difference, idx))
            pb_save.setObjectName('butt')
            pb_save.setStyleSheet('#butt {max-width: 40px; icon-size: 35px;}')
            pb_save.setIcon(qta.icon('fa5.save'))
            pb_save.setToolTip('Save diff to file')
            gdl.addWidget(pb_save, 0, 2, 2, 1)

        unit = 'm' if self.is_orb else 'rad'
        for pln in ('x', 'y'):
            wid = QWidget(grpbx)
            vbl.addWidget(wid)
            vbl.setSpacing(2)
            hbl = QHBoxLayout(wid)
            hbl.setSpacing(0)
            cbx = QCheckBox('{0:s}:'.format(pln.upper()), wid)
            cbx.setObjectName(pln + 'checkbox')
            cbx.setChecked(False)
            hbl.addWidget(cbx)

            lab_avg = Label(unit, '-100.00 mrad', wid)
            self.updater[idx].ave[pln].connect(lab_avg.setFloat)
            lab_avg.setStyleSheet("""min-width:4.5em;""")
            lab_avg.setAlignment(Qt.AlignRight)
            hbl.addWidget(lab_avg)
            hbl.addWidget(
                QLabel(" <html><head/><body><p>&#177;</p></body></html> ",
                       wid))
            lab_std = Label(unit, '100.00 mrad', wid)
            self.updater[idx].std[pln].connect(lab_std.setFloat)
            lab_std.setStyleSheet("""min-width:4.5em;""")
            lab_std.setAlignment(Qt.AlignLeft)
            hbl.addWidget(lab_std)

            hbl.addWidget(QLabel('(pp. ', wid))
            lab_p2p = Label(unit, '100.00 mrad', wid)
            self.updater[idx].p2p[pln].connect(lab_p2p.setFloat)
            lab_p2p.setStyleSheet("""min-width:4.5em;""")
            lab_p2p.setAlignment(Qt.AlignLeft)
            hbl.addWidget(lab_p2p)
            hbl.addWidget(QLabel(')', wid))
        return grpbx
Example #8
0
class TOFTOFSetupWidget(BaseWidget):
    ''' The one and only tab page. '''
    name = 'TOFTOF Reduction'

    class TofTofDataTableModel(DataTableModel):
        def _textToData(self, row, col, text):
            """
            converts a displayable text back to stored data.
            """
            if col == 2:
                return OptionalFloat(text)
            else:
                return text  # just return the value, it is already str.

    # tooltips
    TIP_prefix = ''
    TIP_dataDir = ''
    TIP_saveDir = ''
    TIP_btnDataDir = ''
    TIP_btnSaveDir = ''

    TIP_vanRuns = ''
    TIP_vanCmnt = ''
    TIP_vanTemp = 'Temperature (K). Optional.'
    TIP_vanEcFactor = ''

    TIP_ecRuns = ''
    TIP_ecTemp = 'Temperature (K). Optional.'
    TIP_ecFactor = ''

    TIP_binEon = ''
    TIP_binEstart = ''
    TIP_binEstep = ''
    TIP_binEend = ''

    TIP_binQon = ''
    TIP_binQstart = ''
    TIP_binQstep = ''
    TIP_binQend = ''

    TIP_maskDetectors = ''

    TIP_dataRunsView = ''

    TIP_chkSubtractECVan = ''
    TIP_chkReplaceNaNs = 'Replace NaNs with 0'
    TIP_chkCreateDiff = ''
    TIP_chkKeepSteps = ''

    TIP_chkSofQW = ''
    TIP_chkSofTW = ''
    TIP_chkNxspe = 'Save for MSlice'
    TIP_chkNexus = 'Save for Mantid'
    TIP_chkAscii = 'Save as text'

    TIP_rbtNormaliseNone = ''
    TIP_rbtNormaliseMonitor = ''
    TIP_rbtNormaliseTime = ''

    TIP_rbtCorrectTOFNone = ''
    TIP_rbtCorrectTOFVan = ''
    TIP_rbtCorrectTOFSample = ''

    def dir_browse_dialog(self, default_dir=''):
        """
            Pop up a directory dialog box.
        """
        dirname = QFileDialog.getExistingDirectory(
            self, "Select Directory", default_dir,
            QFileDialog.DontUseNativeDialog)
        if isinstance(dirname, tuple):
            dirname = dirname[0]

        return dirname

    def __init__(self, settings):
        BaseWidget.__init__(self, settings=settings)

        inf = float('inf')

        def set_spin(spin, minVal=-inf, maxVal=+inf, decimals=3):
            spin.setRange(minVal, maxVal)
            spin.setDecimals(decimals)
            spin.setSingleStep(0.01)

        def tip(widget, text):
            if text:
                widget.setToolTip(text)
            return widget

        def setEnabled(widget, *widgets):
            """enables widget, when value of all widgets evaluates to true"""
            def setEnabled():
                widget.setEnabled(all(w.isChecked() for w in widgets))

            for w in widgets:
                w.toggled.connect(setEnabled)
            return widget

        def DoubleEdit():
            edit = SmallQLineEdit()
            edit.setValidator(QDoubleValidator())
            return edit

        # ui data elements
        self.prefix = tip(QLineEdit(), self.TIP_prefix)
        self.dataDir = tip(QLineEdit(), self.TIP_dataDir)
        self.saveDir = tip(QLineEdit(), self.TIP_saveDir)

        self.vanRuns = tip(QLineEdit(), self.TIP_vanRuns)
        self.vanCmnt = tip(QLineEdit(), self.TIP_vanCmnt)
        self.vanTemp = tip(DoubleEdit(), self.TIP_vanTemp)

        self.ecRuns = tip(SmallQLineEdit(), self.TIP_ecRuns)
        self.ecTemp = tip(DoubleEdit(), self.TIP_ecTemp)
        self.ecFactor = tip(QDoubleSpinBox(), self.TIP_ecFactor)

        set_spin(self.ecFactor, 0, 1)

        self.binEon = tip(QCheckBox(), self.TIP_binEon)
        self.binEstart = setEnabled(tip(QDoubleSpinBox(), self.TIP_binEstart),
                                    self.binEon)
        self.binEstep = setEnabled(tip(QDoubleSpinBox(), self.TIP_binEstep),
                                   self.binEon)
        self.binEend = setEnabled(tip(QDoubleSpinBox(), self.TIP_binEend),
                                  self.binEon)

        set_spin(self.binEstart)
        set_spin(self.binEstep, decimals=4)
        set_spin(self.binEend)

        self.binQon = setEnabled(tip(QCheckBox(), self.TIP_binQon),
                                 self.binEon)
        self.binQstart = setEnabled(tip(QDoubleSpinBox(), self.TIP_binQstart),
                                    self.binEon, self.binQon)
        self.binQstep = setEnabled(tip(QDoubleSpinBox(), self.TIP_binQstep),
                                   self.binEon, self.binQon)
        self.binQend = setEnabled(tip(QDoubleSpinBox(), self.TIP_binQend),
                                  self.binEon, self.binQon)

        set_spin(self.binQstart)
        set_spin(self.binQstep)
        set_spin(self.binQend)

        self.maskDetectors = tip(QLineEdit(), self.TIP_maskDetectors)

        headers = ('Data runs', 'Comment', 'T (K)')
        self.dataRunsView = tip(
            DataTableView(self, headers,
                          TOFTOFSetupWidget.TofTofDataTableModel),
            self.TIP_dataRunsView)
        self.dataRunsView.horizontalHeader().setStretchLastSection(True)
        self.dataRunsView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.runDataModel = self.dataRunsView.model()

        # ui controls
        self.btnDataDir = tip(QPushButton('Browse'), self.TIP_btnDataDir)
        self.btnSaveDir = tip(QPushButton('Browse'), self.TIP_btnSaveDir)

        self.chkSubtractECVan = tip(
            QCheckBox('Subtract empty can from vanadium'),
            self.TIP_chkSubtractECVan)
        self.vanEcFactor = setEnabled(
            tip(QDoubleSpinBox(), self.TIP_vanEcFactor), self.chkSubtractECVan)
        set_spin(self.vanEcFactor, 0, 1)
        self.chkReplaceNaNs = setEnabled(
            tip(QCheckBox(u'Replace special values in S(Q, ω) with 0'),
                self.TIP_chkReplaceNaNs), self.binEon)
        self.chkCreateDiff = setEnabled(
            tip(QCheckBox('Create diffractograms'), self.TIP_chkCreateDiff),
            self.binEon)
        self.chkKeepSteps = tip(QCheckBox('Keep intermediate steps'),
                                self.TIP_chkKeepSteps)

        self.chkSofTWNxspe = setEnabled(
            tip(QCheckBox('NXSPE'), self.TIP_chkNxspe), self.binEon)
        self.chkSofTWNexus = tip(QCheckBox('NeXus'), self.TIP_chkNexus)
        self.chkSofTWAscii = tip(QCheckBox('Ascii'), self.TIP_chkAscii)

        self.chkSofQWNexus = setEnabled(
            tip(QCheckBox('NeXus'), self.TIP_chkNexus), self.binEon,
            self.binQon)
        self.chkSofQWAscii = setEnabled(
            tip(QCheckBox('Ascii'), self.TIP_chkAscii), self.binEon,
            self.binQon)

        self.rbtNormaliseNone = tip(QRadioButton('none'),
                                    self.TIP_rbtNormaliseNone)
        self.rbtNormaliseMonitor = tip(QRadioButton('to monitor'),
                                       self.TIP_rbtNormaliseMonitor)
        self.rbtNormaliseTime = tip(QRadioButton('to time'),
                                    self.TIP_rbtNormaliseTime)

        self.rbtCorrectTOFNone = tip(QRadioButton('none'),
                                     self.TIP_rbtCorrectTOFNone)
        self.rbtCorrectTOFVan = tip(QRadioButton('vanadium'),
                                    self.TIP_rbtCorrectTOFVan)
        self.rbtCorrectTOFSample = tip(QRadioButton('sample'),
                                       self.TIP_rbtCorrectTOFSample)

        # ui layout
        def _box(cls, widgets):
            box = cls()
            for wgt in widgets:
                if isinstance(wgt, QLayout):
                    box.addLayout(wgt)
                elif isinstance(wgt, QWidget):
                    box.addWidget(wgt)
                else:
                    box.addStretch(wgt)
            return box

        def hbox(*widgets):
            return _box(QHBoxLayout, widgets)

        def vbox(*widgets):
            return _box(QVBoxLayout, widgets)

        def label(text, tip):
            label = QLabel(text)
            if tip:
                label.setToolTip(tip)
            return label

        self.gbSave = QGroupBox('Save reduced data')
        self.gbSave.setCheckable(True)

        gbDataDir = QGroupBox('Data search directory')
        gbPrefix = QGroupBox('Workspace prefix')
        gbOptions = QGroupBox('Options')
        gbInputs = QGroupBox('Inputs')
        gbBinning = QGroupBox('Binning')
        gbData = QGroupBox('Data')

        box = QVBoxLayout()
        self._layout.addLayout(box)

        box.addLayout(
            hbox(vbox(gbDataDir, gbInputs, gbBinning, gbOptions, 1),
                 vbox(gbPrefix, gbData, self.gbSave)))

        gbDataDir.setLayout(hbox(self.dataDir, self.btnDataDir))
        gbPrefix.setLayout(hbox(self.prefix, ))

        grid = QGridLayout()
        grid.addWidget(self.chkSubtractECVan, 0, 0, 1, 4)
        grid.addWidget(label('Normalise', 'tip'), 1, 0)
        grid.addWidget(self.rbtNormaliseNone, 1, 1)
        grid.addWidget(self.rbtNormaliseMonitor, 1, 2)
        grid.addWidget(self.rbtNormaliseTime, 1, 3)
        grid.addWidget(QLabel('Correct TOF'), 2, 0)
        grid.addWidget(self.rbtCorrectTOFNone, 2, 1)
        grid.addWidget(self.rbtCorrectTOFVan, 2, 2)
        grid.addWidget(self.rbtCorrectTOFSample, 2, 3)
        grid.addWidget(self.chkReplaceNaNs, 3, 0, 1, 4)
        grid.addWidget(self.chkCreateDiff, 4, 0, 1, 4)
        grid.addWidget(self.chkKeepSteps, 5, 0, 1, 4)
        grid.setColumnStretch(4, 1)

        gbOptions.setLayout(grid)

        btnGroup = QButtonGroup(self)
        btnGroup.addButton(self.rbtNormaliseNone)
        btnGroup.addButton(self.rbtNormaliseMonitor)
        btnGroup.addButton(self.rbtNormaliseTime)

        btnGroup = QButtonGroup(self)
        btnGroup.addButton(self.rbtCorrectTOFNone)
        btnGroup.addButton(self.rbtCorrectTOFVan)
        btnGroup.addButton(self.rbtCorrectTOFSample)

        grid = QGridLayout()
        grid.addWidget(QLabel('Vanadium runs'), 0, 0)
        grid.addWidget(self.vanRuns, 0, 1, 1, 3)
        grid.addWidget(QLabel('Van. comment'), 1, 0)
        grid.addWidget(self.vanCmnt, 1, 1, 1, 1)
        grid.addLayout(hbox(QLabel('EC factor'), self.vanEcFactor), 1, 2, 1, 1)
        grid.addLayout(hbox(QLabel('T (K)'), self.vanTemp), 1, 3)
        grid.addWidget(QLabel('Empty can runs'), 2, 0)
        grid.addWidget(self.ecRuns, 2, 1, 1, 1)
        grid.addLayout(hbox(QLabel('EC factor'), self.ecFactor), 2, 2, 1, 1)
        grid.addLayout(hbox(QLabel('T (K)'), self.ecTemp), 2, 3)
        grid.addWidget(QLabel('Mask detectors'), 3, 0)
        grid.addWidget(self.maskDetectors, 3, 1, 1, 3)

        gbInputs.setLayout(grid)

        grid = QGridLayout()
        grid.addWidget(QLabel('on'), 0, 1)
        grid.addWidget(QLabel('start'), 0, 2)
        grid.addWidget(QLabel('step'), 0, 3)
        grid.addWidget(QLabel('end'), 0, 4)

        grid.addWidget(QLabel('Energy'), 1, 0)
        grid.addWidget(self.binEon, 1, 1)
        grid.addWidget(self.binEstart, 1, 2)
        grid.addWidget(self.binEstep, 1, 3)
        grid.addWidget(self.binEend, 1, 4)

        grid.addWidget(QLabel('Q'), 2, 0)
        grid.addWidget(self.binQon, 2, 1)
        grid.addWidget(self.binQstart, 2, 2)
        grid.addWidget(self.binQstep, 2, 3)
        grid.addWidget(self.binQend, 2, 4)

        for col in (0, 2, 3, 4):
            grid.setColumnStretch(col, 1)

        gbBinning.setLayout(grid)

        gbData.setLayout(hbox(self.dataRunsView))

        grid = QGridLayout()
        saveDirGroup = hbox(self.saveDir, self.btnSaveDir)
        grid.addWidget(QLabel('Directory'), 0, 0)
        grid.addLayout(saveDirGroup, 0, 1, 1, 4)
        grid.addWidget(setEnabled(QLabel(u'S(Q, ω):'), self.binEon), 1, 0)
        grid.addWidget(self.chkSofQWNexus, 1, 1)
        grid.addWidget(self.chkSofQWAscii, 1, 2)
        grid.addItem(QSpacerItem(5, 5, hPolicy=QSizePolicy.Expanding), 1, 4)
        grid.addWidget(QLabel(u'S(2θ, ω):'), 2, 0)
        grid.addWidget(self.chkSofTWNexus, 2, 1)
        grid.addWidget(self.chkSofTWAscii, 2, 2)
        grid.addWidget(self.chkSofTWNxspe, 2, 3)

        self.gbSave.setLayout(grid)

        # handle signals
        self.btnDataDir.clicked.connect(self._onDataDir)
        self.btnSaveDir.clicked.connect(self._onSaveDir)
        self.runDataModel.selectCell.connect(self._onSelectedCell)

    def _onDataDir(self):
        dirname = self.dir_browse_dialog(self.dataDir.text())
        if dirname:
            self.dataDir.setText(dirname)

    def _onSaveDir(self):
        dirname = self.dir_browse_dialog(self.saveDir.text())
        if dirname:
            self.saveDir.setText(dirname)

    def _onSelectedCell(self, index):
        self.dataRunsView.setCurrentIndex(index)
        self.dataRunsView.setFocus()

    def get_state(self):
        elem = TOFTOFScriptElement()

        def line_text(lineEdit):
            return str(lineEdit.text()).strip()

        def is_checked(checkBox):
            return checkBox.isChecked() and checkBox.isEnabled()

        elem.facility_name = self._settings.facility_name
        elem.instrument_name = self._settings.instrument_name

        elem.prefix = line_text(self.prefix)
        elem.dataDir = line_text(self.dataDir)

        elem.vanRuns = line_text(self.vanRuns)
        elem.vanCmnt = line_text(self.vanCmnt)
        elem.vanTemp = OptionalFloat(line_text(self.vanTemp))
        elem.vanEcFactor = self.vanEcFactor.value()

        elem.ecRuns = line_text(self.ecRuns)
        elem.ecTemp = OptionalFloat(line_text(self.ecTemp))
        elem.ecFactor = self.ecFactor.value()

        elem.dataRuns = self.runDataModel.tableData

        elem.binEon = is_checked(self.binEon)
        elem.binEstart = self.binEstart.value()
        elem.binEstep = self.binEstep.value()
        elem.binEend = self.binEend.value()

        elem.binQon = is_checked(self.binQon)
        elem.binQstart = self.binQstart.value()
        elem.binQstep = self.binQstep.value()
        elem.binQend = self.binQend.value()

        elem.maskDetectors = line_text(self.maskDetectors)

        elem.subtractECVan = is_checked(self.chkSubtractECVan)
        elem.replaceNaNs = is_checked(self.chkReplaceNaNs)
        elem.createDiff = is_checked(self.chkCreateDiff)
        elem.keepSteps = is_checked(self.chkKeepSteps)

        elem.saveDir = line_text(self.saveDir)
        elem.saveSofTWNxspe = is_checked(self.chkSofTWNxspe)
        elem.saveSofTWNexus = is_checked(self.chkSofTWNexus)
        elem.saveSofTWAscii = is_checked(self.chkSofTWAscii)
        elem.saveSofQWNexus = is_checked(self.chkSofQWNexus)
        elem.saveSofQWAscii = is_checked(self.chkSofQWAscii)

        elem.normalise     = elem.NORM_MONITOR    if self.rbtNormaliseMonitor.isChecked() else \
            elem.NORM_TIME       if self.rbtNormaliseTime.isChecked()    else \
            elem.NORM_NONE

        elem.correctTof    = elem.CORR_TOF_VAN    if self.rbtCorrectTOFVan.isChecked()    else \
            elem.CORR_TOF_SAMPLE if self.rbtCorrectTOFSample.isChecked() else \
            elem.CORR_TOF_NONE
        return elem

    def set_state(self, toftofScriptElement):
        elem = toftofScriptElement

        self.prefix.setText(elem.prefix)

        self.dataDir.setText(elem.dataDir)

        self.vanRuns.setText(elem.vanRuns)
        self.vanCmnt.setText(elem.vanCmnt)
        self.vanTemp.setText(str(elem.vanTemp))
        self.vanEcFactor.setValue(elem.vanEcFactor)

        self.ecRuns.setText(elem.ecRuns)
        self.ecTemp.setText(str(elem.ecTemp))
        self.ecFactor.setValue(elem.ecFactor)

        self.runDataModel.beginResetModel()
        self.runDataModel.tableData = elem.dataRuns
        self.runDataModel.endResetModel()

        self.binEon.setChecked(elem.binEon)

        self.binEstart.setValue(elem.binEstart)
        self.binEstep.setValue(elem.binEstep)
        self.binEend.setValue(elem.binEend)

        self.binQon.setChecked(elem.binQon)

        self.binQstart.setValue(elem.binQstart)
        self.binQstep.setValue(elem.binQstep)
        self.binQend.setValue(elem.binQend)

        self.maskDetectors.setText(elem.maskDetectors)

        self.chkSubtractECVan.setChecked(elem.subtractECVan)
        self.chkReplaceNaNs.setChecked(elem.replaceNaNs)
        self.chkCreateDiff.setChecked(elem.createDiff)
        self.chkKeepSteps.setChecked(elem.keepSteps)

        self.saveDir.setText(elem.saveDir)
        self.chkSofTWNxspe.setChecked(elem.saveSofTWNxspe)
        self.chkSofTWNexus.setChecked(elem.saveSofTWNexus)
        self.chkSofTWAscii.setChecked(elem.saveSofTWAscii)
        self.chkSofQWNexus.setChecked(elem.saveSofQWNexus)
        self.chkSofQWAscii.setChecked(elem.saveSofQWAscii)
        self.gbSave.setChecked(
            any((elem.saveSofTWNxspe, elem.saveSofTWNexus, elem.saveSofTWAscii,
                 elem.saveSofQWNexus, elem.saveSofQWAscii)))

        if elem.normalise == elem.NORM_MONITOR:
            self.rbtNormaliseMonitor.setChecked(True)
        elif elem.normalise == elem.NORM_TIME:
            self.rbtNormaliseTime.setChecked(True)
        else:
            self.rbtNormaliseNone.setChecked(True)

        if elem.correctTof == elem.CORR_TOF_VAN:
            self.rbtCorrectTOFVan.setChecked(True)
        elif elem.correctTof == elem.CORR_TOF_SAMPLE:
            self.rbtCorrectTOFSample.setChecked(True)
        else:
            self.rbtCorrectTOFNone.setChecked(True)
Example #9
0
class WidgetGallery(QWidget):
    def __init__(self, parent=None):
        super(WidgetGallery, self).__init__(parent)

        self.originalPalette = QApplication.palette()

        styleComboBox = QComboBox()
        styleComboBox.addItems(QStyleFactory.keys())

        styleLabel = QLabel("&Style:")
        styleLabel.setBuddy(styleComboBox)

        self.useStylePaletteCheckBox = QCheckBox(
            "&Use style's standard palette")
        self.useStylePaletteCheckBox.setChecked(True)

        disableWidgetsCheckBox = QCheckBox("&Disable widgets")

        self.createTopLeftGroupBox()
        self.createTopRightGroupBox()
        self.createBottomLeftTabWidget()
        self.createBottomRightGroupBox()
        self.createProgressBar()

        styleComboBox.activated[str].connect(self.changeStyle)
        self.useStylePaletteCheckBox.toggled.connect(self.changePalette)
        disableWidgetsCheckBox.toggled.connect(
            self.topLeftGroupBox.setDisabled)
        disableWidgetsCheckBox.toggled.connect(
            self.topRightGroupBox.setDisabled)
        disableWidgetsCheckBox.toggled.connect(
            self.bottomLeftTabWidget.setDisabled)
        disableWidgetsCheckBox.toggled.connect(
            self.bottomRightGroupBox.setDisabled)

        topLayout = QHBoxLayout()
        topLayout.addWidget(styleLabel)
        topLayout.addWidget(styleComboBox)
        topLayout.addStretch(1)
        topLayout.addWidget(self.useStylePaletteCheckBox)
        topLayout.addWidget(disableWidgetsCheckBox)

        mainLayout = QGridLayout()
        mainLayout.addLayout(topLayout, 0, 0, 1, 2)
        mainLayout.addWidget(self.topLeftGroupBox, 1, 0)
        mainLayout.addWidget(self.topRightGroupBox, 1, 1)
        mainLayout.addWidget(self.bottomLeftTabWidget, 2, 0)
        mainLayout.addWidget(self.bottomRightGroupBox, 2, 1)
        mainLayout.addWidget(self.progressBar, 3, 0, 1, 2)
        mainLayout.setRowStretch(1, 1)
        mainLayout.setRowStretch(2, 1)
        mainLayout.setColumnStretch(0, 1)
        mainLayout.setColumnStretch(1, 1)
        self.setLayout(mainLayout)

        self.changeStyle('Windows')

        self.val = 0

    def changeStyle(self, styleName):
        QApplication.setStyle(QStyleFactory.create(styleName))
        self.changePalette()

    def changePalette(self):
        if (self.useStylePaletteCheckBox.isChecked()):
            QApplication.setPalette(QApplication.style().standardPalette())
        else:
            QApplication.setPalette(self.originalPalette)

    def advanceProgressBar(self):
        self.progressBar.setMaximum(100)
        self.val += 1
        self.progressBar.setValue(self.val)

    def createTopLeftGroupBox(self):
        self.topLeftGroupBox = QGroupBox("Group 1")

        radioButton1 = QRadioButton("Radio button 1")
        radioButton2 = QRadioButton("Radio button 2")
        radioButton3 = QRadioButton("Radio button 3")
        radioButton1.setChecked(True)

        checkBox = QCheckBox("Tri-state check box")
        checkBox.setTristate(True)
        checkBox.setCheckState(Qt.PartiallyChecked)

        layout = QVBoxLayout()
        layout.addWidget(radioButton1)
        layout.addWidget(radioButton2)
        layout.addWidget(radioButton3)
        layout.addWidget(checkBox)
        layout.addStretch(1)
        self.topLeftGroupBox.setLayout(layout)

    def createTopRightGroupBox(self):
        self.topRightGroupBox = QGroupBox("Group 2")

        defaultPushButton = QPushButton("Default Push Button")
        defaultPushButton.setDefault(True)

        togglePushButton = QPushButton("Toggle Push Button")
        togglePushButton.setCheckable(True)
        togglePushButton.setChecked(True)

        flatPushButton = QPushButton("Flat Push Button")
        flatPushButton.setFlat(True)

        layout = QVBoxLayout()
        layout.addWidget(defaultPushButton)
        layout.addWidget(togglePushButton)
        layout.addWidget(flatPushButton)
        layout.addStretch(1)
        self.topRightGroupBox.setLayout(layout)

    def createBottomLeftTabWidget(self):
        self.bottomLeftTabWidget = QTabWidget()
        self.bottomLeftTabWidget.setSizePolicy(QSizePolicy.Preferred,
                                               QSizePolicy.Ignored)

        tab1 = QWidget()
        tableWidget = QTableWidget(10, 10)

        tab1hbox = QHBoxLayout()
        tab1hbox.setContentsMargins(5, 5, 5, 5)
        tab1hbox.addWidget(tableWidget)
        tab1.setLayout(tab1hbox)

        tab2 = QWidget()
        textEdit = QTextEdit()

        textEdit.setPlainText("Twinkle, twinkle, little star,\n"
                              "How I wonder what you are.\n"
                              "Up above the world so high,\n"
                              "Like a diamond in the sky.\n"
                              "Twinkle, twinkle, little star,\n"
                              "How I wonder what you are!\n")

        tab2hbox = QHBoxLayout()
        tab2hbox.setContentsMargins(5, 5, 5, 5)
        tab2hbox.addWidget(textEdit)
        tab2.setLayout(tab2hbox)

        self.bottomLeftTabWidget.addTab(tab1, "&Table")
        self.bottomLeftTabWidget.addTab(tab2, "Text &Edit")

    def createBottomRightGroupBox(self):
        self.bottomRightGroupBox = QGroupBox("Group 3")
        self.bottomRightGroupBox.setCheckable(True)
        self.bottomRightGroupBox.setChecked(True)

        lineEdit = QLineEdit('s3cRe7')
        lineEdit.setEchoMode(QLineEdit.Password)

        spinBox = QSpinBox(self.bottomRightGroupBox)
        spinBox.setValue(50)

        dateTimeEdit = QDateTimeEdit(self.bottomRightGroupBox)
        dateTimeEdit.setDateTime(QDateTime.currentDateTime())

        slider = QSlider(Qt.Horizontal, self.bottomRightGroupBox)
        slider.setValue(40)

        scrollBar = QScrollBar(Qt.Horizontal, self.bottomRightGroupBox)
        scrollBar.setValue(60)

        dial = QDial(self.bottomRightGroupBox)
        dial.setValue(30)
        dial.setNotchesVisible(True)

        layout = QGridLayout()
        layout.addWidget(lineEdit, 0, 0, 1, 2)
        layout.addWidget(spinBox, 1, 0, 1, 2)
        layout.addWidget(dateTimeEdit, 2, 0, 1, 2)
        layout.addWidget(slider, 3, 0)
        layout.addWidget(scrollBar, 4, 0)
        layout.addWidget(dial, 3, 1, 2, 1)
        layout.setRowStretch(5, 1)
        self.bottomRightGroupBox.setLayout(layout)

    def createProgressBar(self):
        self.progressBar = QProgressBar()
        self.progressBar.setRange(0, 10000)
        self.progressBar.setValue(0)

        timer = QTimer(self)
        timer.timeout.connect(self.advanceProgressBar)
        timer.start(1000)
Example #10
0
class KernelConnectionDialog(QDialog):
    """Dialog to connect to existing kernels (either local or remote)."""
    def __init__(self, parent=None):
        super(KernelConnectionDialog, self).__init__(parent)
        self.setWindowTitle(_('Connect to an existing kernel'))

        main_label = QLabel(
            _("<p>Please select a local JSON connection file (<i>e.g.</i> "
              "<tt>kernel-1234.json</tt>) of the existing kernel.  "
              "<br><br>"
              "If connecting to a remote machine, enter the SSH information, "
              "adjust the command how to get jupyter runtime directory (if needed) "
              "push the button to fetch remote configuration files and select one "
              "of the loaded options."
              "<br><br>"
              "To learn more about starting external kernels and connecting "
              "to them, see <a href=\"https://docs.spyder-ide.org/"
              "ipythonconsole.html#connect-to-an-external-kernel\">"
              "our documentation</a>.</p>"))
        main_label.setWordWrap(True)
        main_label.setAlignment(Qt.AlignJustify)
        main_label.setOpenExternalLinks(True)

        self.TEXT_FETCH_REMOTE_CONN_FILES_BTN = 'Fetch remote connection files'
        self.DEFAULT_CMD_FOR_JUPYTER_RUNTIME = 'jupyter --runtime-dir'

        # Connection file
        cf_label = QLabel(_('Connection file:'))
        self.cf = QLineEdit()
        self.cf.setPlaceholderText(_('Kernel connection file path'))
        self.cf.setMinimumWidth(350)
        cf_open_btn = QPushButton(_('Browse'))
        cf_open_btn.clicked.connect(self.select_connection_file)

        cf_layout = QHBoxLayout()
        cf_layout.addWidget(cf_label)
        cf_layout.addWidget(self.cf)
        cf_layout.addWidget(cf_open_btn)

        # Remote kernel groupbox
        self.rm_group = QGroupBox(_("This is a remote kernel (via SSH)"))

        # SSH connection
        hn_label = QLabel(_('Hostname:'))
        self.hn = QLineEdit()
        pn_label = QLabel(_('Port:'))
        self.pn = QLineEdit()
        self.pn.setMaximumWidth(75)

        un_label = QLabel(_('Username:'******'Password:'******'SSH keyfile:'))

        self.pw = QLineEdit()
        self.pw.setEchoMode(QLineEdit.Password)
        self.pw_radio.toggled.connect(self.pw.setEnabled)
        self.kf_radio.toggled.connect(self.pw.setDisabled)

        self.kf = QLineEdit()
        kf_open_btn = QPushButton(_('Browse'))
        kf_open_btn.clicked.connect(self.select_ssh_key)
        kf_layout = QHBoxLayout()
        kf_layout.addWidget(self.kf)
        kf_layout.addWidget(kf_open_btn)

        kfp_label = QLabel(_('Passphase:'))
        self.kfp = QLineEdit()
        self.kfp.setPlaceholderText(_('Optional'))
        self.kfp.setEchoMode(QLineEdit.Password)

        self.kf_radio.toggled.connect(self.kf.setEnabled)
        self.kf_radio.toggled.connect(self.kfp.setEnabled)
        self.kf_radio.toggled.connect(kf_open_btn.setEnabled)
        self.kf_radio.toggled.connect(kfp_label.setEnabled)
        self.pw_radio.toggled.connect(self.kf.setDisabled)
        self.pw_radio.toggled.connect(self.kfp.setDisabled)
        self.pw_radio.toggled.connect(kf_open_btn.setDisabled)
        self.pw_radio.toggled.connect(kfp_label.setDisabled)

        # Button to fetch JSON files listing
        self.kf_fetch_conn_files_btn = QPushButton(
            _(self.TEXT_FETCH_REMOTE_CONN_FILES_BTN))
        self.kf_fetch_conn_files_btn.clicked.connect(
            self.fill_combobox_with_fetched_remote_connection_files)
        self.cb_remote_conn_files = QComboBox()
        self.cb_remote_conn_files.currentIndexChanged.connect(
            self._take_over_selected_remote_configuration_file)

        # Remote kernel groupbox
        self.start_remote_kernel_group = QGroupBox(_("Start remote kernel"))

        # Advanced settings to get remote connection files
        jupyter_runtime_location_cmd_label = QLabel(
            _('Command to get Jupyter runtime:'))
        self.jupyter_runtime_location_cmd_lineedit = QLineEdit()
        self.jupyter_runtime_location_cmd_lineedit.setPlaceholderText(
            _(self.DEFAULT_CMD_FOR_JUPYTER_RUNTIME))

        # SSH layout
        ssh_layout = QGridLayout()
        ssh_layout.addWidget(hn_label, 0, 0, 1, 2)
        ssh_layout.addWidget(self.hn, 0, 2)
        ssh_layout.addWidget(pn_label, 0, 3)
        ssh_layout.addWidget(self.pn, 0, 4)
        ssh_layout.addWidget(un_label, 1, 0, 1, 2)
        ssh_layout.addWidget(self.un, 1, 2, 1, 3)

        # SSH authentication layout
        auth_layout = QGridLayout()
        auth_layout.addWidget(self.pw_radio, 1, 0)
        auth_layout.addWidget(pw_label, 1, 1)
        auth_layout.addWidget(self.pw, 1, 2)
        auth_layout.addWidget(self.kf_radio, 2, 0)
        auth_layout.addWidget(kf_label, 2, 1)
        auth_layout.addLayout(kf_layout, 2, 2)
        auth_layout.addWidget(kfp_label, 3, 1)
        auth_layout.addWidget(self.kfp, 3, 2)

        auth_layout.addWidget(jupyter_runtime_location_cmd_label, 4, 1)
        auth_layout.addWidget(self.jupyter_runtime_location_cmd_lineedit, 4, 2)
        auth_layout.addWidget(self.kf_fetch_conn_files_btn, 5, 1)
        auth_layout.addWidget(self.cb_remote_conn_files, 5, 2)

        auth_group.setLayout(auth_layout)

        # Remote kernel layout
        rm_layout = QVBoxLayout()
        rm_layout.addLayout(ssh_layout)
        rm_layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 8)))
        rm_layout.addWidget(auth_group)
        self.rm_group.setLayout(rm_layout)
        self.rm_group.setCheckable(True)
        self.rm_group.toggled.connect(self.pw_radio.setChecked)

        # Ok and Cancel buttons
        self.accept_btns = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)

        self.accept_btns.accepted.connect(self.save_connection_settings)
        self.accept_btns.accepted.connect(self.accept)
        self.accept_btns.rejected.connect(self.reject)

        # Save connection settings checkbox
        self.save_layout = QCheckBox(self)
        self.save_layout.setText(_("Save connection settings"))

        btns_layout = QHBoxLayout()
        btns_layout.addWidget(self.save_layout)
        btns_layout.addWidget(self.accept_btns)

        # Dialog layout
        layout = QVBoxLayout(self)
        layout.addWidget(main_label)
        layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 8)))
        layout.addLayout(cf_layout)
        layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 12)))
        layout.addWidget(self.rm_group)
        layout.addLayout(btns_layout)

        # List with connection file paths found on the remote host
        self.remote_conn_file_paths = []

        self.load_connection_settings()

    def load_connection_settings(self):
        """Load the user's previously-saved kernel connection settings."""
        existing_kernel = CONF.get("existing-kernel", "settings", {})

        connection_file_path = existing_kernel.get("json_file_path", "")
        is_remote = existing_kernel.get("is_remote", False)
        username = existing_kernel.get("username", "")
        hostname = existing_kernel.get("hostname", "")
        port = str(existing_kernel.get("port", 22))
        is_ssh_kf = existing_kernel.get("is_ssh_keyfile", False)
        ssh_kf = existing_kernel.get("ssh_key_file_path", "")
        cmd_jupyter_runtime = existing_kernel.get("cmd_jupyter_runtime")

        if connection_file_path != "":
            self.cf.setText(connection_file_path)
        if username != "":
            self.un.setText(username)
        if hostname != "":
            self.hn.setText(hostname)
        if ssh_kf != "":
            self.kf.setText(ssh_kf)
        if cmd_jupyter_runtime != "":
            self.jupyter_runtime_location_cmd_lineedit.setText(
                cmd_jupyter_runtime)

        self.rm_group.setChecked(is_remote)
        self.pn.setText(port)
        self.kf_radio.setChecked(is_ssh_kf)
        self.pw_radio.setChecked(not is_ssh_kf)

        try:
            import keyring
            ssh_passphrase = keyring.get_password("spyder_remote_kernel",
                                                  "ssh_key_passphrase")
            ssh_password = keyring.get_password("spyder_remote_kernel",
                                                "ssh_password")
            if ssh_passphrase:
                self.kfp.setText(ssh_passphrase)
            if ssh_password:
                self.pw.setText(ssh_password)
        except Exception:
            pass

    def save_connection_settings(self):
        """Save user's kernel connection settings."""

        if not self.save_layout.isChecked():
            return

        is_ssh_key = bool(self.kf_radio.isChecked())
        connection_settings = {
            "json_file_path":
            self.cf.text(),
            "is_remote":
            self.rm_group.isChecked(),
            "username":
            self.un.text(),
            "hostname":
            self.hn.text(),
            "port":
            self.pn.text(),
            "is_ssh_keyfile":
            is_ssh_key,
            "ssh_key_file_path":
            self.kf.text(),
            "cmd_jupyter_runtime":
            self.jupyter_runtime_location_cmd_lineedit.text()
        }
        CONF.set("existing-kernel", "settings", connection_settings)

        try:
            import keyring
            if is_ssh_key:
                keyring.set_password("spyder_remote_kernel",
                                     "ssh_key_passphrase", self.kfp.text())
            else:
                keyring.set_password("spyder_remote_kernel", "ssh_password",
                                     self.pw.text())
        except Exception:
            pass

    def select_connection_file(self):
        cf = getopenfilename(self, _('Select kernel connection file'),
                             jupyter_runtime_dir(), '*.json;;*.*')[0]
        self.cf.setText(cf)

    def select_ssh_key(self):
        kf = getopenfilename(self, _('Select SSH keyfile'), get_home_dir(),
                             '*.pem;;*')[0]
        self.kf.setText(kf)

    def _take_over_selected_remote_configuration_file(
            self, chosen_idx_of_combobox_with_remote_conn_files):
        remote_path_filename = self.remote_conn_file_paths[
            chosen_idx_of_combobox_with_remote_conn_files]
        self.cf.setText(remote_path_filename)

    def fill_combobox_with_fetched_remote_connection_files(self):
        """
        Fill the combobox with found remote connection json files.

        :return: None
        """
        _, username, _, only_host, port, keyfile, password = KernelConnectionDialog._get_remote_config(
            self)
        cmd_to_get_location_of_jupyter_runtime_files = self.jupyter_runtime_location_cmd_lineedit.text(
        )
        self.remote_conn_file_paths = self._fetch_connection_files_list(
            host=only_host,
            keyfile=keyfile,
            password=password,
            username=username,
            port=port,
            cmd_to_get_location_of_jupyter_runtime_files=
            cmd_to_get_location_of_jupyter_runtime_files)
        conn_files_short = [
            c.rsplit('/', 1)[1] if '/' in c else c
            for c in self.remote_conn_file_paths
        ]
        self.cb_remote_conn_files.addItems(conn_files_short)

    def _fetch_connection_files_list(
            self, host: str, keyfile: Optional[str], password: Optional[str],
            username: Optional[str], port: str,
            cmd_to_get_location_of_jupyter_runtime_files: Optional[str]):
        """

        :param host: URL or IP of the host.
        :param keyfile: SSH key path or None if no key was provided.
        :param password: Password for SSH connection or None if no password is used.
        :rtype: List[str]
        :return:
        """
        import paramiko
        client = paramiko.SSHClient()
        self.kf_fetch_conn_files_btn.setDisabled(True)
        list_of_copied_connection_files = []
        try:
            client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            client.connect(hostname=host,
                           port=int(port),
                           key_filename=keyfile,
                           passphrase=password,
                           username=username,
                           timeout=10,
                           auth_timeout=10)
            if cmd_to_get_location_of_jupyter_runtime_files is None:
                cmd_to_get_location_of_jupyter_runtime_files = self.DEFAULT_CMD_FOR_JUPYTER_RUNTIME

            self.kf_fetch_conn_files_btn.setText(
                'Getting location of jupyter runtime...')
            stdin, stdout, stderr = client.exec_command(
                cmd_to_get_location_of_jupyter_runtime_files)
            location_of_jupyter_runtime = stdout.readlines()
            if len(location_of_jupyter_runtime) > 0:
                location_of_jupyter_runtime = location_of_jupyter_runtime[
                    0].strip()

                # get absolute paths
                stdin, stdout, stderr = client.exec_command(
                    f'ls -d {location_of_jupyter_runtime}/*')
                list_of_connection_files = stdout.readlines()

                if len(list_of_connection_files) > 0:
                    list_of_connection_files = [
                        l.strip() for l in list_of_connection_files
                    ]

                    import tempfile
                    import os

                    temp_dir = tempfile.gettempdir()
                    only_filenames = [
                        f.rsplit('/', 1)[1] for f in list_of_connection_files
                    ]
                    list_of_copied_connection_files = [
                        os.path.join(temp_dir, f) for f in only_filenames
                    ]
                    self.kf_fetch_conn_files_btn.setText(
                        f'Downloading {len(list_of_connection_files)} connection files...'
                    )
                    for remote_path, filename_only in zip(
                            list_of_connection_files, only_filenames):
                        sftp = client.open_sftp()
                        sftp.get(remote_path,
                                 os.path.join(temp_dir, filename_only))
                    sftp.close()
                else:
                    show_info_dialog(
                        "Warning",
                        f"Could not find any jupyter configuration files in {location_of_jupyter_runtime}."
                    )
            else:
                show_info_dialog(
                    "Warning",
                    f"Could not extract jupyter runtime location. Error from command line: {stderr.readlines()}"
                )
        finally:
            client.close()
            self.kf_fetch_conn_files_btn.setText(
                self.TEXT_FETCH_REMOTE_CONN_FILES_BTN)
            self.kf_fetch_conn_files_btn.setEnabled(True)

        return list_of_copied_connection_files

    @staticmethod
    def _get_remote_config(dialog):
        only_host = None
        username = None
        port = '22'

        if dialog.hn.text() and dialog.un.text():
            port = dialog.pn.text() if dialog.pn.text() else '22'
            only_host = dialog.hn.text()
            username = dialog.un.text()
            hostname = "{0}@{1}:{2}".format(username, only_host, port)
        else:
            hostname = None
        if dialog.pw_radio.isChecked():
            password = _falsy_to_none(dialog.pw.text())
            keyfile = None
        elif dialog.kf_radio.isChecked():
            keyfile = _falsy_to_none(dialog.kf.text())
            password = _falsy_to_none(dialog.kfp.text())
        else:  # imposible?
            keyfile = None
            password = None
        return dialog.cf.text(
        ), username, hostname, only_host, port, keyfile, password

    @staticmethod
    def get_connection_parameters(parent=None, dialog=None):
        if not dialog:
            dialog = KernelConnectionDialog(parent)
        result = dialog.exec_()
        is_remote = bool(dialog.rm_group.isChecked())
        accepted = result == QDialog.Accepted

        if is_remote:
            cf_text, _, hostname, _, _, keyfile, password = KernelConnectionDialog._get_remote_config(
                dialog)
            return cf_text, hostname, keyfile, password, accepted
        else:
            path = dialog.cf.text()
            _dir, filename = osp.dirname(path), osp.basename(path)
            if _dir == '' and not filename.endswith('.json'):
                path = osp.join(jupyter_runtime_dir(),
                                'kernel-' + path + '.json')
            return path, None, None, None, accepted
Example #11
0
class EFMDialog(QDialog):
    """A dialog to set up EFM calculation"""
    def __init__(self, appdata: CnaData, centralwidget):
        QDialog.__init__(self)
        self.setWindowTitle("Elementary Flux Mode Computation")

        self.appdata = appdata
        self.centralwidget = centralwidget
        self.eng = appdata.engine
        self.out = io.StringIO()
        self.err = io.StringIO()

        self.layout = QVBoxLayout()

        l1 = QHBoxLayout()
        self.constraints = QCheckBox("consider 0 in current scenario as off")
        self.constraints.setCheckState(Qt.Checked)
        l1.addWidget(self.constraints)
        self.layout.addItem(l1)

        l2 = QHBoxLayout()
        self.flux_bounds = QGroupBox(
            "use flux bounds to calculate elementary flux vectors")
        self.flux_bounds.setCheckable(True)
        self.flux_bounds.setChecked(False)

        vbox = QVBoxLayout()
        label = QLabel("Threshold for bounds to be unconstrained")
        vbox.addWidget(label)
        self.threshold = QLineEdit("100")
        validator = QIntValidator()
        validator.setBottom(0)
        self.threshold.setValidator(validator)
        vbox.addWidget(self.threshold)
        self.flux_bounds.setLayout(vbox)
        l2.addWidget(self.flux_bounds)
        self.layout.addItem(l2)

        l3 = QHBoxLayout()
        self.check_reversibility = QCheckBox("check reversibility")
        self.check_reversibility.setCheckState(Qt.Checked)
        l3.addWidget(self.check_reversibility)
        self.layout.addItem(l3)

        l4 = QHBoxLayout()
        self.convex_basis = QCheckBox("only convex basis")
        l4.addWidget(self.convex_basis)
        self.layout.addItem(l4)

        l5 = QHBoxLayout()
        self.isozymes = QCheckBox("consider isozymes only once")
        l5.addWidget(self.isozymes)
        self.layout.addItem(l5)

        # TODO: choose solver

        l7 = QHBoxLayout()
        self.rational_numbers = QCheckBox("use rational numbers")
        l7.addWidget(self.rational_numbers)
        self.layout.addItem(l7)

        lx = QHBoxLayout()
        self.button = QPushButton("Compute")
        self.cancel = QPushButton("Close")
        lx.addWidget(self.button)
        lx.addWidget(self.cancel)
        self.layout.addItem(lx)

        self.setLayout(self.layout)

        # Connecting the signal
        self.cancel.clicked.connect(self.reject)
        self.button.clicked.connect(self.compute)

    def compute(self):

        # create CobraModel for matlab
        self.appdata.create_cobra_model()
        legacy.read_cnapy_model(self.eng)

        # get some data
        reac_id = self.eng.get_reacID()

        # setting parameters
        a = self.eng.eval("constraints = {};",
                          nargout=0,
                          stdout=self.out,
                          stderr=self.err)
        scenario = {}
        if self.constraints.checkState(
        ) == Qt.Checked or self.flux_bounds.isChecked():
            onoff_str = ""
            for r in reac_id:
                if r in self.appdata.project.scen_values.keys():
                    (vl, vu) = self.appdata.project.scen_values[r]
                    if vl == vu:
                        if vl > 0:
                            onoff_str = onoff_str + " NaN"  # efmtool does not support 1
                        elif vl == 0:
                            scenario[r] = (0, 0)
                            onoff_str = onoff_str + " 0"
                        else:
                            onoff_str = onoff_str + " NaN"
                            print("WARN: negative value in scenario")
                    else:
                        onoff_str = onoff_str + " NaN"
                        print("WARN: not fixed value in scenario")
                else:
                    onoff_str = onoff_str + " NaN"

            onoff_str = "reaconoff = [" + onoff_str + "];"
            a = self.eng.eval(onoff_str,
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

            a = self.eng.eval("constraints.reaconoff = reaconoff;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

        if self.flux_bounds.isChecked():
            threshold = float(self.threshold.text())
            lb_str = ""
            ub_str = ""
            for r in reac_id:
                c_reaction = self.appdata.project.cobra_py_model.reactions.get_by_id(
                    r)
                if r in self.appdata.project.scen_values:
                    (vl, vu) = self.appdata.project.scen_values[r]
                else:
                    vl = c_reaction.lower_bound
                    vu = c_reaction.upper_bound
                if vl <= -threshold:
                    vl = "NaN"
                if vu >= threshold:
                    vu = "NaN"
                if vl == 0 and vu == 0:  # already in reaconoff, can be skipped here
                    vl = "NaN"
                    vu = "NaN"
                lb_str = lb_str + " " + str(vl)
                ub_str = ub_str + " " + str(vu)

            lb_str = "lb = [" + lb_str + "];"
            a = self.eng.eval(lb_str,
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)
            a = self.eng.eval("constraints.lb = lb;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

            ub_str = "ub = [" + ub_str + "];"
            a = self.eng.eval(ub_str,
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)
            a = self.eng.eval("constraints.ub = ub;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

        # TODO set solver 4 = EFMTool 3 = MetaTool, 1 = cna Mex file, 0 = cna functions
        a = self.eng.eval("solver = 4;",
                          nargout=0,
                          stdout=self.out,
                          stderr=self.err)

        if self.check_reversibility.checkState() == Qt.Checked:
            a = self.eng.eval("irrev_flag = 1;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)
        else:
            a = self.eng.eval("irrev_flag = 0;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

        # convex basis computation is only possible with METATOOL solver=3
        if self.convex_basis.checkState() == Qt.Checked:
            a = self.eng.eval("conv_basis_flag = 1; solver = 3;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)
        else:
            a = self.eng.eval("conv_basis_flag = 0;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

        if self.isozymes.checkState() == Qt.Checked:
            a = self.eng.eval("iso_flag = 1;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)
        else:
            a = self.eng.eval("iso_flag = 0;",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

        # default we have no macromolecules and display is et to ALL
        a = self.eng.eval("c_macro=[]; display= 'ALL';",
                          nargout=0,
                          stdout=self.out,
                          stderr=self.err)

        if self.rational_numbers.checkState() == Qt.Checked:
            a = self.eng.eval(
                "efmtool_options = {'arithmetic', 'fractional'};",
                nargout=0,
                stdout=self.out,
                stderr=self.err)
        else:
            a = self.eng.eval("efmtool_options = {};",
                              nargout=0,
                              stdout=self.out,
                              stderr=self.err)

        if self.appdata.is_matlab_set():
            try:
                a = self.eng.eval(
                    "[ems, irrev_ems, ems_idx, ray] = CNAcomputeEFM(cnap, constraints,solver,irrev_flag,conv_basis_flag,iso_flag,c_macro,display,efmtool_options);",
                    nargout=0)

            except Exception:
                output = io.StringIO()
                traceback.print_exc(file=output)
                exstr = output.getvalue()
                print(exstr)
                QMessageBox.warning(
                    self, 'Unknown exception occured!',
                    exstr + '\nPlease report the problem to:\n\
                                    \nhttps://github.com/cnapy-org/CNApy/issues'
                )
                return
            else:
                ems = self.eng.workspace['ems']
                idx = self.eng.workspace['ems_idx']
                irreversible = numpy.squeeze(self.eng.workspace['irrev_ems'])
                unbounded = numpy.squeeze(self.eng.workspace['ray'])
                ems = numpy.array(ems)
                self.result2ui(ems, idx, reac_id, irreversible, unbounded,
                               scenario)

                self.accept()
        elif self.appdata.is_octave_ready():
            a = self.eng.eval(
                "[ems, irrev_ems, ems_idx, ray] = CNAcomputeEFM(cnap, constraints,solver,irrev_flag,conv_basis_flag,iso_flag,c_macro,display,efmtool_options);",
                nargout=0)

            ems = self.eng.pull('ems')
            idx = self.eng.pull('ems_idx')
            irreversible = numpy.squeeze(self.eng.pull('irrev_ems'))
            unbounded = numpy.squeeze(self.eng.pull('ray'))

            self.result2ui(ems, idx, reac_id, irreversible, unbounded,
                           scenario)

    def result2ui(self, ems, idx, reac_id, irreversible, unbounded, scenario):
        if len(ems) == 0:
            QMessageBox.information(
                self, 'No modes',
                'Modes have not been calculated or do not exist.')
        else:
            self.appdata.project.modes = FluxVectorContainer(
                ems, [reac_id[int(i) - 1] for i in idx[0]], irreversible,
                unbounded)
            self.centralwidget.mode_navigator.current = 0
            self.centralwidget.mode_navigator.scenario = scenario
            self.centralwidget.mode_navigator.title.setText("Mode Navigation")
            self.centralwidget.update_mode()
Example #12
0
class ImageRotationDialog(ExToolWindow):

    def __init__(self, signal, axes, parent, plugin):
        super(ImageRotationDialog, self).__init__(parent)
        self.ui = parent
        self.create_controls()
        self.accepted.connect(self.ok)
        self.rejected.connect(self.close_new)
        self.signal = signal
        self.plugin = plugin
        self.new_out = None
        self._connected_updates = False
        if isinstance(axes, str):
            axm = signal.signal.axes_manager
            if axes.startswith("nav"):
                axes = (axm._axes.index(axm.navigation_axes[0]),
                        axm._axes.index(axm.navigation_axes[1]))
            elif axes.startswith("sig"):
                axes = (axm._axes.index(axm.signal_axes[0]),
                        axm._axes.index(axm.signal_axes[1]))
        self.axes = axes
        self.setWindowTitle(tr("Rotate"))

        # TODO: TAG: Functionality check
        if not hasattr(signal.signal, 'events'):
            self.gbo_preview.setVisible(False)

        # TODO: Add dynamic rotation, e.g. one that rotates when source
        # signal's data_changed event triggers

    def connect(self):
        # TODO: Don't have to con/dis those in gbo
        self.opt_new.toggled.connect(self.close_new)
        self.num_angle.valueChanged.connect(self.update)
        self.chk_grid.toggled.connect(self.update)
        self.num_grid.valueChanged.connect(self.update)
        self.chk_reshape.toggled.connect(self.update)
        self.opt_new.toggled.connect(self.update)
        self.opt_replace.toggled.connect(self.update)

    def disconnect(self):
        self.num_angle.valueChanged.disconnect(self.update)
        self.chk_grid.toggled.disconnect(self.update)
        self.num_grid.valueChanged.disconnect(self.update)
        self.chk_reshape.toggled.disconnect(self.update)
        self.opt_new.toggled.disconnect(self.update)
        self.opt_replace.toggled.disconnect(self.update)

    def ok(self):
        # Draw figure if not already done
        # TODO: TAG: Functionality check
        if not hasattr(self.signal.signal, 'events') or \
                not self.gbo_preview.isChecked():
            self.update()
        angle = self.num_angle.value()
        reshape = self.chk_reshape.isChecked()
        self.plugin.record_code(
            r"<p>.rotate_signal({0}, reshape={1}, axes={2})".format(
                angle, reshape, self.axes))
        # Clean up event connections
        if self.new_out is not None:
            self.connect_update_plot(self.new_out.signal, disconnect=True)

    def close_new(self, value=False):
        if self.new_out is not None and not value:
            self.new_out.close()
            self.new_out = None
            self._connected_updates = False

    def set_preview(self, value):
        if not hasattr(self.signal.signal, 'events'):
            return
        if value:
            self.connect()
            self.update()
        else:
            self.disconnect()
            self.close_new()

    def _axes_in_nav(self):
        axm = self.signal.signal.axes_manager
        navidx = [axm._axes.index(ax) for ax in axm.navigation_axes]
        if self.axes[0] in navidx:
            return True
        return False

    def connect_update_plot(self, signal, disconnect=False):
        if self._connected_updates != disconnect:
            return  # Nothing to do, prevent double connections
        if self._axes_in_nav():
            f = signal._plot.navigator_plot.update
        else:
            f = signal._plot.signal_plot.update

        # TODO: TAG: Functionality check
        if hasattr(signal, 'events') and hasattr(
                signal.events, 'data_changed'):
            if disconnect:
                signal.events.data_changed.disconnect(f)
            else:
                signal.events.data_changed.connect(f, [])
        self._connected_updates = not disconnect

    def update(self):
        angle = self.num_angle.value()
        reshape = self.chk_reshape.isChecked()
        if self.opt_new.isChecked():
            if self.new_out is None:
                out = None
            else:
                out = self.new_out.signal
        elif self.opt_replace.isChecked():
            out = self.signal.signal
        else:
            return  # Indeterminate state, do nothing

        s = self.plugin.rotate_signal(angle, self.signal.signal, record=False,
                                      reshape=reshape, out=out, axes=self.axes)

        if out is None:
            s.metadata.General.title = self.signal.name + "[Rotated]"
            s.plot()
            self.connect_update_plot(s)
            if (self.gbo_preview.isChecked() and self.opt_new.isChecked() and
                                                         self.new_out is None):
                self.new_out = self.ui.lut_signalwrapper[s]
        else:
            s = out

        if self.chk_grid.isChecked() is True:
            pass    # TODO: Draw grid

    def create_controls(self):
        """
        Create UI controls.
        """
        vbox = QVBoxLayout()

        form = QFormLayout()
        self.num_angle = QDoubleSpinBox()
        self.num_angle.setValue(0.0)
        self.num_angle.setMinimum(-360)
        self.num_angle.setMaximum(360)
        form.addRow(tr("Angle:"), self.num_angle)
        vbox.addLayout(form)

        self.gbo_preview = QGroupBox(tr("Preview"))
        self.gbo_preview.setCheckable(True)
        self.gbo_preview.setChecked(False)
        gbo_vbox = QVBoxLayout()
        self.chk_grid = QCheckBox(tr("Grid"))
        self.chk_grid.setChecked(False)
        self.num_grid = QSpinBox()
        self.num_grid.setValue(4)
        self.num_grid.setMinimum(1)
        self.num_grid.setEnabled(False)
        self.chk_grid.toggled[bool].connect(self.num_grid.setEnabled)
        gbo_vbox.addWidget(self.chk_grid)
        gbo_vbox.addWidget(self.num_grid)
        self.gbo_preview.setLayout(gbo_vbox)
        vbox.addWidget(self.gbo_preview)

        self.gbo_preview.toggled[bool].connect(self.set_preview)

        self.gbo_output = QGroupBox(tr("Output"))
        self.opt_new = QRadioButton(tr("New signal"))
        self.opt_replace = QRadioButton(tr("In place"))
        self.opt_new.setChecked(True)
        gbo_vbox2 = QVBoxLayout()
        gbo_vbox2.addWidget(self.opt_new)
        gbo_vbox2.addWidget(self.opt_replace)
        self.gbo_output.setLayout(gbo_vbox2)
        vbox.addWidget(self.gbo_output)

        self.chk_reshape = QCheckBox(tr("Resize to fit"))
        self.chk_reshape.setChecked(False)
        vbox.addWidget(self.chk_reshape)

        self.btn_ok = QPushButton(tr("&OK"))
        self.btn_ok.setDefault(True)
        self.btn_ok.clicked.connect(self.accept)
        self.btn_cancel = QPushButton(tr("&Cancel"))
        self.btn_cancel.clicked.connect(self.reject)
        hbox = QHBoxLayout()
        hbox.addWidget(self.btn_ok)
        hbox.addWidget(self.btn_cancel)
        vbox.addLayout(hbox)

        vbox.addStretch(1)
        self.setLayout(vbox)
Example #13
0
class FitMapsWidget(FormBaseWidget):

    # Signal that is sent (to main window) to update global state of the program
    update_global_state = Signal()
    computations_complete = Signal(object)

    signal_map_fitting_complete = Signal()
    signal_activate_tab_xrf_maps = Signal()

    def __init__(self, *, gpc, gui_vars):
        super().__init__()

        # Global processing classes
        self.gpc = gpc
        # Global GUI variables (used for control of GUI state)
        self.gui_vars = gui_vars

        # Reference to the main window. The main window will hold
        #   references to all non-modal windows that could be opened
        #   from multiple places in the program.
        self.ref_main_window = self.gui_vars["ref_main_window"]

        self.update_global_state.connect(self.ref_main_window.update_widget_state)

        self.initialize()

    def initialize(self):

        v_spacing = global_gui_parameters["vertical_spacing_in_tabs"]

        self._setup_settings()
        self._setup_start_fitting()
        self._setup_compute_roi_maps()
        self._setup_save_results()
        self._setup_quantitative_analysis()

        vbox = QVBoxLayout()
        vbox.addWidget(self.group_settings)
        vbox.addSpacing(v_spacing)

        vbox.addWidget(self.pb_start_map_fitting)
        vbox.addWidget(self.pb_compute_roi_maps)
        vbox.addSpacing(v_spacing)

        vbox.addWidget(self.group_save_results)

        vbox.addWidget(self.group_quant_analysis)

        self.setLayout(vbox)

        self._set_tooltips()

        # Timer is currently used to simulate processing
        self._timer = None
        self._timer_counter = 0

    def _setup_settings(self):
        self.group_settings = QGroupBox("Options")

        self._dset_n_rows = 1
        self._dset_n_cols = 1
        self._area_row_min = 1
        self._area_row_max = 1
        self._area_col_min = 1
        self._area_col_max = 1
        self._validator_selected_area = IntValidatorStrict()

        self.le_start_row = LineEditExtended()
        self.le_start_row.textChanged.connect(self.le_start_row_text_changed)
        self.le_start_row.editingFinished.connect(self.le_start_row_editing_finished)
        self.le_start_col = LineEditExtended()
        self.le_start_col.textChanged.connect(self.le_start_col_text_changed)
        self.le_start_col.editingFinished.connect(self.le_start_col_editing_finished)
        self.le_end_row = LineEditExtended()
        self.le_end_row.textChanged.connect(self.le_end_row_text_changed)
        self.le_end_row.editingFinished.connect(self.le_end_row_editing_finished)
        self.le_end_col = LineEditExtended()
        self.le_end_col.textChanged.connect(self.le_end_col_text_changed)
        self.le_end_col.editingFinished.connect(self.le_end_col_editing_finished)

        self.group_save_plots = QGroupBox("Save spectra for pixels in the selected region")
        self.group_save_plots.setCheckable(True)
        self.group_save_plots.setChecked(False)
        self.group_save_plots.toggled.connect(self.group_save_plots_toggled)

        vbox = QVBoxLayout()
        grid = QGridLayout()
        grid.addWidget(QLabel("Start row:"), 0, 0)
        grid.addWidget(self.le_start_row, 0, 1)
        grid.addWidget(QLabel("column:"), 0, 2)
        grid.addWidget(self.le_start_col, 0, 3)
        grid.addWidget(QLabel("End row:"), 1, 0)
        grid.addWidget(self.le_end_row, 1, 1)
        grid.addWidget(QLabel("column:"), 1, 2)
        grid.addWidget(self.le_end_col, 1, 3)
        vbox.addLayout(grid)

        self.group_save_plots.setLayout(vbox)

        vbox = QVBoxLayout()
        vbox.addWidget(self.group_save_plots)

        self.group_settings.setLayout(vbox)

    def _setup_start_fitting(self):
        self.pb_start_map_fitting = QPushButton("Start XRF Map Fitting")
        self.pb_start_map_fitting.clicked.connect(self.pb_start_map_fitting_clicked)

    def _setup_compute_roi_maps(self):
        self.pb_compute_roi_maps = QPushButton("Compute XRF Maps Based on ROI ...")
        self.pb_compute_roi_maps.clicked.connect(self.pb_compute_roi_maps_clicked)

    def _setup_save_results(self):
        self.group_save_results = QGroupBox("Save Results")

        self.pb_save_to_db = QPushButton("Save to Database (Databroker) ...")
        self.pb_save_to_db.setEnabled(False)

        self.pb_save_q_calibration = QPushButton("Save Quantitative Calibration ...")
        self.pb_save_q_calibration.clicked.connect(self.pb_save_q_calibration_clicked)

        self.pb_export_to_tiff_and_txt = QPushButton("Export to TIFF and TXT ...")
        self.pb_export_to_tiff_and_txt.clicked.connect(self.pb_export_to_tiff_and_txt_clicked)

        grid = QGridLayout()
        grid.addWidget(self.pb_save_to_db, 0, 0, 1, 2)
        grid.addWidget(self.pb_save_q_calibration, 1, 0, 1, 2)
        grid.addWidget(self.pb_export_to_tiff_and_txt, 2, 0, 1, 2)

        self.group_save_results.setLayout(grid)

    def _setup_quantitative_analysis(self):
        self.group_quant_analysis = QGroupBox("Quantitative Analysis")

        self.pb_load_quant_calib = QPushButton("Load Quantitative Calibration ...")
        self.pb_load_quant_calib.clicked.connect(self.pb_load_quant_calib_clicked)

        vbox = QVBoxLayout()
        vbox.addWidget(self.pb_load_quant_calib)
        self.group_quant_analysis.setLayout(vbox)

    def _set_tooltips(self):
        set_tooltip(
            self.group_settings,
            "Raw spectra of individual pixels are saved as <b>.png</b> files for "
            "the selected region of the map."
            "The region is selected by specifying the <b>Start</b> and <b>End</b> coordinates "
            "(ranges of rows and columns) in pixels. The first and last rows and columns are "
            "included in the selection.",
        )
        set_tooltip(
            self.le_start_row,
            "Number of the <b>first row</b> of the map to be included in the selection. "
            "The number must be less than the number entered into 'End row' box.",
        )
        set_tooltip(
            self.le_start_col,
            "Number of the <b>first column</b> of the map to be included in the selection. "
            "The number must be less than the number entered into 'End column' box.",
        )
        set_tooltip(
            self.le_end_row,
            "Number of the <b>last row</b> included in the selection. "
            "The number must be greater than the number entered into 'Start row' box.",
        )
        set_tooltip(
            self.le_end_col,
            "Number of the <b>last column</b> included in the selection. "
            "The number must be greater than the number entered into 'Start column' box.",
        )

        set_tooltip(
            self.pb_start_map_fitting,
            "Click to start <b>fitting of the XRF Maps</b>. The generated XRF Maps can be viewed "
            "in <b>'XRF Maps' tab</b>",
        )

        set_tooltip(
            self.pb_compute_roi_maps,
            "Opens the window for setting up <b>spectral ROIs</b> and computating XRF Maps based on the ROIs",
        )

        set_tooltip(self.pb_save_to_db, "Save generated XRF Maps to a <b>database</b> via Databroker")

        set_tooltip(
            self.pb_save_q_calibration,
            "Opens a Dialog Box which allows to preview and save <b>Quantitative Calibration data</b>",
        )
        set_tooltip(
            self.pb_export_to_tiff_and_txt,
            "Open a Dialog box which allows to export XRF Maps as <b>TIFF</b> and <b>TXT</b> files",
        )

        set_tooltip(
            self.pb_load_quant_calib,
            "Open a window with GUI tools for loading and managing previously saved "
            "<b>Quantitative Calibration data</b> used for processing (normalization) "
            "of XRF Maps. The loaded calibration data is applied to XRF Maps if 'Quantitative' "
            "box is checked in 'XRF Maps' tab",
        )

    def update_widget_state(self, condition=None):
        if condition == "tooltips":
            self._set_tooltips()

        state_file_loaded = self.gui_vars["gui_state"]["state_file_loaded"]
        state_model_exist = self.gui_vars["gui_state"]["state_model_exists"]
        state_xrf_map_exists = self.gui_vars["gui_state"]["state_xrf_map_exists"]

        self.group_settings.setEnabled(state_file_loaded & state_model_exist)
        self.pb_start_map_fitting.setEnabled(state_file_loaded & state_model_exist)
        self.pb_compute_roi_maps.setEnabled(state_file_loaded & state_model_exist)
        self.group_save_results.setEnabled(state_xrf_map_exists)
        self.group_quant_analysis.setEnabled(state_xrf_map_exists)

    def slot_update_for_new_loaded_run(self):
        self.gpc.set_enable_save_spectra(False)
        selected_area = {"row_min": 1, "row_max": 1, "col_min": 1, "col_max": 1}
        self.gpc.set_selection_area_save_spectra(selected_area)

        self._update_area_selection_controls()

    def pb_compute_roi_maps_clicked(self):
        # Position the window in relation ot the main window (only when called once)
        pos = self.ref_main_window.pos()
        self.ref_main_window.wnd_compute_roi_maps.position_once(pos.x(), pos.y())

        if not self.ref_main_window.wnd_compute_roi_maps.isVisible():
            self.ref_main_window.wnd_compute_roi_maps.show()
        self.ref_main_window.wnd_compute_roi_maps.activateWindow()

    def pb_save_q_calibration_clicked(self):
        msg = ""
        if not self.gpc.is_quant_standard_selected():
            # This is a safeguard. The button should be disabled if no standard is selected.
            msg += (
                "No quantitative standard is selected. "
                "Use 'Load Quantitative Standard ...' button in 'Model' tab"
            )
        if not self.gpc.is_quant_standard_fitting_available():
            msg = msg + "\n" if msg else msg
            msg += (
                "Select a dataset containing fitted XRF maps for the quantitative standard "
                "in XRF Maps tab (dataset name must end with 'fit')."
            )
        if msg:
            msgbox = QMessageBox(
                QMessageBox.Information, "Additional Steps Needed", msg, QMessageBox.Ok, parent=self
            )
            msgbox.exec()
            return

        try:
            params = self.gpc.get_displayed_quant_calib_parameters()
            distance_to_sample = params["distance_to_sample"]
            file_name = params["suggested_file_name"]
            preview = params["preview"]

            file_dir = self.gpc.get_current_working_directory()
            file_dir = os.path.expanduser(file_dir)
            file_path = os.path.join(file_dir, file_name)

            dlg = DialogSaveCalibration(file_path=file_path)
            dlg.distance_to_sample = distance_to_sample
            dlg.preview = preview
            res = dlg.exec()
            if res:
                self.gpc.save_quantitative_standard(dlg.file_path, dlg.overwrite_existing)
                logger.info(f"Quantitative calibration was saved to the file '{dlg.file_path}'")
            else:
                logger.info("Saving quantitative calibration was cancelled.")

            # We want to save distance to sample even if saving was cancelled
            self.gpc.save_distance_to_sample(dlg.distance_to_sample)
        except Exception as ex:
            msg = str(ex)
            msgbox = QMessageBox(QMessageBox.Critical, "Error", msg, QMessageBox.Ok, parent=self)
            msgbox.exec()

    def pb_export_to_tiff_and_txt_clicked(self):
        # TODO: Propagate full path to the saved file here
        dir_path = self.gpc.get_current_working_directory()
        dir_path = os.path.expanduser(dir_path)

        params = self.gpc.get_parameters_for_exporting_maps()

        dlg = DialogExportToTiffAndTxt(dir_path=dir_path)
        dlg.dset_list = params["dset_list"]
        dlg.dset_sel = params["dset_sel"]
        dlg.scaler_list = params["scaler_list"]
        dlg.scaler_sel = params["scaler_sel"]
        dlg.interpolate_on = params["interpolate_on"]
        dlg.quant_norm_on = params["quant_norm_on"]

        res = dlg.exec()
        if res:
            try:
                result_path = dlg.dir_path
                dataset_name = dlg.get_selected_dset_name()
                scaler_name = dlg.get_selected_scaler_name()
                interpolate_on = dlg.interpolate_on
                quant_norm_on = dlg.quant_norm_on
                file_formats = []
                if dlg.save_tiff:
                    file_formats.append("tiff")
                if dlg.save_txt:
                    file_formats.append("txt")
                self.gpc.export_xrf_maps(
                    results_path=result_path,
                    dataset_name=dataset_name,
                    scaler_name=scaler_name,
                    interpolate_on=interpolate_on,
                    quant_norm_on=quant_norm_on,
                    file_formats=file_formats,
                )
                if file_formats:
                    formats_text = " and ".join([_.upper() for _ in file_formats])
                else:
                    formats_text = "No"
                msg = f"{formats_text} files were saved to the directory '{result_path}'"
                logger.info(msg)

                msgbox = QMessageBox(QMessageBox.Information, "Files Saved", msg, QMessageBox.Ok, parent=self)
                msgbox.exec()

            except Exception as ex:
                msg = str(ex)
                msgbox = QMessageBox(QMessageBox.Critical, "Error", msg, QMessageBox.Ok, parent=self)
                msgbox.exec()

    def pb_load_quant_calib_clicked(self):
        # Position the window in relation ot the main window (only when called once)
        pos = self.ref_main_window.pos()
        self.ref_main_window.wnd_load_quantitative_calibration.position_once(pos.x(), pos.y())

        if not self.ref_main_window.wnd_load_quantitative_calibration.isVisible():
            self.ref_main_window.wnd_load_quantitative_calibration.show()
        self.ref_main_window.wnd_load_quantitative_calibration.activateWindow()

    def pb_start_map_fitting_clicked(self):
        def cb():
            try:
                self.gpc.fit_individual_pixels()
                success, msg = True, ""
            except Exception as ex:
                success, msg = False, str(ex)

            return {"success": success, "msg": msg}

        self._compute_in_background(cb, self.slot_start_map_fitting_clicked)

    @Slot(object)
    def slot_start_map_fitting_clicked(self, result):
        self._recover_after_compute(self.slot_start_map_fitting_clicked)

        success = result["success"]
        if success:
            self.gui_vars["gui_state"]["state_xrf_map_exists"] = True
        else:
            msg = result["msg"]
            msgbox = QMessageBox(
                QMessageBox.Critical, "Failed to Fit Individual Pixel Spectra", msg, QMessageBox.Ok, parent=self
            )
            msgbox.exec()

        self.signal_map_fitting_complete.emit()
        self.update_global_state.emit()
        if success:
            self.signal_activate_tab_xrf_maps.emit()

    """
    @Slot()
    def timerExpired(self):
        self._timer_counter += 1
        progress_bar = self.ref_main_window.statusProgressBar
        progress_bar.setValue(self._timer_counter)
        if self._timer_counter >= 100:
            self._timer.stop()
            self._timer.timeout.disconnect(self.timerExpired)
            self._timer = None
            progress_bar.setValue(0)
            status_bar = self.ref_main_window.statusBar()
            status_bar.showMessage("XRF Maps are generated. "
                                   "Results are presented in 'XRF Maps' tab.", 5000)
            self.gui_vars["gui_state"]["running_computations"] = False
            self.update_global_state.emit()
    """

    def group_save_plots_toggled(self, state):
        self.gpc.set_enable_save_spectra(state)

    def le_start_row_text_changed(self, text):
        valid = self._validate_row_number(text) and int(text) <= self._area_row_max
        self.le_start_row.setValid(valid)

    def le_start_row_editing_finished(self):
        text = self.le_start_row.text()
        valid = self._validate_row_number(text) and int(text) <= self._area_row_max
        if valid:
            self._save_selected_area(row_min=int(text))
        else:
            self._show_selected_area()

    def le_end_row_text_changed(self, text):
        valid = self._validate_row_number(text) and int(text) >= self._area_row_min
        self.le_end_row.setValid(valid)

    def le_end_row_editing_finished(self):
        text = self.le_end_row.text()
        valid = self._validate_row_number(text) and int(text) >= self._area_row_min
        if valid:
            self._save_selected_area(row_max=int(text))
        else:
            self._show_selected_area()

    def le_start_col_text_changed(self, text):
        valid = self._validate_col_number(text) and int(text) <= self._area_col_max
        self.le_start_col.setValid(valid)

    def le_start_col_editing_finished(self):
        text = self.le_start_col.text()
        valid = self._validate_col_number(text) and int(text) <= self._area_col_max
        if valid:
            self._save_selected_area(col_min=int(text))
        else:
            self._show_selected_area()

    def le_end_col_text_changed(self, text):
        valid = self._validate_col_number(text) and int(text) >= self._area_col_min
        self.le_end_col.setValid(valid)

    def le_end_col_editing_finished(self):
        text = self.le_end_col.text()
        valid = self._validate_col_number(text) and int(text) >= self._area_col_min
        if valid:
            self._save_selected_area(col_max=int(text))
        else:
            self._show_selected_area()

    def _validate_row_number(self, value_str):
        if self._validator_selected_area.validate(value_str, 0)[0] != IntValidatorStrict.Acceptable:
            return False
        value = int(value_str)
        if 1 <= value <= self._dset_n_rows:
            return True
        else:
            return False

    def _validate_col_number(self, value_str):
        if self._validator_selected_area.validate(value_str, 0)[0] != IntValidatorStrict.Acceptable:
            return False
        value = int(value_str)
        if 1 <= value <= self._dset_n_cols:
            return True
        else:
            return False

    def _update_area_selection_controls(self):
        map_size = self.gpc.get_dataset_map_size()
        map_size = (1, 1) if map_size is None else map_size
        self._dset_n_rows, self._dset_n_cols = map_size

        self.group_save_plots.setChecked(self.gpc.get_enable_save_spectra())
        area = self.gpc.get_selection_area_save_spectra()
        self._area_row_min = area["row_min"]
        self._area_row_max = area["row_max"]
        self._area_col_min = area["col_min"]
        self._area_col_max = area["col_max"]
        self._show_selected_area()

    def _show_selected_area(self):
        self.le_start_row.setText(f"{self._area_row_min}")
        self.le_end_row.setText(f"{self._area_row_max}")
        self.le_start_col.setText(f"{self._area_col_min}")
        self.le_end_col.setText(f"{self._area_col_max}")

    def _save_selected_area(self, row_min=None, row_max=None, col_min=None, col_max=None):
        if row_min is not None:
            self._area_row_min = row_min
        if row_max is not None:
            self._area_row_max = row_max
        if col_min is not None:
            self._area_col_min = col_min
        if col_max is not None:
            self._area_col_max = col_max
        area = {
            "row_min": self._area_row_min,
            "row_max": self._area_row_max,
            "col_min": self._area_col_min,
            "col_max": self._area_col_max,
        }
        self.gpc.set_selection_area_save_spectra(area)
        self._update_area_selection_controls()

    def _compute_in_background(self, func, slot, *args, **kwargs):
        """
        Run function `func` in a background thread. Send the signal
        `self.computations_complete` once computation is finished.

        Parameters
        ----------
        func: function
            Reference to a function that is supposed to be executed at the background.
            The function return value is passed as a signal parameter once computation is
            complete.
        slot: qtpy.QtCore.Slot or None
            Reference to a slot. If not None, then the signal `self.computation_complete`
            is connected to this slot.
        args, kwargs
            arguments of the function `func`.
        """
        signal_complete = self.computations_complete

        def func_to_run(func, *args, **kwargs):
            class LoadFile(QRunnable):
                def run(self):
                    result_dict = func(*args, **kwargs)
                    signal_complete.emit(result_dict)

            return LoadFile()

        if slot is not None:
            self.computations_complete.connect(slot)
        self.gui_vars["gui_state"]["running_computations"] = True
        self.update_global_state.emit()
        QThreadPool.globalInstance().start(func_to_run(func, *args, **kwargs))

    def _recover_after_compute(self, slot):
        """
        The function should be called after the signal `self.computations_complete` is
        received. The slot should be the same as the one used when calling
        `self.compute_in_background`.
        """
        if slot is not None:
            self.computations_complete.disconnect(slot)
        self.gui_vars["gui_state"]["running_computations"] = False
        self.update_global_state.emit()