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
Dynamic
does not generally know about things changed via the scheduled task mechanism, particularly when you localize usingDynamicModule
, which means that the localized variable is owned by the FrontEnd. One way that works is to use something likeDynamic[x, UpdateInterval -> 1]
instead of justDynamic[x]
. $\endgroup$