class FloatOrEmptyValidator(QValidator): def __init__(self, parent, allow_empty=False, bottom=float("-inf"), top=float("inf"), default_text=""): super().__init__(parent) self.dv = QDoubleValidator(parent) self.allow_empty = allow_empty self.default_text = default_text self.dv.setLocale(QLocale.c()) self.setBottom(bottom) self.setTop(top) def setDefault(self, s): self.default_text = s def setBottom(self, b): self.dv.setBottom(b) def setTop(self, t): self.dv.setTop(t) def fixup(self, p_str): # = only called at editingFinished so an Orange controlled value can still contain # invalid, because they are synchronized and every change # = called before returnPressedHandler try: f = float(p_str) if f > self.dv.top(): return str(self.dv.top()) if f < self.dv.bottom(): return str(self.dv.bottom()) except ValueError: return self.default_text def validate(self, s, pos): if self.allow_empty and len(s) == 0: return (QValidator.Acceptable, s, pos) if "," in s: return (QValidator.Invalid, s, pos) else: return self.dv.validate(s, pos)
def _create_buttons(self, box): """Create radio buttons""" def intspin(): s = QSpinBox(self) s.setMinimum(2) s.setMaximum(10) s.setFixedWidth(60) s.setAlignment(Qt.AlignRight) s.setContentsMargins(0, 0, 0, 0) return s, s.valueChanged def widthline(validator): s = QLineEdit(self) s.setFixedWidth(60) s.setAlignment(Qt.AlignRight) s.setValidator(validator) s.setContentsMargins(0, 0, 0, 0) return s, s.textChanged def manual_cut_editline(text="", enabled=True) -> QLineEdit: edit = QLineEdit( text=text, placeholderText="e.g. 0.0, 0.5, 1.0", toolTip='<p style="white-space:pre">' + 'Enter cut points as a comma-separate list of \n' 'strictly increasing numbers e.g. 0.0, 0.5, 1.0).</p>', enabled=enabled, ) edit.setValidator(IncreasingNumbersListValidator()) edit.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) @edit.textChanged.connect def update(): validator = edit.validator() if validator is not None and edit.text().strip(): state, _, _ = validator.validate(edit.text(), 0) else: state = QValidator.Acceptable palette = edit.palette() colors = { QValidator.Intermediate: (Qt.yellow, Qt.black), QValidator.Invalid: (Qt.red, Qt.black), }.get(state, None) if colors is None: palette = QPalette() else: palette.setColor(QPalette.Base, colors[0]) palette.setColor(QPalette.Text, colors[1]) cr = edit.cursorRect() p = edit.mapToGlobal(cr.bottomRight()) edit.setPalette(palette) if state != QValidator.Acceptable and edit.isVisible(): validator.show_tip(edit, p, edit.toolTip(), textFormat=Qt.RichText) else: validator.show_tip(edit, p, "") return edit, edit.textChanged children = [] def button(id_, *controls, stretch=True): layout = QHBoxLayout() desc = Options[id_] button = QRadioButton(desc.label) button.setToolTip(desc.tooltip) self.button_group.addButton(button, id_) layout.addWidget(button) if controls: if stretch: layout.addStretch(1) for c, signal in controls: layout.addWidget(c) if signal is not None: @signal.connect def arg_changed(): self.button_group.button(id_).setChecked(True) self.update_hints(id_) children.append(layout) button_box.layout().addLayout(layout) return (*controls, (None, ))[0][0] button_box = gui.vBox(box) button_box.layout().setSpacing(0) button_box.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)) self.button_group = QButtonGroup(self) self.button_group.idClicked.connect(self.update_hints) button(Methods.Keep) button(Methods.Remove) self.binning_spin = button(Methods.Binning, intspin()) validator = QDoubleValidator() validator.setBottom(0) self.width_line = button(Methods.FixedWidth, widthline(validator)) self.width_time_unit = u = QComboBox(self) u.setContentsMargins(0, 0, 0, 0) u.addItems([unit + "(s)" for unit in time_units]) validator = QIntValidator() validator.setBottom(1) self.width_time_line = button(Methods.FixedWidthTime, widthline(validator), (u, u.currentTextChanged)) self.freq_spin = button(Methods.EqualFreq, intspin()) self.width_spin = button(Methods.EqualWidth, intspin()) button(Methods.MDL) self.copy_to_custom = FixedSizeButton( text="CC", toolTip="Copy the current cut points to manual mode") self.copy_to_custom.clicked.connect(self._copy_to_manual) self.threshold_line = button(Methods.Custom, manual_cut_editline(), (self.copy_to_custom, None), stretch=False) button(Methods.Default) maxheight = max(w.sizeHint().height() for w in children) for w in children: w.itemAt(0).widget().setFixedHeight(maxheight) button_box.layout().addStretch(1)