2
$\begingroup$

I need to use Manipulate[] with many controls, because I have around 40 parameters which I want to be able to alter. Since the number is too large, I want to split these controls into multiple columns, and it would be even better if I can add "Delimiter" within the each columns, so that I can further group the parameters.

I found Grid[] may be needed here, however, I have to convert each line into the form of "Control[{{},}]", I wanted to use Map to do this in batch, but somehow the function will return the correct result only when I run the code for the first time, after that, the variables will be assigned to a specific value, so that it can no longer generate a control any longer.

The attached sample code and figures are attached; the third line will ouput the same figure as the second line, if excuted for the first time

My question is: how to conviently realize the desired grouping of controls into different columns and then subgroups? For example, if I already have the first Manipulate[] function with several Delimiter, how do I conveniently convert the controls into multiple columns?

Manipulate[
Plot[a*x^3 + b*x^2 + c*x + d, {x, -4, 4}], {{a, 1, "a"}, 0, 
3}, {{b, 1, "b"}, 0, 3}, Delimiter, {{e, 2, "e"}, 0, 
5}, Delimiter, {{c, 1, "c"}, 0, 4}, {{d, 0, "d"}, 0, 2}]

Manipulate[Plot[a*x^3 + b*x^2 + c*x + d, {x, -4, 4}], 
Grid[{{Control[{{a, 1, "a"}, 0, 3}], Control[{{b, 1, "b"}, 0, 3}], 
Control[{{e, 2, "e"}, 0, 5}], Control[{{c, 1, "c"}, 0, 4}], 
 Spacer[20], Control[{{d, 0, "d"}, 0, 2}]}}, 
Dividers -> {All, All}], 
ControlPlacement -> {Left, Left, Left, Left}]

Manipulate[Plot[a*x^3 + b*x^2 + c*x + d, {x, -4, 4}], 
Grid[{Map[
Control, {{{a, 1, "a"}, 0, 3}, {{b, 1, "b"}, 0, 3}, {{e, 2, "e"}, 
  0, 5}, {{c, 1, "c"}, 0, 4}, {{d, 0, "d"}, 0, 2}}]}, 
Dividers -> {All, All}], 
ControlPlacement -> {Left, Left, Left, Left}]

enter image description here

$\endgroup$
4
  • $\begingroup$ Not relevant perhaps, but have you considered NMinimize to reduce the problem-space? Also, are all params continuous or do some have discrete values, such true/false states? $\endgroup$
    – Syed
    Oct 2 '21 at 9:18
  • $\begingroup$ @Syed, thanks for your reply, but it's not a matter of optimization, I'm using Manipulate to generate some figures, where I want to modify those parameters to see how the graphs changes; the code itself is not time consuming. they are mostly continuous, though I do have a few with discrete values. $\endgroup$
    – larry
    Oct 2 '21 at 9:26
  • $\begingroup$ mathematica.stackexchange.com/questions/67111/… should get you started. To label your panels: mathematica.stackexchange.com/questions/94680/… $\endgroup$
    – Syed
    Oct 2 '21 at 10:29
  • $\begingroup$ For complicated interfaces, you are probably better off using DynamicModule directly. Manipulate saves the programmer effort by automatically constructing, linking, and laying out controls. But when it does not automatically do what you want, the savings are quickly spent trying to get Manipulate to do it just right. $\endgroup$
    – Michael E2
    Oct 2 '21 at 15:52
2
$\begingroup$
ClearAll[controlLayout]

SetAttributes[controlLayout, HoldAll]

controlLayout[layout_: Automatic] := Module[{foo = Switch[layout, 
       Automatic | "Column" | "Vertical",
         Column[Map[Row[#, Spacer[5], Alignment -> {Center, Center}] &]@#,
           Alignment -> {Center, Center}] &,
       "Row" | "Horizontal",
          Row[Map[Column[#, Alignment -> {Center, Center}] &]@#, Spacer[5],
            Alignment -> {Center, Center}] &)]},
    foo @ DeleteCases[{}] @ SequenceSplit[Control /@ #, 
        {a : (Except[_[Delimiter]] ...), Control[Delimiter]} :> {a}]] &;

Examples:

Put your controls in a list and wrap with Evaluate @ controlLayout[]:

Manipulate[Plot[a*x^3 + b*x^2 + c*x + d, {x, -4, 4}],
 Evaluate @ controlLayout[] @
   {{{a, 1, "a"}, 0, 3},
    {{b, 1, "b"}, 0, 3},
    {{g, 1, "g"}, 0, 10},
    Delimiter,
    {{e, 2, "e"}, 0, 5},
    Delimiter,
    {{c, 1, "c"}, 0, 4},
    {{d, 0, "d"}, 0, 2}, 
    Delimiter},
 Alignment -> Center]

enter image description here

Replace controlLayout[] with controlLayout["Row"] to get:

enter image description here

$\endgroup$
5
  • $\begingroup$ Thanks for your code, which basically solves the problem; I prefer the controlLayout["Row"] way, but one more question is, is it possible to introduce some Delimiter in within the columns of the second figure, like between control b and control g? And is it possible to display all the columns of controls in places other than the top, e.g., can we make them display on the left side? $\endgroup$
    – larry
    Oct 3 '21 at 2:31
  • $\begingroup$ Another thing that makes me concern is that, I have to make sure that I don't make mistakes when I call this function. For example, even if I have an extra comma before Delimiter, then when I execute the function, an error "does not have the correct form for a variable specification" will arise, and if this happens, I have to clear all the variables before call the revised version of the function, otherwise, the relevant variables within the controls will no longer be recongnized as variables, but as their initial values, so no controls will be generated. Any way to overcome this issue? $\endgroup$
    – larry
    Oct 3 '21 at 2:36
  • $\begingroup$ @larry, regarding the second issue, you can wrap Manipulate[....] inside DynamicModule, that is, use DynamicModule[{a,b,c,d,e,g},Manipulate[...] ] (maybe ClearAll[a,b,c,d,e,g] before Manipulate is more straightforward). $\endgroup$
    – kglr
    Oct 3 '21 at 3:39
  • $\begingroup$ Re "to display all the columns of controls in places other than the top", you can use ControlPlacement -> Left , ControlPlacement ->Bottom,ControlPlacement ->Right. $\endgroup$
    – kglr
    Oct 3 '21 at 3:40
  • $\begingroup$ thanks a lot for your comments, now the code is functioning very properly. One last question is, currently, Delimiter is used as a separator to distinguish different rows/columns, but preferrably, I'd like to keep some Delimiter within the rows/columns. Is it possible to add Delimiter or Spacer[] within the different rows/columns, so that the code looks clearer? $\endgroup$
    – larry
    Oct 3 '21 at 5:05
2
$\begingroup$

If you use Manipulate and want to put controls in a Grid or Row or Column, you need to add: Control to every control specification, e.g. Control[{x,0,1}]. If you want Manipulate to handle this for simplicity, you can only use one column.

Here is an artificial example for the simple case without Control(Fourier synthesis, Sin/Cos and phases for the sake of this example). The number of terms is specified by n:

n = 2; (*Number of Fourier terms*)
ctrls1 = Table[{Subscript[ampls, i], 0, 1}, {i, 0, n}];
vars1 = Table[Subscript[ampls, i], {i, 0, n}];
ctrls2 = Table[{Subscript[phases, i], 0, 1}, {i, 0, n}];
vars2 = Table[Subscript[phases, i], {i, 0, n}];
ctrls3 = Table[{Subscript[amplc, i], 0, 1}, {i, 0, n}];
vars3 = Table[Subscript[amplc, i], {i, 0, n}];
ctrls4 = Table[{Subscript[phasec, i], 0, 1}, {i, 0, n}];
vars4 = Table[Subscript[phasec, i], {i, 0, n}];
fun[x_, vars1_, vars2_, vars3_, vars4_] := 
  Total@MapThread[#1 Sin[2 Pi x #2 + #3] + #4 Cos[
        2 Pi x #5 + #6] &, {vars1, Range[0, n], vars2, vars3, 
     Range[0, n], vars4}];

With[{vars1 = vars1, vars2 = vars2, vars3 = vars3, vars4 = vars4}, 
 Manipulate[
  Plot[fun[x, vars1, vars2, vars3, vars4], {x, 0, 1}],
  Style["Amplitudes Sines", 12, Bold], Evaluate[Sequence @@ ctrls1],
  Delimiter,
  Style["Phases Sines", 12, Bold], Evaluate[Sequence @@ ctrls2],
  Style["Amplitudes Cosines", 12, Bold], Evaluate[Sequence @@ ctrls3],
  Delimiter,
  Style["Phases Cosines", 12, Bold], Evaluate[Sequence @@ ctrls4]
  ]
 ]

enter image description here

An example with Grid, where an additional Control is needed, is the following:

n = 2; (*Number of Fourier terms*)
ctrls1 = Table[{Subscript[ampls, i], 0, 1}, {i, 0, n}];
vars1 = Table[Subscript[ampls, i], {i, 0, n}];
ctrls2 = Table[{Subscript[phases, i], 0, 1}, {i, 0, n}];
vars2 = Table[Subscript[phases, i], {i, 0, n}];
ctrls3 = Table[{Subscript[amplc, i], 0, 1}, {i, 0, n}];
vars3 = Table[Subscript[amplc, i], {i, 0, n}];
ctrls4 = Table[{Subscript[phasec, i], 0, 1}, {i, 0, n}];
vars4 = Table[Subscript[phasec, i], {i, 0, n}];
fun[x_, vars1_, vars2_, vars3_, vars4_] := 
  Total@MapThread[#1 Sin[2 Pi x #2 + #3] + #4 Cos[
        2 Pi x #5 + #6] &, {vars1, Range[0, n], vars2, vars3, 
     Range[0, n], vars4}];

With[{vars1 = vars1, vars2 = vars2, vars3 = vars3, vars4 = vars4},
 Manipulate[
  Plot[fun[x, vars1, vars2, vars3, vars4], {x, 0, 1}],
  Evaluate@Grid[{
     {Style["Amplitudes Sines", 10, Bold], 
      Evaluate[Sequence @@ (Control /@ ctrls1)]},
     {Style["Phases Sines", 10, Bold], 
      Evaluate[Sequence @@ (Control /@ ctrls2)]},
     {Style["Amplitudes Cosines", 10, Bold], 
      Evaluate[Sequence @@ (Control /@ ctrls3)]},
     {Style["Phases Cosines", 10, Bold], 
      Evaluate[Sequence @@ (Control /@ ctrls4)]}
     }, Frame -> All]
  ]
 ]

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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