How to trigger multiple events with one action in Dynamic@Graphics

I have this simple code for making an interactive graphic.

y = 1;
f[] := Do[y = 2; Pause[2]; y = 4, {1}];
DynamicModule[{pt = {0, 0}}, 
 Magnify[EventHandler[
   ClickPane[
    Dynamic@Graphics[
      Flatten[{Rectangle[{0, 0}, {y, 
          1}]}]], (pt = #) &], {"MouseClicked" :> f[]}], 0.5]]

After the execution, I want that with one click on the square, I can see the square shrink to a rectangle of size (4,2) for two seconds, and then shrinks again to a rectangle of size (4,1), as indicated in the function f[].For the moment, the action of clicking on the square does trigger these events, but I can only see the final result after the two seconds have passed.

Thank you.

I believe you only see the final result after f[] has fully evaluated because the “MouseClicked” event is using preemptive evaluation, which basically disallows the interrupting dynamic update you were expecting when y = 2 is evaluated.

You can use the Method option on some objects to set their evaluation type. The choice is generally (and I think always, to the best of my knowledge) either “Preemptive” or “Queued”. There are a few details about these evaluation methods at ref/Button:

With the default setting Method → “Preemptive”, button actions are performed immediately, preempting any other evaluation, but are allocated only a limited time to complete.

With the setting Method → “Queued”, button actions are added to the current queue of evaluations, and are performed when other evaluations are complete. No time limit is applied.

With “Queued”, an update of y during the action evaluation would trigger an update of visible dynamic objects depending on y. With “Preemptive”, the action evaluation finishes before dynamic updates can occur.

For a quick demo, evaluate these three inputs and try the buttons. Watch the value of y shown by the Dynamic:

Dynamic@y

y = 1; Button["testing preemptive", y = 2; Pause@2; y = 3]

y = 1; Button["testing queued", y = 2; Pause@2; y = 3, 
 Method -> "Queued"]

If you’re really interested and have already read the two essential Dynamic tutorials (Intro and Advanced), there are some interesting notes on the underlying dynamic mechanism here: Some Notes on Internal Implementation—Wolfram Documentation

I don’t know if there is a way to force the “MouseClicked” event callback to use non-preemptive evaluation (if my guess about the cause of this behavior is in fact correct).

You can use Clock inside Dynamic to make the value represented by y depend on the time. It will auto-update inside the rectangle while the `Clock` is present. Once a certain amount of time has elapsed, the code for y resets y to the constant value 1, which removes the dynamic Clock. Dynamic updating stops until the next mouse click instantiates another Clock.

DynamicModule[{pt = {0, 0}, t0, t1, y = 1},
 Magnify[
  ClickPane[
   Graphics[{
     Rectangle[{0, 0}, {Dynamic[y], 1}],
     Red, PointSize[Large], Dynamic@Point[pt]},
    PlotRange -> {{0, 4}, {0, 2}}, Frame -> True, 
    FrameLabel -> Dynamic[{t0, t1}]],
   (
     pt = #;
     y = (                             (* set y to dynamic clock *)
      t0 = Null;                       (* flags start of clock *)
      Dynamic[
        t1 = Clock[Infinity];          (* Clock inside Dynamic triggers continual updating *)
        If[! NumericQ[t0], t0 = t1];   (* initialize t0 first time through *)
        If[t1 - t0 > 4,                (* stopping criterion *)
          y = 1,                       (* reset y to constant 1 *)
          If[t1 - t0 > 2, 4, 2]]       (* dynamic value of y while Clock is running *)
        ]
       )
     ) &], 0.5]]

At a second glance, I realize FinishDynamic is exactly what’s needed here. Add it to your definition of f, just after you set y to 2:

y = 1;
f[] := Do[y = 2; FinishDynamic[]; Pause[2]; y = 4, {1}];
DynamicModule[{pt = {0, 0}}, 
 Magnify[EventHandler[
   ClickPane[
    Dynamic@Graphics[
      Flatten[{Rectangle[{0, 0}, {y, 
          1}]}]], (pt = #) &], {"MouseClicked" :> f[]}], 0.5]]

Thank you so much Mr Rummler for providing exactly the function that is needed here. I thought for a while it was impossible to evaluate events dynamically within a EventHandler action/function, but FinishDynamic[] did exactly what I wanted.

Thank you also Mr Rogers for letting us know more about the Clock option and its use.