1
$\begingroup$

I want to create a custom ui control with interacting elements.

For simplicity, assume there are going to be three Sliders, where each successive Slider's max value depends on the current value of the preceding Slider.

{
 Slider[Dynamic[x, (y=f[y, # - 1]; x = #)&], {3, 10, 1}],
 Slider[Dynamic[y, (z=f[z, # - 1]; y = #)&], {2, Dynamic[x-1], 1}],
 Slider[Dynamic[z], {1, Dynamic[y-1], 1}]
 } // Column

where f, is an auxiliary function that limits the value of the first argument, if it exceeds the value of the second argument:

f[x_, max_] := Which[
  x > max, max,
  True, x
 ]

After a certain amount of experimentation, I realized that the second argument of Dynamic needs to include all the expressions that involve changing variables, otherwise the outcome is not user friendly.

For example, this is how I'd modify the second argument of Dynamic in the first Slider:

...(y=f[y,#-1];z=f[z,y-1];y=#)&... (* first slider mod *)

Notice how the mod includes z, now. Similar modifications should be made in the second Slider.

However, notice that including at the second argument of Dynamic all the expressions that are needed to change the respective variables, becomes cumbersome for anything larger than an ensamble of three Sliderss.

Also, it seems that automating the construction of such a composite ui element becomes progressively difficult as the dependence relations between variables get more elaborate (and the f functions get more complicated).

So, my question is, is there another way to construct a composite ui element, like the one above, where each successive element responds to the values of previous elements?

The response I'm more interested here, is for the max value of dependent sliders to change in an expected manner, when the value of a preceding master sliders changes.

$\endgroup$

1 Answer 1

3
$\begingroup$

Concerning this:

Also, it seems that automating the construction of such a composite ui element becomes progressively difficult as the dependence relations between variables get more elaborate (and the f functions get more complicated).

All I can say is that complicated relationships might require complicated code. Here's a way to keep it all in one place, so that updates are done in a consistent way ("consistent" being defined by the programmer through the code). The tracking function looks the same for all controls, at least in this MWE. If the update can be checked quickly, then the UI should be satisfactorily responsive.

ClearAll[update];
SetAttributes[update, HoldAll];
update[vars_] := Function[{val, expr}
   , expr = val (* perform default update *)
   ; (* update all vars in "consistent" way: *)
   vars = FoldList[Min[#2, #1 - 1] &, vars]
   , HoldRest];

 {Slider[Dynamic[x, update[{x, y, z}]], {3, 10, 1}],
  Slider[Dynamic[y, update[{x, y, z}]], {2, Dynamic[x - 1], 1}],
  Slider[Dynamic[z, update[{x, y, z}]], {1, Dynamic[y - 1], 1}]
  } // Column

Expanding this MWE is straightforward:

 {Slider[Dynamic[w, update[{w, x, y, z}]], {4, 10, 1}],
  Slider[Dynamic[x, update[{w, x, y, z}]], {3, Dynamic[w - 1], 1}],
  Slider[Dynamic[y, update[{w, x, y, z}]], {2, Dynamic[x - 1], 1}],
  Slider[Dynamic[z, update[{w, x, y, z}]], {1, Dynamic[y - 1], 1}]
  } // Column
$\endgroup$
1
  • $\begingroup$ I was hoping there'd be something I was missing and that could've possibly made the difference; unfortunately, it seems I wasn't, but that's ok; this approach seems viable enough and I think it's better to have something to work with, rather than nothing; I think I can wrangle something out of it. It's really elegant how the Function does the heavy lifting in update; I'll just wait it out for a couple of days in case another approach surfaces, but hey, thanks! $\endgroup$
    – joka
    Commented Jan 31, 2021 at 7:37

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.