Group Abstract Group Abstract

Message Boards Message Boards

Controls in DynamicModule: animation does not start

Posted 2 days ago

I have a problem when using DynamicModule to represent a dynamical system of the type State(t)->F(State(t+1)). In the attached code, the animation does not start. Could any one tell me why? Thanks.

POSTED BY: Christian Mullon
7 Replies

DynamicModule[decl, body, opts] evaluates to another DynamicModule[] with the body evaluated and values for the variables declared saved in the output. One key to understanding the behavior of the output is how the module's variables are localized. This is complicated. A second key is to realized what is left in the (new) DynamicModule[] when the body has evaluated.

How variables are localized. People who look into Module[{t},...] versus DynamicModule[{t},...] first notice the ordinary Module[] variables have the form t$nnn, where the nnn represents a module serial number (given by $ModuleNumber); and DynamicModule[] variables have the form t$$nnn where the number nnn is another serial number (not given by $ModuleNumber). But of course, it's not that simple.

Module[] variables generally are created Global`t$nnn by Module[] in the kernel. DynamicModule[] variables generally are created FE`t$$nnn by the Front End when instantiating the DynamicModule[] — though, again (!), it's not that simple.

Let inDM represent the DynamicModule[] input code, and let outDM represent the DynamicModule[] code returned by the kernel, outDM = inDM. This may be inspected by InputForm[outDM], the body of which you will note below differs from the input.

outDM = 
  (* inDM= *)
  DynamicModule[{t = 0},
   {t, Unevaluated@t, SymbolName@Unevaluated@t, Dynamic@Unevaluated@t}
   ];

InputForm[outDM]
(*
DynamicModule[{t = 0},
 {0, Unevaluated[t], "t$24845620", Dynamic[Unevaluated[t]]}, 
 DynamicModuleValues :> {}]
*)

outDM
(*  {0, Unevaluated[t$$], "t$24845620", FE`t$$25450}  *)

outDM // MakeBoxes[#, StandardForm] &
(*
DynamicModuleBox[{t$$ = 0},
 RowBox[{"{",                                               (* begin List *)
   RowBox[{
     "0", ",",                                              (* 1st element *)
     RowBox[{"Unevaluated", "[", "t$$", "]"}], ",",         (* 2nd element *)
     "\"t$24845620\"", ",",                                 (* 3rd element *)
     DynamicBox[ToBoxes[Unevaluated[t$$], StandardForm]]    (* 4th element *)
    }],
   "}"}],                                                   (* end List *)
 DynamicModuleValues :> {}]
*)

Here is what happens in effect: The body is evaluated with t being replaced with an ordinary Module[] variable t$24845620 (cf. 3rd element of list). This is set to 0, and that's the value in the output in the 1st element of the list: it's the static number 0 and not a dynamic value of a t. The two ts that remain, which are both t$24845620, in the output are replaced by t$$ (cf. 2nd, 4th elements of list). Finally, when the Front End instantiates an active dynamic module, the ts inside a Dynamic[] (4th element only) are replaced by the variable t$$25450 created for that instance. (If you copy the output and paste in a new cell, you will get a new instance and a new variable in the 4th element.)

Beware global versus local. The difference between global and local variables is usually not hard to understand. The consequences sometimes are. The symbol created in the OP for stepOnce is Global`stepOnce. The value (or definition) created lasts until the symbol is cleared or the kernel quits. In the OP's code, the definition is created when the input is evaluated, and the definition is not in the output. If the DynamicModule[] output is present in the notebook when it first opens, then the Front End will run the DynamicModule[] with stepOnce undefined. In a more complicated example, this could lead to many error messages.

Now suppose the definition of a global stepOnce were included in the dynamic module (see the Initialization option further down). Then if the output were pasted in a new cell, and a new DynamicModule were instantiated, a new definition of stepOnce would overwrite the old one. The new definition would refer to the new variable for t, and the old dynamic module would stop working properly.

OK, I think that was complicated. There are four, fairly long tech notes in the documentation, two on Dynamic[] and two on Manipulate[]. At least this was shorter. :)

Here is the OP's code with some extras and comments above snippets of code. Lines of the form (***<optional code>***) indicate variations that might be included or omitted in different combinations.

DynamicModule[
 (*" Symbols initialized with ownvalues are stored
      in the DynamicModule output in a list of assignments
      in the first argument
  "*)
 {t = 0, slider = 0
  (*" Local symbols initialized with downvalues are stored in
      the DynamicModule output in the DynamicModuleValues
      option to DynamicModule
  "*)
  (***, stepOnce***)  (* localizes stepOnce if included *)
  },
 (*" If stepOnce is a Global` symbol, then its values is
     not stored in the DynamicModule output
  "*)
 (***stepOnce[]:=(t++;);***)  (* include here or in the Initialization below *)
 Column[{
   Grid[
    (*" Dynamic@output: 
        slider is given a DynamicModule localization
        Instantiated by the Front End
     "*)
    {{"Slider", Slider[Dynamic[slider], {0, 1}, ContinuousAction -> False], 
      Dynamic@NumberForm[slider, {4, 2}]}}, Alignment -> Left, 
    Spacings -> {2, 1}],
   (*" not Dynamic@output: 
       Unevaluated t is given a Module localization
    "*)
   SymbolName@Unevaluated@t,
   (*" not Dynamic@output: 
       Evaluated t becomes the current value 0
    "*)
   t, 
   Dynamic[
    Refresh[
     (*" Dynamic@output: 
         Literally present symbols t given a
         DynamicModule localization; instantiated by 
         the Front End; symbol t does not literally
         appear in `Downvalues@stepOnce`, and the t in 
         the downvalues is as it was when stepOnce[]
         was constructed.
      "*)
     stepOnce[];
     Row[{"t = ", t
       , ", ", SymbolName@Unevaluated@t
       , ", ", DownValues@stepOnce}]
     , UpdateInterval -> 2, TrackedSymbols :> {}]]
   }
  ]
 (*" Executed when DynamicModule is instantiated
     by the Front End; all symbols t in D.M.
     instantiated with the same D.M. localization
  "*)
 (***, Initialization:>(stepOnce[]:=(t++;);)***)  (* alternative place to defined things *)
 ]

Ways that work. The first two are best. The second has a global stepOnce[] defined in a way that works for all dynamic modules. The lines marked (* OMIT *) were included to illustrate what variables are being used for t.

Best (local stepOnce[]):

DynamicModule[{t = 0, slider = 0, stepOnce},
 stepOnce[] := (t++;);
 Column[{
   Grid[{{"Slider", 
      Slider[Dynamic[slider], {0, 1}, ContinuousAction -> False], 
      Dynamic@NumberForm[slider, {4, 2}]}}, Alignment -> Left, 
    Spacings -> {2, 1}],
   SymbolName@Unevaluated@t, (* OMIT *)
   t,  (* OMIT *)
   Dynamic[
    Refresh[stepOnce[];
     Row[{"t = ", t
       , ", ", SymbolName@Unevaluated@t  (* OMIT *)
       , ", ", DownValues@stepOnce  (* OMIT *)
       }]
     , UpdateInterval -> 2, TrackedSymbols :> {}]]
   }
  ]
 ]

Best (global stepOnce[]):

DynamicModule[{t = 0, slider = 0},
 Column[{
   Grid[{{"Slider", 
      Slider[Dynamic[slider], {0, 1}, ContinuousAction -> False], 
      Dynamic@NumberForm[slider, {4, 2}]}}, Alignment -> Left, 
    Spacings -> {2, 1}],
   SymbolName@Unevaluated@t, (* OMIT *)
   t,  (* OMIT *)
   Dynamic[
    Refresh[stepOnce[t];
     Row[{"t = ", t
       , ", ", SymbolName@Unevaluated@t  (* OMIT *)
       , ", ", DownValues@stepOnce  (* OMIT *)
       }]
     , UpdateInterval -> 2, TrackedSymbols :> {}]]
   }
  ],
 Initialization :> (ClearAll[stepOnce]; 
   SetAttributes[stepOnce, HoldAll]; stepOnce[var_] := (var++;);)
 ]
POSTED BY: Michael Rogers

Thanks for these clear explanation of a subject that appears not clear. A question I have about your last code is: you write that stepOnce is global; however you clear it and redefine it in the Initialization. What would you suggest to define stepOnce outside the DynamicModule?

POSTED BY: Christian Mullon

A good goal to have is for the DynamicModule to be self-contained, depending only on things in the system that essentially don't change. The point of Initialization is that when the kernel starts up, stepOnce[] is not defined. If the DynamicModule[] is displayed when someone opens the notebook, the Front End will start running the module before the user has a chance to define stepOnce[]. The Front End will run the Initialization before running the body of the module. If stepOnce[] is defined there, you can be sure it will always be defined in any instance of a module. I'd recommend Initialization over the option SaveDefinitions -> True, but you could look that up. It figures out the initialization automatically, but sometimes one is surprised by what it did.

The use of ClearAll[stepOnce] is good practice when making definitions. The thing to remember is that func[...] := value adds a definition rule to the list of definitions for func (DownValues[func]). The addition will replace an old definition if the patterns on the left of the := are equivalent, but any previous definitions that are not equivalent will remain and could cause trouble. The same considerations apply to clearing attributes, etc.

Personally, I would use the localized version. The definition does not use much memory or take much time. Sometimes different dynamic modules need access to the same global resources. Then the global version might be a helpful example. Bulletproofing this can be more work than it's worth. At the least, you need to make sure that the global resources are defined and available before your modules try to use them.

POSTED BY: Michael Rogers

This is exactly what I need. Thank you. If find the logic of DynamicModule quite unclear.

POSTED BY: Christian Mullon

Here's a fix;

DynamicModule[{t = 0, slider = 0, stepOnce},
 stepOnce[] := (t++;);
 Column[{
   Grid[{{"Slider", 
      Slider[Dynamic[slider], {0, 1}, ContinuousAction -> False], 
      Dynamic@NumberForm[slider, {4, 2}]}}, Alignment -> Left, 
    Spacings -> {2, 1}],
   Dynamic[
    Refresh[stepOnce[];
     Row[{"t = ", t}], UpdateInterval -> 2, TrackedSymbols :> {}]]
   }
  ]
 ]

If I can find the time, I'll explain later. It's complicated.

POSTED BY: Michael Rogers

Thank you Gianluca, your solution works; in the real application, step is a much more complicated module; I believe that the issue comes from the use of a function such as step inside a dynamic module.

POSTED BY: Christian Mullon

Getting rid of stepOnce[] makes the animation start:

DynamicModule[{t = 0},
 Dynamic[t++, UpdateInterval -> 20]]

However, the UpdateInterval option is not obeyed. I have no idea why all this.

POSTED BY: Gianluca Gorni
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard