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
| Parameter | Type | Default | Description |
|---|---|---|---|
label | str | None | Descriptive label for the range |
min_val | int or float | None | Minimum value of the range |
max_val | int or float | None | Maximum value of the range |
colour | Colour | Colour.blue | Color for visual indication |
show_on_graph | bool | True | Whether 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
| Parameter | Type | Description |
|---|---|---|
name | str | Internal identifier (value sent to callbacks) |
display_name | str | Human-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
| Color | Constant | Hex Equivalent |
|---|---|---|
| Blue | Colour.blue | Standard blue |
| Yellow | Colour.yellow | Standard yellow |
| Red | Colour.red | Standard red |
| Green | Colour.green | Standard green |
| Magenta | Colour.magenta | Standard magenta |
| Lime Green | Colour.limegreen | #32CD32 |
| Tomato | Colour.tomato | #FF6347 |
| Orange | Colour.orange | Standard orange |
| Purple | Colour.purple | Standard purple |
| Grey | Colour.grey | Standard grey |
You can also use any valid HTML color name or hex code through Colour.from_string() or Colour.from_hex().
Best Practices
Use consistent colors across your application for similar concepts (e.g., always use green for "OK", red for "error")
Define meaningful ranges that help users quickly understand value states
Choose appropriate widgets based on the data:
Widget.radialfor values within a circular range (gauges, dials)Widget.linearfor values on a linear scale (progress bars, levels)
Label your ranges clearly so users understand what each zone means
Use
show_on_graphto control graph clutter while maintaining display indicatorsCreate descriptive option names that clearly indicate what each choice does
See Also
- Display Elements - Using ranges and widgets with variables
- Interactive Elements - Using colors and options with controls
- Containers - Organizing styled elements
- UIManager - Managing the complete UI