class DialogPileupPeakParameters(QDialog): def __init__(self, parent=None): super().__init__(parent) self._data = {"element1": "", "element2": "", "energy": 0} # Reference to function that computes pileup energy based on two emission lines self._compute_energy = None self.setWindowTitle("Pileup Peak Parameters") self.le_element1 = LineEditExtended() set_tooltip(self.le_element1, "The <b>name</b> of the emission line #1") self.le_element2 = LineEditExtended() set_tooltip(self.le_element2, "The <b>name</b> of the emission line #2") self.peak_energy = LineEditReadOnly() set_tooltip( self.peak_energy, "The <b>energy</b> (location) of the pileup peak center. The energy can not" "be edited: it is set based on the selected emission lines", ) self._validator_eline = QRegExpValidator() # TODO: the following regex is too broad: [a-z] should be narrowed down self._validator_eline.setRegExp(QRegExp(r"^[A-Z][a-z]?_[KLM][a-z]\d$")) instructions = QLabel( "Specify two emission lines, e.g. Si_Ka1 and Fe_Ka1") grid = QGridLayout() grid.addWidget(QLabel("Emission line 1:"), 0, 0) grid.addWidget(self.le_element1, 0, 1) grid.addWidget(QLabel("Emission line 2:"), 1, 0) grid.addWidget(self.le_element2, 1, 1) grid.addWidget(QLabel("Peak energy, keV:"), 2, 0) grid.addWidget(self.peak_energy, 2, 1) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.button(QDialogButtonBox.Cancel).setDefault(True) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) self.pb_ok = button_box.button(QDialogButtonBox.Ok) vbox = QVBoxLayout() vbox.addWidget(instructions) vbox.addSpacing(10) vbox.addLayout(grid) vbox.addWidget(button_box) self.setLayout(vbox) self.le_element1.editingFinished.connect( self.le_element1_editing_finished) self.le_element2.editingFinished.connect( self.le_element2_editing_finished) self.le_element1.textChanged.connect(self.le_element1_text_changed) self.le_element2.textChanged.connect(self.le_element2_text_changed) def set_compute_energy_function(self, func): self._compute_energy = func def set_parameters(self, data): self._data = data.copy() self._show_data() def get_parameters(self): return self._data def _format_float(self, v): return f"{v:.12g}" def _show_data(self): self.le_element1.setText(self._data["element1"]) self.le_element2.setText(self._data["element2"]) self._show_energy() def _show_energy(self, energy=None): if energy is None: energy = self._data["energy"] text = self._format_float(energy) self.peak_energy.setText(text) self._validate_all() def _validate_le_element1(self, text=None): return self._validate_eline(self.le_element1, text) def _validate_le_element2(self, text=None): return self._validate_eline(self.le_element2, text) def _validate_peak_energy(self, energy=None): # Peak energy is not edited, so it is always has a valid floating point number # The only problem is that it may be out of range. if energy is None: energy = self._data["energy"] valid = self._data["range_low"] < energy < self._data["range_high"] self.peak_energy.setValid(valid) return valid def _validate_all(self): valid = self._validate_le_element1() and self._validate_le_element2() # Energy doesn't influence the success of validation, but the Ok button # should be disabled if energy if out of range. valid_energy = self._validate_peak_energy() self.pb_ok.setEnabled(valid and valid_energy) return valid def _validate_eline(self, le_widget, text): if text is None: text = le_widget.text() valid = True if self._validator_eline.validate(text, 0)[0] != QDoubleValidator.Acceptable: valid = False else: # Try to compute energy for the pileup peak if self._compute_energy(text, text) == 0: valid = False le_widget.setValid(valid) return valid def _refresh_energy(self): eline1 = self._data["element1"] eline2 = self._data["element2"] try: energy = self._compute_energy(eline1, eline2) self._data["energy"] = energy self._show_energy() except Exception: self._data["energy"] = 0 self._show_energy() def le_element1_editing_finished(self): if self._validate_le_element1(): self._data["element1"] = self.le_element1.text() self._refresh_energy() else: self.le_element1.setText(self._data["element1"]) self._validate_all() def le_element2_editing_finished(self): if self._validate_le_element2(): self._data["element2"] = self.le_element2.text() self._refresh_energy() else: self.le_element2.setText(self._data["element2"]) self._validate_all() def le_element1_text_changed(self, text): if self._validate_all(): self._data["element1"] = self.le_element1.text() print(f"element1 - {self._data['element1']}") self._refresh_energy() def le_element2_text_changed(self, text): if self._validate_all(): self._data["element2"] = self.le_element2.text() print(f"element2 - {self._data['element2']}") self._refresh_energy()
class DialogSelectScan(QDialog): def __init__(self): super().__init__() self.resize(400, 200) self.setWindowTitle("Load Run From Database") self._id_uid = None self._mode_id_uid = "id" label = QLabel("Enter run ID or UID:") self.le_id_uid = LineEditExtended() self.le_id_uid.textChanged.connect(self.le_id_uid_text_changed) self.le_id_uid.editingFinished.connect(self.le_id_uid_editing_finished) set_tooltip(self.le_id_uid, "Enter <b>Run ID</b> or <b>Run UID</b>.") self._validator_id = IntValidatorStrict() # Short UID example: "04c9afa7" self._validator_uid_short = QRegExpValidator(QRegExp(r"[0-9a-f]{8}")) # Full UID example: "04c9afa7-a43a-4af1-8e55-2034384d4a77" self._validator_uid_full = QRegExpValidator( QRegExp( r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" )) self.rb_id = QRadioButton("Run ID") set_tooltip( self.rb_id, "The value in the line edit box is <b>Run ID</b> (e.g. <b>34235</b> or <b>-1</b>)" ) self.rb_id.setChecked(self._mode_id_uid == "id") self.rb_uid = QRadioButton("Run UID") self.rb_uid.setChecked(self._mode_id_uid == "uid") set_tooltip( self.rb_uid, "The value in the line edit box is <b>Run UID</b> " "(e.g. <b>04c9afb7-a43a-4af1-8e55-2034384d4a77</b> or <b>04c9afb7</b>)", ) self.btn_group = QButtonGroup() self.btn_group.addButton(self.rb_id) self.btn_group.addButton(self.rb_uid) self.btn_group.buttonToggled.connect(self.btn_group_button_toggled) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) self.pb_ok = button_box.button(QDialogButtonBox.Ok) vbox = QVBoxLayout() vbox.addStretch(1) hbox = QHBoxLayout() hbox.addWidget(label) hbox.addStretch(1) vbox.addLayout(hbox) vbox.addWidget(self.le_id_uid) hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.rb_id) hbox.addStretch(1) hbox.addWidget(self.rb_uid) hbox.addStretch(1) vbox.addLayout(hbox) vbox.addStretch(1) vbox.addWidget(button_box) self.setLayout(vbox) # This is how the button from QDialogButtonBox can be disabled # button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.le_id_uid.setText("") self._validate_id_uid() def btn_group_button_toggled(self, button, state): if state: if button == self.rb_id: self._mode_id_uid = "id" elif button == self.rb_uid: self._mode_id_uid = "uid" text = self.le_id_uid.text() if self._validate_id_uid(text): self._read_id_uid(text) def le_id_uid_text_changed(self, text): self._validate_id_uid(text) def le_id_uid_editing_finished(self): text = self.le_id_uid.text() if self._validate_id_uid(text): self._read_id_uid(text) def get_id_uid(self): """ Read the selected scan ID or UID Returns ------- (str, int) or (str, str) keyword "id" or "uid" depending on whether the second element is scan ID or UID, scan ID (int), scan UID (str) or None if no scan ID or UID is selected """ return self._mode_id_uid, self._id_uid def _validate_id_uid(self, text=None): valid = False if text is None: text = self.le_id_uid.text() if self._mode_id_uid == "id": if self._validator_id.validate(text, 0)[0] == QIntValidator.Acceptable: valid = True elif self._mode_id_uid == "uid": if (self._validator_uid_short.validate( text, 0)[0] == QIntValidator.Acceptable or self._validator_uid_full.validate( text, 0)[0] == QIntValidator.Acceptable): valid = True self.le_id_uid.setValid(valid) self.pb_ok.setEnabled(valid) return valid def _read_id_uid(self, text): # It is assumed that the entered text is valid for the selected mode if text is None: text = self.le_id_uid.text() if self._mode_id_uid == "id": self._id_uid = int(text) elif self._mode_id_uid == "uid": self._id_uid = text def _set_mode(self, mode): if mode == "id": self._mode_id_uid = mode self.rb_id.setChecked(True) elif mode == "uid": self._mode_id_uid = mode self.rb_uid.setChecked(True)