11 KiB
dpg-gauges Feature Specification
This document defines the public behaviour and API contract for dpg-gauges.
dpg-gauges is a Python package providing easy-to-use, high-performance Dear PyGui gauge
widgets for vehicle telemetry, dyno displays, dashboards, and general numeric/status data.
It should be usable as:
import dpg_gauges as dpgg
The first release must be practical to use quickly, but still built cleanly enough to avoid a rewrite after the first real dashboard.
Product goals
- Provide a healthy set of common gauges out of the box.
- Feel natural in Dear PyGui code and layout containers.
- Support high-rate telemetry updates from the GUI thread without flicker.
- Smooth or animate rapidly changing values so humans can read them.
- Keep public API stable once introduced.
- Be configurable enough for vehicle and dyno dashboards without forcing users to subclass.
- Remain display-only for the first release.
Non-goals for the first release
Do not implement these in the first release:
- Non-Dear PyGui backends
- Arbitrary image/icon rendering inside gauges
- Historical traces or sparklines
- Gauge-driven threshold callbacks or alarms
- Editable knobs/sliders
- Complex custom shader/GPU renderers
- Theme engines that replace Dear PyGui theming
- Background-thread Dear PyGui updates
Core requirements
1. Dear PyGui-native usage
Gauge widgets should behave like normal Dear PyGui widgets as much as possible:
with dpgg.analog_gauge(tag="rpm", label="RPM", min_value=0, max_value=8000, unit="rpm"):
pass
dpgg.set_value("rpm", 3250)
Creation functions are context managers so users can write layouts naturally:
with dpg.window(label="Dashboard"):
with dpgg.gauge_panel(label="Engine"):
dpgg.analog_gauge(tag="rpm", label="RPM", max_value=8000, redline_start=6500)
dpgg.digital_gauge(tag="speed", label="Speed", unit="km/h")
2. GUI-thread update contract
Dear PyGui item operations must happen on the GUI thread. Public creation functions are GUI-thread-only. Runtime updates are also intended for the GUI thread unless a function is explicitly documented otherwise.
High-rate data may be produced in worker threads, but the app should marshal the latest values to
the GUI thread before calling dpgg.set_value(...) or dpgg.update_gauge(...).
The package should still be robust against high-rate GUI-thread calls:
- value updates should be cheap
- repeated value updates may coalesce to the latest value before draw
- animation/smoothing should be optional
- static gauge geometry should be reused where practical
- dynamic draw layers should be rebuilt without flickering through empty states
3. Gauge catalogue
Required MVP gauge types:
digital_gauge: large numeric readout with label, unit, precision, optional sign, and state coloranalog_gauge: circular or arc dial with ticks, labels, needle, thresholds, target markers, and redlinebar_gauge: horizontal or vertical fill bar with thresholds and optional target markerlevel_gauge: tank/level style gauge, useful for fuel, battery, fluids, and percentagesstatus_light: LED/status indicator with text, colors, blink/pulse option, and simple statessegmented_gauge: segmented bar or LED strip, useful for RPM shift lights or battery cellscompass_gauge: circular heading display for degrees, direction labels, and optional bearing markerthermometer_gauge: vertical temperature-style display with colored bandsbattery_gauge: battery-specific gauge with percentage/value display and charge/discharge state stylingmulti_value_gauge: compact grouped numeric readout for related values, such as AFR/lambda/fuel pressure
Nice-to-have after MVP stability:
shift_light_gauge: specialized segmented RPM/shift light stripdelta_gauge: positive/negative centered bar for lap delta, trim, or correction valuesmini_dial_gauge: small dial optimized for dense dashboardsradial_progress_gauge: donut-style progress/value indicator
4. Configuration model
Common gauge configuration should cover most needs without requiring later public API churn.
Shared parameters should include where applicable:
tag: str | int | None = None
label: str | None = None
value: float | int | bool | str | None = None
min_value: float = 0.0
max_value: float = 100.0
unit: str = ""
precision: int = 0
width: int = 0
height: int = 0
autosize_x: bool = False
autosize_y: bool = False
show: bool = True
show_label: bool = True
show_value: bool = True
show_unit: bool = True
tooltip: str | None = None
callback: Callable | None = None
user_data: Any = None
theme: str | GaugeTheme | None = None
style: GaugeStyle | None = None
animation: AnimationConfig | bool | None = None
smoothing: SmoothingConfig | bool | None = None
clamp: bool = True
format_value: Callable[[float], str] | None = None
Visual configuration should include where applicable:
background_color: Color | None = None
frame_color: Color | None = None
value_color: Color | None = None
text_color: Color | None = None
muted_text_color: Color | None = None
border_color: Color | None = None
needle_color: Color | None = None
tick_color: Color | None = None
thresholds: Sequence[ThresholdBand] | None = None
markers: Sequence[GaugeMarker] | None = None
redline_start: float | None = None
redline_color: Color = (255, 45, 45, 255)
Analog-specific configuration should include:
start_angle: float = 225.0
end_angle: float = -45.0
major_ticks: int = 8
minor_ticks: int = 4
show_tick_labels: bool = True
needle_length: float = 0.82
needle_width: float = 3.0
center_cap_radius: float = 7.0
arc_width: float = 8.0
maintain_aspect: bool = True
Bar/level-specific configuration should include:
orientation: Literal["horizontal", "vertical"] = "horizontal"
fill_mode: Literal["clamp", "centered"] = "clamp"
corner_radius: float = 4.0
show_scale: bool = True
stretch: bool = True
Status-specific configuration should include:
state: str | bool | int = False
states: Mapping[Any, StatusState] | None = None
blink: bool = False
pulse: bool = False
5. Thresholds and markers
Thresholds are visual bands only. They must not trigger callbacks or business logic.
@dataclass(frozen=True)
class ThresholdBand:
start: float
end: float
color: Color
label: str | None = None
@dataclass(frozen=True)
class GaugeMarker:
value: float
color: Color
label: str | None = None
thickness: float = 2.0
Rules:
- Thresholds and markers are clamped to the gauge range for drawing.
- Invalid threshold ordering raises during configuration.
- Overlapping thresholds are allowed and draw in given order.
redline_startis convenience syntax for a threshold fromredline_starttomax_value.
6. Value behaviour
- Numeric values clamp to
min_valueandmax_valueby default. - Displayed value should match the clamped visual value.
- Non-finite numeric values should enter an invalid display state rather than crash.
Noneshould show an empty/unknown state if supported by the gauge.- Precision and formatting must be consistent across digital text and visual geometry.
7. Animation and smoothing
Animation should be optional per gauge and globally configurable.
@dataclass(frozen=True)
class AnimationConfig:
enabled: bool = True
duration: float = 0.15
easing: str = "linear"
@dataclass(frozen=True)
class SmoothingConfig:
enabled: bool = False
alpha: float = 0.35
max_step: float | None = None
Rules:
- Animation changes what is drawn, not the logical target value.
- Smoothing is for readability, not data processing.
- Users can disable animation for exact instantaneous displays.
- High-rate updates should converge toward the newest target value without queue buildup.
8. Layout and sizing
Gauges must support common Dear PyGui size behaviour:
width=0andheight=0preserve Dear PyGui default behaviour.width=-1fills available width.height=-1fills available height where Dear PyGui allows it.- Positive dimensions are respected.
autosize_xandautosize_yare supported where practical.- Analog/compass/radial gauges maintain square aspect by default.
- Digital/bar/segmented gauges can stretch horizontally.
- Widgets should work in windows, groups, child windows, tabs, tables, and hidden/show-later layouts.
9. Dashboard helpers
Provide light helpers, not a full layout engine:
with dpgg.gauge_panel(label="Engine", width=-1):
...
with dpgg.gauge_grid(columns=3, width=-1):
...
Dashboard helpers should use normal Dear PyGui containers internally and not block users from using native Dear PyGui layouts directly.
10. Public API
Required exports:
configure
GaugeTheme
GaugeStyle
AnimationConfig
SmoothingConfig
ThresholdBand
GaugeMarker
StatusState
GaugeValue
GaugeStats
analog_gauge
digital_gauge
bar_gauge
level_gauge
status_light
segmented_gauge
compass_gauge
thermometer_gauge
battery_gauge
multi_value_gauge
gauge_panel
gauge_grid
set_value
get_value
update_gauge
configure_gauge
set_thresholds
set_markers
set_show
delete_gauge
get_gauge_debug_state
11. Global configuration
dpgg.configure(
*,
default_theme: str | GaugeTheme | None = None,
animation: AnimationConfig | bool | None = None,
smoothing: SmoothingConfig | bool | None = None,
default_width: int | None = None,
default_height: int | None = None,
frame_rate_limit: float | None = None,
debug: bool = False,
) -> None
12. Runtime API
dpgg.set_value(tag, value: GaugeValue) -> None
dpgg.get_value(tag) -> GaugeValue
dpgg.update_gauge(
tag,
*,
value: GaugeValue | None = None,
label: str | None = None,
unit: str | None = None,
min_value: float | None = None,
max_value: float | None = None,
thresholds: Sequence[ThresholdBand] | None = None,
markers: Sequence[GaugeMarker] | None = None,
show: bool | None = None,
) -> None
dpgg.configure_gauge(tag, **config) -> None
dpgg.set_thresholds(tag, thresholds: Sequence[ThresholdBand]) -> None
dpgg.set_markers(tag, markers: Sequence[GaugeMarker]) -> None
dpgg.set_show(tag, show: bool) -> None
dpgg.delete_gauge(tag) -> None
Runtime functions are GUI-thread functions unless explicitly documented otherwise.
13. Acceptance tests
Before the first usable release:
- Every gauge type can be created in a basic example.
- Analog, digital, bar, level, status, segmented, compass, thermometer, battery, and multi-value gauges render without terminal errors.
- Gauges update from GUI-thread frame callbacks at normal telemetry rates.
- Stress examples tolerate producer rates above 60 Hz by using latest-value marshalling to the GUI thread.
- Animation and smoothing can be enabled and disabled.
- Thresholds and markers render correctly and do not trigger callbacks.
- Out-of-range values clamp and display min/max.
- Hidden tab examples do not permanently collapse gauge size.
- Analog gauges maintain square aspect by default.
- Bar/digital gauges can stretch horizontally.
- Dashboard helpers work with grouped gauges.
- Local editable install works with
uv add --editable ../dpg-gauges.