6
$\begingroup$

I want to change the color of a button while it is pressed, pause for a short time and then change the color back to the original color.

I can't seem to figure out how to do this (tried various forms of DynamicModule and Dynamic, but no luck). This is what I tried so far:

Manipulate[
  DynamicModule[{simulationFlag, bgnd = Automatic},
    simulationFlag = newSimulation;
    Pause[0.5];
    Dynamic[bgnd = Automatic; {simulationFlag}]],
  {{newSimulation, 0.02}, ControlType -> None},
  Row[{
    Button["New Simulation", Dynamic[newSimulation = newSimulation+1; {bgnd=Yellow}],
      Background -> Dynamic[bgnd]]}]
]
$\endgroup$
3
  • 4
    $\begingroup$ Could you please post whatever code you have tried so far? Also, do you want the button to revert after a short time while it is still pressed? $\endgroup$ Dec 2, 2013 at 17:32
  • $\begingroup$ There are examples in the documentation: Button > Options > Background. $\endgroup$
    – Silvia
    Dec 2, 2013 at 19:47
  • $\begingroup$ If you rework the example in the docs and add a Pause you will get what you need. $\endgroup$ Dec 2, 2013 at 21:32

3 Answers 3

2
$\begingroup$

For some unknown reason, @István's answer doesn't work here, so I rolled my own:

DynamicModule[{c = Yellow}, 
              Dynamic@Button["Press", Print["Pressed"]; 
                             c = Red; 
                             RunScheduledTask[c = Yellow; 
                                     RemoveScheduledTask[$ScheduledTask], {0}, AbsoluteTime[] + 3], 
                             Background -> c]]
$\endgroup$
6
  • $\begingroup$ I've corrected an error in my code (see comment below my post); could you please check whether it works on your end now? BTW, the problem with your approach is that multiple clicks start multiple tasks which stack up (check with Dynamic@Refresh[ScheduledTasks[], UpdateInterval->.1]). That's why I had to remove all tasks beforehand (not an elegant solution). $\endgroup$ Dec 2, 2013 at 22:42
  • $\begingroup$ @IstvánZachar Your code still fails here. BTW, I see no problem with the tasks stacking up, as they are all removed after the delay from the last click has expired. $\endgroup$ Dec 2, 2013 at 22:46
  • $\begingroup$ Ok, I see now, we've implemented different things here. Your code reverts back n sec after the button is released, while mine reverts n sec after the button is pushed but not released. $\endgroup$ Dec 2, 2013 at 22:56
  • $\begingroup$ @IstvánZachar Ha! I was testing your code with my understanding :) The failure was the test case $\endgroup$ Dec 2, 2013 at 23:00
  • 1
    $\begingroup$ @JackLaVigne Just type $ScheduledTask on your notebook and press F1 ... and get used to do that. Everybody does it all the time while working in Mathematica $\endgroup$ Dec 2, 2013 at 23:29
5
$\begingroup$

Explanation in detail what this code does:

  • Since the Button[..., Background -> color] has a rather ugly look, I use a Panel with colored background as the label of the button, this causes the button-in-a-button look.
  • When simply clicked (pressed and released immediately), nothing special happes, the button (and Panel) only changes appearance for the short time the button is pressed, then reverts back to normal.
  • However, when the button is pressed but not released, the behaviour is different: with the first registered event of "MouseDown", a timed task is started that evaluates delay seconds later and reverts the color of the Panel back to normal no matter whether the button is released or not, but still leaves the Button look pressed.
  • Releasing the button immediately removes any existing timed tasks.

So this code is a proof of principle that events can happen even while a button is in the iteraction phase. This might not be exactly what OP asked for but is my interpretation of the question.

delay = 2; (* time to stay blue when pressed *)
col = None; (* default color *)
down = False; (* default button state *)
EventHandler[Button[Dynamic@Panel[
     If[down, "Pushed", "Push!"], Background -> col, ImageMargins -> 5], 
     Appearance -> Dynamic@If[down, "Pressed", "Normal"]],
 {
  "MouseDown" :> (
    down = True;
    RemoveScheduledTask /@ ScheduledTasks[]; (* to prevent multiple click-overlaps *)
    col = Hue[.6, 1, 1, .2]; 
    task = RunScheduledTask[col = None; RemoveScheduledTask@task, {delay}]),
  "MouseUp" :> (down = False; Quiet@RemoveScheduledTask@task; col = None)
  }, PassEventsDown -> True
 ]

Mathematica graphics

$\endgroup$
8
  • $\begingroup$ Strange ... doesn't work here (Mma 9 on winxp). It behaves as a normal button $\endgroup$ Dec 2, 2013 at 20:13
  • $\begingroup$ I don't know how to get a carriage return so the comment I make is going to run all together. Your solution works fine but is above my level. Here is what I tried but it doesn't work. Manipulate[ DynamicModule[{simulationFlag, bgnd = Automatic}, simulationFlag = newSimulation; Pause[0.5]; Dynamic[ bgnd = Automatic; {simulationFlag} ] ] , {{newSimulation, 0.02}, ControlType -> None}, Row[{ Button[ "New Simulation", Dynamic[newSimulation = newSimulation + 1; {bgnd = Yellow}], Background -> Dynamic[bgnd] ] }] ] $\endgroup$ Dec 2, 2013 at 20:59
  • $\begingroup$ @István I cannot understand why, when the scheduled task is executed, the button also gets raised (no more pressed). I do not find anything doing that in your code. Is it possible to keep it pressed? (MMA v9) $\endgroup$
    – user8074
    Dec 2, 2013 at 21:11
  • $\begingroup$ @user8074 Thanks for pointing it out! Actually, if you check the previous version, you can see that I made a mistake and accidentally merged Button option Appearance into the Panel. Strangely enough, it worked, so I missed it. This new version should behave as expected. $\endgroup$ Dec 2, 2013 at 22:37
  • 1
    $\begingroup$ @StuartPoss RunScheduledTask is phased out with v12.1, you have to switch to TaskExecute and friends. More importantly, it seems that the definition of RunScheduledTask (and e.g. ScheduledTasks) have changed under the hood as they behave differently, as you have pointed it out (I guess that task is not visible anymore in v12.1 to be deleted, hence the message). For example, Dynamic@ScheduledTasks[] is now freezing the frontend, while Dynamic@Tasks[] works nicely. $\endgroup$ May 16, 2020 at 13:10
2
$\begingroup$

Here are a couple of ways. One uses UpdateInterval. The second uses Clock. The Clock method continually uses some CPU to update the front end all the while the alternate color (Yellow) is displayed. The first method makes only one update. In this way it is more efficient.

In both cases, when the button is pressed, the background color is replaced by a dynamic object that updates itself and persists only while the alternate color is displayed. The dynamic object is then destroyed after the interval has elasped and the color is replaced with the default color (Red, by my choice).

UpdateInterval method:

DynamicModule[{bgnd = Red, timer = Null, switch},
 Button["go",
  bgnd = Yellow;
  switch = 0;
  timer = Hold @ Refresh[If[switch < 1, switch++, timer = Null; bgnd = Red], 
     UpdateInterval -> 2, TrackedSymbols :> {}],
  Background -> Dynamic[ReleaseHold @ timer; bgnd]
  ]
 ]

Clock method:

DynamicModule[{bgnd = Red, clicktime = 0, duration = 2},
 Button["go",
   clicktime = Clock[Infinity]; 
   bgnd = Hold @ If[Clock[Infinity] < clicktime + duration, Green, bgnd = Red],
   Background -> Dynamic[ReleaseHold @ bgnd]
   ]
 ]

With respect to the code added in an edit, which seems possibly to be seeking a different sort of behavior -- that is, not for a certain amount of time, but while the Manipulate updates -- here is a simplified version that works:

Manipulate[
 Pause[0.5];
 bgnd = Automatic;
 newSimulation,
 {{newSimulation, 0.02}, ControlType -> None},
 {{bgnd, Automatic}, ControlType -> None}, 
 Button["New Simulation", newSimulation = newSimulation + 1; 
  bgnd = Yellow, Background -> Dynamic[bgnd]]]
$\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.