pyapp-kit / superqt

Missing widgets and components for Qt-python
https://pyapp-kit.github.io/superqt/
BSD 3-Clause "New" or "Revised" License
210 stars 38 forks source link

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

Open alisterburt opened 1 year ago

alisterburt commented 1 year ago

Hey Talley, I wanted a labelled QSlider where the label could be used to update the slider range like your labelled double slider. This PR is a minimal change that allows this to work for QLabeledSlider if the label range is manually updated

I've opened this PR so that we can discuss if there might be a better approach/worth adding some convenience for on the labelled slider class. If you think this is okay as-is then I'll add some tests before merge

from qtpy.QtCore import Qt
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget

from superqt import QLabeledSlider

app = QApplication([])

ORIENTATION = Qt.Orientation.Horizontal

w = QWidget()
w.setLayout(QVBoxLayout())

qls = QLabeledSlider(ORIENTATION)
qls.valueChanged.connect(lambda e: print("qls valueChanged", e))
qls.setRange(0, 100)
qls.setValue(30)

# update the label range - comment this line to revert to default behaviour
qls._label.setRange(0, 1000)

w.layout().addWidget(qls)

w.show()
app.exec_()

behaviour without this change (value can't be set outside of slider range (0, 100)):

https://github.com/pyapp-kit/superqt/assets/7307488/2bcc94f8-dc1f-4a89-9f31-9f901f04b8b3

behaviour with this change (range can be extended (value can be set to any value in label range (0, 1000), slider updates):

https://github.com/pyapp-kit/superqt/assets/7307488/e8cacde2-8aff-4ccf-b6d0-d4ba49c39caf

codecov[bot] commented 1 year ago

Codecov Report

Patch coverage: 60.00% and project coverage change: -0.09 :warning:

Comparison is base (402d237) 85.91% compared to head (8f83091) 85.82%.

:exclamation: Current head 8f83091 differs from pull request most recent head 52d63ed. Consider uploading reports for the commit 52d63ed to get more accurate results

Additional details and impacted files ```diff @@ Coverage Diff @@ ## main #172 +/- ## ========================================== - Coverage 85.91% 85.82% -0.09% ========================================== Files 32 32 Lines 2641 2646 +5 ========================================== + Hits 2269 2271 +2 - Misses 372 375 +3 ``` | [Impacted Files](https://app.codecov.io/gh/pyapp-kit/superqt/pull/172?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyapp-kit) | Coverage Δ | | |---|---|---| | [src/superqt/sliders/\_labeled.py](https://app.codecov.io/gh/pyapp-kit/superqt/pull/172?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyapp-kit#diff-c3JjL3N1cGVycXQvc2xpZGVycy9fbGFiZWxlZC5weQ==) | `83.85% <60.00%> (-0.30%)` | :arrow_down: | ... and [1 file with indirect coverage changes](https://app.codecov.io/gh/pyapp-kit/superqt/pull/172/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyapp-kit)

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Do you have feedback about the report comment? Let us know in this issue.

tlambert03 commented 1 year ago

Seems very reasonable... @Czaki you expressed concern over in the other thread... what's your hesitation?

Czaki commented 1 year ago

All Qt sliders, spinboxes, etc, do not allow for silent go out of range. This is really unexpected behavior. There is also no update of docs for that.

So if anyone uses this widget as input, then they could write the following code that will assume that emitted value is in a given range.

So at least, it should be mentioned in the documentation.

Or a new class like QLabeledSliderDynamic should be added.

tlambert03 commented 1 year ago

You say silently, but couldn't we make sure that the proper signal is emitted?

Most sliders don't have labels next to them in Qt, but with a label there, it's very similar to programmatically calling .setRange right? (And that's fully supported)

Czaki commented 1 year ago

Yes. But setRange is called from code. The user from GUI cannot change range.

tlambert03 commented 1 year ago

Perhaps we could add a flag to the init method to gate this feature? Like extend_range_to_value

(Then the code writer would know whether or not they need to pay attention to the rangeChanged signal)

Czaki commented 1 year ago

Perhaps we could add a flag to the init method to gate this feature? Like extend_range_to_value

Yes. This is one of a possible solution.

tlambert03 commented 1 year ago

@alisterburt could you do that please? Let's make it false by default so that this feature is opt in. And then add a test to make sure that the rangeChanged (or whatever it is) signal is emitted when the feature is on and the label is changed, but not otherwise.

alisterburt commented 1 year ago

Hi both, happy to gate in whatever way is best but it’s worth noting that with this implementation the behaviour is already gated:- unless the label (spinbox) range, not the slider range, is explicitly changed in code the user cannot specify a value outside the slider range in the label- if the label (spinbox) range is extended programmatically, the user can specify a value within that larger range and the slider will updateI can add an extra parameter to the init if we still feel it’s necessary but the current behaviour feels safe to me… what do we think?Sent from mobile - apologies for brevityOn 21 Jun 2023, at 12:13, Talley Lambert @.***> wrote: @alisterburt could you do that please? Let's make it false by default so that this feature is opt in. And then add a test to make sure that the rangeChanged (or whatever it is) signal is emitted when the feature is on and the label is changed, but not otherwise.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

tlambert03 commented 1 year ago

unless the label (spinbox) range, not the slider range, is explicitly changed in code the user cannot specify a value outside the slider range in the label-

ohhhh, right! Sorry, I missed the importance of that line in your example:

# update the label range - comment this line to revert to default behaviour
qls._label.setRange(0, 1000)

can add an extra parameter to the init if we still feel it’s necessary but the current behaviour feels safe to me… what do we think?

That said, I still think we need a somewhat public exposure of this. I don't want napari, for example, accessing ._label.setRange to "opt in" to this behavior... since _label may change in the future.

alisterburt commented 1 year ago

cool! I'll cook something up now :)

alisterburt commented 1 year ago

I had a go but got a little stuck, there is a side effect when emitting the editingFinished event which somehow resets slider._label.minimum() and slider._label.maximum() - I thought I had it pinned down (see comment in code above) but unfortunately that's not the culprit and I can't see where else this is being modified...

Would appreciate some help if possible - spent an hour or so bashing my head on this to no avail, happy to provide more context if needed