Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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}]]
]
share|improve this question
    
The problem is that for my real code is with DynamicModule (and can't use manipulate due to pack import). Tks –  Murta Nov 30 '13 at 21:53
    
+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. –  Ajasja Nov 30 '13 at 23:56
1  
As an FYI SetDelayed doesn't work within the body of a DynamicModule. You need to use Initialization. –  Mike Honeychurch Dec 1 '13 at 7:58
    
Referrence to @Mike's statement: evaluation inside DynamicModule –  Kuba Dec 1 '13 at 8:25
    
Hmm, would NotebookWrite and NotebookDelete be an acceptable solution? (So each manipulate would be in it's own cell) –  Ajasja Dec 2 '13 at 20:42

3 Answers 3

up vote 3 down vote accepted

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

share|improve this answer
    
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? –  Kuba Dec 1 '13 at 8:19
1  
This LocalizeVariables -> False is interesting, but I need they to be independent as @Kuba stated. –  Murta Dec 1 '13 at 10:41
    
@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. –  Mike Honeychurch Dec 1 '13 at 20:24
    
@Murta please see update. This delivers what you asked for. –  Mike Honeychurch Dec 1 '13 at 22:16
    
Tks Mike, nice way. I hope you don't mind, I added Button["Remove"] into the code. +1! –  Murta Dec 1 '13 at 22:28

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}]]]
share|improve this answer
    
It is horrible that there is no simple way to do this. :/ –  Kuba Dec 1 '13 at 8:56
    
tks Michael. Your new solutions worked just nice. In my problem, myCDF is actually another dynamic module. @Kuba, I agree with you. –  Murta Dec 1 '13 at 11:17

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

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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