Files
dpg-gauges/codex/FEATURES.md
2026-07-02 12:40:20 +02:00

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 color
  • analog_gauge: circular or arc dial with ticks, labels, needle, thresholds, target markers, and redline
  • bar_gauge: horizontal or vertical fill bar with thresholds and optional target marker
  • level_gauge: tank/level style gauge, useful for fuel, battery, fluids, and percentages
  • status_light: LED/status indicator with text, colors, blink/pulse option, and simple states
  • segmented_gauge: segmented bar or LED strip, useful for RPM shift lights or battery cells
  • compass_gauge: circular heading display for degrees, direction labels, and optional bearing marker
  • thermometer_gauge: vertical temperature-style display with colored bands
  • battery_gauge: battery-specific gauge with percentage/value display and charge/discharge state styling
  • multi_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 strip
  • delta_gauge: positive/negative centered bar for lap delta, trim, or correction values
  • mini_dial_gauge: small dial optimized for dense dashboards
  • radial_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_start is convenience syntax for a threshold from redline_start to max_value.

6. Value behaviour

  • Numeric values clamp to min_value and max_value by default.
  • Displayed value should match the clamped visual value.
  • Non-finite numeric values should enter an invalid display state rather than crash.
  • None should 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=0 and height=0 preserve Dear PyGui default behaviour.
  • width=-1 fills available width.
  • height=-1 fills available height where Dear PyGui allows it.
  • Positive dimensions are respected.
  • autosize_x and autosize_y are 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:

  1. Every gauge type can be created in a basic example.
  2. Analog, digital, bar, level, status, segmented, compass, thermometer, battery, and multi-value gauges render without terminal errors.
  3. Gauges update from GUI-thread frame callbacks at normal telemetry rates.
  4. Stress examples tolerate producer rates above 60 Hz by using latest-value marshalling to the GUI thread.
  5. Animation and smoothing can be enabled and disabled.
  6. Thresholds and markers render correctly and do not trigger callbacks.
  7. Out-of-range values clamp and display min/max.
  8. Hidden tab examples do not permanently collapse gauge size.
  9. Analog gauges maintain square aspect by default.
  10. Bar/digital gauges can stretch horizontally.
  11. Dashboard helpers work with grouped gauges.
  12. Local editable install works with uv add --editable ../dpg-gauges.