Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[proof of concept] make labelled range sliders expandable by modifying label range #172

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
25 changes: 21 additions & 4 deletions src/superqt/sliders/_labeled.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,12 @@ def __init__(self, *args, **kwargs) -> None:

def _setValue(self, value: float):
"""Convert the value from float to int before setting the slider value."""
self._slider.setValue(int(value))
value = int(value)
if self._slider.maximum() < value:
self._slider.setMaximum(value)
elif self._slider.minimum() > value:
self._slider.setMinimum(value)
self._slider.setValue(value)

def _rename_signals(self):
# for subclasses
Expand Down Expand Up @@ -209,6 +214,10 @@ def setEdgeLabelMode(self, opt: EdgeLabelMode) -> None:

QApplication.processEvents()

def setLabelSpinBoxRange(self, min: int, max: int):
"""Change valid label values independently of slider range."""
self._label.setRange(int(min), int(max))


class QLabeledDoubleSlider(QLabeledSlider):
_slider_class = QDoubleSlider
Expand All @@ -222,9 +231,17 @@ def __init__(self, *args, **kwargs) -> None:
self.setDecimals(2)

def _setValue(self, value: float):
"""Convert the value from float to int before setting the slider value."""
"""Override method in the subclass which converts float to int."""
if self._slider.maximum() < value:
self._slider.setMaximum(value)
elif self._slider.minimum() > value:
self._slider.setMinimum(value)
self._slider.setValue(value)

def setLabelSpinBoxRange(self, min: float, max: float):
"""Change valid label values independently of slider range."""
self._label.setRange(min, max)

def _rename_signals(self):
self.valueChanged = self._fvalueChanged
self.sliderMoved = self._fsliderMoved
Expand Down Expand Up @@ -574,8 +591,8 @@ def setMode(self, opt: EdgeLabelMode):
with contextlib.suppress(Exception):
self._slider.rangeChanged.disconnect(self.setRange)
else:
self.setMinimum(self._slider.minimum())
self.setMaximum(self._slider.maximum())
self.setMinimum(min(self._slider.minimum(), self.minimum()))
self.setMaximum(max(self._slider.maximum(), self.maximum()))
self._slider.rangeChanged.connect(self.setRange)
self._update_size()

Expand Down
40 changes: 40 additions & 0 deletions tests/test_sliders/test_labeled_slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,43 @@ def test_editing_float(qtbot):
slider._label.setValue(0.5)
slider._label.editingFinished.emit()
assert slider.value() == 0.5


@pytest.mark.parametrize("cls", [QLabeledSlider, QLabeledDoubleSlider])
def test_extended_label_spinbox_range(cls, qtbot):
slider = cls()
mock = Mock()
qtbot.addWidget(slider)
slider.setRange(0, 10)
slider.rangeChanged.connect(mock)
assert slider._label.minimum() == 0
assert slider._label.maximum() == 10
assert slider.minimum() == 0
assert slider.maximum() == 10

# changing slider value without changing spinbox range should return a clipped value
# slider._label.setValue(20)
# slider._label.editingFinished.emit() # simulate editing the label manually
# qtbot.wait(20)
# assert slider.value() == 10
# assert slider.maximum() == 10

# after changing label range, setting the label to a value outside the range should
# update the slider min/max as appropriate
slider.setLabelSpinBoxRange(-10, 20)

with qtbot.waitSignal(slider.rangeChanged):
slider._label.setValue(15)
assert slider._label.minimum() == -10
slider._label.editingFinished.emit() # simulate editing the label manually
assert slider._label.minimum() == -10
Comment on lines +115 to +117
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something is going wrong here

editingFinished is the signal used to propagate label value changes down to the slider so I manually emit it for the test. This has the unintended side effect of changing slider._label.minimum().

I tried to fix this by modifying

else:
self.setMinimum(min(self._slider.minimum(), self.minimum()))
self.setMaximum(max(self._slider.maximum(), self.maximum()))
self._slider.rangeChanged.connect(self.setRange)

but the value for the label minimum has already been reset by the time we get here - I can't figure out where it has been reset...

assert slider.value() == 15
assert slider.minimum() == 0 # unchanged
assert slider.maximum() == 15 # changed

with qtbot.waitSignal(slider.rangeChanged):
slider._label.setValue(-5)
slider._label.editingFinished.emit() # simulate editing the label manually
assert slider.value() == -5
assert slider.minimum() == -5 # changed
assert slider.maximum() == 15 # unchanged