Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/superqt/sliders/_generic_slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/superqt/sliders/_labeled.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 (
Expand Down
8 changes: 7 additions & 1 deletion src/superqt/sliders/_sliders.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
16 changes: 16 additions & 0 deletions tests/zz_test_sliders/test_labeled_slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
20 changes: 17 additions & 3 deletions tests/zz_test_sliders/test_range_slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Loading