4
$\begingroup$

Consider the following example:

DynamicModule[{foo = {Red, Green}, switchColor},
 Attributes[switchColor] = {HoldAll};
 switchColor[c_] := If[c === Red, c = Green, c = Red];
 {
  Dynamic[Print["redrawing first"]; foo[[1]]],
  Dynamic[Print["redrawing Second"]; foo[[2]]],
  Button["Switch first", switchColor[foo[[1]]]],
  Button["Switch second", switchColor[foo[[2]]]]
  }
 ]

(in the gif for some reason it appears that the first color is updated incorrectly, while this is not the case)

This works totally fine, but a quick look to the messages reveals that even when changing only a single element of the list, both of the Dynamic are updated.

Now, this is not really surprising: there is just one variable to be tracked. However, I was wondering if there was a way to go around this, as it can be handy to be able to track independently single elements of a list.

One way around it is to fine foo as a list of held variables, and wire Dynamic to those variables directly. This works, as can be seen in the following:

DynamicModule[{
  foo = Table[Hold@Evaluate@Unique["color$"], {2}],
  switchColor
  },
 Scan[# /. Hold[s_] :> Set[s, Red] &, foo];
 switchColor[c_Hold] := If[
   ReleaseHold@c === Red,
   c /. Hold[s_] :> Set[s, Green],
   c /. Hold[s_] :> Set[s, Red]
   ];
 {
  Dynamic[Print["redrawing first"];
   ReleaseHold@foo[[1]]
   ],
  Dynamic[Print["redrawing second"];
   ReleaseHold@foo[[2]]
   ],
  Button["Switch first", switchColor[foo[[1]]]],
  Button["Switch second", switchColor[foo[[2]]]]
  }
 ]

enter image description here

My problem with this approach is that I suspect that this makes the variables that are actually being used, that is, color$xxxx, not being defined in front end, as would be the case for variables localised inside a DynamicModule.

Is there a way around this? Or can I somehow force the variables to be defined in the front end like they would be in a DynamicModule?


Bonus question:

Why does the above code work? Having Dynamic the HoldFirst attribute I would have expected it not to be able to understand that the variable to be tracked is the one inside the hold in the first element of foo. How does it do that?

$\endgroup$
  • $\begingroup$ @Kuba I am kind of curious if the method I posted below would to some degree satisfy that question of yours. Obviously it is splitting things up but you can always do e.g. List @@ foo to recover the whole thing. $\endgroup$ – Mr.Wizard Feb 22 '17 at 19:43
  • $\begingroup$ I reposted your Bonus question as a minimal example because I do not have an answer for it and I find it an interesting question in itself. mathematica.stackexchange.com/q/138453/121 I would not be surprised if it has been asked before but this will give it better visibility. $\endgroup$ – Mr.Wizard Feb 23 '17 at 9:58
  • $\begingroup$ @Mr.Wizard thanks. I'm relieved I was not the only one wondering about that behaviour :P shall I delete it from here? $\endgroup$ – glS Feb 23 '17 at 11:42
  • $\begingroup$ No, please do not delete it; Kuba's answer references it if nothing else, and I really did not mean to take credit for the question itself, I just wanted to put it out there as the main point of a question rather than an afterthought. $\endgroup$ – Mr.Wizard Feb 23 '17 at 11:44
4
$\begingroup$

I think I got it. Again using bump from Elegant manipulation of the variables list

func_[a___, bump[lst_, idx___], b___] ^:= 
  func[a, #, b] & @ Part[List @@@ Unevaluated @@ {lst}, {1}, idx]

Unique @ Table["color$", {2}] /. _[x__] :>
  DynamicModule[{foo, switchColor, x},
    foo = Hold[x];

    switchColor[c_] := c = If[c === Red, Green, Red];

    switchColor /@ bump[foo];

    {
     Dynamic[ Context @ bump[foo, 1] ],
     Dynamic[ Print["redrawing first"];  foo[[1]] ],
     Dynamic[ Print["redrawing second"]; foo[[2]] ],
     Button["Switch first",  switchColor @ bump[foo, 1]], 
     Button["Switch second", switchColor @ bump[foo, 2]]
    }
  ]

enter image description here

$\endgroup$
  • $\begingroup$ nice one, +1! btw, that definition for bump looks like a spell taken from some old magician's book, with all those funny symbols! I have only one problem with this solution, which also Kuba raised in his answer: what if you want the number of variables inside foo to change dynamically? This is actually also my usecase, as this question arised while tackling the same problem from which my other question stemmed. $\endgroup$ – glS Feb 23 '17 at 11:58
  • $\begingroup$ @glS (1) Thanks. (2) I have joked before that (though it has nothing to do with my user name) programming in Mathematica does sometimes feel like using incantations. :^) (3) If we want these Symbols localized by the Front End I think any solution is going to amount to the same generation of a fixed-length list that I used here, unless you can get Mathematica to track part of an expression (as Kuba references) which presently appears to be an intractable problem. Could you perhaps localize a sufficient number of Symbols then use them in order? (continued) $\endgroup$ – Mr.Wizard Feb 23 '17 at 12:13
  • $\begingroup$ @glS I mean if we use Unique @ Table["color$", {1000}] in the code above to localize one thousand Symbols but only use as many as we need at a time? $\endgroup$ – Mr.Wizard Feb 23 '17 at 12:14
  • $\begingroup$ well sure that would be possible solution. Not very elegant though :P $\endgroup$ – glS Feb 23 '17 at 12:59
4
$\begingroup$

In addition to Mr.Wizard's nice workaround:

  • However, I was wondering if there was a way to go around this, as it can be handy to be able to track independently single elements of a list.

    In general, no. But as usual in MMA, you can tweak things. (see e.g MW answer )

    Your question is probably a duplicate of an old one of mine: How to track Part of Symbol though this one is more nicely written. At the end Dynamic doesn't scale well in this aspect and you will end up with workarounds like the one from linked topic or e.g. Allow multiple GUI elements to react dynamically to interaction with a single element

  • My problem with this approach is that I suspect that this makes the variables that are actually being used, that is, color$xxxx, not being defined in front end, as would be the case for variables localised inside a DynamicModule.

    Correct, only symbols from the first argument of DM will be properly scoped. During one session it is not a major problem but if your DM is reopened in future and you/anyone will use the same technique there is no guarantee that they will be unique. So the more popular it gets the worse it becomes :)

    Mr.Wizard solution solves that problem by injecting a sequence of pregenerated symbols to that DynamicModule so they will end up properly scoped.

  • Is there a way around this? Or can I somehow force the variables to be defined in the front end like they would be in a DynamicModule?

    If your foo is supposed to grow/shrink and you don;t know how many special variables you need then Mr.Wizard's answer does not apply.

  • Why does the above code work? Having Dynamic the HoldFirst attribute I would have expected it not to be able to understand that the variable to be tracked is the one inside the hold in the first element of foo. How does it do that?

    By default Dynamic tracks All possible symbols so it scans definitions and collects what is needed. And foo depends of those symbols. Here is a small example:

    x := y; y=1;
    Dynamic[x]
    Dynamic[x, TrackedSymbols -> Full]
    Button["test", y++]
    

    Read more in: Dynamic Symbol tracking from a list of Symbols

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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