Message Boards Message Boards

[✓] Use a Button with a Dynamic?

GROUPS:

In this code after the stop button is pressed, why doesn’t the counter count up and the word “Stopping” not appear?

DynamicModule[
 {counter = 0,
  state = "I"},
 Column[{
   Row[
    {
     Button["Arm",
      counter = 0;
      state = "Active",

      Method -> "Queued"
      ],
     Button["Stop",
      state = "Stopping";
      counter = 0;
      Do[counter++, {i, 1000000}];
      state = "Stopped",
      Method -> "Preemptive"
      ]
     }

    ], Dynamic[counter, TrackedSymbols -> {counter}, 
    UpdateInterval -> 0.25],
   Dynamic[state, UpdateInterval -> 0.5]
   }
  ]
 ]
POSTED BY: Jeff Burns
Answer
2 months ago

In the doc page for Button, you can read this statement:

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

So, use "Queued" for the 2nd Button's Method.

POSTED BY: Chad Knutson
Answer
2 months ago

Thanks for the reply. Changing “Preemptive” to “Queued” does make my example code work as expected. My real code (see below) is a bit more complicated. In the first button I make a .NET call to a camera that will not return control until it captures an image or times out. To make it possible to interrupt the capture, the call is inside a loop. With “Preemptive” it eventually stops the image capture, but never shows the word “Stopping”. With “Queued” it never stops.

DynamicModule[
 {run = True,
  state = "    Ready"},
 Column[{
   Row[
    {
     Button["Arm",
      run = True;
      state = "    Active";
      myCam@StartAcquisition[];
      While[run,
       currentImage = currentImageXIMea[myCam, 1000 (*ms*)]
       ],
      Method -> "Queued"
      ],
     Button["Stop",
      run = False;
      state = "    Stopping";
      Pause[2];
      myCam@StopAcquisition[];
      state = "    Stopped",
      Method -> "Queued"
      ],
     Dynamic[state, UpdateInterval -> 0.25]
     }
    ],
   Dynamic[
    Image[currentImage, ImageSize -> 300],
    TrackedSymbols -> {currentImage}
    ]
   }
  ]
 ]
POSTED BY: Jeff Burns
Answer
2 months ago

Ah, Jeff, I should have recognized your name!

Hmmm, I'm not sure the best solution for this. Have you tried wrapping the .NET call with TimeConstrained? I'm not sure if that will allow you to interrupt the call, but at least it will give you a secondary method for stopping unresponsive calls.

POSTED BY: Chad Knutson
Answer
2 months ago

"stopping" does not appear because the button parses the entire CompoundExpression in the second argument. For example,

DynamicModule[{state = 1}, Column[{Dynamic[state], Button["Trigger", state = 2; Pause[1]; state = 3]}]]

only updates with "3". I'm not sure why you're using UpdateInterval in your dynamics. If the dynamic is only a wrapper, then usually you want that to update as fast as possible.

I would suggest using EventHandler. It's a lower-level function, but you can use it to separate out the updates to MouseDown versus MouseUp events:

EventHandler[Button["Trigger", Print["A"], Active -> False], {"MouseDown" :> Print["B"], "MouseUp" :> Print["C"]}]

In the above code I still use a Button so you get a similar interface, but I disable the button's action. By putting your "stopping" update with MouseDown and the rest in MouseUp, you should get your interface to update as you want. Better still, you can make the button not do anything except change its appearance when clicked:

Column[{  
    Dynamic[state],  
    EventHandler[Button["Trigger", Null], {"MouseDown" :> (state = 2), "MouseUp" :> (state = 3)}, PassEventsDown -> True]
}]
POSTED BY: Kevin Daily
Answer
2 months ago

Group Abstract Group Abstract