From 76567d1485c3540713ceae5e030d33137c145e4c Mon Sep 17 00:00:00 2001 From: Phoenix0531-sudo Date: Wed, 22 Jul 2026 21:17:14 +0800 Subject: [PATCH 1/3] Fix large integer ranges on QRangeSlider and QLabeledRangeSlider QRangeSlider left rangeChanged bound to QSlider's native Signal(int, int), so setRange(0, 10**11) failed on PyQt6 when emitting rangeChanged. The double variants already rebind rangeChanged to frangeChanged; do the same for the integer range slider and labeled wrapper so examples/labeled_sliders.py works on Windows. Fixes #308 --- src/superqt/sliders/_labeled.py | 2 ++ src/superqt/sliders/_sliders.py | 8 +++++++- tests/zz_test_sliders/test_labeled_slider.py | 16 ++++++++++++++++ tests/zz_test_sliders/test_range_slider.py | 20 +++++++++++++++++--- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/superqt/sliders/_labeled.py b/src/superqt/sliders/_labeled.py index 3bcbcf3f..21758613 100644 --- a/src/superqt/sliders/_labeled.py +++ b/src/superqt/sliders/_labeled.py @@ -351,6 +351,7 @@ def setDecimals(self, prec: int) -> None: class QLabeledRangeSlider(_SliderProxy, QAbstractSlider): valuesChanged = Signal(tuple) editingFinished = Signal() + frangeChanged = Signal(float, float) _slider_class = QRangeSlider _slider: QRangeSlider @@ -515,6 +516,7 @@ def _setBarColor(self, color: str) -> None: # ------------- private methods ---------------- def _rename_signals(self) -> None: self.valueChanged = self.valuesChanged + self.rangeChanged = self.frangeChanged def _reposition_labels(self) -> None: if ( diff --git a/src/superqt/sliders/_sliders.py b/src/superqt/sliders/_sliders.py index 28663fc7..9d85d926 100644 --- a/src/superqt/sliders/_sliders.py +++ b/src/superqt/sliders/_sliders.py @@ -33,7 +33,13 @@ class QIntSlider(_IntMixin, _GenericSlider): class QRangeSlider(_IntMixin, _GenericRangeSlider): - pass + def _rename_signals(self) -> None: + super()._rename_signals() + # QSlider.rangeChanged is Signal(int, int) and cannot carry values + # outside the signed 32-bit range. Rebind to frangeChanged so large + # integer ranges (e.g. 0..10**11 in examples/labeled_sliders.py) work. + # Mirrors QDoubleRangeSlider. See #308. + self.rangeChanged = self.frangeChanged class QDoubleRangeSlider(_FloatMixin, QRangeSlider): diff --git a/tests/zz_test_sliders/test_labeled_slider.py b/tests/zz_test_sliders/test_labeled_slider.py index 31d462bc..3b944409 100644 --- a/tests/zz_test_sliders/test_labeled_slider.py +++ b/tests/zz_test_sliders/test_labeled_slider.py @@ -132,3 +132,19 @@ def test_slider_label_decimals_update_text(qtbot): slider.setDecimals(4) assert slider._label.text() == "3.1416" + + +def test_qlabeled_range_slider_large_range_does_not_typeerror(qtbot): + """Regression for examples/labeled_sliders.py on Windows/PyQt6 (#308).""" + sld = QLabeledRangeSlider() + qtbot.addWidget(sld) + big = 10**11 + # setRange should not raise TypeError from rangeChanged.emit + sld.setRange(0, big) + assert sld.minimum() == 0 + assert sld.maximum() == big + # example also sets a large value tuple + sld.setValue((20, 60 * 10**9)) + v = sld.value() + assert v[0] == 20 + assert v[1] == 60 * 10**9 diff --git a/tests/zz_test_sliders/test_range_slider.py b/tests/zz_test_sliders/test_range_slider.py index af9e37ec..37f38991 100644 --- a/tests/zz_test_sliders/test_range_slider.py +++ b/tests/zz_test_sliders/test_range_slider.py @@ -236,24 +236,27 @@ def test_rangeslider_signals(cls, orientation, qtbot): _assert_types(mock.call_args.args, tuple) _assert_types(mock.call_args.args[0], type_) + # rangeChanged is rebound to frangeChanged (float, float) so values outside + # the signed 32-bit range work (see #308). Signal args are therefore float + # for all range slider variants, including the integer ones. mock = Mock() sld.rangeChanged.connect(mock) with qtbot.waitSignal(sld.rangeChanged): sld.setMinimum(3) mock.assert_called_once_with(3, 99) - _assert_types(mock.call_args.args, type_) + _assert_types(mock.call_args.args, float) mock.reset_mock() with qtbot.waitSignal(sld.rangeChanged): sld.setMaximum(15) mock.assert_called_once_with(3, 15) - _assert_types(mock.call_args.args, type_) + _assert_types(mock.call_args.args, float) mock.reset_mock() with qtbot.waitSignal(sld.rangeChanged): sld.setRange(1, 2) mock.assert_called_once_with(1, 2) - _assert_types(mock.call_args.args, type_) + _assert_types(mock.call_args.args, float) @pytest.mark.parametrize("cls, orientation", ALL_SLIDER_COMBOS) @@ -290,3 +293,14 @@ def test_range_slider_with_equal_min_max(cls, orientation, qtbot): assert sld2.minimum() == 0 assert sld2.maximum() == 0 assert sld2.value() == (0, 0) + + +def test_qrange_slider_large_range_does_not_typeerror(qtbot): + """Large integer ranges must not explode on rangeChanged emit (#308).""" + sld = QRangeSlider() + qtbot.addWidget(sld) + big = 10**11 + with qtbot.waitSignal(sld.rangeChanged): + sld.setRange(0, big) + assert sld.minimum() == 0 + assert sld.maximum() == big From 771bb10bd82e8b4dd37c3701875151872343dd50 Mon Sep 17 00:00:00 2001 From: Phoenix0531-sudo Date: Wed, 22 Jul 2026 21:38:52 +0800 Subject: [PATCH 2/3] Cast rangeChanged emit args to float for PySide6 on Windows After rebinding rangeChanged to frangeChanged, older PySide6 builds on Windows still raise OverflowError when the payload is a Python int larger than C long. Emit float(min)/float(max) so large ranges such as 10**11 work with both PyQt6 and PySide6. Refs #308 --- src/superqt/sliders/_generic_slider.py | 6 +++++- src/superqt/sliders/_labeled.py | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/superqt/sliders/_generic_slider.py b/src/superqt/sliders/_generic_slider.py index 706bfc83..059fd38b 100644 --- a/src/superqt/sliders/_generic_slider.py +++ b/src/superqt/sliders/_generic_slider.py @@ -168,7 +168,11 @@ def setRange(self, min: float, max_: float) -> None: if oldMin != self._minimum or oldMax != self._maximum: self.sliderChange(self.SliderChange.SliderRangeChange) - self.rangeChanged.emit(self._minimum, self._maximum) + # Cast to float: rangeChanged is rebound to frangeChanged + # (Signal(float, float)). On Windows + older PySide6, emitting a + # Python int larger than C long (e.g. 10**11) raises OverflowError + # even though the signal is typed as double. See #308. + self.rangeChanged.emit(float(self._minimum), float(self._maximum)) self.setValue(self._value) # re-bound def tickInterval(self) -> float: # type: ignore diff --git a/src/superqt/sliders/_labeled.py b/src/superqt/sliders/_labeled.py index 21758613..9dc8dcfc 100644 --- a/src/superqt/sliders/_labeled.py +++ b/src/superqt/sliders/_labeled.py @@ -300,7 +300,9 @@ def _on_slider_range_changed(self, min_: int, max_: int) -> None: self._label.setSuffix(f" / {max_}") else: self._label.setSuffix("") - self.rangeChanged.emit(min_, max_) + # float() so large integer ranges work when rangeChanged is rebound to + # frangeChanged (QLabeledDoubleSlider / #308 on Windows + PySide6). + self.rangeChanged.emit(float(min_), float(max_)) def _on_slider_value_changed(self, v: Any) -> None: self._label.setValue(v) From 84b80f095ab456c1d0837c3273e49f6d99f9a88f Mon Sep 17 00:00:00 2001 From: Phoenix0531-sudo Date: Wed, 22 Jul 2026 21:40:04 +0800 Subject: [PATCH 3/3] Revert float cast on QLabeledSlider.rangeChanged emit QLabeledSlider still uses QAbstractSlider's native rangeChanged(int, int). Only _GenericSlider.setRange needs float() for frangeChanged / #308. --- src/superqt/sliders/_labeled.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/superqt/sliders/_labeled.py b/src/superqt/sliders/_labeled.py index 9dc8dcfc..21758613 100644 --- a/src/superqt/sliders/_labeled.py +++ b/src/superqt/sliders/_labeled.py @@ -300,9 +300,7 @@ def _on_slider_range_changed(self, min_: int, max_: int) -> None: self._label.setSuffix(f" / {max_}") else: self._label.setSuffix("") - # float() so large integer ranges work when rangeChanged is rebound to - # frangeChanged (QLabeledDoubleSlider / #308 on Windows + PySide6). - self.rangeChanged.emit(float(min_), float(max_)) + self.rangeChanged.emit(min_, max_) def _on_slider_value_changed(self, v: Any) -> None: self._label.setValue(v)