CITE THIS NOTEBOOK: Wolfram R&D LIVE: Principles of Dynamic Interfaces by Lou D'Andria. Wolfram Community JAN 12 2023.
The Wolfram Language includes a rich set of primitives for building user interfaces which look and behave in desirable ways. But the best way to combine those primitives to satisfy a particular interface goal is not always clear. In this talk, we’ll discuss how to utilize Dynamic and other primitives to build responsive and maintainable interfaces. We’ll pay special attention to progress indication and issues of synchronicity.
Talk Structure
Talk Structure
We’ll outline some key principles to keep in mind while developing user interfaces in the Wolfram Language, and along the way highlight some essential concepts and key techniques that should be in every interface programmer’s toolkit.
Principles
Principles
• The Principle of Programmatic Generation
• The Principle of Programmatic Generation
• The Principle of Minimal Dynamics
• The Principle of Minimal Dynamics
• The Principle of Maximum Encapsulation
• The Principle of Maximum Encapsulation
• The Principle of Evaluation Blocking
• The Principle of Evaluation Blocking
The Principle of Programmatic Generation
The Principle of Programmatic Generation
Every interface should be generated by a program.
The language primitives
The language primitives
Dynamics and controls
Dynamics and controls
Dynamic, DynamicWrapper, DynamicModule, …
Button, Checkbox, Slider, IntervalSlider, …
LocatorPane, EventHandler, …
Layout and styling
Layout and styling
Style, Panel, Framed, …
Grid, Row, Column, …
PaneSelector, Overlay, …
TabView, TableView, …
Deployment
Deployment
Deploy, CloudDeploy, …
CreateDocument, CreatePalette, CreateDialog, …
DockedCells, AttachCell, …
Generated on demand
Generated on demand
Manipulate
SystemInformation
WolframAlpha[]
Audio, Video, …
Iconize, Dataset, Summary boxes, …
Pre-generated
Pre-generated
Palettes: About Box, Typesetting, Assistants, …
Dialogs: Preferences, Cloud Open / Save / Publish, Headers and Footers, …
Toolbars: Help Viewer, Presenter Tools, Multipurpose Bar, Wolfram|Alpha Notebook Edition, …
Contextual Interfaces: Drawing Tools, Image Editors, …
Dynamic wants to be current
Dynamic wants to be current
The goal of Dynamic[expr] is to display expr as it is right now. And now. And now. You’re (almost) always looking at now.
Dynamic[x]
0
x=0.5;
Slider[Dynamic[x]]
When a Dynamic is evaluated for display, the kernel computes a full dependency graph.
Any change to any variable in that dependency graph triggers another update.
f[x_]:=g[x]+a;g[x_]:=h[x]+b;h[x_]:=x^2;
Dynamic[f[3]]
f[3]
a=2
2
g[x_]:=h[x]+2b
Dynamic really wants to be current
Dynamic really wants to be current
Dynamics that update too frequently are just as useless as dynamics that update too rarely.
When updates are too frequent
When updates are too frequent
Restrict the scope of the Dynamic, either structurally, or via Refresh, TrackedSymbols, ControlActive, etc.
c=0;Dynamic[++c]
c=0;Dynamic[++c,TrackedSymbols{},UpdateInterval1]
When updates are too rare
When updates are too rare
Make sure the symbols you think should be triggering an update actually are involved in the Dynamic.
Dynamic[Now,TrackedSymbols{c}]
Slider[Dynamic[c]]
Or, you can always use UpdateInterval.
Dynamic[Now]
Dynamic[Now,UpdateInterval1]
Dynamic wants to be seen
Dynamic wants to be seen
Dynamic does nothing unless it is typeset and rendered by the front end.
Dynamic[var=456];
This doesn’t set the value of var.
Dynamic[var]
var
Neither does this.
CreateDocument[CellGroup[{TextCell["example section","Section"],ExpressionCell[Dynamic[var=123],"Output"]},Closed]];
Advanced tip: Avoid measuring unseen content
Advanced tip: Avoid measuring unseen content
There are constructs (PaneSelector, Mouseover, TabView, OpenerView, MenuView, …) that hold several expressions, and only display one at a time.
TabView[99^{11,55,99}]
1
2
3
But the front end needs to render every pane up front when ImageSize is All.
TabView[{1Dynamic[Echo["first pane"]],2Dynamic[Echo["second pane"]]},Dynamic[pane]]
1
2
»
second pane
»
second pane
»
first pane
»
first pane
»
inner:0
Use ImageSize Automatic to avoid this.
TabView[{1Dynamic[Echo["first pane"]],2Dynamic[Echo["second pane"]]},Dynamic[pane],ImageSizeAutomatic]
The Principle of Minimal Dynamics
The Principle of Minimal Dynamics
A Dynamic should never encompass unnecessary evaluation.
What is “unnecessary”?
What is “unnecessary”?
Typical example of minimizing the scope of a Dynamic.
Wrap Dynamic around everything
Wrap Dynamic around everything
Wrap Dynamic around fewer things
Wrap Dynamic around fewer things
Wrap Dynamic around one thing
Wrap Dynamic around one thing
Nesting Dynamics
Nesting Dynamics
When nested, an inner Dynamic can update independently of whether outer Dynamics have been triggered. However, an outer Dynamic always force inner Dynamics to re-evaluate.
Avoiding Dynamics
Avoiding Dynamics
Sometimes, it’s worth asking if Dynamics are really needed at all for a given task.
Notebook Programming
Notebook Programming
Notebooks are structured documents whose contents can be manipulated by Wolfram Language code.
◼
Buttons can overwrite themselves via EvaluationBox / BoxObject.
◼
Construct a new notebook containing all cells from this notebook with a given cell tag.
◼
Any other document gymnastics you can imagine…
The Principle of Maximum Encapsulation
The Principle of Maximum Encapsulation
Interfaces should require no unnecessary initialization and produce no unnecessary side effects.
Two different solutions:
◼
Deploy the result of running the code. (About Box, Preferences, …)
◼
Deploy the code itself, and run it on the user’s machine. (Manipulate, SystemInformation, …)
Use DynamicModule
Use DynamicModule
Scoping construct
Scoping construct
Local variables in one instance of your structure are automatically isolated from any other instances.
This is what Manipulate uses.
Saved state
Saved state
Variable values will be preserved across sessions.
Initialization
Initialization
Initialization code can be included manually, or automatically, or both.
SaveDefinitions
SaveDefinitions
SaveDefinitions is an option to Manipulate and DynamicModule which embeds a copy of all relevant definitions into the output (via FullDefinition).
Passing variables by reference
Passing variables by reference
When producing interfaces via program, it can quickly become awkward to develop everything inside of a single DynamicModule.
Here’s a more maintainable and extensible architecture, which doesn’t quite work…
Why not?
Why not?
Each utility is only getting access to the value of the local symbol, not a (pre-evaluated) reference to the local symbol.
Passing variables by reference
Passing variables by reference
This fixed code relies on the fact that Dynamic doesn’t evaluate its first argument.
Using holding attributes would also allow passing variables by reference.
The Principle of Evaluation Blocking
The Principle of Evaluation Blocking
Motivating example
Motivating example
Analogy
Analogy
Imagine you’re in a grocery store with only two checkout lanes open:
◼
an express lane (which is not self-serve) if you have 6-or-fewer items, and
◼
the main checkout lane for everyone else.
Personnel
Personnel
Now imagine there's only one person working at the store, Cornell, who is staffing both lanes according to some rules:
◼
If there is no customer in the express lane, Cornell takes care of customers in the main checkout lane.
◼
If there are customers in the express lane, Cornell pauses work on the main checkout lane long enough to check them out.
◼
If a customer in the express lane has more than 6 items, Cornell stops that checkout without finishing.
Dynamic
Dynamic
◼
By default, Dynamic[expr] is “synchronous” -- it will only run for about 6 seconds before timing out.
◼
The front end stops doing everything else when waiting for a synchronous dynamic.
◼
Dynamic[expr, SynchronousUpdating -> False] can take as long as it wants, without the front end stopping other processing.
◼
SynchronousUpdating -> False uses the same evaluation queue as shift-enter.
Terminology
Terminology
Take home
Take home
If you have more than six items, stay out of the express lane!
Let slow things be slow
Let slow things be slow
Any evaluation that might take more than a few seconds
Any evaluation that might take more than a few seconds
Asking the user for blocking input
Asking the user for blocking input
Getting data from a network
Getting data from a network
Rendering a complicated graphic or box structure
Rendering a complicated graphic or box structure
Assembling an extensive report
Assembling an extensive report
Showing progress
Showing progress
What to do when you have a long running evaluation that you want to keep tabs on?
Monitor
Monitor
See also: PrintTemporary, StatusArea, Annotation, …
What’s really happening here?
What’s really happening here?
Synchronous dynamics can update during long-running queued evaluations.
Note that synchronous dynamics will not update during preemptive evaluations.
More Button examples
More Button examples
◼
Determinate progress indicators:
◼
Indeterminate progress indicators:
◼
However, this sample code is missing one important feature…
Fixed Button example
Fixed Button example
It is especially important for interfaces to handle aborts without getting totally confused.
EventHandler Method
EventHandler Method
EventHandler also supports Method “Queued”.
And because Button uses EventHandler in Graphics, we can do things like this:
Synchronous dynamics can trigger long evaluations
Synchronous dynamics can trigger long evaluations
Not many controls can directly run queued evaluations. Button, EventHandler, ActionMenu. That’s about it.
Even though you can’t use a synchronous control to perform a long running evaluation, you can use a synchronous control to kick off a long-running evaluation, by having it trigger an asynchronous dynamic.
Example
Example
ControlActive
ControlActive
PerformanceGoal
PerformanceGoal
ControlActive
ControlActive
When ControlActive appears in a dynamic that has SynchronousUpdating set to Automatic, then the draft renderings (1st argument) are done as preemptive evaluations, and the final rendering (2nd argument) uses a queued evaluation.
Advanced example
Advanced example
PreemptProtect
PreemptProtect
Todd Gayley’s Essay on preemptive evaluation
Read this to understand details of preemption, PreemptProtect, and preemption-safe code.
Don’t be afraid to use the slow lane
Don’t be afraid to use the slow lane
It’s there for a reason. There’s no shame in using it.
Don’t change the DynamicEvaluationTimeout!
Lots more examples…
Lots more examples…
Wolfram Function Repository
Wolfram Function Repository
And the source code for every one of these functions is freely available!
Just a few examples of…
◼
Controls
◼
Utilities
◼
Complete interfaces
Summary
Summary
The Principle of Programmatic Generation
The Principle of Programmatic Generation
Every interface should be generated by a program.
The Principle of Minimal Dynamics
The Principle of Minimal Dynamics
A Dynamic should never encompass unnecessary evaluation.
The Principle of Maximum Encapsulation
The Principle of Maximum Encapsulation
Interfaces should require no unnecessary initialization and produce no unnecessary side effects.
The Principle of Evaluation Blocking
The Principle of Evaluation Blocking
Only queued evaluations should ever block for user input. Most time-consuming evaluations should also be queued.