9
$\begingroup$

I need to duplicate a simulation inside one DynamicModule. I believe it's simpler to understand using an toy example. Here is the start screen:

enter image description here

Now if I play with a value, like this...

enter image description here

and then press Add button, I get:

enter image description here

with the first module reset.

Someone know how can I preserve the Manipulate's states, when I Add or Remove an iten?

Here is my current code:

DynamicModule[{myCDF,controls,grid,btn},
    myCDF[]:=Manipulate[Plot[Sinc[a x],{x,0,10}],{{a,1},1,10}];
    grid={myCDF[]};
    btn["Add"]=Button["Add",AppendTo[grid,myCDF[]]];
    btn["Rem"]=Button["Remove",If[Length[grid]>1,grid=grid[[1;;-2]]]];

    controls=Grid[{{btn["Add"],btn["Rem"]}}];
    Dynamic@Panel[Column[{Grid[Transpose@{grid}],controls}]]
]
$\endgroup$
5
  • $\begingroup$ The problem is that for my real code is with DynamicModule (and can't use manipulate due to pack import). Tks $\endgroup$
    – Murta
    Commented Nov 30, 2013 at 21:53
  • $\begingroup$ +1 good problem. Putting Print inside myCDF shows it only gets called once per adding a new plot, so everything should have worked as you expected. $\endgroup$
    – Ajasja
    Commented Nov 30, 2013 at 23:56
  • 1
    $\begingroup$ As an FYI SetDelayed doesn't work within the body of a DynamicModule. You need to use Initialization. $\endgroup$ Commented Dec 1, 2013 at 7:58
  • $\begingroup$ Referrence to @Mike's statement: evaluation inside DynamicModule $\endgroup$
    – Kuba
    Commented Dec 1, 2013 at 8:25
  • $\begingroup$ Hmm, would NotebookWrite and NotebookDelete be an acceptable solution? (So each manipulate would be in it's own cell) $\endgroup$
    – Ajasja
    Commented Dec 2, 2013 at 20:42

4 Answers 4

3
$\begingroup$

Okay after the comments here is a rewrite that delivers what you need in a relatively simple way. You have mentioned that in your real life example you are not using Manipulate. Good. I don't like it so I'll use DynamicModule. So I'll start with a new version of myCDF:

myCDF[Dynamic[var_], num_] := DynamicModule[{a = var[[num]]},
  Panel@Column[{
     Manipulator[Dynamic[a, (a = #; var[[num]] = #) &], {1, 10}],
     Framed[
      Dynamic@
       Plot[Sinc[a x], {x, 0, 10}, ImageSize -> 300, PlotLabel -> num],
      Background -> White,
      FrameStyle -> GrayLevel[0.7]]
     }]
  ]

the value of a gets carried over to the next version of myCDF. var is a list of the initial values of a in each plot. Stick this all in a small DynamicModule (I have only included the "Add" button. Adding the remove should be straight forward, just Drop and Decrement).

DynamicModule[{var = {1}, i = 1, grid},

 grid = {myCDF[Dynamic[var], 1]};

 Column[{
   Dynamic@Column[grid],
   Button["Add", AppendTo[var, var[[-1]]]; ++i; 
    grid = myCDF[Dynamic[var], #] & /@ Range[1, i], ImageSize -> 100],
   Button["Remove", If[Length@var>1,var=Drop[var,-1]; --i;
grid = Drop[grid,-1]], ImageSize -> 100]
   }],

 Initialization :> {myCDF[Dynamic[var_], num_] := 
    DynamicModule[{a = var[[num]]},
     Panel@Column[{
        Manipulator[Dynamic[a, (a = #; var[[num]] = #) &], {1, 10}],
        Framed[
         Dynamic@
          Plot[Sinc[a x], {x, 0, 10}, ImageSize -> 300, 
           PlotLabel -> num],
         Background -> White,
         FrameStyle -> GrayLevel[0.7]]
        }]
     ]}]

And this now works fine:

enter image description here

$\endgroup$
6
  • $\begingroup$ That's something but Murta is probably looking for a grid of independent animations, that's how I've understood it. Do you know why this preserves the state of manipulate? $\endgroup$
    – Kuba
    Commented Dec 1, 2013 at 8:19
  • 1
    $\begingroup$ This LocalizeVariables -> False is interesting, but I need they to be independent as @Kuba stated. $\endgroup$
    – Murta
    Commented Dec 1, 2013 at 10:41
  • $\begingroup$ @Murta while that makes sense now that I spend a minute thinking about it, it was not mentioned in the question -- you only asked about preserving the states. Let me think about it over morning coffee. $\endgroup$ Commented Dec 1, 2013 at 20:24
  • $\begingroup$ @Murta please see update. This delivers what you asked for. $\endgroup$ Commented Dec 1, 2013 at 22:16
  • $\begingroup$ Tks Mike, nice way. I hope you don't mind, I added Button["Remove"] into the code. +1! $\endgroup$
    – Murta
    Commented Dec 1, 2013 at 22:28
5
$\begingroup$

Introduction

I present two methods below. The new way has some advantages over the old way. The original is a straightforward method that maintains a variable that stores the states of the copies of the DynamicModule/Manipulate myCDFadded, which is similar to Mike Honeychurch's answer. (The primary difference is that mine injects the state variable into the definition of myCDF and Mike's passes it as an argument, which seems a better way.) The problem with these two methods is that the existing myCDFs are all replaced by new ones when another is added or one is removed. This would be a problem if they take a long time to initialize. Another is if myCDF has many state variables. They all have to be updated. The new way adds a new myCDF or removes the last one, but it leaves all the others intact without regenerating them. This seems the right behavior to strive for. The code is only slightly more complicated than the OP's starting point.

The problem is that the thing you want to copy is a front end object, not a kernel variable. You can copy front end objects from the front end (select/copy) and paste them into the front end manually. I don't know how to do this programmatically. Perhaps there are undocumented functions that give access to the dynamic objects in the front end.

The problem with grid being a kernel variable is that the code in the Manipulates each set their variable a equal to 1 on instantiation by the front end. Whenever grid is changed by AppendTo, all of the Manipulates are re-instantiated by in the front, which resets the values of a.

New way

Instead of a Grid or even a simple list, here is a way using a sort linked list structure that ends with a pair of buttons. The add button replaces the pair with a new Manipulate and another pair buttons. (Actually, it switches views using PaneSelector.) The remove button cause the parent to switch back, replacing the Manipulate/Button combo with another pair of buttons.

One particular odd problem was getting the remove button linked back to the parent. If you don't do something to make the variable sel unique, the symbol that gets passed around is $CellContext`sel$$. If that symbol is passed to the new DynamicModule in the call

newMan[Dynamic @ sel]

then the symbol for oldsel and sel will be the same! And the remove button is linked to the current element and not the parent. I thought of various ways to get around this: Use Module for sel and inject and Unique symbol with ReplaceAll. All gave red highlighting, a warning for a local scope conflict. They all seemed to work, on this example at least. I chose to present the one with Function. (I had to choose one.) Another odd problem is that without ImageSize -> Automatic, the ImageSizeCache of the enclosing Dynamic is not resized automatically. I don't know if that's a bug, nor exactly why the ImageSize option fixes it.

ClearAll[newMan, myCDF];

myCDF = Manipulate[Plot[Sinc[a x], {x, 0, 10}], {a, 1, 10}];

newMan[] := newMan[Dynamic[0]]; (* initial call *)
newMan[Dynamic[oldsel_]] :=
 Function[{sel},
   DynamicModule[{addbtn, rembtn, sel},
    sel = "buttons";
    addbtn = Button["Add", sel = "manipulate"];
    rembtn = Button["Remove", oldsel = "buttons", Enabled -> oldsel =!= 0];

    Column[{myCDF, PaneSelector[{
        "buttons" -> Grid[{{addbtn, rembtn}}], 
        "manipulate" -> Dynamic @ newMan[Dynamic @ sel]},
         Dynamic @ sel,
         ImageSize -> Automatic]}]

    ]] @ Unique["sel"]

Panel@newMan[]

Original way

The first way I could figure out how to work around this limitation was to keep track of all the states of the Manipulates that are created. If the Manipulate has many state variables, perhaps this might be aided by auxiliary functions. In any case, I hope following might help in the actual use case. The state variable a0 is updated whenever one of the Manipulates reevaluates. In addition to a state variable, I added Initialization code that makes sure each Manipulate is properly initialized whenever they are recreated after grid is appended to.

DynamicModule[{myCDF, controls, grid, btn, a0, i},
 i = 0;
 myCDF[] := With[{j = ++i},
   If[j == 1, a0 = {1}, AppendTo[a0, Last@a0]]; 
   Manipulate[Plot[Sinc[(a0[[j]] = a) x], {x, 0, 10}], {a, 1, 10}, 
    Initialization :> (a = a0[[j]])]];
 grid = {myCDF[]};
 btn["Add"] = Button["Add", AppendTo[grid, myCDF[]]];
 btn["Rem"] = Button["Remove", If[Length[grid] > 1, grid = grid[[1 ;; -2]]]];
 controls = Grid[{{btn["Add"], btn["Rem"]}}];
 Dynamic @ Panel[Column[{Grid[Transpose @ {grid}], controls}]]]
$\endgroup$
2
  • $\begingroup$ It is horrible that there is no simple way to do this. :/ $\endgroup$
    – Kuba
    Commented Dec 1, 2013 at 8:56
  • $\begingroup$ tks Michael. Your new solutions worked just nice. In my problem, myCDF is actually another dynamic module. @Kuba, I agree with you. $\endgroup$
    – Murta
    Commented Dec 1, 2013 at 11:17
1
$\begingroup$

I don't think this is exactly what you were hoping for, but it might constitute a work-around. It basically let's you add/remove little static snapshots of the contents of the Manipulates (rather than active Manipulates themselves).

plotA[a_] := Plot[Sinc[a x], {x, 0, 10}];
DynamicModule[{controls, grid, btn},
  myCDF = Manipulate[aGlob = a; plotA[a], {{a, 1}, 1, 10}];
  grid = {myCDF};
  btn["Add"] = Button["Add", AppendTo[grid, plotA[aGlob]]];
  btn["Rem"] =  Button["Remove", If[Length[grid] > 1, grid = grid[[1 ;; -2]]]];
  controls = Grid[{{btn["Add"], btn["Rem"]}}];
  Dynamic@Panel[Column[{Grid[Transpose@{grid}], controls}]]]

enter image description here

$\endgroup$
0
$\begingroup$

It might be a helpful improvement to the appended plots to have them labeled using the PlotLabel option to plot included in the code below:

plotA[a_] := 
Plot[Sinc[a x], {x, 0, 10}, PlotLabels -> "Expressions"];
DynamicModule[{controls, grid, btn}, 
myCDF = Manipulate[aGlob = a; plotA[a], {{a, 1}, 1, 10, 1}];
grid = {myCDF};
btn["Add"] = Button["Add", AppendTo[grid, plotA[aGlob]]];
btn["Rem"] = 
Button["Remove", If[Length[grid] > 1, grid = grid[[1 ;; -2]]]];
controls = Grid[{{btn["Add"], btn["Rem"]}}];
Dynamic@Panel[Column[{Grid[Transpose@{grid}], controls}]]]

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.