From 7b964beb895f7c4e0cf9c394f03a029c1ed62345 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sun, 6 Aug 2023 08:57:22 -0400 Subject: [PATCH] feat: add stepType to largeInt spinbox (#179) --- src/superqt/spinbox/_intspin.py | 36 +++++++++++++++++++++++++++++---- tests/test_large_int_spinbox.py | 13 ++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/superqt/spinbox/_intspin.py b/src/superqt/spinbox/_intspin.py index 843d1b8..5ead23d 100644 --- a/src/superqt/spinbox/_intspin.py +++ b/src/superqt/spinbox/_intspin.py @@ -1,3 +1,4 @@ +import math from enum import Enum from qtpy.QtCore import QSize, Qt, Signal @@ -42,6 +43,9 @@ class QLargeIntSpinBox(QAbstractSpinBox): self._minimum: int = 0 self._maximum: int = 2**64 - 1 self._single_step: int = 1 + self._step_type: QAbstractSpinBox.StepType = ( + QAbstractSpinBox.StepType.DefaultStepType + ) self._pending_emit = False validator = _AnyIntValidator(self) self.lineEdit().setValidator(validator) @@ -78,7 +82,13 @@ class QLargeIntSpinBox(QAbstractSpinBox): def setSingleStep(self, step): self._single_step = int(step) - # TODO: add prefix/suffix/stepType + def setStepType(self, stepType: QAbstractSpinBox.StepType) -> None: + self._step_type = stepType + + def stepType(self) -> QAbstractSpinBox.StepType: + return self._step_type + + # TODO: add prefix/suffix # ############### QtOverrides ####################### @@ -102,13 +112,16 @@ class QLargeIntSpinBox(QAbstractSpinBox): return super().keyPressEvent(e) def stepBy(self, steps: int) -> None: - step = self._single_step old = self._value e = _EmitPolicy.EmitIfChanged if self._pending_emit: self._interpret(_EmitPolicy.NeverEmit) if self._value != old: e = _EmitPolicy.AlwaysEmit + if self._step_type == QAbstractSpinBox.StepType.AdaptiveDecimalStepType: + step = self._calculate_adaptive_decimal_step(steps) + else: + step = self._single_step self._setValue(self._bound(self._value + (step * steps)), e) def stepEnabled(self): @@ -164,9 +177,12 @@ class QLargeIntSpinBox(QAbstractSpinBox): v = int(text) self._setValue(v, policy) - def _editor_text_changed(self, t): + def _editor_text_changed(self, t: str) -> None: if self.keyboardTracking(): - self._setValue(int(t), _EmitPolicy.EmitIfChanged) + try: + self._setValue(int(t), _EmitPolicy.EmitIfChanged) + except ValueError: + pass self.lineEdit().setFocus() self._pending_emit = False else: @@ -174,3 +190,15 @@ class QLargeIntSpinBox(QAbstractSpinBox): def _bound(self, value): return max(self._minimum, min(self._maximum, value)) + + def _calculate_adaptive_decimal_step(self, steps: int) -> int: + abs_value = abs(self._value) + if abs_value < 100: + return 1 + + value_negative = self._value < 0 + steps_negative = steps < 0 + sign_compensation = 0 if value_negative == steps_negative else 1 + + log = int(math.log10(abs_value - sign_compensation)) - 1 + return int(math.pow(10, log)) diff --git a/tests/test_large_int_spinbox.py b/tests/test_large_int_spinbox.py index cdf236b..d7578e1 100644 --- a/tests/test_large_int_spinbox.py +++ b/tests/test_large_int_spinbox.py @@ -72,3 +72,16 @@ def test_keyboard_tracking(qtbot): sb.lineEdit().setText("25") assert sb._pending_emit is False assert sgnl.args == [25] + + +def test_large_spinbox_step_type(qtbot): + sb = QLargeIntSpinBox() + qtbot.addWidget(sb) + sb.setMaximum(1_000_000_000) + sb.setStepType(sb.StepType.AdaptiveDecimalStepType) + sb.setValue(1_000_000) + sb.stepBy(1) + assert sb.value() == 1_100_000 + sb.setStepType(sb.StepType.DefaultStepType) + sb.stepBy(1) + assert sb.value() == 1_100_001