Function Repository Resource:

DynamicEnabledButton

Source Notebook

Create a button that is automatically disabled when clicked and enabled when it finishes running its code

Contributed by: Richard Hennigan (Wolfram Research)

ResourceFunction["DynamicEnabledButton"][label,action]

represents a button that is labeled with label, evaluates action whenever it is clicked and automatically becomes disabled while action is evaluating.

ResourceFunction["DynamicEnabledButton"][Dynamic[x],label,action]

binds the enabled state to x.

Details and Options

ResourceFunction["DynamicEnabledButton"] is similar to Button but automatically sets up dynamic behavior for the Enabled option.
By default, ResourceFunction["DynamicEnabledButton"] starts enabled, becomes disabled when action starts evaluating and becomes enabled again as soon as action completes.
label can be any expression, including a dynamic one.
ResourceFunction["DynamicEnabledButton"] by default displays label in "Button" style, which typically uses the system button font.
ResourceFunction["DynamicEnabledButton"][label,action] maintains action in unevaluated form, evaluating it each time the button is clicked.
ResourceFunction["DynamicEnabledButton"] has the same options as Button, but the default value for Method is "Queued" instead of "Preemptive":
AlignmentAutomatichow to align contents within the button
AppearanceAutomaticthe overall appearance of the button
AutoActionFalsewhether to click the button automatically when the mouse is over it
BackgroundAutomaticbutton background color
BaselinePositionAutomaticalignment relative to surrounding text
BaseStyle"GenericButton"base style specifications for the button
ContentPaddingTruewhether to shrink the margins tightly around the contents
EnabledAutomaticwhether the button is enabled or grayed out
EvaluatorAutomaticthe kernel in which to evaluate expr
FrameMarginsAutomaticminimum margins to leave inside the frame
ImageMargins0margins around the image of the displayed button
ImageSizeFullthe overall image size of the displayed button
Method"Queued"the evaluation method to use
TooltipNonethe tooltip for the button
TooltipDelay0.`how long to delay before displaying the tooltip
TooltipStyle{}style specifications for the tooltip
Setting values for Enabled only controls the initial state. ResourceFunction["DynamicEnabledButton"] always becomes disabled when action starts evaluating and becomes enabled when action finishes evaluating.
In ResourceFunction["DynamicEnabledButton"][Dynamic[x],label,action], the value for x is automatically initialized to True or False, depending on the Enabled option.
With the default setting ImageSizeFull, a button will be sized to fill out its enclosing region in a Grid or related construct.
The setting ImageSizeAutomatic specifies that the button will be sized to fit its contents but will not expand to fill out an enclosing region.
Settings of Tiny, Small, Medium and Large for ImageSize specify buttons with certain system-standard minimum sizes.
Typical possible settings for the Appearance option include "DialogBox", "Frameless", "Palette" and "FramedPalette". In some cases, "AbuttingLeftRight", "AbuttingRight", etc. are also supported.
Appearance"Pressed" gives a button with a pressed appearance. Appearance{type,"Pressed"} gives a button of a certain type with a pressed appearance.
With AppearanceNone, label is displayed literally, without being placed in a button.
Typical possible settings for the Method option include "Preemptive" and "Queued".
ResourceFunction["DynamicEnabledButton"][prims,action] can be used within graphics objects to specify that action should be evaluated whenever the graphics primitives prims are clicked.
ResourceFunction["DynamicEnabledButton"][None,Dynamic[label],action,] can be used to specify a dynamic label and no dynamic binding without ambiguity.

Examples

Basic Examples (4) 

Create a button that automatically becomes disabled while it is running code:

In[1]:=
ResourceFunction["DynamicEnabledButton"]["test", Pause[5]]
Out[1]=

Provide a symbol that is updated automatically as the button changes state:

In[2]:=
{ResourceFunction["DynamicEnabledButton"][Dynamic[x], "test", Pause[5]], Dynamic[x]}
Out[2]=

Use a dynamic label:

In[3]:=
ResourceFunction["DynamicEnabledButton"][Dynamic[z], Row[{"Button is enabled: ", Dynamic[z]}], Pause[5]]
Out[3]=

Embed in other expressions:

In[4]:=
ResourceFunction["BirdSay"][
 ResourceFunction["DynamicEnabledButton"]["awesome", Pause[5]]]
Out[4]=

Scope (9) 

Control the state of the button by setting values for the given symbol:

In[5]:=
ResourceFunction["DynamicEnabledButton"][Dynamic[enabled], "test", Pause[5]]
Out[5]=
In[6]:=
enabled = False
Out[6]=

Use another control:

In[7]:=
Checkbox[Dynamic[enabled]]
Out[7]=

Change the label depending on the state of the button:

In[8]:=
ResourceFunction["DynamicEnabledButton"][
 Dynamic[doing],
 PaneSelector[{True -> "Do the thing", False -> ProgressIndicator[Appearance -> "Percolate"]}, Dynamic[doing]],
 Pause[5]
 ]
Out[8]=

Create a group of buttons where only one can be clicked at a time:

In[9]:=
Column[{ResourceFunction["DynamicEnabledButton"][Dynamic[z], "first", Pause[5]], ResourceFunction["DynamicEnabledButton"][Dynamic[z], "second", Pause[5]]}]
Out[9]=

Create multiple button groups:

In[10]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/8f5fd9d1-5891-4424-9d7d-db4bc56c6cf9"]
Out[10]=

Use in a graphics expression:

In[11]:=
Graphics[{Dynamic[If[g, Red, Gray]], ResourceFunction["DynamicEnabledButton"][Dynamic[g], Disk[], Pause[5]]}]
Out[11]=

Display the label literally, without the appearance of a button:

In[12]:=
ResourceFunction["DynamicEnabledButton"][
 Mouseover["click", Style["click", Italic]], Pause[5], Appearance -> None]
Out[12]=

The state need not be bound to a symbol:

In[13]:=
states = <||>;
{
 ResourceFunction["DynamicEnabledButton"][Dynamic[states["first"]], "first", Pause[5]],
 ResourceFunction["DynamicEnabledButton"][Dynamic[states["second"]], "second", Pause[5]],
 Dynamic[states]
 }
Out[6]=

Use the TaggingRules of the current notebook to store the button state:

In[14]:=
ResourceFunction["DynamicEnabledButton"][
 Dynamic[CurrentValue[
   EvaluationNotebook[], {TaggingRules, "tag"}]], "test", Pause[5]]
Out[14]=

Get the button state using CurrentValue:

In[15]:=
CurrentValue[EvaluationNotebook[], {TaggingRules, "tag"}]
Out[15]=

Change the state:

In[16]:=
CurrentValue[EvaluationNotebook[], {TaggingRules, "tag"}] = False
Out[16]=

To use a label of the form Dynamic[] without specifying the dynamic binding, None can be used as the first argument to avoid ambiguity:

In[17]:=
ResourceFunction["DynamicEnabledButton"][None, Dynamic[DateString[], UpdateInterval -> 1], Pause[5]]
Out[17]=

Options (21) 

Alignment (1) 

Different predefined alignment options:

In[18]:=
Row[Table[
  ResourceFunction["DynamicEnabledButton"]["y", Pause[5], ImageSize -> {Automatic, 50}, Alignment -> a], {a, {Top, Center, Bottom}}], "xxx"]
Out[18]=

Appearance (4) 

Predefined button appearances:

In[19]:=
Table[ResourceFunction["DynamicEnabledButton"]["xxx", Pause[5], Appearance -> a], {a, {"DialogBox", "Palette", "Frameless"}}]
Out[19]=

On some platforms, the following appearances are also defined:

In[20]:=
Table[ResourceFunction["DynamicEnabledButton"]["xxx", Null, Appearance -> a], {a, {"AbuttingLeft", "AbuttingRight"}}]
Out[20]=

Use a second element to get the appearance of a pressed button:

In[21]:=
Table[ResourceFunction["DynamicEnabledButton"]["xxx", Null, Appearance -> {a, "Pressed"}], {a, {"DialogBox", "Palette"}}]
Out[21]=

Use a special appearance for the button:

In[22]:=
ResourceFunction["DynamicEnabledButton"]["xxx", Null, Appearance -> None]
Out[22]=

AutoAction (2) 

By default, the button function does not evaluate until you click it:

In[23]:=
x = 0; {ResourceFunction["DynamicEnabledButton"]["xxx", x++], Dynamic[x]}
Out[23]=

By setting AutoAction, the button function evaluates as you mouse over the button area:

In[24]:=
y = 0; {ResourceFunction["DynamicEnabledButton"]["yyy", y++, AutoAction -> True], Dynamic[y]}
Out[24]=

Background (2) 

Change the background colors:

In[25]:=
Table[ResourceFunction["DynamicEnabledButton"]["xxx", Null, Background -> c], {c, {Pink, Green, Gray, Yellow}}]
Out[25]=

Change the background color on every click:

In[26]:=
DynamicModule[{x = 0}, ResourceFunction["DynamicEnabledButton"]["xxx", x = Mod[x + 1/10, 1],
   Background -> Dynamic[Hue[x]]]]
Out[26]=

BaselinePosition (2) 

Align with the surrounding text:

In[27]:=
Row[Table[
  ResourceFunction["DynamicEnabledButton"]["xxx", Null, BaselinePosition -> p], {p, {Top, Center, Bottom}}], "xxx"]
Out[27]=

Change the baseline position on every button click:

In[28]:=
DynamicModule[{x = 1}, {"x", ResourceFunction["DynamicEnabledButton"]["xxx", x = Mod[x + 1, 3, 1], BaselinePosition -> Dynamic[Part[{Top, Center, Bottom}, x]]], "x"} // Row]
Out[28]=

ContentPadding (1) 

Remove extra whitespace around a label with no ascenders or descenders:

In[29]:=
ResourceFunction["DynamicEnabledButton"][xxx, Null, FrameMargins -> 0,
  ContentPadding -> False]
Out[29]=

Enabled (3) 

By default, DynamicEnabledButton is enabled:

In[30]:=
ResourceFunction["DynamicEnabledButton"]["aaa", Null]
Out[30]=

By setting EnabledFalse, the button is disabled but visible in its current state:

In[31]:=
ResourceFunction["DynamicEnabledButton"]["aaa", Null, Enabled -> False]
Out[31]=

By starting the button with EnabledFalse, the button can still be enabled manually by altering the given symbol:

In[32]:=
ResourceFunction["DynamicEnabledButton"][Dynamic[aaa], "aaa", Null, Enabled -> False]
Out[32]=
In[33]:=
Checkbox[Dynamic[aaa]]
Out[33]=

FrameMargins (1) 

By setting FrameMargins, you make the button content area larger:

In[34]:=
Table[ResourceFunction["DynamicEnabledButton"]["xxx", Null, FrameMargins -> m], {m, {0, 5, 10, 20}}]
Out[34]=

ImageMargins (1) 

By setting ImageMargins, you make the button area larger:

In[35]:=
Table[Framed@
  ResourceFunction["DynamicEnabledButton"]["xxx", Null, ImageMargins -> m], {m, {0, 5, 10, 20}}]
Out[35]=

ImageSize (4) 

Use preset values:

In[36]:=
Table[ResourceFunction["DynamicEnabledButton"]["xxx", Null, ImageSize -> i], {i, {Tiny, Small, Medium, Large}}]
Out[36]=

Or use any values:

In[37]:=
Column[Table[
  ResourceFunction["DynamicEnabledButton"]["xxx", Null, ImageSize -> i], {i, {50, 100, 150, 200}}]]
Out[37]=

By setting the second element, you can also control the height:

In[38]:=
ResourceFunction["DynamicEnabledButton"]["xxx", Null, ImageSize -> {Automatic, 50}]
Out[38]=

A fully customized image size:

In[39]:=
ResourceFunction["DynamicEnabledButton"]["xxx", Null, ImageSize -> {100, 50}]
Out[39]=

Applications (2) 

Make a button that automatically indicates when it is busy computing something:

In[40]:=
ResourceFunction["DynamicEnabledButton"][
 Dynamic[running],
 PaneSelector[{True -> "Run", False -> ProgressIndicator[Appearance -> "Percolate"]}, Dynamic[running]],
 Print[Table[Length[FactorInteger[2^n - 1]], {n, 50, 500, 50}]]
 ]
Out[40]=

Create a button that deletes itself after spending a few seconds to express how it feels about being deleted:

In[41]:=
ResourceFunction["DynamicEnabledButton"][
 Dynamic[del],
 PaneSelector[{True -> "Delete", False -> Style["\[SadSmiley]", Large]}, Dynamic[del]],
 Pause[5];
 NotebookDelete[EvaluationCell[]]
 ]

Properties and Relations (2) 

Similar behavior to DynamicEnabledButton[Dynamic[x],label,action] can be achieved with Button:

In[42]:=
ResourceFunction["DynamicEnabledButton"][Dynamic[aa], "test", Pause[5]]
Out[42]=
In[43]:=
bb = True;
Button["test", bb = False; Pause[5]; bb = True, Enabled -> Dynamic[bb], Method -> "Queued"]
Out[32]=

The enabled state of the button is restored even if an abort occurs:

In[44]:=
ResourceFunction["DynamicEnabledButton"][Dynamic[cc], "test", Pause[1]; Abort[]; Pause[10]]
Out[44]=

This prevents the button from ending up in a “bad” state:

In[45]:=
dd = True;
Button["test", dd = False; Pause[1]; Abort[]; Pause[10]; dd = True, Enabled -> Dynamic[dd], Method -> "Queued"]
Out[32]=

Possible Issues (4) 

The appearance of the button will not update when using Method"Preemptive":

In[46]:=
ResourceFunction["DynamicEnabledButton"]["test", Pause[5], Method -> "Preemptive"]
Out[46]=

In DynamicEnabledButton[Dynamic[x],label,action], the symbol x must not be a DynamicModule symbol in order for the enabled state to update:

In[47]:=
DynamicModule[{x}, ResourceFunction["DynamicEnabledButton"][Dynamic[x], "test", Pause[5]]]
Out[47]=

This is also true for Button:

In[48]:=
DynamicModule[{x = True},
 Button["test", x = False; Pause[5]; x = True, Enabled -> Dynamic[x], Method -> "Queued"]
 ]
Out[48]=

The dynamic binding must have just one argument and no options:

In[49]:=
ResourceFunction["DynamicEnabledButton"][
 Dynamic[symbol, SingleEvaluation -> True], "label", Pause[5]]
Out[49]=

If the binding is unable to be set, no errors will be triggered when the button is created:

In[50]:=
protected = True;
Protect[protected]
Out[48]=
In[51]:=
ResourceFunction["DynamicEnabledButton"][Dynamic[protected], "label", Print["running"]; Pause[5]; Print["done"]]
Out[51]=

Messages will appear when the button code executes and the state of the button will fail to update:

In[52]:=
ResourceFunction["DynamicEnabledButton"][Dynamic[protected], "label", Print["running"]; Pause[5]; Print["done"]]
Out[52]=

Neat Examples (1) 

Watch Birdnardo do a flip:

In[53]:=
ResourceFunction["DynamicEnabledButton"][
 Dynamic[bs],
 PaneSelector[{
   True -> ResourceFunction["BirdSay"]["Click to watch me do a flip"],
   False -> ResourceFunction["BirdSay"]["Oh no! I'm stuck!", {Top, Right}]
   },
  Dynamic[bs]
  ],
 Pause[5]
 ]
Out[53]=

Version History

  • 1.0.0 – 08 November 2019

Related Resources

License Information