jupyter-widgets / ipywidgets

Interactive Widgets for the Jupyter Notebook
https://ipywidgets.readthedocs.io
BSD 3-Clause "New" or "Revised" License
3.16k stars 950 forks source link

Non Overlapping Ranged Sliders #1312

Open deisi opened 7 years ago

deisi commented 7 years ago

Hey, I have a need for ranged sliders, that can not be overlapped, so that the values are never the same. Can this be achieved somehow?

Cheers

jasongrout commented 7 years ago

Great question. The underlying slider (jqueryui slider) doesn't have an option for that, but perhaps you could do it for now by listening to changes on the value, and reset that value to enforce your minimum distance.

from ipywidgets import IntRangeSlider
x = IntRangeSlider()
def enforce_gap(change):
    gap = 10
    min, max = change.new
    oldmin, oldmax = change.old
    if (max-min) < gap:
        if oldmin == min:
            # max changed
            max = min + gap
        else:
            min = max - gap
    x.value = (min, max)
x.observe(enforce_gap, 'value')
x
jasongrout commented 7 years ago

Another way is making a new class with a validator:

from traitlets import validate
class IntRangeSliderGap(IntRangeSlider):
    @validate('value')
    def enforce_gap(self, proposal):
        gap=10
        min, max = proposal.value
        oldmin, oldmax = self.value
        if (max-min) < gap:
            if oldmin == min:
                # max changed
                max = min + gap
            else:
                min = max - gap
        return (min, max)
jasongrout commented 7 years ago

Does that help?

deisi commented 7 years ago

I will try, but looks promising. Thx

Am 26.04.2017 15:30 schrieb "Jason Grout" notifications@github.com:

Does that help?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/jupyter-widgets/ipywidgets/issues/1312#issuecomment-297408437, or mute the thread https://github.com/notifications/unsubscribe-auth/AFL2NNx8cwnNrcltLoIlwkEEti8GQgsiks5rz0bfgaJpZM4NInrA .

deisi commented 7 years ago

We are almost there. The only problem with the validated class is, that it can get out of bounds, when you move the lower slider up until its end. It will then push the upper one out of the max value. I tried fixing it like this:

from traitlets import validate
class IntRangeSliderGap(widgets.IntRangeSlider):
    @validate('value')
    def enforce_gap(self, proposal):
        gap=1
        min, max = proposal.value
        oldmin, oldmax = self.value
        if min == proposal.max:
            min -= 1

        if (max-min) < gap:
            if oldmin == min:
                # max changed
                max = min + gap
            else:
                min = max - gap
        return (min, max)

but proposal.max doesn't exist.

deisi commented 7 years ago

Never minde, got it with:

from traitlets import validate
class IntRangeSliderGap(widgets.IntRangeSlider):
    @validate('value')
    def enforce_gap(self, proposal):
        gap=1
        min, max = proposal.value
        oldmin, oldmax = self.value

        if min == self.max:
            min -= 1

        if (max-min) < gap:
            if oldmin == min:
                # max changed
                max = min + gap
            else:
                min = max - gap
        return (min, max)
deisi commented 7 years ago

Okay, now there is some interference with the continuous_update=False option. If you set it, the slider gui can get stuck in an overlapping position, while the value of the slider is just fine. To get the slider into that position,you have to move the lower value against the upper. It then gets not pushed up right away.

Any ideas how to circumvent that?

jasongrout commented 7 years ago

Any ideas how to circumvent that?

That's because the python side doesn't see the slider value change until after you release if continuous_update is false. In order to fix that, we have to do something with the javascript code.

We could support a new attribute, say "min_gap" or something better named, that would propagated over to the javascript, and the javascript could check that value anytime the slider value changed. Would you like to put in a pull request for that? Basically, you'd add one attribute to the slider, copying and modifying one of the other ones (like 'max'). Then you'd probably add validation in both python and javascript. Each side, on the value changing, would make sure the correct gap size existed.

deisi commented 7 years ago

Would you like to put in a pull request for that?

In principle yes, but I cant promise it will happen any soon. My Javascript skills are very basic, but I think I can work my way through. I have to give it a try and we will see if I can transform your suggestion into a pull request.

jasongrout commented 7 years ago

Awesome!

jasongrout commented 7 years ago

To help you get started, the code that is triggered when the slider values change is at https://github.com/jupyter-widgets/ipywidgets/blob/c3e7ee6a8d7fee9519c6faeb048628e44dd1389b/jupyter-js-widgets/src/widget_int.ts#L337. We use the jquery ui slider (http://api.jqueryui.com/slider/, https://jqueryui.com/slider/#range).

jasongrout commented 7 years ago

And feel free to ask questions.

dharmaquark commented 6 years ago

I'll take a look at this one

anuzk13 commented 7 months ago

Hi is this available?