Beispiel #1
0
    def __init__(self, parent=None, **kwargs):
        BaseEditor.__init__(self, parent, **kwargs)
        self.__method = DiscretizeEditor.EqualFreq
        self.__nintervals = 4

        layout = QVBoxLayout()
        self.setLayout(layout)
        self.__group = group = QButtonGroup(self, exclusive=True)

        for method in [
                self.EntropyMDL, self.EqualFreq, self.EqualWidth, self.Drop
        ]:
            rb = QRadioButton(self,
                              text=self.Names[method],
                              checked=self.__method == method)
            layout.addWidget(rb)
            group.addButton(rb, method)

        group.buttonClicked.connect(self.__on_buttonClicked)

        self.__slbox = slbox = QGroupBox(
            title="Number of intervals (for equal width/frequency)", flat=True)
        slbox.setLayout(QHBoxLayout())
        self.__slider = slider = Slider(orientation=Qt.Horizontal,
                                        minimum=2,
                                        maximum=10,
                                        value=self.__nintervals,
                                        enabled=self.__method
                                        in [self.EqualFreq, self.EqualWidth],
                                        pageStep=1,
                                        tickPosition=QSlider.TicksBelow)
        slider.valueChanged.connect(self.__on_valueChanged)
        slbox.layout().addWidget(slider)
        self.__slabel = slabel = QLabel()
        slbox.layout().addWidget(slabel)

        container = QHBoxLayout()
        container.setContentsMargins(13, 0, 0, 0)
        container.addWidget(slbox)
        self.layout().insertLayout(3, container)
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
Beispiel #2
0
    def __init__(self, *args, thresholds=(0.0, 1.0), center=None, **kwargs):
        super().__init__(*args, **kwargs)

        low = round(clip(thresholds[0], 0., 1.), 2)
        high = round(clip(thresholds[1], 0., 1.), 2)
        high = max(low, high)
        self.__threshold_low, self.__threshold_high = low, high
        self.__center = center
        form = QFormLayout(formAlignment=Qt.AlignLeft,
                           labelAlignment=Qt.AlignLeft,
                           fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow)
        form.setContentsMargins(0, 0, 0, 0)
        self.gradient_cb = QComboBox(
            None,
            objectName="gradient-combo-box",
        )
        self.gradient_cb.setAttribute(Qt.WA_LayoutUsesWidgetRect)
        icsize = self.style().pixelMetric(QStyle.PM_SmallIconSize, None,
                                          self.gradient_cb)
        self.gradient_cb.setIconSize(QSize(64, icsize))
        model = itemmodels.ContinuousPalettesModel()
        model.setParent(self)

        self.gradient_cb.setModel(model)
        self.gradient_cb.activated[int].connect(self.activated)
        self.gradient_cb.currentIndexChanged.connect(self.currentIndexChanged)

        if center is not None:

            def __on_center_changed():
                self.__center = float(self.center_edit.text() or "0")
                self.centerChanged.emit(self.__center)

            self.center_box = QWidget()
            center_layout = QHBoxLayout()
            self.center_box.setLayout(center_layout)
            width = QFontMetrics(self.font()).boundingRect("9999999").width()
            self.center_edit = QLineEdit(text=f"{self.__center}",
                                         maximumWidth=width,
                                         placeholderText="0",
                                         alignment=Qt.AlignRight)
            self.center_edit.setValidator(QDoubleValidator())
            self.center_edit.editingFinished.connect(__on_center_changed)
            center_layout.setContentsMargins(0, 0, 0, 0)
            center_layout.addStretch(1)
            center_layout.addWidget(QLabel("Centered at"))
            center_layout.addWidget(self.center_edit)
            self.gradient_cb.currentIndexChanged.connect(
                self.__update_center_visibility)
        else:
            self.center_box = None

        slider_low = Slider(objectName="threshold-low-slider",
                            minimum=0,
                            maximum=100,
                            value=int(low * 100),
                            orientation=Qt.Horizontal,
                            tickPosition=QSlider.TicksBelow,
                            pageStep=10,
                            toolTip=self.tr("Low gradient threshold"),
                            whatsThis=self.tr(
                                "Applying a low threshold will squeeze the "
                                "gradient from the lower end"))
        slider_high = Slider(objectName="threshold-low-slider",
                             minimum=0,
                             maximum=100,
                             value=int(high * 100),
                             orientation=Qt.Horizontal,
                             tickPosition=QSlider.TicksAbove,
                             pageStep=10,
                             toolTip=self.tr("High gradient threshold"),
                             whatsThis=self.tr(
                                 "Applying a high threshold will squeeze the "
                                 "gradient from the higher end"))
        form.setWidget(0, QFormLayout.SpanningRole, self.gradient_cb)
        if self.center_box:
            form.setWidget(1, QFormLayout.SpanningRole, self.center_box)
        form.addRow(self.tr("Low:"), slider_low)
        form.addRow(self.tr("High:"), slider_high)
        self.slider_low = slider_low
        self.slider_high = slider_high
        self.slider_low.valueChanged.connect(self.__on_slider_low_moved)
        self.slider_high.valueChanged.connect(self.__on_slider_high_moved)
        self.setLayout(form)
Beispiel #3
0
    def __init__(self, *args, thresholds=(0.0, 1.0), center=None, **kwargs):
        super().__init__(*args, **kwargs)

        low = round(clip(thresholds[0], 0., 1.), 2)
        high = round(clip(thresholds[1], 0., 1.), 2)
        high = max(low, high)
        self.__threshold_low, self.__threshold_high = low, high
        self.__center = center
        form = QFormLayout(
            formAlignment=Qt.AlignLeft,
            labelAlignment=Qt.AlignLeft,
            fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow
        )
        form.setContentsMargins(0, 0, 0, 0)
        self.gradient_cb = QComboBox(
            None, objectName="gradient-combo-box",
        )
        self.gradient_cb.setAttribute(Qt.WA_LayoutUsesWidgetRect)
        icsize = self.style().pixelMetric(
            QStyle.PM_SmallIconSize, None, self.gradient_cb
        )
        self.gradient_cb.setIconSize(QSize(64, icsize))
        model = itemmodels.ContinuousPalettesModel()
        model.setParent(self)

        self.gradient_cb.setModel(model)
        self.gradient_cb.activated[int].connect(self.activated)
        self.gradient_cb.currentIndexChanged.connect(self.currentIndexChanged)

        if center is not None:
            def on_center_spin_value_changed(value):
                if self.__center != value:
                    self.__center = value
                    self.centerChanged.emit(self.__center)

            self.center_box = QWidget()
            center_layout = QHBoxLayout()
            self.center_box.setLayout(center_layout)
            self.center_edit = DoubleSpinBox(
                value=self.__center,
                minimum=DBL_MIN, maximum=DBL_MAX, minimumStep=0.01,
                minimumContentsLenght=8,
                stepType=DoubleSpinBox.AdaptiveDecimalStepType,
                keyboardTracking=False,
                sizePolicy=QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
            )
            self.center_edit.valueChanged.connect(on_center_spin_value_changed)
            center_layout.setContentsMargins(0, 0, 0, 0)
            center_layout.addStretch(1)
            center_layout.addWidget(QLabel("Centered at"))
            center_layout.addWidget(self.center_edit)
            self.gradient_cb.currentIndexChanged.connect(
                self.__update_center_visibility)
        else:
            self.center_box = None

        slider_low = Slider(
            objectName="threshold-low-slider", minimum=0, maximum=100,
            value=int(low * 100), orientation=Qt.Horizontal,
            tickPosition=QSlider.TicksBelow, pageStep=10,
            toolTip=self.tr("Low gradient threshold"),
            whatsThis=self.tr("Applying a low threshold will squeeze the "
                              "gradient from the lower end")
        )
        slider_high = Slider(
            objectName="threshold-low-slider", minimum=0, maximum=100,
            value=int(high * 100), orientation=Qt.Horizontal,
            tickPosition=QSlider.TicksAbove, pageStep=10,
            toolTip=self.tr("High gradient threshold"),
            whatsThis=self.tr("Applying a high threshold will squeeze the "
                              "gradient from the higher end")
        )
        form.setWidget(0, QFormLayout.SpanningRole, self.gradient_cb)
        if self.center_box:
            form.setWidget(1, QFormLayout.SpanningRole, self.center_box)
        form.addRow(self.tr("Low:"), slider_low)
        form.addRow(self.tr("High:"), slider_high)
        self.slider_low = slider_low
        self.slider_high = slider_high
        self.slider_low.valueChanged.connect(self.__on_slider_low_moved)
        self.slider_high.valueChanged.connect(self.__on_slider_high_moved)
        self.setLayout(form)
Beispiel #4
0
    def __init__(self, parent: QWidget, variable: ContinuousVariable,
                 min_value: float, max_value: float, callback: Callable):
        super().__init__(parent, callback)

        if np.isnan(min_value) or np.isnan(max_value):
            raise ValueError("Min/Max cannot be NaN.")

        n_decimals = variable.number_of_decimals
        abs_max = max(abs(min_value), max_value)
        if abs_max * 10 ** n_decimals > self.MAX_FLOAT:
            n_decimals = int(np.log10(self.MAX_FLOAT / abs_max))

        self._value: float = min_value
        self._n_decimals: int = n_decimals
        self._min_value: float = self.__round_value(min_value)
        self._max_value: float = self.__round_value(max_value)

        sp_spin = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        sp_spin.setHorizontalStretch(1)
        sp_slider = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        sp_slider.setHorizontalStretch(5)
        sp_edit = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        sp_edit.setHorizontalStretch(1)

        class DoubleSpinBox(QDoubleSpinBox):
            def sizeHint(self) -> QSize:
                size: QSize = super().sizeHint()
                return QSize(size.width(), size.height() + 2)

        self._spin = DoubleSpinBox(
            parent,
            value=self._min_value,
            minimum=-np.inf,
            maximum=np.inf,
            singleStep=10 ** (-self._n_decimals),
            decimals=self._n_decimals,
            minimumWidth=70,
            sizePolicy=sp_spin,
        )
        self._slider = Slider(
            parent,
            minimum=self.__map_to_slider(self._min_value),
            maximum=self.__map_to_slider(self._max_value),
            singleStep=1,
            orientation=Qt.Horizontal,
            sizePolicy=sp_slider,
        )
        self._label_min = QLabel(
            parent,
            text=variable.repr_val(min_value),
            alignment=Qt.AlignRight,
            minimumWidth=60,
            sizePolicy=sp_edit,
        )
        self._label_max = QLabel(
            parent,
            text=variable.repr_val(max_value),
            alignment=Qt.AlignLeft,
            minimumWidth=60,
            sizePolicy=sp_edit,
        )

        self._slider.valueChanged.connect(self._apply_slider_value)
        self._spin.valueChanged.connect(self._apply_spin_value)

        self.layout().addWidget(self._spin)
        self.layout().addWidget(self._label_min)
        self.layout().addWidget(self._slider)
        self.layout().addWidget(self._label_max)

        self.setFocusProxy(self._spin)

        def deselect():
            self._spin.lineEdit().deselect()
            try:
                self._spin.lineEdit().selectionChanged.disconnect(deselect)
            except TypeError:
                pass

        # Invoking self.setFocusProxy(self._spin), causes the
        # self._spin.lineEdit()s to have selected texts (focus is set to
        # provide keyboard functionality, i.e.: pressing ESC after changing
        # spinbox value). Since the spin text is selected only after the
        # delegate draws it, it cannot be deselected during initialization.
        # Therefore connect the deselect() function to
        # self._spin.lineEdit().selectionChanged only for editor creation.
        self._spin.lineEdit().selectionChanged.connect(deselect)

        self._slider.installEventFilter(self)
        self._spin.installEventFilter(self)
class ContinuousVariableEditor(VariableEditor):
    MAX_FLOAT = 2147483647

    def __init__(self, parent: QWidget, variable: ContinuousVariable,
                 min_value: float, max_value: float, callback: Callable):
        super().__init__(parent, callback)

        if np.isnan(min_value) or np.isnan(max_value):
            raise ValueError("Min/Max cannot be NaN.")

        n_decimals = variable.number_of_decimals
        abs_max = max(abs(min_value), max_value)
        if abs_max * 10 ** n_decimals > self.MAX_FLOAT:
            n_decimals = int(np.log10(self.MAX_FLOAT / abs_max))

        self._value: float = min_value
        self._n_decimals: int = n_decimals
        self._min_value: float = self.__round_value(min_value)
        self._max_value: float = self.__round_value(max_value)

        sp_spin = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        sp_spin.setHorizontalStretch(1)
        sp_slider = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        sp_slider.setHorizontalStretch(5)
        sp_edit = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        sp_edit.setHorizontalStretch(1)

        class DoubleSpinBox(QDoubleSpinBox):
            def sizeHint(self) -> QSize:
                size: QSize = super().sizeHint()
                return QSize(size.width(), size.height() + 2)

            def validate(self, text: str, pos: int) -> Tuple[int, str, int]:
                state, text, pos = super().validate(text, pos)
                if text == "":
                    state = QValidator.Acceptable
                return state, text, pos

            def textFromValue(self, value):
                if not np.isfinite(value):
                    return "?"
                return super().textFromValue(value)

        self._spin = DoubleSpinBox(
            parent,
            value=self._min_value,
            minimum=-np.inf,
            maximum=np.inf,
            singleStep=10 ** (-self._n_decimals),
            decimals=self._n_decimals,
            minimumWidth=70,
            sizePolicy=sp_spin,
        )
        self._slider = Slider(
            parent,
            minimum=self.__map_to_slider(self._min_value),
            maximum=self.__map_to_slider(self._max_value),
            singleStep=1,
            orientation=Qt.Horizontal,
            sizePolicy=sp_slider,
        )
        self._label_min = QLabel(
            parent,
            text=variable.repr_val(min_value),
            alignment=Qt.AlignRight,
            minimumWidth=60,
            sizePolicy=sp_edit,
        )
        self._label_max = QLabel(
            parent,
            text=variable.repr_val(max_value),
            alignment=Qt.AlignLeft,
            minimumWidth=60,
            sizePolicy=sp_edit,
        )

        self._slider.valueChanged.connect(self._apply_slider_value)
        self._spin.valueChanged.connect(self._apply_spin_value)

        self.layout().addWidget(self._spin)
        self.layout().addWidget(self._label_min)
        self.layout().addWidget(self._slider)
        self.layout().addWidget(self._label_max)

        self.setFocusProxy(self._spin)

        def deselect():
            self._spin.lineEdit().deselect()
            try:
                self._spin.lineEdit().selectionChanged.disconnect(deselect)
            except TypeError:
                pass

        # Invoking self.setFocusProxy(self._spin), causes the
        # self._spin.lineEdit()s to have selected texts (focus is set to
        # provide keyboard functionality, i.e.: pressing ESC after changing
        # spinbox value). Since the spin text is selected only after the
        # delegate draws it, it cannot be deselected during initialization.
        # Therefore connect the deselect() function to
        # self._spin.lineEdit().selectionChanged only for editor creation.
        self._spin.lineEdit().selectionChanged.connect(deselect)

        self._slider.installEventFilter(self)
        self._spin.installEventFilter(self)

    @property
    def value(self) -> float:
        return self.__round_value(self._value)

    @value.setter
    def value(self, value: float):
        if self._value is None or self.__round_value(value) != self.value:
            self._value = value
            self.valueChanged.emit(self.value)
            self._spin.setValue(self.value)
            # prevent emitting self.valueChanged again, due to slider change
            slider_value = self.__map_to_slider(self.value)
            self._value = self.__map_from_slider(slider_value)
            self._slider.setValue(slider_value)
            self._value = value

    def _apply_slider_value(self):
        self.value = self.__map_from_slider(self._slider.value())

    def _apply_spin_value(self):
        value = self._spin.value()
        self.value = value if np.isfinite(value) else np.nan

    def __round_value(self, value):
        return round(value, self._n_decimals)

    def __map_to_slider(self, value: float) -> int:
        value = min(self._max_value, max(self._min_value, value))
        return round(value * 10 ** self._n_decimals)

    def __map_from_slider(self, value: int) -> float:
        return value * 10 ** (-self._n_decimals)

    def eventFilter(self, obj: Union[QSlider, QDoubleSpinBox], event: QEvent) \
            -> bool:
        if event.type() == QEvent.Wheel:
            return True
        return super().eventFilter(obj, event)