开发者

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.

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

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}

 ]

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

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]}
     }]

   ]
 ]

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

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.

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


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]}]]


I was going to give a solution almost the same as Leonid's and use With to insert the code, but he beat me to it, so here's an alternative way. Define a dynamic local function using ControlType -> None that does your styling:

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

By the way, the Style[] in Style["f(x)="] is redundant, as you are not actually setting any styles...


One could do this:

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

If one is in the habit of assigning down-values to symbols whose names do not begin with $, then it would be prudent to wrap the whole thing in Block[{x, opts}, ...] in case x and opts have globally-defined values.

A similar technique is possible for the case of multiple controls:

Manipulate[
  {x, y, z}
, Grid[
    { {control1[x, 0, "x=", 0, 1, .1]}
    , {control1[y, 0, "y=", 0, 2, .1]}
    , {control1[z, 0, "z=", 0, 10, .1]}
    }
  ]
] /. control1[var_, initialValue_, str_, from_, to_, incr_] :>
       Control[{{var, initialValue, str}, from, to, incr, ImageSize -> Tiny}]
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜