14

I guess I have to try harder and focus more but timed evaluation always frustrates me.

I will show the issue on simple example of delayed trigger.

 ClearAll@time; time = 0;
 CreateScheduledTask[time++;, {1, 5}]
 StartScheduledTask@ScheduledTasks[];

DynamicWrapper[Dynamic@time, If[time === 5, RemoveScheduledTask[ScheduledTasks[]]; Print["end"]]]

1... 2... 3... 4... 5 "end" (*so it works OK*)

In general one may want to scope such procedure:

DynamicModule[{x},
ClearAll@x;

Column[{Button["start", x = 0; RemoveScheduledTask[ScheduledTasks[]]; StartScheduledTask@CreateScheduledTask[x++;, {1, 5}]], DynamicWrapper[Dynamic@x, If[x === 5, Print@1]]
}] ]

output before clicking:

FE`x$$number (*as expected*)

output after clicking:

0 (*and it does not change*)

We can see that the only procedure that was enabled was x = 0;.

I was suspecting problems because ScheduledTasks are not related to FrontEnd while DynamicModule is.

What is interesting is that changing DynamicModule to Module fixes the problem.

The question are:

  • What am I missing in case of DynamicModule? I've seen Module variable scoping in ScheduledTasks but I couldn't use it for my purpose. (*I've added ClearAll but it does not seem to make any difference.

  • Schould I use Module even if this is only a minimal example of the code that will be implemented in larger cdf? Moreover, John Fultz reminded couple of times (e.g. here) that such constructs are incorrect.

Kuba
  • 136,707
  • 13
  • 279
  • 740
  • @JacobAkkerboom :) any thoughts? – Kuba Dec 05 '13 at 13:02
  • unfortunately I don't know much about any of this :( – Jacob Akkerboom Dec 05 '13 at 13:20
  • 2
    My guess is that the update mechanism of Dynamic does not generally know about things changed via the scheduled task mechanism, particularly when you localize using DynamicModule, which means that the localized variable is owned by the FrontEnd. One way that works is to use something like Dynamic[x, UpdateInterval -> 1] instead of just Dynamic[x]. – Leonid Shifrin Dec 05 '13 at 14:46

2 Answers2

13

Updated

This happens because your DynamicModule returns a dynamic object of which x is passed on to the front-end before the scheduled task starts, so the front-end-x cannot be modified anymore by any process (more details at the end).

The problem can be further simplified. This works:

RemoveScheduledTask@ScheduledTasks[];
DynamicModule[{x = 0}, RunScheduledTask[x++, {1, 5}]; Print@Dynamic@x;];

but this does not (at least not how one would expect):

RemoveScheduledTask@ScheduledTasks[];
DynamicModule[{x = 0}, RunScheduledTask[x++, {1, 5}]; Dynamic@x]

Examining the second one:

RemoveScheduledTask[ScheduledTasks[]];
d = DynamicModule[{x = 0}, Print[HoldForm@x -> x]; 
RunScheduledTask[x++, {1, 5}]; Dynamic[HoldForm@x -> x]]
 x$1725 -> 0  (* the real name of x inside DynamicModule & the sched.task *)

FE`x$$18 -> 0 (* the displayed x as returned from DynamicModule *)

Clearly, the returned x has nothing to do with the x used in the scheduled task.

As OP realized, Module works where DynamicModule does not. The same analysis as above:

RemoveScheduledTask[ScheduledTasks[]];
d = Module[{x = 0}, Print[HoldForm@x -> x]; 
RunScheduledTask[x++, {1, 5}]; Dynamic[HoldForm@x -> x]]
x$1740 -> 0  (* the real name of x inside DynamicModule & the sched.task *)

x$1740 -> 0 (* the displayed dynamic value of x as returned from DynamicModule *)

The two variables are not decoupled, so the scheduled task will modify the same variable that is displayed. The problem here is that the variable that is actually displayed and modified dynamically (x$1740) are not owned by the front-end, though it should be according to John Fultz's explanation. See below for a better solution.

Better understanding evaluation sequence

As Leonid has pointed it out, the returned dynamic value of a DynamicModule is owned by the frontend:

DynamicModule[{x}, Print@Dynamic@x; Dynamic@x]
x$4513     (* printed *)

FE`x$$88 (* returned *)

One can change the first one from the outside as x$4513 = 99, but not the second one, so scheduled tasks failed.

Digging deeper, I managed to get to the depths of the problem. The main reason why the returned x does not update is because when the return value of the DynamicModule is returned and displayed on screen, it is passed on to the front end without evaluation, and gets renamed from x$4513 to x$$88. Only after it is displayed will the front end look after how to update x, however, the internal scheduled task has already been called before returning Dynamic[x] from the DynamicModule, and it still refers to x$4513, so the two references to x gets decoupled and will never be linked anymore.

According to this hypothesis, delaying the scheduled task AFTER the returned value is displayed should work. The correct method for this is to use Initialization, that is evaluated right after the returned value of a DynamicModule is displayed:

RemoveScheduledTask[ScheduledTasks[]];
d = DynamicModule[{x = 0},
  Print["In DM: ", HoldForm@x -> x];
  Dynamic[x, TrackedSymbols :> {x}],
  Initialization :> (Print["initialized"]; 
    RunScheduledTask[x++; Print["In ST: ", HoldForm@x -> x], {.5, 3}];)
  ]

When evaluated, one can see that the two Print statements refer to different variables:

In ST: FE`x$$11->1

In DM: x$1729->0

meaning that the scheduled task (ST printing) now correctly uses the front-end variable instead of the kernel variable (DM printing)! For the whole thing to work, one needs an extra TrackedSymbols :> {x} in Dynamic, or something similar, to tell the front-end what variable/trigger should be checked. Any of the followings should work:

Dynamic[x, TrackedSymbols :> {x}]
Dynamic@{x}
Dynamic[x, UpdateInterval -> .1]

but not this:

Dynamic@x
István Zachar
  • 47,032
  • 20
  • 143
  • 291
  • Short answer: use Module instead of DynamicModule. – István Zachar Dec 05 '13 at 15:10
  • @Kuba Does the edit provide a better understanding of the case? I still don't know exactly why the Module-based version is to be avoided, but I fully trust the words of John Fultz. Would be nice if he could throw in some extra information... – István Zachar Dec 06 '13 at 14:57
  • 1
    I like the update :) great you've managed to figure it out. There are always problems with timed procedures/precedence/or evaluation order (at least to me) but this is important part of GUI creation and such problems should already have been discussed in possible issues sections. Thanks again. – Kuba Dec 07 '13 at 06:42
  • @Kuba, fine by me :) – István Zachar May 05 '14 at 13:02
  • The solution is to put SchTasks in Initialization while relevant variables should be displayed in body via Dynamic. Ok. but what if x is not meant to be displayed, it is only an iterator or whatever. So it's not going to be displayed, the issue appears again. I can unscope x you can say, since it's not for displaying it should not be in DynModVar. Ok, but I can't put it in Module as the whole body is wrapped in Dynamic[Refresh[..,None]] so it will be against "do not put Module vars to Dynamic internal to that. – Kuba May 05 '14 at 13:07
  • At the end the only solution I can use is to unscope it so x is a Global variable. It works but this construction is ugly, ugly but forced by MMA design. Or have I missed something? If someting is not clear, feel free to ask :) – Kuba May 05 '14 at 13:07
  • @Kuba So what's wrong with this code? Module[{x}, Button["start", x = 0; RunScheduledTask[x++; If[x === 3, Print@1], {1, 5}]]] It scopes x and executes button code & sch.task correctly. – István Zachar May 05 '14 at 13:18
  • It is what I had to do in this StarWars topic. Variables op andhareGlobal` to make it work. I find it ugly. – Kuba May 05 '14 at 13:18
  • In this example there is no Dynamics or DynamicModules. If there is onlyModule` it is ok. But I'm talkingin context of CDF creation, like http://mathematica.stackexchange.com/a/47273/5478 – Kuba May 05 '14 at 13:19
  • @Kuba If I put h and op in the first argument of the DynamicModule the crawl still works nicely at my end. – István Zachar May 05 '14 at 13:27
  • Too bad I'm not at home, I can't test it but I remember that it was not so ok for me. I will try this ater and will post you my observations. So, thank you for your time now. I will try to make a MWE later :) – Kuba May 05 '14 at 13:34
  • @Kuba If you find out what the discrepancy is (it still looks ok to me), ping me, I still check SE regularly. – István Zachar May 05 '14 at 13:39
  • p.s. Don't you think that the first statement in details section DM first gives unique names to local variables in expr, just like Module, then evaluates the resulting expression, and then returns a version of this wrapped in DM. is just not true, or at least requires additional info about this first scoping being kernel scoping? – Kuba Nov 17 '14 at 23:12
  • @Kuba I think that this statement is perfectly true. The variable x$1729 you see above is indeed a localized kernel variable, It is printed by the kernel during the kernel evaluation of the DM-command, before the result was wrapped in another DM and sent to the frontend for constructing the output. – Fred Simons Nov 18 '14 at 20:09
  • @FredSimons Maybe the wording is too radical. But what I think is that documentation at least creates missleading view on DM behaviour. AdvDynFunc tutorial says DM variables are owned by FE. Details sections says scoping is done at the beginning. So I see no reason to expect that DM scopes in two different ways. Topics like this are only confirmation - noone expects that, unless has faced it. :/ – Kuba Nov 18 '14 at 20:20
  • @Kuba The first statement in details section says DM first gives unique names to local variables in expr, just like Module, then evaluates the resulting expression, and then returns a version of this wrapped in DM. That means, I think, that the kernel evaluation of a DM results in a DM kernel expression. That resulting DM expression, which you can inspect with InputForm or FullForm, is sent to the frontend for displaying and it is the frontend that localizes the variables in the displayed DynamicBox. – Fred Simons Nov 18 '14 at 20:37
  • 1
    @FredSimons I don't arguee if it's formally correct or not. My point is that double localization by DM is at least not obvious... – Kuba Nov 18 '14 at 20:55
  • @IstvánZachar. With much interest I studied your solution, and I agree with some, but not all of your remarks and observations. If after such a long time you are still interested, I just posted another solution, in which I refer to yours. – Fred Simons Dec 04 '14 at 13:22
  • 1
2

The following is a slightly simplified version of your DynamicModule:

DynamicModule[{x},
 Column[{Button["start", x = 0; RunScheduledTask[x++;, {1, 5}]],
 Row[{Dynamic[HoldForm[x]], " = ", Dynamic[x]}]}]
]

When you evaluate this for the first time, underneath the button you see the output

(* FE`x$$82 = FE`x$$82 *)

(very likely with a different DynamicModule number.) Here FE``x\$\$82 is a kernel variable that is created by the frontend to be able to call the kernel with respect to the frontend variable \$CellContext`x$$, that we see when we inspect the cell expression of the displayed DynamicModule.

When we press the button, the output changes to

(* FE`x$$882 = 0 *)

and nothing indicates that the scheduled tasks were executed. But they are executed! We can ask for the value of the kernel variable:

FE`x$$82
(* 5 *)

Hence we only have to force the updating. That can be done by including TrackedSymbols:

DynamicModule[{x},
  Column[{Button["start",x=0; RunScheduledTask[x++;,{1,5}]],
  Row[{Dynamic[HoldForm[x]]," = ", Dynamic[x, TrackedSymbols:>{x}]}]}]
]

Now it works.

A short remark on Istvan Zachar's solution. I think that his explanation is not quite correct. The kernel evaluation of a DynamicModule expression gives another DynamicModule kernel expression and that last one is displayed by the frontend. It can easily be found by converting the displayed DynamicModule (which belongs completely to the frontend) to InputForm. In the adapted example of Kuba it is (I left out the Button options):

DynamicModule[{x = 0}, 
  Column[{Button[start, x = 0; RunScheduledTask[x++; , {1, 5}],
  Row[{Dynamic[HoldForm[x]], " = ", Dynamic[x]}]},
   ItemSize -> {Automatic, Automatic}], 
  DynamicModuleValues :> {}
]

The variable in the scheduled task is the local variable of the DynamicModule! There is no reason for using the Initialization option for entering the scheduled task. The crucial point is that the updating in Dynamic does not work, whether we implement the scheduled task in the Initialization option or not. Therefore, also in Istvan's solution the option TrackedSymbols in Dynamic is required.

Fred Simons
  • 10,181
  • 18
  • 49