4
$\begingroup$

I would like to generate a function which takes a list of rules as input and generates sliders which are altering the values of the list. Then I would like to use this function in an dynamic module as input. This can be used as a dynamic way to generate a user interface for a model which is constantly evolving and I don't won't to hardcode the GUI and change it every time.

So here is a minimal example:

input:

ruleslist = {"valu1" -> 1, "value2" -> 2};
rangelist = {"valu1" -> {0, 2}, "value" -> {0, 5}};
ruleslist2 = {"valu3" -> 10, "value4" -> 20};
rangelist2 = {"valu4" -> {0, 20}, "value4" -> {0, 50}};

the function

rulestoinput[ruleslist_, ranges_, output_] := 
DynamicModule[{values, numInputFields, sliderlistn, names, 
origvalues, rangeslist},

origvalues = ruleslist[[All, 2]];
numInputFields = Length@ruleslist;
names = ruleslist[[All, 1]];
rangeslist = ranges[[All, 2]];

Do[values[i] = ruleslist[[i, 2]], {i, numInputFields}];
Panel@Column@
Flatten@{Column@{names[[#]], Dynamic[values[#]], 
      Slider[Dynamic[values[#]], rangeslist[[#]]]} & /@ 
   Range[numInputFields],
  Button["save settings", 
   output = 
    Flatten@Table[{names[[i]] -> values[i]}, {i, numInputFields}]]}
]

the dynamic module which uses the "rulestoinput" function and uses their results to Run something.

DynamicModule[{output1, output2, result},

Panel@Column@{rulestoinput[ruleslist, rangelist, output1],
Dynamic@output1,
rulestoinput[ruleslist2, rangelist2, output2],
Dynamic@output2,
Button["Run", result = Flatten@{output1, output2}],
Dynamic@result
}

]

The interface looks like this

how it looks

It works like this, but I don't want to press the "save settings" before "Run".

It would be good if just by clicking the Run button that the output1 and output2 would be updated from the sliders. I guess I am doing something wrong with the dynamic module. I searched for how to pass results from one Dynamic module to another, without success.

Of a slightly different approach. Here the results are not yet passed from the rulestoinput2 function to the "Run" button.

rulestoinput2[ruleslist_, rangelist_] := 
DynamicModule[{x = Table[ruleslist[[i, 2]], {i, Length@ruleslist}]},
Column[{Row[
 Table[With[{i = i}, 
   Slider[Dynamic[x[[i]]], rangelist[[i, 2]]]], {i, 1, 
   Length@ruleslist}]],
Dynamic@Thread[ruleslist[[All, 1]] -> x]}]]

DynamicModule[{result},

Panel@Column@{
rulestoinput2[ruleslist, rangelist],
rulestoinput2[ruleslist2, rangelist2],
Button["Run", 
 result = Flatten@{"new ruleslist 1", "new ruleslist 2"}],
Dynamic@result

}

]
$\endgroup$

1 Answer 1

2
$\begingroup$

I found an answer to my own question by reading the answer to Make dynamic controls from existing list.

So I guess the answer to my question is to define the functions internally in the dynamic module with the HoldFirst attribute.

DynamicModule[{controlGenerate, ruleslist = ruleslist, 
ruleslist2 = ruleslist2, result, controlfromList},


Panel@Column[{Dynamic@ruleslist,
Dynamic@ruleslist2,
Dynamic@controlfromList[ruleslist, rangelist],
Dynamic@controlfromList[ruleslist2, rangelist2],
Button["Run", result = Flatten@{ruleslist, ruleslist2}],
Dynamic@If[Length@result >= 1, result, "press Run"]
}]

, Initialization :> (SetAttributes[controlGenerate, HoldFirst];
controlGenerate[var_, range_] := Slider[var, Last@range];
SetAttributes[controlfromList, HoldFirst];
controlfromList[ruleslist_, rangelist_] := 
Column@(Column@{Row@{ruleslist[[#, 1]], " ", 
        Dynamic@ruleslist[[#, 2]]}, 
      Dynamic@controlGenerate[Dynamic@ruleslist[[#, 2]], 
        rangelist[[#]]]} & /@ Range[Length@ruleslist])
)]

The interface then looks like this and expands dynamically as the ruleslist and rangelist are changed.

enter image description here

$\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.