1
$\begingroup$

I want to create a plot using Dynamic. Ideally, I'd have some persistent, locally scoped state variables living in a Module; as these variables are updated the plot should change.

I've tried defining a function within the Module scope: calling this function updates the state variables. From the Module, I return both the plot object and the function. Both can be passed around wherever I need them, but calling the function from anywhere needs to always update the plot.

ClearAll[callback, plot];
{callback, plot} = Module[{plot, callfunc, currPath, currT0, currT1},
   currPath = Function[{t}, 0.0];
   currT0 = 0.0;
   currT1 = 1.0;
   plot = Dynamic[Plot[currPath[t], {t, currT0, currT1}]];
   callfunc = Function[{pp, t0, a, t1, b}, (
      currPath = pp;
      currT0 = t0;
      currT1 = t1;
      )];
   {callfunc, plot}];

Here's what I've got so far. This works when calling the function directly in the frontend but not in other circumstances, and also there are red-highlighted warning texts everywhere. Perhaps I should be using DynamicModule, but in that case I can't return values from it in the same straightforward way (it doesn't just return the 2-element list, it includes lots of other stuff too).

What's the right way for me to do this?

Edit:

I think I have this working now.

Do[callback[4*#&, 0.0,1.0,1.0,1.0],{i,5}]

worked as expected. However,

Do[callback[i*#&, 0.0,1.0,1.0,1.0],{i,5}]

failed. This was a scoping issue. The fix is:

Do[callback[Module[{ii=i},ii*#&], 0.0,1.0,1.0,1.0],{i,5}]

On the other hand, the frontend still gives me glaring red warnings in this code, which suggests I'm doing something wrong. Can someone explain this?

$\endgroup$
  • 2
    $\begingroup$ It seems to work for me. What exactly are the "other circumstances"? $\endgroup$ – Michael E2 May 18 '17 at 2:03
  • 1
    $\begingroup$ @MichaelCurry see the second point in my answer: 138404. Please add some usage examples/cases. $\endgroup$ – Kuba May 18 '17 at 5:35
  • 1
    $\begingroup$ @MichaelCurry That's what I tried. As Kuba asks, some examples where it fails are needed. $\endgroup$ – Michael E2 May 18 '17 at 10:17
  • 1
    $\begingroup$ The problem here is that you're trying to localize the variables with Module, but Module does not give you persistently scoped symbols: it implements lexical scoping, meaning that the variable are only localized to the bit of code you put inside of the Module. When you use callback outside of the Module the localization is gone. The fact that is works at all should be more surprising than that is sometimes fails. What you probably want here, are variables that live in a different context. Read the documentation for Begin and see if that does what you need. $\endgroup$ – Sjoerd Smit May 18 '17 at 11:40
  • 1
    $\begingroup$ ...In other words, I disagree with @SjoerdSmit's assertion that "outside of the Module the localization is gone." The localization is given by the unique symbol name, and that is still around outside the Module. Just examine the definitions ? callback and ? plot, and evaluate the Module variables found in it. $\endgroup$ – Michael E2 May 18 '17 at 12:12
2
$\begingroup$

First of all, your Do loop problem is not related to the main issue here. As pointed out, use With[{i=i},... or something. See more in:

To the point now:

The rule of thumb is not to use Module variables inside Dynamic, see:

It is not banned though and can be useful as long as you know what do you do, why and what consequences it has.

And the fundamental problem is that your scoped e.g. currT0 -> currT0$100 is guaranteed to be unique only within current kernel session.

See the second point in:

But if the gui is not suppose to be around across different sessions, you should be fine.

What's the right way for me to do this?

So shortly, within DynamicModule, FE takes care about making its variables local. But they change their names in each session, this is why your code needs to be executed in Initialization.

Here's a small example that may address your problem more precisely:

ClearAll[call, plot];

plot = DynamicModule[{a = 1, t1 = 0.0, t2 = 0.1}
  , Dynamic[Plot[a t, {t, t1, t2}]]
  , Initialization :> (call = Function[{values}, {a, t1, t2} = values] )
];

But notice that call is not assigned till plot "instance" is displayed. So show it:

plot

and run, e.g. call[{2, 0, 2}].

$\endgroup$
  • $\begingroup$ Thanks! The Initialization option seems to make DynamicModule do what I want. There are still some scoping subtleties to figure out here, but that option is key and now I can persist my plots across sessions. $\endgroup$ – Michael Curry May 21 '17 at 4:23

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.