Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I'm implementing a two-sided slider to be able to set upper and lower bound in just one slider:

enter image description here

The problem is that it can get stuck when both locators are at the far left:

enter image description here

And I'm unable to grab the blue locator.

SetAttributes[TwoSidedSlider, HoldAll];
TwoSidedSlider[sliderMin_, sliderMax_, var1_, var2_, 
  maxIsInfinity_: False] := DynamicModule[
  {pt = {{var1, 0}, {If[var2 == Infinity, sliderMax, var2], 0}}},
  LocatorPane[
   Dynamic[pt,
    ((* Update while dragging \*)
      pt = #1;
      var1 = #1[[1, 1]];
      var2 = 
       If[maxIsInfinity && #1[[2, 1]] == sliderMax, 
        Infinity, #1[[2, 1]]];
      ) &
    ],
   Graphics[{
     Gray, Line[{{sliderMin, 0}, {Dynamic[pt[[1, 1]]], 0}}],(* min to lower \*)
     Black, 
     Line[{{Dynamic[pt[[1, 1]]], 0}, {Dynamic[pt[[2, 1]]], 0}}], (* lower to upper \*)
     Gray, 
     Line[{{Dynamic[pt[[2, 1]]], 0}, {sliderMax, 0}}] (* upper to max \*)
     }, AspectRatio -> 1/5],
   { (* Locator Region and stepsize \*)
    {{sliderMin, 0},
     {Dynamic[pt[[2, 1]]], 0}, {1, 1}},
    {{Dynamic[pt[[1, 1]]], 0},
     {sliderMax, 0}, {1, 1}}
    }
   , Appearance -> {Style["[", Red, Larger, Bold], 
     Style["]", Blue, Larger, Bold]}]]

It is used something like this:

lower = 0; upper = Infinity;
{{Dynamic[lower], TwoSidedSlider[0, 5000, lower, upper, True], 
   Dynamic[upper]}} // TableForm

Any tips on how to get a two-sided slider or what causes this one to get stuck?

share|improve this question
    
You might find the Experimental` context interesting: Needs@"Experimental`"; Experimental`IntervalSlider[Dynamic@z, {-10, 10}]. –  István Zachar Jul 6 '12 at 15:27
    
Yea, that's interesting! Thanks –  ssch Jul 6 '12 at 15:32
    
@IstvánZachar how did you learn that? It seems to be undocumented (Experimental/guide/ExperimentalPackage) –  Mike Honeychurch Jul 6 '12 at 23:21
    
@Mike: Yes, it is undocumented, but you can find an Experimental directory in the AddOns\Packages subdir, which made me to try to load it with Needs. And the rest was just trial and error. It is also mentioned here. –  István Zachar Jul 7 '12 at 6:14

2 Answers 2

up vote 4 down vote accepted

Use:

pt = Clip[#1\[Transpose]*{1, 0}, {sliderMin, sliderMax}]\[Transpose];

as the update function, and remove the min-max restrictions from the LocatorPane (3rd argument). Also, when you update pt based on #1, you should use pt instead of #1 when you update var1 and var2:

SetAttributes[TwoSidedSlider, HoldAll];
TwoSidedSlider[sliderMin_, sliderMax_, var1_, var2_, 
  maxIsInfinity_: False] := DynamicModule[
  {pt = {{var1, 0}, {If[var2 == Infinity, sliderMax, var2], 0}}},
  LocatorPane[Dynamic[pt, (
      pt = 
       Clip[#1\[Transpose]*{1, 0}, {sliderMin, sliderMax}]\[Transpose];
      var1 = pt[[1, 1]];
      var2 = 
       If[maxIsInfinity && pt[[2, 1]] == sliderMax, Infinity, 
        pt[[2, 1]]];
      ) &],
   Graphics[{Gray,
     Line[{{sliderMin, 0}, {Dynamic[pt[[1, 1]]], 0}}],
      Black, 
     Line[{{Dynamic[pt[[1, 1]]], 0}, {Dynamic[pt[[2, 1]]], 0}}],
      Gray, Line[{{Dynamic[pt[[2, 1]]], 0}, {sliderMax, 0}}]}, 
    AspectRatio -> 1/5],
   (*{{{sliderMin,0},{var1,0},{1,1}},{{0,0},{sliderMax,0},{1,1}}},*)
   Appearance -> {Style["[", Red, Larger, Bold], 
     Style["]", Blue, Larger, Bold]}]]

lower = 0; upper = Infinity;
{{Dynamic[lower], TwoSidedSlider[0, 5000, lower, upper, True], 
   Dynamic[upper]}} // TableForm
share|improve this answer
    
Nope, with this it is possible to move the left slider over the right one. –  halirutan Jul 6 '12 at 15:44
    
That nearly works, with a pt = Sort[...] it does well –  ssch Jul 6 '12 at 15:55
    
And with pt = Round[Sort[...],stepsize] it's perfect! –  ssch Jul 6 '12 at 16:11
    
@halirutan: it wasn't stated that the first locator cannot have the larger value... –  István Zachar Jul 6 '12 at 16:17

The problem is that when the sliders have the same value, Dynamic chooses the wrong to move when you click near it. You could make the sliders have a small gap between them and it works as expected

SetAttributes[TwoSidedSlider, HoldAll];
TwoSidedSlider[sliderMin_, sliderMax_, var1_, var2_, 
  maxIsInfinity_: False] := 
 DynamicModule[{pt = {{var1, 
      0}, {If[var2 == Infinity, sliderMax, var2], 0}}}, 
  LocatorPane[Dynamic[pt, ((*Update while dragging *) pt = #1;
      var1 = #1[[1, 1]];
      var2 = 
       If[maxIsInfinity && #1[[2, 1]] == sliderMax, 
        Infinity, #1[[2, 1]]];) &], 
   Graphics[{Gray, 
     Line[{{sliderMin, 0}, {Dynamic[pt[[1, 1]]], 0}}],(*min to lower *) 
     Black, Line[{{Dynamic[pt[[1, 1]]], 0}, {Dynamic[pt[[2, 1]]], 
        0}}],(*lower to upper *) Gray, 
     Line[{{Dynamic[pt[[2, 1]]], 0}, {sliderMax, 0}}]
      (*upper to max *)}, AspectRatio -> 1/5], {
     (*Locator Region and stepsize *) {{sliderMin, 
      0}, {Dynamic[pt[[2, 1]] - 1], 0}, {1, 
      1}}, {{Dynamic[pt[[1, 1]] + 1], 0}, {sliderMax, 0}, {1, 1}}}, 
   Appearance -> {Style["[", Red, Larger, Bold], 
     Style["]", Blue, Larger, Bold]}]]

Note the change in the last argument to LocatorPane.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.