14
$\begingroup$

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.

$\endgroup$
3
  • $\begingroup$ @JacobAkkerboom :) any thoughts? $\endgroup$
    – Kuba
    Dec 5, 2013 at 13:02
  • $\begingroup$ unfortunately I don't know much about any of this :( $\endgroup$ Dec 5, 2013 at 13:20
  • 2
    $\begingroup$ 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]. $\endgroup$ Dec 5, 2013 at 14:46

2 Answers 2

13
$\begingroup$

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
$\endgroup$
19
  • $\begingroup$ Short answer: use Module instead of DynamicModule. $\endgroup$ Dec 5, 2013 at 15:10
  • $\begingroup$ @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... $\endgroup$ Dec 6, 2013 at 14:57
  • 1
    $\begingroup$ 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. $\endgroup$
    – Kuba
    Dec 7, 2013 at 6:42
  • 1
    $\begingroup$ @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... $\endgroup$
    – Kuba
    Nov 18, 2014 at 20:55
  • 1
    $\begingroup$ @FredSimons and Istvan, those comments are quite on topic. $\endgroup$
    – Kuba
    Mar 30, 2015 at 7:10
2
$\begingroup$

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.

$\endgroup$

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.