Overview
Let's start with an example:
Broadly, this script proceeds as follows:
- Tells the IDE to display an interactive user interface and its current configuration (the slider returned by the
renderfunction) - Waits for messages from the IDE informing it how the UI has changed (eg: when the user moves the slider to a new position)
- Reacts to that change by updating the
ui.Stateinstance and, potentially, re-invokingrender.
The steps above repeat in a loop until the script is terminated.
Let's dig into each of these steps, starting with the ui.run function.
ui.run
The
runfunction has a single required argument - a function (renderer) that returns the current user interface. In the example above, this would be therenderfunction that returns a singleSlideras the UI.All other positional and keyword arguments are bound to the
rendererfunction (equivalent tofunctools.partial). These are useful for passing in expensive resources that you don't want to re-create every time the user interfaces changes (theCanvasinstance in our example).
The
rendererfunction is invoked at the beginning to render the initial UI. Therunfunction then enters an infinite loop receiving and processing UI events (like slider drags, button clicks, etc).The rendered UI is completely dynamic, and some events (like the slider drag in our example) can trigger a re-rendering by re-invoking the
rendererfunction. You can also perform other updates here that are a function of the UI state (like the canvas drawing in our example).
ui.State
You might have noticed the ui.State instance in the example earlier:
It provides a few key functionalities:
It acts as a simple namespace for grouping all mutable state for the user interface.
state = ui.State( learning_rate=0.03, warmup=True ) # Simple access to the attributes assert type(state.warmup) == bool # Mutable state.learning_rate = 0.2The special
bindaccessor provides a way for widgets (likeui.Slider) to "bind" their value to a particular state attribute.ui.Slider( # While state.radius is just an int or a float, state.bind.radius # is a special "Binding" instance that allows for both reading and # updating the value of state.radius value=state.bind.radius, range=(10, 100) )The
valueargument forui.Slidercan accept either a numeric type (intorfloat), or aBindinginstance that acts as an indirection to both fetching and mutating a numeric value. This essentially allows the IDE to auto-updatestate.radiuswhenever the slider is changed.Mutating any attributes of a
ui.Stateinstance (including via binding auto-updates) automatically triggers a UI re-rendering. In our slider example, this is how therenderfunction is invoked whenever the user drags the radius slider.