如何定义Manipulate控件变量定义的一部分以减少代码重复

时间:2011-09-26 07:05:57

标签: wolfram-mathematica

这与此问题有点相关

Define control as variable in Mathematica

但上述问题没有回答我的问题,因为它谈到了完整的控制定义。 (我也尝试过那里显示的一些技巧,但它们不能解决我的问题。)

我现在只询问部分控件的定义。 (使用这种论坛格式也很难跟进一个老问题。因为使用微小的评论区域,很难提出更多的问题,并且更像是在询问空间较大的新问题时,可以粘贴代码和图像)。

我所做的所有尝试都不起作用。我将从简单的例子开始解释这个问题。

假设有人想写

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

您可以看到每个控件定义中都存在重复的代码重复。 (像ImageSize,Spacings->等许多装饰设置,会为每个控件反复重复。

enter image description here

如果我能写出类似

的内容,那将会是多么美好
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}
   )
 ]

但这不起作用。我在上面的那一行尝试了许多其他的东西,没有任何作用。像

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

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

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

无法让它发挥作用。

但是这里有游戏规则:这将是一个演示,因此,代码必须以Manipulate 开头。在Manipulate之外不能有Module。此外,无法使用Hold及其朋友。但可以使用Unevaluated。

我希望这里的专家可以有一个技巧来做到这一点。如果可以的话,将减少代码大小,因为我拥有的许多控件都包含许多与上述相同的“选项”,并且能够执行上述操作将使代码更易于阅读和管理

感谢,

PS。我所要求的,有点类似于Plot选项,其中可以使用SetOptions设置一些常见的默认选项,这样他们就不必每次都为每个Plot命令复制它们。但在这种情况下没有这样的事情。

更新

使用下面Leonid所示的方法(宏技巧),我想用它来帮助我定义控件数量,所有这些都使用一个常用设置。这就是我试过的:

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

   ]
 ]

问题只是整个事情的额外{},否则它会起作用。将继续努力解决这个问题。但越来越近了。试过序列[],和Flatten [..,1]等,但还不能做到。多喝咖啡应该有所帮助。

更新2

这是一个使用Simon方法来帮助定义多个控件的通用定义的示例。这样,可以使用它来减少单独控件集上的常用选项的代码重复

注意,必须使用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

更新3

并且Leonid方法也适用于多个控件。诀窍是使用Control[]。不能使用普通的旧{{x,0,"x"},...} [编辑,是的,你可以,只需要序列@@方法,如Leonid更新所示。]。

这是:

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

我会尝试将其中一种方法集成到我的主要演示中(到目前为止,有超过600行代码仅用于控件布局,并且按分钟增长,希望这些方法可以缩小这一点)

更新9/26/11。晚上7点

我以为我通过使用'宏'定义包含许多常见样板代码的控件来发布代码保存的“鸟眼”视图。这是一个之前和之后的屏幕截图。

再次感谢所有答案和帮助。

enter image description here

3 个答案:

答案 0 :(得分:10)

这个怎么样?

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

这实际上是代码生成的一个很小的例子,因为如果你查看结果FullForm的{​​{1}},你会看到你最初开始使用的表达式。 Manipulate实际上不是此处的函数,而是使用styleAndpopup本地定义的宏。

修改

根据OP的要求 - 概括为许多控件。最简单的解决方法是将With作为Sequence@@...插入。但是,还有一些无关紧要的东西也可以删除:

Sequence @@ {First@control1[...

答案 1 :(得分:7)

我打算提供一个与Leonid几乎相同的解决方案,并使用With来插入代码,但是他打败了我,所以这是另一种方式。使用ControlType -> None定义动态本地函数,使其具有样式:

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

顺便说一下,Style[]中的Style["f(x)="]是多余的,因为您实际上并没有设置任何样式......

答案 2 :(得分:1)

可以这样做:

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]

如果有人习惯为名称不以$开头的符号分配下行值,那么在Block[{x, opts}, ...]中将整个内容包装起来x是明智的。 }和opts具有全局定义的值。

对于多个控件的情况,可以使用类似的技术:

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