Skip to content

Styling

The pydoover UI system provides styling utilities to customize the appearance of your device interface. These include colors, value ranges for visual indicators, widget types for display, and options for selection elements.

Import

from pydoover.ui import (
    Colour,
    Range,
    Widget,
    Option,
)

Colour

The Colour class provides predefined color constants and methods for creating custom colors. Colors can be used throughout the UI for buttons, ranges, indicators, and other visual elements.

Predefined Colors

from pydoover.ui import Colour

# Available color constants
Colour.blue      # "blue"
Colour.yellow    # "yellow"
Colour.red       # "red"
Colour.green     # "green"
Colour.magenta   # "magenta"
Colour.limegreen # "limegreen"
Colour.tomato    # "tomato"
Colour.orange    # "orange"
Colour.purple    # "purple"
Colour.grey      # "grey"

Custom Colors

from_hex()

Create a color from a hex string.

custom_blue = Colour.from_hex("#3498db")
custom_dark = Colour.from_hex("#2c3e50")

from_string()

Use any HTML color name.

coral = Colour.from_string("coral")
navy = Colour.from_string("navy")
teal = Colour.from_string("teal")

Usage Examples

from pydoover.ui import Action, Colour

# Using predefined colors
restart_button = Action(
    name="restart",
    display_name="Restart",
    colour=Colour.red
)

save_button = Action(
    name="save",
    display_name="Save",
    colour=Colour.green
)

# Using custom hex color
custom_button = Action(
    name="custom",
    display_name="Custom Action",
    colour=Colour.from_hex("#9b59b6")
)

# Using HTML color name
coral_button = Action(
    name="coral_action",
    display_name="Coral Action",
    colour=Colour.from_string("coral")
)

Range

Ranges define value boundaries with associated visual properties. They are used with NumericVariable elements to provide visual feedback about value states.

Constructor

Range(
    label: str = None,
    min_val: Union[int, float] = None,
    max_val: Union[int, float] = None,
    colour: Colour = Colour.blue,
    show_on_graph: bool = True,
)

Parameters

ParameterTypeDefaultDescription
labelstrNoneDescriptive label for the range
min_valint or floatNoneMinimum value of the range
max_valint or floatNoneMaximum value of the range
colourColourColour.blueColor for visual indication
show_on_graphboolTrueWhether to display on graphs

Basic Example

from pydoover.ui import NumericVariable, Range, Colour

temperature = NumericVariable(
    name="temperature",
    display_name="Temperature (C)",
    curr_val=25.0,
    ranges=[
        Range(label="Cold", min_val=0, max_val=15, colour=Colour.blue),
        Range(label="Normal", min_val=15, max_val=28, colour=Colour.green),
        Range(label="Hot", min_val=28, max_val=50, colour=Colour.red),
    ]
)

Adding Ranges Dynamically

# Create variable without ranges
battery = NumericVariable(
    name="battery",
    display_name="Battery Level",
    curr_val=75
)

# Add ranges later
battery.add_ranges(
    Range("Critical", 0, 10, Colour.red),
    Range("Low", 10, 30, Colour.orange),
    Range("Good", 30, 80, Colour.green),
    Range("Full", 80, 100, Colour.blue),
)

Graph Display Control

# Range visible on graphs
visible_range = Range(
    label="Target Zone",
    min_val=20,
    max_val=25,
    colour=Colour.green,
    show_on_graph=True  # Will appear on time-series graphs
)

# Range for display only (not on graphs)
display_only = Range(
    label="Warning",
    min_val=25,
    max_val=30,
    colour=Colour.orange,
    show_on_graph=False  # Hidden from graphs
)

Converting to/from Dictionary

# Convert to dictionary (for serialization)
range_dict = my_range.to_dict()
# {'min': 0, 'max': 100, 'colour': 'green', 'show_on_graph': True, 'label': 'Normal'}

# Create from dictionary
from_dict = Range.from_dict({
    "label": "Safe Zone",
    "min": 10,
    "max": 90,
    "colour": "green",
    "show_on_graph": True
})

Widget

The Widget class defines display widget types for numeric variables.

Available Widgets

from pydoover.ui import Widget

# Linear gauge (horizontal bar)
Widget.linear  # "linearGauge"

# Radial gauge (circular dial)
Widget.radial  # "radialGauge"

Custom Widget Types

# Use from_string for custom widget identifiers
custom_widget = Widget.from_string("customGauge")

Usage with NumericVariable

from pydoover.ui import NumericVariable, Widget, Range, Colour

# Linear gauge display
fuel_level = NumericVariable(
    name="fuel",
    display_name="Fuel Level",
    curr_val=65,
    form=Widget.linear,
    ranges=[
        Range("Empty", 0, 10, Colour.red),
        Range("Low", 10, 25, Colour.orange),
        Range("Normal", 25, 75, Colour.green),
        Range("Full", 75, 100, Colour.blue),
    ]
)

# Radial gauge display
speed = NumericVariable(
    name="speed",
    display_name="Speed (km/h)",
    curr_val=60,
    precision=0,
    form=Widget.radial,
    ranges=[
        Range("Slow", 0, 30, Colour.green),
        Range("Normal", 30, 80, Colour.blue),
        Range("Fast", 80, 120, Colour.orange),
        Range("Danger", 120, 200, Colour.red),
    ]
)

Option

Options define selectable choices for StateCommand elements. Each option has an internal name and a display label.

Constructor

Option(name: str, display_name: str)

Parameters

ParameterTypeDescription
namestrInternal identifier (value sent to callbacks)
display_namestrHuman-readable label shown in UI

Basic Example

from pydoover.ui import StateCommand, Option

mode_command = StateCommand(
    name="operating_mode",
    display_name="Operating Mode",
    user_options=[
        Option("auto", "Automatic"),
        Option("manual", "Manual"),
        Option("standby", "Standby"),
    ]
)

def on_mode_change(new_value):
    # new_value will be "auto", "manual", or "standby"
    print(f"Mode changed to: {new_value}")

mode_command.callback = on_mode_change

Adding Options Dynamically

# Create command with initial options
fan_speed = StateCommand(
    name="fan_speed",
    display_name="Fan Speed",
    user_options=[
        Option("off", "Off"),
        Option("low", "Low"),
    ]
)

# Add more options later
fan_speed.add_user_options(
    Option("medium", "Medium"),
    Option("high", "High"),
    Option("turbo", "Turbo")
)

Converting to/from Dictionary

# Convert to dictionary
option_dict = my_option.to_dict()
# {'name': 'auto', 'displayString': 'Automatic', 'type': 'uiElement'}

# Create from dictionary
from_dict = Option.from_dict({
    "name": "eco",
    "display_str": "Eco Mode"
})

Complete Styling Example

from pydoover.ui import (
    UIManager,
    Submodule,
    NumericVariable,
    StateCommand,
    Action,
    Slider,
    Colour,
    Range,
    Widget,
    Option,
)

ui = UIManager(app_key="styled_device", client=my_client)

# Create styled variables with ranges and widgets
temperature = NumericVariable(
    name="temperature",
    display_name="Temperature",
    curr_val=22.5,
    precision=1,
    form=Widget.radial,
    ranges=[
        Range(label="Cold", min_val=-10, max_val=15, colour=Colour.blue),
        Range(label="Comfortable", min_val=15, max_val=26, colour=Colour.green),
        Range(label="Warm", min_val=26, max_val=32, colour=Colour.orange),
        Range(label="Hot", min_val=32, max_val=50, colour=Colour.red),
    ]
)

pressure = NumericVariable(
    name="pressure",
    display_name="Pressure (bar)",
    curr_val=3.2,
    precision=2,
    form=Widget.linear,
    ranges=[
        Range(label="Low", min_val=0, max_val=2, colour=Colour.yellow),
        Range(label="Normal", min_val=2, max_val=5, colour=Colour.green),
        Range(label="High", min_val=5, max_val=10, colour=Colour.red, show_on_graph=True),
    ]
)

# Create styled state command
mode_selector = StateCommand(
    name="mode",
    display_name="Operating Mode",
    user_options=[
        Option("standby", "Standby"),
        Option("normal", "Normal Operation"),
        Option("eco", "Eco Mode"),
        Option("boost", "Boost Mode"),
    ]
)

# Create styled action buttons
start_button = Action(
    name="start",
    display_name="Start System",
    colour=Colour.green,
    requires_confirm=False
)

stop_button = Action(
    name="stop",
    display_name="Emergency Stop",
    colour=Colour.red,
    requires_confirm=True
)

reset_button = Action(
    name="reset",
    display_name="Reset to Defaults",
    colour=Colour.from_hex("#f39c12"),  # Custom orange
    requires_confirm=True
)

# Create styled slider
setpoint_slider = Slider(
    name="setpoint",
    display_name="Temperature Setpoint",
    min_val=15,
    max_val=30,
    step_size=0.5,
    dual_slider=False,
    colours="blue,green,red"  # Gradient colors
)

# Organize into submodules
readings_module = Submodule(
    name="readings",
    display_name="Sensor Readings",
    status="All Normal",
    children=[temperature, pressure]
)

controls_module = Submodule(
    name="controls",
    display_name="System Controls",
    children=[
        mode_selector,
        setpoint_slider,
        start_button,
        stop_button,
        reset_button,
    ]
)

# Add to UI
ui.add_children(readings_module, controls_module)
ui.push()

Color Reference

ColorConstantHex Equivalent
BlueColour.blueStandard blue
YellowColour.yellowStandard yellow
RedColour.redStandard red
GreenColour.greenStandard green
MagentaColour.magentaStandard magenta
Lime GreenColour.limegreen#32CD32
TomatoColour.tomato#FF6347
OrangeColour.orangeStandard orange
PurpleColour.purpleStandard purple
GreyColour.greyStandard grey

You can also use any valid HTML color name or hex code through Colour.from_string() or Colour.from_hex().

Best Practices

  1. Use consistent colors across your application for similar concepts (e.g., always use green for "OK", red for "error")

  2. Define meaningful ranges that help users quickly understand value states

  3. Choose appropriate widgets based on the data:

    • Widget.radial for values within a circular range (gauges, dials)
    • Widget.linear for values on a linear scale (progress bars, levels)
  4. Label your ranges clearly so users understand what each zone means

  5. Use show_on_graph to control graph clutter while maintaining display indicators

  6. Create descriptive option names that clearly indicate what each choice does

See Also