Skip to content

UI System

The UI system lets applications define interactive dashboards that are rendered in the Doover web portal. Instead of writing frontend code, you declare UI elements as Python class attributes, and the framework generates the portal interface automatically.

UI elements fall into three categories:

CategoryPurposeExamples
Display elementsRead-only values updated by the deviceNumericVariable, TextVariable, BooleanVariable, Multiplot
Interactive elementsUser inputs that trigger actions on the deviceButton, Switch, Select, Slider, FloatInput, TextInput
ContainersStructural grouping of other elementsSubmodule, TabContainer, RemoteComponent

Defining a UI

UIs are defined declaratively by subclassing UI and assigning elements as class attributes.

from pydoover.ui import (
    UI, NumericVariable, TextVariable, Button,
    Submodule, Colour,
)
from pydoover.tags import Tags, Number, Boolean

class MyTags(Tags):
    temperature = Number(default=0.0)
    pump_running = Boolean(default=False)

class MyUI(UI):
    temp_display = NumericVariable(
        "Temperature",
        value=MyTags.temperature,
        units="C",
        precision=1,
    )
    status = TextVariable("Status", value="Online")
    restart_btn = Button("Restart")

Each element is instantiated once at class definition time. When the framework creates a UI instance, it deep-copies these templates so each instance has its own mutable copy.

Connecting UI to an Application

Assign your UI subclass to the ui_cls class attribute on your application. The framework wires it up during startup.

from pydoover.docker import Application

class MyApp(Application):
    tags_cls = MyTags
    ui_cls = MyUI

    async def setup(self):
        pass

    async def main_loop(self):
        await self.tags.temperature.set(22.5)

The framework instantiates MyUI, binds any tag references to the running tags instance, collects all interactive elements for command routing, and publishes the serialised schema to the portal.

Tag Binding

UI elements display live values by binding to tags. Pass the tag definition directly as the value parameter:

temp_display = NumericVariable(
    "Temperature",
    value=MyTags.temperature,
)

The framework automatically resolves the tag to a lookup string like $tag.app().temperature:number:0.0, which the portal evaluates against the live tag channel to display the current value.

When you don't have a Tags class definition (e.g., referencing a tag by name from a different app), use tag_ref() with a string:

from pydoover.ui import tag_ref

temp_display = NumericVariable(
    "Temperature",
    value=tag_ref("temperature", tag_type="number"),
)

See Declarative UI for details.

Config References

UI properties can also reference configuration values using the $config.app().KEY syntax. This is used internally for properties like display_name, hidden, and position.

class MyUI(UI, display_name="$config.app().APP_DISPLAY_NAME"):
    pass

When the schema is resolved, these references are replaced with the actual config values.

Element Hierarchy

All UI elements inherit from the Element base class, which provides common properties shared across every element type.

PropertyDescription
display_nameLabel shown in the portal
nameInternal key (auto-generated from display_name if not set)
positionOrdering within the parent container
hiddenWhether the element is hidden (can be a tag reference for dynamic visibility)
iconIcon identifier displayed alongside the element
colourColour applied to the element
unitsUnits label (e.g., "C", "kPa")
help_strTooltip help text
conditionsConditions under which the element is displayed

Next Steps

  • Declarative UI -- the UI base class, tag binding, and schema generation
  • Display Elements -- read-only variables, charts, and status displays
  • Interactive Elements -- buttons, sliders, inputs, and other user controls
  • Containers -- structural grouping with submodules, tabs, and remote components
  • Styling -- colours, icons, widgets, and conditional display
  • UI Commands Manager -- interaction handling and the @handler() decorator
  • Widgets -- building custom JavaScript micro-frontends with module federation