Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define part of a Manipulate control variable definition to reduce code duplication

This is a little related to this question

Define control as variable in Mathematica

But the above question did not answer my problem, as it talks about the full control definition. (I also tried some of the tricks shown there, but they do not work for my problem).

I am now asking about definition for only part of the control. (It is also very hard to follow up on an old question using this forum format. Because using the tiny comment area, it hard to ask and show more like when asking a new question where the space is larger, and one can paste code and images).

All the tries I have made do not work. I'll start by simple example to explain the problem.

Assume one want to write

Clear["Global`*"];

Manipulate[Plot[f*g, {x, -1, 1}],
 Grid[{
   {Style["f(x)="], 
    PopupMenu[Dynamic[f], {x, x^2, x^3}, ImageSize -> Tiny]},{Style["g(x)="], 
    PopupMenu[Dynamic[g], {x, x^2, x^3}, ImageSize -> Tiny]}
   }]
 ]

you can see there is allot of code duplication in each control definition. (things like ImageSize, Spacings-> and many other decoration settings, are repeated over and over for each control.

enter image description here

What will be great, if I can write something like

Manipulate[Plot[f*g, {x, -1, 1}],
 Grid[{
   {Style["f(x)="], PopupMenu[Dynamic[f], Evaluate@Sequence@v]},
   {Style["g(x)="], PopupMenu[Dynamic[g], Evaluate@Sequence@v]}
   }],

 Initialization :>
  (
   v = {{x, x^2, x^3}, ImageSize -> Tiny}
   )
 ]

But this does not work. I tries many other things along the above line, and nothing works. Like

{Style["f(x)="], PopupMenu[Dynamic[f], v]},

and

{Style["f(x)="], PopupMenu[Dynamic[f], Evaluate@v]}

and

Manipulate[Plot[f*g, {x, -1, 1}],

 {{v, {{x, x^2, x^3}, ImageSize -> Tiny}}, None},
 Grid[{
   {Style["f(x)="], PopupMenu[Dynamic[f], Evaluate@v]},
   {Style["g(x)="], PopupMenu[Dynamic[g], v]}
   }]
 ]

can't get it to work.

But here are the rules of the game: This will be for a demo, hence, code must start with Manipulate. Can't have Module outside Manipulate. Also, can not use Hold and its friends. But can use Unevaluated.

I was hoping the experts here might have a trick to do this. The will reduce the code size if it is possible to do, as many of the control I have, contain many 'options' like the above that are the same, and being able to do the above will make the code easier to read and manage.

thanks,

ps. What I am asking for, is sort of similar to what one does for say Plot options, where one can use SetOptions to set some common default options so they do not have to duplicate them for each Plot command each time. But there is no such thing in this case.

update

Using the method shown by Leonid below, (the Macro trick), I wanted to use it to help me define number of controls, all using one common setting. This is what I tried:

Manipulate[{x, y},

 Evaluate@With[
   {
    control1 = Function[{var, initialValue, str, from, to, incr},
      {
       {{var, initialValue, str}, from, to, incr, ImageSize -> Tiny}
       }
      ,
      HoldAll
      ]
    },

   {
      First@control1[x, 0, "x=", 0, 1, .1],
      First@control1[y, 0, "y=", 0, 2, .1],
      First@control1[z, 0, "z=", 0, 10, .1]
    }, 

   ]
 ]

The problem is just an extra {} around the whole thing, else it will work. Will keep trying to solve this. But getting close. Tried Sequence[], and Flatten[..,1] and such, but can not do it yet. Making more coffee, should help.

Update 2

This is below an example using Simon method to use to help define common definition across more than one control. This way, one can use it to reduce code duplication for common options on set of separate controls

Notice, had to use Control[] to get it to control.

Manipulate[{x, y, z},

 Dynamic[Grid[{
    {control1[x, 0, "x=", 0, 1, .1]},
    {control1[y, 0, "y=", 0, 2, .1]},
    {control1[z, 0, "z=", 0, 10, .1]}
    }]],

 {{control1, 
   Function[{var, initialValue, str, from, to, incr}, 
    Control[{{var, initialValue, str}, from, to, incr, 
      ImageSize -> Tiny}], HoldFirst]}, None}

 ]

enter image description here

Update 3

And got Leonid method to work also on more than one control. The trick is to use Control[]. Can't use plain old {{x,0,"x"},...} [EDIT, yes, you can, just need the Sequence@@ method as shown below by Leonid update.].

Here it is:

Manipulate[{x, y, z},

 Evaluate@With[
   {
    control1 = Function[{var, initialValue, str, from, to, incr},
      Control[{{var, initialValue, str}, from, to, incr, 
        ImageSize -> Tiny}]
      , HoldAll
      ]
    },

   Grid[{
     {control1[x, 0, "x=", 0, 1, .1]},
     {control1[y, 0, "y=", 0, 2, .1]},
     {control1[z, 0, "z=", 0, 10, .1]}
     }]

   ]
 ]

enter image description here

I'll try to integrate one of these methods into my main demo (has over 600 lines of code just for the control layout so far and growing by the minute, hopefully these methods will shrink this by quite a bit)

Update 9/26/11. 7 pm

I thought I post a 'birds eye' view of the code saving by using 'macros' to define controls which contains many common boiler-plate code. Here is a screen shot of before and after.

Thanks again for all the answer and help.

enter image description here

like image 532
Nasser Avatar asked Sep 26 '11 07:09

Nasser


1 Answers

What about this

Manipulate[Plot[f*g, {x, -1, 1}],
 Evaluate@
  With[{styleAndpopup = 
      Function[{st, fun}, 
         {
           Style[st], 
           PopupMenu[Dynamic[fun], {x, x^2, x^3}, ImageSize -> Tiny]
         }, 
         HoldAll]},
    Grid[{styleAndpopup["f(x)=", f], styleAndpopup["g(x)=", g]}]]]

This is actually a tiny example of the code-generation at work, since if you look at the FullForm of the resulting Manipulate, you will see the same expression you originally started with. The styleAndpopup is actually not a function here, but a macro, locally defined using With.

EDIT

Per request of the OP - generalizing to many controls. The easiest fix is to insert Sequence@@... as Sequence @@ {First@control1[.... However, there is some extraneous stuff that can be removed as well:

Manipulate[{x, y}, 
 Evaluate@With[{control1 = 
     Function[{var, initialValue, str, from, to, incr}, 
       Unevaluated@{{var, initialValue, str}, from, to, incr, ImageSize -> Tiny}, 
       HoldAll]}, 
   Sequence @@ {
     control1[x, 0, "x=", 0, 1, .1], 
     control1[y, 0, "y=", 0, 2, .1], 
     control1[z, 0, "z=", 0, 10, .1]}]]
like image 80
Leonid Shifrin Avatar answered Sep 20 '22 05:09

Leonid Shifrin