5 Commits

Author SHA1 Message Date
accd1d1dee Merge dev for 1.0.2 release 2026-07-07 16:42:34 +01:00
ceba71927d Release 1.0.2 2026-07-07 16:42:15 +01:00
fe6670af9e Merge dev for 1.0.1 release 2026-07-07 17:17:55 +02:00
ac678e8dce Release 1.0.1 2026-07-07 17:17:28 +02:00
63e79ebf8d Merge dev for 1.0.0 release 2026-07-07 16:44:20 +02:00
10 changed files with 83 additions and 21 deletions

View File

@@ -43,6 +43,17 @@ with dpg.window(label="Engine"):
dpgg.status_light(tag="ecu", label="ECU", state=True) dpgg.status_light(tag="ecu", label="ECU", state=True)
``` ```
Gauges also accept Dear PyGui's usual `parent` argument for adding a gauge to an
existing container after that container has already been created:
```python
dpg.add_window(tag="data_input", label="Data Input")
dpgg.analog_gauge(tag="rpm", parent="data_input", label="RPM")
```
The `parent` is applied to the gauge's outer group. The gauge's internal drawlist
is then parented to that group.
## GUI-Thread Contract ## GUI-Thread Contract
Dear PyGui item operations must happen on the GUI thread. Gauge creation and runtime updates such as Dear PyGui item operations must happen on the GUI thread. Gauge creation and runtime updates such as

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "dpg-gauges" name = "dpg-gauges"
version = "1.0.0" version = "1.0.2"
description = "Dear PyGui gauge widgets for dashboards and telemetry displays" description = "Dear PyGui gauge widgets for dashboards and telemetry displays"
readme = "README.md" readme = "README.md"
authors = [ authors = [

View File

@@ -35,7 +35,7 @@ from .types import (
ThresholdBand, ThresholdBand,
) )
__version__ = "1.0.0" __version__ = "1.0.2"
__all__ = [ __all__ = [
"configure", "configure",

View File

@@ -4,7 +4,6 @@ from __future__ import annotations
from typing import Any from typing import Any
from . import widget as _widget
from .gauges import GaugeConfig from .gauges import GaugeConfig
from .state import ( from .state import (
configure_gauges, configure_gauges,
@@ -17,6 +16,44 @@ from .state import (
update_runtime_gauge, update_runtime_gauge,
) )
from .types import GaugeMarker, GaugeValue, Tag, ThresholdBand from .types import GaugeMarker, GaugeValue, Tag, ThresholdBand
from .widget import (
analog_gauge,
bar_gauge,
battery_gauge,
compass_gauge,
digital_gauge,
gauge_grid,
gauge_panel,
level_gauge,
multi_value_gauge,
segmented_gauge,
status_light,
thermometer_gauge,
)
__all__ = [
"analog_gauge",
"bar_gauge",
"battery_gauge",
"compass_gauge",
"configure",
"configure_gauge",
"delete_gauge",
"digital_gauge",
"gauge_grid",
"gauge_panel",
"get_value",
"level_gauge",
"multi_value_gauge",
"segmented_gauge",
"set_markers",
"set_show",
"set_thresholds",
"set_value",
"status_light",
"thermometer_gauge",
"update_gauge",
]
def configure(**config: Any) -> None: def configure(**config: Any) -> None:
@@ -24,20 +61,6 @@ def configure(**config: Any) -> None:
configure_gauges(**config) configure_gauges(**config)
analog_gauge = _widget.analog_gauge
digital_gauge = _widget.digital_gauge
bar_gauge = _widget.bar_gauge
level_gauge = _widget.level_gauge
status_light = _widget.status_light
segmented_gauge = _widget.segmented_gauge
compass_gauge = _widget.compass_gauge
thermometer_gauge = _widget.thermometer_gauge
battery_gauge = _widget.battery_gauge
multi_value_gauge = _widget.multi_value_gauge
gauge_panel = _widget.gauge_panel
gauge_grid = _widget.gauge_grid
def set_value(tag: Tag, value: GaugeValue) -> None: def set_value(tag: Tag, value: GaugeValue) -> None:
"""Set a gauge target value from the Dear PyGui GUI thread.""" """Set a gauge target value from the Dear PyGui GUI thread."""
set_runtime_value(tag, value) set_runtime_value(tag, value)
@@ -78,4 +101,4 @@ def delete_gauge(tag: Tag) -> None:
delete_runtime_gauge(tag) delete_runtime_gauge(tag)
__all_configs__ = (GaugeConfig,) __all_configs__: tuple[type[GaugeConfig], ...] = (GaugeConfig,)

View File

@@ -36,6 +36,7 @@ def _normalize_smoothing(value: SmoothingConfig | bool | None) -> SmoothingConfi
@dataclass(frozen=True) @dataclass(frozen=True)
class GaugeConfig: class GaugeConfig:
tag: Tag | None = None tag: Tag | None = None
parent: Tag | None = None
label: str | None = None label: str | None = None
value: GaugeValue = None value: GaugeValue = None
min_value: float = 0.0 min_value: float = 0.0

0
src/dpg_gauges/py.typed Normal file
View File

View File

@@ -50,7 +50,7 @@ class GaugeRenderer:
def __init__(self, state: GaugeState) -> None: def __init__(self, state: GaugeState) -> None:
self.state = state self.state = state
self.layers = DrawLayers() self.layers: DrawLayers = DrawLayers()
self._scheduled = False self._scheduled = False
def render(self) -> None: def render(self) -> None:

View File

@@ -122,6 +122,7 @@ def create_gauge_context(gauge_type: str, config: ConfigT) -> GaugeContext[Confi
container = dpg.add_group( container = dpg.add_group(
tag=container_tag or 0, tag=container_tag or 0,
parent=config.parent or 0,
width=width, width=width,
height=height, height=height,
show=config.show, show=config.show,
@@ -153,6 +154,7 @@ def create_gauge_context(gauge_type: str, config: ConfigT) -> GaugeContext[Confi
def gauge_panel( def gauge_panel(
*, *,
tag: Tag | None = None, tag: Tag | None = None,
parent: Tag | None = None,
label: str | None = None, label: str | None = None,
width: int = 0, width: int = 0,
height: int = 0, height: int = 0,
@@ -168,6 +170,7 @@ def gauge_panel(
return LayoutContext(None) return LayoutContext(None)
container = dpg.add_child_window( container = dpg.add_child_window(
tag=tag or 0, tag=tag or 0,
parent=parent or 0,
label=label or "", label=label or "",
width=width, width=width,
height=height, height=height,
@@ -185,6 +188,7 @@ def gauge_grid(
*, *,
columns: int = 1, columns: int = 1,
tag: Tag | None = None, tag: Tag | None = None,
parent: Tag | None = None,
width: int = 0, width: int = 0,
height: int = 0, height: int = 0,
show: bool = True, show: bool = True,
@@ -196,6 +200,7 @@ def gauge_grid(
columns = max(int(columns), 1) columns = max(int(columns), 1)
container = dpg.add_group( container = dpg.add_group(
tag=tag or 0, tag=tag or 0,
parent=parent or 0,
width=width, width=width,
height=height, height=height,
show=show, show=show,
@@ -208,6 +213,7 @@ def gauge_grid(
def analog_gauge( def analog_gauge(
*, *,
tag: Tag | None = None, tag: Tag | None = None,
parent: Tag | None = None,
label: str | None = None, label: str | None = None,
value: GaugeValue = None, value: GaugeValue = None,
min_value: float = 0.0, min_value: float = 0.0,
@@ -253,6 +259,7 @@ def analog_gauge(
def digital_gauge( def digital_gauge(
*, *,
tag: Tag | None = None, tag: Tag | None = None,
parent: Tag | None = None,
label: str | None = None, label: str | None = None,
value: GaugeValue = None, value: GaugeValue = None,
min_value: float = 0.0, min_value: float = 0.0,
@@ -287,6 +294,7 @@ def digital_gauge(
def bar_gauge( def bar_gauge(
*, *,
tag: Tag | None = None, tag: Tag | None = None,
parent: Tag | None = None,
label: str | None = None, label: str | None = None,
value: GaugeValue = None, value: GaugeValue = None,
min_value: float = 0.0, min_value: float = 0.0,
@@ -326,6 +334,7 @@ def bar_gauge(
def level_gauge( def level_gauge(
*, *,
tag: Tag | None = None, tag: Tag | None = None,
parent: Tag | None = None,
label: str | None = None, label: str | None = None,
value: GaugeValue = None, value: GaugeValue = None,
min_value: float = 0.0, min_value: float = 0.0,
@@ -365,6 +374,7 @@ def level_gauge(
def status_light( def status_light(
*, *,
tag: Tag | None = None, tag: Tag | None = None,
parent: Tag | None = None,
label: str | None = None, label: str | None = None,
value: GaugeValue = None, value: GaugeValue = None,
min_value: float = 0.0, min_value: float = 0.0,
@@ -403,6 +413,7 @@ def status_light(
def segmented_gauge( def segmented_gauge(
*, *,
tag: Tag | None = None, tag: Tag | None = None,
parent: Tag | None = None,
label: str | None = None, label: str | None = None,
value: GaugeValue = None, value: GaugeValue = None,
min_value: float = 0.0, min_value: float = 0.0,
@@ -443,6 +454,7 @@ def segmented_gauge(
def compass_gauge( def compass_gauge(
*, *,
tag: Tag | None = None, tag: Tag | None = None,
parent: Tag | None = None,
label: str | None = None, label: str | None = None,
value: GaugeValue = None, value: GaugeValue = None,
min_value: float = 0.0, min_value: float = 0.0,
@@ -488,6 +500,7 @@ def compass_gauge(
def thermometer_gauge( def thermometer_gauge(
*, *,
tag: Tag | None = None, tag: Tag | None = None,
parent: Tag | None = None,
label: str | None = None, label: str | None = None,
value: GaugeValue = None, value: GaugeValue = None,
min_value: float = 0.0, min_value: float = 0.0,
@@ -529,6 +542,7 @@ def thermometer_gauge(
def battery_gauge( def battery_gauge(
*, *,
tag: Tag | None = None, tag: Tag | None = None,
parent: Tag | None = None,
label: str | None = None, label: str | None = None,
value: GaugeValue = None, value: GaugeValue = None,
min_value: float = 0.0, min_value: float = 0.0,
@@ -569,6 +583,7 @@ def battery_gauge(
def multi_value_gauge( def multi_value_gauge(
*, *,
tag: Tag | None = None, tag: Tag | None = None,
parent: Tag | None = None,
label: str | None = None, label: str | None = None,
value: GaugeValue = None, value: GaugeValue = None,
min_value: float = 0.0, min_value: float = 0.0,

View File

@@ -1,6 +1,7 @@
# pyright: reportGeneralTypeIssues=false # pyright: reportGeneralTypeIssues=false
from collections.abc import Generator from collections.abc import Generator
from importlib.resources import files
import dearpygui.dearpygui as dpg import dearpygui.dearpygui as dpg
import pytest import pytest
@@ -30,11 +31,22 @@ def dpg_context() -> Generator[None]:
def test_public_exports_and_version() -> None: def test_public_exports_and_version() -> None:
assert dpgg.__version__ == "1.0.0" assert dpgg.__version__ == "1.0.2"
assert files("dpg_gauges").joinpath("py.typed").is_file()
for name in dpgg.__all__: for name in dpgg.__all__:
assert hasattr(dpgg, name) assert hasattr(dpgg, name)
def test_gauge_creation_supports_explicit_parent(dpg_context: None) -> None:
dpg.add_window(tag="late_parent", label="Late Parent")
dpgg.analog_gauge(tag="late_gauge", parent="late_parent", width=180, height=180)
state = get_gauge_state("late_gauge")
assert state.config.parent == "late_parent"
assert dpg.get_item_parent(state.container_tag) == "late_parent"
def test_unknown_duplicate_and_deleted_gauge_errors() -> None: def test_unknown_duplicate_and_deleted_gauge_errors() -> None:
with pytest.raises(GaugeNotFoundError): with pytest.raises(GaugeNotFoundError):
dpgg.set_value("missing", 1) dpgg.set_value("missing", 1)

2
uv.lock generated
View File

@@ -36,7 +36,7 @@ wheels = [
[[package]] [[package]]
name = "dpg-gauges" name = "dpg-gauges"
version = "1.0.0" version = "1.0.2"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "dearpygui" }, { name = "dearpygui" },