10
$\begingroup$

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?

$\endgroup$
4
  • $\begingroup$ You might find the Experimental` context interesting: Needs@"Experimental"; ExperimentalIntervalSlider[Dynamic@z, {-10, 10}]. $\endgroup$ Commented Jul 6, 2012 at 15:27
  • $\begingroup$ Yea, that's interesting! Thanks $\endgroup$
    – ssch
    Commented Jul 6, 2012 at 15:32
  • $\begingroup$ @IstvánZachar how did you learn that? It seems to be undocumented (Experimental/guide/ExperimentalPackage) $\endgroup$ Commented Jul 6, 2012 at 23:21
  • $\begingroup$ @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. $\endgroup$ Commented Jul 7, 2012 at 6:14

2 Answers 2

4
$\begingroup$

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
$\endgroup$
4
  • $\begingroup$ Nope, with this it is possible to move the left slider over the right one. $\endgroup$
    – halirutan
    Commented Jul 6, 2012 at 15:44
  • $\begingroup$ That nearly works, with a pt = Sort[...] it does well $\endgroup$
    – ssch
    Commented Jul 6, 2012 at 15:55
  • $\begingroup$ And with pt = Round[Sort[...],stepsize] it's perfect! $\endgroup$
    – ssch
    Commented Jul 6, 2012 at 16:11
  • $\begingroup$ @halirutan: it wasn't stated that the first locator cannot have the larger value... $\endgroup$ Commented Jul 6, 2012 at 16:17
4
$\begingroup$

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.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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