17
$\begingroup$

When I'm creating DynamicModules for CDF purposes I'm usualy basing on my experience with that, particular goal and content rather than on deep insight.

I want to ask for advice what is a good place for definitions of rather non-dynamic parts of the code. More precise question is at the end so you can scroll down and come back later :)

I'm going to show couple of ways, their advantages and disadvantages.

Initialization :>

DynamicModule[{}, st1@"title"
              , Initialization :> (st1 = Style[#, Bold] &)]
st1["title"]

Well, that's not what we want.

  • I know I can use Dynamic but here and after we are assuming that the less inside Dynamic the better. There is no special need for it so I will not do this.

  • Notice that we can Shift+Enter again and saving such DynamicModule will result in proper display of content. Double evaluation is the only inconveniece.

scoped Initialization :>

DynamicModule[{st1}, st1@"title"
              , Initialization :> (st1 = Style[#, Bold] &)]
st1$$["title"]

It is natural one may want to scope this name but now it is not going to work even with double evaluation.

Initialization ->

DynamicModule[{}, st2@"title"
              , Initialization -> (st2 = Style[#, Bold] &)
             ]
title

Whoah, **->** seems to be a solution... nope. Unfortunately I can't recall the example but I've faced a problem when I tried to switch :> to -> once in the past. The CDF was build with :> and -> broke it.

Moreover, if we try to scope the name it is going to fail:

scoped Initialization ->

DynamicModule[{st2}, st2@"title"
              , Initialization -> (st2 = Style[#, Bold] &)
             ]
st2$$["title"]

Body of DynamicModule

This is the way I'm using for defining static parts of the code. It works for scoped and unscoped names:

DynamicModule[{st2},
              st2 = Style[#, Bold] &;
              st2@"title"
             ]

title

  • However there is one issue discussed here. We can not use SetDelayed but we assumed it should be a static content so it doesnt matter (or at least I can't recall any counterexample).

  • This is also not very convenient if there is a lot of static content one want to define. I like to have body skeleton on top so it is easy to manage the whole project easily. I'm afraid I can't have both. ;)


Outisde

In case of static elements that are imidiately on top there is also a way to define them outside DynamicModule. FE is going to store them and such CDF will work. However not everything is on top at start.


Questions

Main question is:

  • Is the habit to define static content in Body and dynamic parts in Initialization the best approach?

Secondary qestion:

  • What is the difference between -> and :> with Initialization, is one better from another? Documentation center says:

    "The typical usage is Initialization:>expr."

but on the other hand InputAutoReplacement puts Initialization->.

$\endgroup$
1
  • 2
    $\begingroup$ Using Rule (->) instead of RuleDelayed (:>) is virtually always the wrong thing to do. In your case, for example, it produces output which is subtly broken in ways it will take you a while to figure out (i.e., if you quit and restart the kernel or FE, you'll lose the definition of st2 in any Dynamic contexts). $\endgroup$
    – John Fultz
    Commented Aug 10, 2013 at 4:17

3 Answers 3

6
$\begingroup$

For this specific example you want to "register" the styling function once but then make the styled stext static from then on -- if I understand you correctly. If that is the case then just use Refresh with None. So with your styling function defined as a kernel function you would implement it this way:

ClearAll[st1];
DynamicModule[{}, Dynamic[Refresh[st1@"title", None]], 
 Initialization :> (st1 = Style[#, Bold] &)]

To test now evaluate st1 = Style[#, Red] & and the styled title does not change. If you replace None within the Refresh or remove Refresh altogether then st1 will update.

DynamicWrapper is also a function you might want to read up on.

Edit

If you do not what to use the code above then the only thing I can suggest is the method you already employ

DynamicModule[{st2},
              st2 = Style[#, Bold] &;
              st2@"title"
             ]

in which your function is defined earlier than the stuff that gets rendered on screen. Personally I prefer the initialization approach. But these seem to be the main options unless I have overlooked something.

$\endgroup$
15
  • $\begingroup$ @Kuba This works if st1 is localized as a DynamicModule variable, too. So you can have st1 be global, hence the same throughout the CDF; or you can have it localized to each DynamicModule in the CDF. Note that st1 will be set/reset whenever the Initialization is executed (not a problem if it is constant). $\endgroup$
    – Michael E2
    Commented Aug 9, 2013 at 3:47
  • $\begingroup$ @MichaelE2 & Mike Honeychurch. Yes, that is also an approach. But notice that wrapping everything, "static" content with Dynamic[... None] and dynamic with Dynamic could be inconvenient or at least is making the code not so transparent. Sometimes it may cause syntax issues/typos for larger projects. Taking this under consideration, double evaluation or putting definitions in body seems to be "better". And that's what I'm asking, are definitions in the body "dangerous" or is it "wrong" approach? $\endgroup$
    – Kuba
    Commented Aug 9, 2013 at 5:08
  • $\begingroup$ I think the "main question" at end of question is poorly worded. There are no dynamic parts in the initialization. You define functions in the initialization. Dynamic content is visible on screen. In this case you want to apply a function to style some text. To do this once so that the test styling doesn't change use the code above. If it is inconvenient then one has to ask why not just wrap styling directly around the text in the first place. I don't see any reason why not to list Set definitions in the body of the DM. Personally I use initializations though. $\endgroup$ Commented Aug 9, 2013 at 5:16
  • $\begingroup$ @Kuba Maybe styling was not a good example of what you actually are wanting to do?? $\endgroup$ Commented Aug 9, 2013 at 5:19
  • $\begingroup$ @MikeHoneychurch Maybe this way too minimal example :) I'm not afraid that styling will change, there is no reason to. I am concerned about that effect of delayed initialization when the definition is ins Initialization and first result is st1["text"]. Style is only an example, what I'm thinking of is breaking code into parts that I can easily modify etc. For bigger project, one big code is not transparent. I want in body only the skeleton, for example Grid[{{panel1, panel2},{panel3,Dynamic@panel4}}] and definitions of static panels sowhere else. $\endgroup$
    – Kuba
    Commented Aug 9, 2013 at 6:05
10
$\begingroup$

Some of what I say concerns notebooks (e.g. saving a notebook) and not CDFs, but most applies to both.

DynamicModule initialization

Here is a simple DynamicModule. One can imagine that the dynamic output below containing further Dynamics or only parts of the output being wrapped in Dynamic. Ignoring Global symbols, there are three places initialization code can occur in a DynamicModule.

DynamicModule[{x = 1},  (* Init 1 *)
 x = 2;                 (* Init 2 *)
 Dynamic[stuff; x]      (* Dynamic output *),
 Initialization :>
   (x = 3)              (* Init 3 *)
 ]

"Init 1" and "Init 2" are done when the input is initially evaluated. (Indeed "Init 2" will overwrite "Init 1".) These are done once and never again dynamically. There's very little difference between them. Any initializations they make to DynamicModule variables will be stored in the dynamic module box in the notebook that it initially returned to the Front End when the input is first evaluated by the Kernel. Note that if a DynamicModule variable changes its value during manipulation of the output, the value stored in the dynamic module box is updated. Finally, "Init 3" is evaluated whenever the output is first displayed by the Front End; for example, after initial evaluation, on opening the notebook, or when the output cell is copy-pasted). It is important to understand clearly when they are executed. If x is a Global variable, it doesn't change when three initialization codes are executed (x could not be as in "Init 1", though). but describing all the cases involving Global variables will be left for the reader to work out.

The stuff in the dynamic output will be executed whenever the output is updated (whenever, say, x changes). It can do further "initialization" before x is displayed. I wouldn't call it "initialization" per se, but sometimes in more complicated situations stuff needs to be updated before you're ready to display the output.

Below is the output cell of the above input. One can see that the store value for x is 3 (see $CellContext`x$$ = 3 in the second line). Note that the "Init 2" code is not in the output. As I said, it is done only once when the input is first sent to the Kernel on Shift+Enter. However, "Init 3" does appear. The difference is important. It's not a question of whether "Init 1"/"Init 2" is better than "Init 3", but of which way does what you want.

Cell[BoxData[
 DynamicModuleBox[{$CellContext`x$$ = 3}, 
  DynamicBox[ToBoxes[$CellContext`stuff; $CellContext`x$$, StandardForm],
    ImageSizeCache->{12., {1., 13.}}],
  DynamicModuleValues:>{},
  Initialization:>($CellContext`x$$ = 3)]], "Output",
 CellChangeTimes->{3.5850410275970697`*^9, 3.585041388595697*^9}]

An example of the difference.

First, here is a slight modification in which we can alter the value of x:

DynamicModule[{x},
 SetterBar[Dynamic@x, Range@9] (* Dynamic output *),
 Initialization :> {x = 3}     (* Init 3 *)
 ]

The initial value of x will be 3. If I click on 5, x will be set to 5.

Mathematica graphics

Cell[BoxData[
 DynamicModuleBox[{$CellContext`x$$ = 5}, 
  (* SetterBar code omitted*),
  DynamicModuleValues:>{},
  Initialization:>{$CellContext`x$$ = 3}]], "Output",
 CellChangeTimes->{(* omitted *)}]

If I copy and paste the output cell, the new cell has its x reset to 3, because of the Initialization. It will also be reset to 3, whenever the notebook is opened and the dynamic module displayed. (So you can't quit in the middle of something, even if you save the notebook.)

Mathematica graphics

Next, here is the other variation:

DynamicModule[{x = 1},         (* Init 1 *)
 SetterBar[Dynamic@x, Range@9] (* Dynamic output *)
 ]

The initial value of x will be 1. If click on 5, x will be set to 5. Now if copy and paste the output cell, the new cell will have its x set to 5. There is no initialization to reset it. If the notebook is saved, when it reopens, x will be 5.

Summary

The difference in behavior seems to me to be the important thing. Initialization is evaluated whenever the dynamic module becomes visible for the first time. The other ways are done only once, the first time the input is evaluated. In cases where a dynamic module variable never changes, there is no effective difference; if the variable is global, then the initialization ought to be in Initialization, so that the variable will be set when the notebook is opened.

Initialization and RuleDelayed

With Rule, the right hand side is evaluated when the Rule is evaluated; with RuleDelayed the right hand side is held until the rule is applied. So the setting

Initialization -> (a = 1; b = 2; x = 3)

gets stored in the dynamic module box as

Initialization :> 3

The initializations a = 1; b = 2; x = 3 are executed at the time the input DynamicModule is first evaluated, but never again. After that the code 3 is evaluated every time the dynamic module needs initialization. This is never what is desired -- that is to say, it is not the purpose of Initialization. You should always use RuleDelayed (:>).

Comment on styles

The particular example in the OP, setting a constant style, is probably best done by editing the StyleSheet and creating a new style, say, "mystyle".

enter image description here

Then hit return and style the cell as desired.

enter image description here

Then you can use it like this:

Style["title", "mystyle"]
$\endgroup$
1
  • 1
    $\begingroup$ Very great and interesting explanations, thank you. $\endgroup$
    – Öskå
    Commented Dec 11, 2013 at 19:55
3
$\begingroup$

Not sure if I well understand all your comments, however I would share a my opinion hoping can be of any help. As a general consideration, it may help to consider that DynamicModule is a little bit different from Manipulate, in particular: Manipulate refresh its whole body any time a triggering variable changes. DynamiModule is most like a static wrapper, its main objective is the scoping of variables, and in order to get dynamic refresh you need to explicitly use Dynamic(s) in the body. Clearly, such Dynamic(s) should wrap only those parts that effectively have to be updated.

Consider the following three examples:

colorstyle = RandomChoice[{Red, Pink, Magenta, Orange, Blue}];
DynamicModule[{},
 Column[{Button["Change color", 
    colorstyle = RandomChoice[{Red, Pink, Magenta, Orange, Blue}]], 
   "This party of the body is not dynamic", Spacer[5], 
   Style["this part should be dynamic", colorstyle, Bold, 24]}]]

The result is an object that never shows the changes even if you click on the button. Indeed, DynamicModule doesn't refresh its body. You need to put the Dynamic wrapper where needed:

colorstyle = RandomChoice[{Red, Pink, Magenta, Orange, Blue}];
DynamicModule[{},
 Column[{Button["Change color", 
    colorstyle = RandomChoice[{Red, Pink, Magenta, Orange, Blue}]], 
   "This party of the body is not dynamic", Spacer[5], 
   Dynamic[Style["this part is dynamic", colorstyle, Bold, 24]]}]]

On the other hand, Manipulate automatically refresh the whole body

 Manipulate[
 x := Random[];
 Column[{x, "This party of the body is dynamic even if you don't see it", Spacer[5], 
   Style["this part is dynamic (and you don't need a Dynamic wrapper)", colorstyle, Bold, 24]}], {colorstyle, {Red, Pink, Magenta, Orange, Blue}}]

So, generally in DynamicModule is not important how many lines of code you put, only those parts with a Dynamic wrapper will be refreshed. This is not true in Manipulate, where the whole body is updated and it means you have to pay attention to what you put inside the body (for instance what you call static content). However, a good approach is also provided by external definitions. If you have a big applications, that means many lines of code defining intermediate functions, I would prefer to define them outside the DynamicModule and the use inside of it by also adding SaveDefinitions->True. Consider, however, that this approach has an impact on the scoping of variables, that means functions defined outside the DynamicModule remain external to the local scoping defined by DynamicModule. This should be analysed case by case. Here is a short example:

fontstyle := RandomChoice[{"Times", "Arial", "Courier", "Helvetica"}];
DynamicModule[{},
 Dynamic[Refresh[
   Style["this part is dynamic", FontFamily -> fontstyle, colorstyle, 
    Bold, 24], UpdateInterval -> 2]],
 Initialization :> {colorstyle := RandomChoice[{Red, Pink, Magenta, Orange, Blue}]},
 SaveDefinitions -> True]

Here, even if you close Mathematica and then open it again, the dynamic content will save the external definition of fontstyle and it will work. So, fontstyle is an example of external definition (coupled with SaveDefintions->True) and colorstyle is an initialization definition. What I normally do is: if I need a short list of easy definitions I prefer to use Initialization, if I need to write pages and pages of preliminary functions I prefer external code embedded by SaveDefinitions. Not sure this is the right technical choice, but it works for me ;-)

$\endgroup$
2
  • $\begingroup$ SaveDefinitions is not an option for DynamicModule $\endgroup$ Commented Aug 9, 2013 at 8:48
  • $\begingroup$ @MikeHoneychurch This is true for V8.0.4. In V9.0.1, SaveDefinitions works in DynamicModule as it does in Manipulate although such a usage is undocumented. $\endgroup$
    – Michael E2
    Commented Aug 9, 2013 at 11:28

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.