chore(gdb): add auto-generated widget wrappers with per-widget snapshot

- Add gen_widget_wrappers.py generator parsing all 36 widget _private.h
- Generate per-file wrapper classes matching C inheritance hierarchy
- Each widget overrides snapshot() to populate widget_data field
- Add Snapshot.__setitem__ for widget_data assignment
- Wrap children and screens as widget types in data_collector
- Helpers use Value.safe_field chain and is_ok checks, no try blocks
- Update dashboard frontend with widget_data rendering
This commit is contained in:
Benign X
2026-04-21 12:21:05 +08:00
committed by VIFEX
parent 16e21a5af3
commit 031bae6b2c
43 changed files with 2743 additions and 3 deletions
@@ -122,7 +122,9 @@ def _collect_object_trees(lvgl) -> list:
@Snapshot.fallback(layer_name=lambda s: addrs.get(int(s)))
def _screen_snapshot(screen):
snap = screen.snapshot(
from lvglgdb.lvgl.core.lv_obj import LVObject
w = LVObject._wrap_as_widget(screen)
snap = w.snapshot(
include_children=True, include_styles=True,
).as_dict()
snap["layer_name"] = addrs.get(int(screen))
@@ -1249,6 +1249,24 @@ ui-topbar .topbar-search::placeholder {
}
color: var(--color-yellow);
}
.detail-adv-toggle {
margin-top: calc(var(--spacing) * 1);
}
.detail-adv-summary {
cursor: pointer;
padding-block: calc(var(--spacing) * 0.5);
font-size: 10px;
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
color: var(--color-overlay0);
list-style: none;
}
.detail-adv-summary::before {
content: "▸ ";
}
.detail-adv-toggle[open] > .detail-adv-summary::before {
content: "▾ ";
}
.scene-controls {
margin-bottom: calc(var(--spacing) * 1);
display: flex;
@@ -2082,6 +2100,13 @@ function buildCard(item, config) {
}
// src/builders/obj-tree.ui.ts
function formatWdVal(v) {
if (v == null)
return "-";
if (typeof v === "object")
return JSON.stringify(v);
return String(v);
}
function renderObjTree(obj, depth = 0) {
let det = document.createElement("details");
if (det.className = "obj-node", obj.addr)
@@ -2200,6 +2225,43 @@ function renderObjDetail(addr, panel) {
wrap.appendChild(el("span", "detail-flag-badge", "skip_trans"));
intSec.appendChild(wrap), panel.appendChild(intSec);
}
if (obj.widget_data && Object.keys(obj.widget_data).length) {
let wd = obj.widget_data, primary = {
lv_label: ["text", "long_mode", "recolor"],
lv_image: ["src", "w", "h", "rotation", "scale_x", "scale_y", "align"],
lv_bar: ["cur_value", "min_value", "max_value", "start_value", "mode"],
lv_slider: ["cur_value", "min_value", "max_value", "start_value", "mode", "dragging"],
lv_arc: ["value", "min_value", "max_value", "rotation", "type"],
lv_switch: ["anim_state", "orientation"],
lv_checkbox: ["txt"],
lv_dropdown: ["options", "option_cnt", "sel_opt_id", "dir"],
lv_textarea: ["placeholder_txt", "max_length", "pwd_show_time"],
lv_tabview: ["tab_cur", "tab_pos", "tab_bar_size"],
lv_roller: ["option_cnt", "sel_opt_id", "mode"],
lv_chart: ["point_cnt", "hdiv_cnt", "vdiv_cnt", "type"],
lv_scale: ["mode", "range_min", "range_max", "total_tick_count", "angle_range", "rotation"],
lv_spinner: ["duration", "angle"],
lv_keyboard: ["mode", "popovers"],
lv_led: ["color", "bright"],
lv_spinbox: ["value", "range_min", "range_max", "step", "digit_count", "dec_point_pos"],
lv_calendar: ["today", "showed_date"],
lv_table: ["col_cnt", "row_cnt"],
lv_buttonmatrix: ["btn_cnt", "row_cnt", "btn_id_sel", "one_check"]
}[obj.class_name] || [], allKeys = Object.keys(wd), priKeys = allKeys.filter((k) => primary.includes(k)), advKeys = allKeys.filter((k) => !primary.includes(k)), wdSec = el("div", "detail-section");
wdSec.appendChild(el("div", "detail-section-title", "Widget · " + obj.class_name));
for (let k of priKeys.length ? priKeys : allKeys.slice(0, 6))
wdSec.appendChild(kvPair(k, formatWdVal(wd[k])));
let rest = priKeys.length ? advKeys : allKeys.slice(6);
if (rest.length) {
let toggle = el("details", "detail-adv-toggle");
toggle.appendChild(el("summary", "detail-adv-summary", `${rest.length} more fields`));
let inner = el("div", "");
for (let k of rest)
inner.appendChild(kvPair(k, formatWdVal(wd[k])));
toggle.appendChild(inner), wdSec.appendChild(toggle);
}
panel.appendChild(wdSec);
}
if (obj.styles?.length) {
let styleSec = el("div", "detail-section");
styleSec.appendChild(el("div", "detail-section-title", "Styles (" + obj.styles.length + ")")), obj.styles.forEach((s) => {
@@ -3173,7 +3235,7 @@ class UiDashboard extends BaseComponent {
customElements.define("ui-dashboard", UiDashboard);
// src/app.ts
document.getElementById("about").innerHTML = `<span>uinspy v${"0.4.6"}</span><span>·</span><span>Built ${"2026-04-02 03:55 GMT+8"}</span><span>·</span><span>${"be336ae"}</span><span>·</span><span>${"Canvas2D"}</span><span>·</span><a href="https://github.com/W-Mai/uinspy" target="_blank" rel="noopener">GitHub</a><span>·</span><a href="https://lvgl.io" target="_blank" rel="noopener">LVGL</a><span>·</span><span>MIT</span>`;
document.getElementById("about").innerHTML = `<span>uinspy v${"0.4.6"}</span><span>·</span><span>Built ${"2026-04-02 13:52 GMT+8"}</span><span>·</span><span>${"07131c2"}</span><span>·</span><span>${"Canvas2D"}</span><span>·</span><a href="https://github.com/W-Mai/uinspy" target="_blank" rel="noopener">GitHub</a><span>·</span><a href="https://lvgl.io" target="_blank" rel="noopener">LVGL</a><span>·</span><span>MIT</span>`;
</script>
</body>
</html>
+11 -1
View File
@@ -210,7 +210,8 @@ class LVObject(Value):
@Snapshot.fallback()
def _snap(c):
return c.snapshot(
w = self._wrap_as_widget(c)
return w.snapshot(
include_children=True, include_styles=include_styles,
).as_dict()
@@ -265,6 +266,15 @@ class LVObject(Value):
return None
return name.string()
@staticmethod
def _wrap_as_widget(obj):
"""Try to wrap an LVObject as its specific widget type."""
try:
from lvglgdb.lvgl.widgets import wrap_widget
return wrap_widget(obj) or obj
except ImportError:
return obj
def dump_obj_info(obj: LVObject):
from lvglgdb.lvgl.formatter import print_info
+3
View File
@@ -77,6 +77,9 @@ class Snapshot:
def __getitem__(self, key: str) -> Any:
return self._data[key]
def __setitem__(self, key: str, value: Any) -> None:
self._data[key] = value
def __contains__(self, key: str) -> bool:
return key in self._data
@@ -0,0 +1,137 @@
"""
Auto-generated LVGL widget wrappers.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from .lv_3dtexture import LV3dtexture
from .lv_image import LVImage
from .lv_animimg import LVAnimimg
from .lv_arc import LVArc
from .lv_arclabel import LVArclabel
from .lv_bar import LVBar
from .lv_button import LVButton
from .lv_buttonmatrix import LVButtonmatrix
from .lv_calendar import LVCalendar
from .lv_canvas import LVCanvas
from .lv_chart import LVChart
from .lv_checkbox import LVCheckbox
from .lv_dropdown import LVDropdown
from .lv_dropdown_list import LVDropdownList
from .lv_imagebutton import LVImagebutton
from .lv_ime_pinyin import LVImePinyin
from .lv_keyboard import LVKeyboard
from .lv_label import LVLabel
from .lv_led import LVLed
from .lv_line import LVLine
from .lv_menu import LVMenu
from .lv_menu_page import LVMenuPage
from .lv_msgbox import LVMsgbox
from .lv_roller import LVRoller
from .lv_scale import LVScale
from .lv_slider import LVSlider
from .lv_spangroup import LVSpangroup
from .lv_textarea import LVTextarea
from .lv_spinbox import LVSpinbox
from .lv_spinner import LVSpinner
from .lv_switch import LVSwitch
from .lv_table import LVTable
from .lv_tabview import LVTabview
from .lv_tileview import LVTileview
from .lv_tileview_tile import LVTileviewTile
from .lv_win import LVWin
WIDGET_REGISTRY: dict[str, type] = {
"lv_3dtexture": LV3dtexture,
"lv_image": LVImage,
"lv_animimg": LVAnimimg,
"lv_arc": LVArc,
"lv_arclabel": LVArclabel,
"lv_bar": LVBar,
"lv_button": LVButton,
"lv_buttonmatrix": LVButtonmatrix,
"lv_calendar": LVCalendar,
"lv_canvas": LVCanvas,
"lv_chart": LVChart,
"lv_checkbox": LVCheckbox,
"lv_dropdown": LVDropdown,
"lv_dropdown_list": LVDropdownList,
"lv_imagebutton": LVImagebutton,
"lv_ime_pinyin": LVImePinyin,
"lv_keyboard": LVKeyboard,
"lv_label": LVLabel,
"lv_led": LVLed,
"lv_line": LVLine,
"lv_menu": LVMenu,
"lv_menu_page": LVMenuPage,
"lv_msgbox": LVMsgbox,
"lv_roller": LVRoller,
"lv_scale": LVScale,
"lv_slider": LVSlider,
"lv_spangroup": LVSpangroup,
"lv_textarea": LVTextarea,
"lv_spinbox": LVSpinbox,
"lv_spinner": LVSpinner,
"lv_switch": LVSwitch,
"lv_table": LVTable,
"lv_tabview": LVTabview,
"lv_tileview": LVTileview,
"lv_tileview_tile": LVTileviewTile,
"lv_win": LVWin,
}
import gdb
def wrap_widget(obj):
"""Wrap an LVObject into its widget class if known."""
cls = WIDGET_REGISTRY.get(obj.class_name)
if cls:
try:
return cls(obj)
except gdb.error:
pass # type not available in debug info
return None
__all__ = [
"LV3dtexture",
"LVImage",
"LVAnimimg",
"LVArc",
"LVArclabel",
"LVBar",
"LVButton",
"LVButtonmatrix",
"LVCalendar",
"LVCanvas",
"LVChart",
"LVCheckbox",
"LVDropdown",
"LVDropdownList",
"LVImagebutton",
"LVImePinyin",
"LVKeyboard",
"LVLabel",
"LVLed",
"LVLine",
"LVMenu",
"LVMenuPage",
"LVMsgbox",
"LVRoller",
"LVScale",
"LVSlider",
"LVSpangroup",
"LVTextarea",
"LVSpinbox",
"LVSpinner",
"LVSwitch",
"LVTable",
"LVTabview",
"LVTileview",
"LVTileviewTile",
"LVWin",
"WIDGET_REGISTRY",
"wrap_widget",
]
@@ -0,0 +1,47 @@
"""Shared field-reading helpers for widget wrappers.
All helpers return a safe value (or None) without raising exceptions.
Protection is provided by Value.safe_field() and Value.string(fallback=).
"""
from lvglgdb.lvgl.data_utils import ptr_or_none # noqa: F401
def safe_string(obj, field_name):
"""Read a char* field as string, or None."""
val = obj.safe_field(field_name)
if val is None or not getattr(val, 'is_ok', True) or not int(val):
return None
return val.string(fallback=None)
def safe_color(obj, field_name):
"""Read lv_color_t as hex string."""
val = obj.safe_field(field_name)
if val is None or not getattr(val, 'is_ok', True):
return None
return f"#{int(val):06x}"
def safe_area(obj, field_name):
"""Read lv_area_t as dict."""
val = obj.safe_field(field_name)
if val is None or not getattr(val, 'is_ok', True):
return None
return {
"x1": int(val.safe_field("x1", 0)),
"y1": int(val.safe_field("y1", 0)),
"x2": int(val.safe_field("x2", 0)),
"y2": int(val.safe_field("y2", 0)),
}
def safe_point(obj, field_name):
"""Read lv_point_t as dict."""
val = obj.safe_field(field_name)
if val is None or not getattr(val, 'is_ok', True):
return None
return {
"x": int(val.safe_field("x", 0)),
"y": int(val.safe_field("y", 0)),
}
@@ -0,0 +1,38 @@
"""
Auto-generated wrapper for lv_3dtexture_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LV3dtexture(LVObject):
"""LVGL 3dtexture widget (lv_3dtexture_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_3dtexture_t", ptr=True)
@property
def id(self):
return int(self._wv.safe_field("id", 0))
@property
def h_flip(self):
return int(self._wv.safe_field("h_flip", 0))
@property
def v_flip(self):
return int(self._wv.safe_field("v_flip", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["id"] = self.id
d["h_flip"] = self.h_flip
d["v_flip"] = self.v_flip
s['widget_data'] = d
return s
@@ -0,0 +1,33 @@
"""
Auto-generated wrapper for lv_animimg_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from .lv_image import LVImage
class LVAnimimg(LVImage):
"""LVGL animimage widget (lv_animimg_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_animimg_t", ptr=True)
@property
def anim(self):
return int(self._wv.safe_field("anim", 0))
@property
def pic_count(self):
return int(self._wv.safe_field("pic_count", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["anim"] = self.anim
d["pic_count"] = self.pic_count
s['widget_data'] = d
return s
+112
View File
@@ -0,0 +1,112 @@
"""
Auto-generated wrapper for lv_arc_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LVArc(LVObject):
"""LVGL arc widget (lv_arc_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_arc_t", ptr=True)
@property
def rotation(self):
return int(self._wv.safe_field("rotation", 0))
@property
def indic_angle_start(self):
return int(self._wv.safe_field("indic_angle_start", 0))
@property
def indic_angle_end(self):
return int(self._wv.safe_field("indic_angle_end", 0))
@property
def bg_angle_start(self):
return int(self._wv.safe_field("bg_angle_start", 0))
@property
def bg_angle_end(self):
return int(self._wv.safe_field("bg_angle_end", 0))
@property
def value(self):
"""Current value of the arc"""
return int(self._wv.safe_field("value", 0))
@property
def min_value(self):
"""Minimum value of the arc"""
return int(self._wv.safe_field("min_value", 0))
@property
def max_value(self):
"""Maximum value of the arc"""
return int(self._wv.safe_field("max_value", 0))
@property
def dragging(self):
return int(self._wv.safe_field("dragging", 0))
@property
def type(self):
return int(self._wv.safe_field("type", 0))
@property
def min_close(self):
"""1: the last pressed angle was closer to minimum end"""
return int(self._wv.safe_field("min_close", 0))
@property
def in_out(self):
"""1: The click was within the background arc angles. 0: Click outside"""
return int(self._wv.safe_field("in_out", 0))
@property
def chg_rate(self):
"""Drag angle rate of change of the arc (degrees/sec)"""
return int(self._wv.safe_field("chg_rate", 0))
@property
def last_tick(self):
"""Last dragging event timestamp of the arc"""
return int(self._wv.safe_field("last_tick", 0))
@property
def last_angle(self):
"""Last dragging angle of the arc"""
return int(self._wv.safe_field("last_angle", 0))
@property
def knob_offset(self):
"""knob offset from the main arc"""
return int(self._wv.safe_field("knob_offset", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["rotation"] = self.rotation
d["indic_angle_start"] = self.indic_angle_start
d["indic_angle_end"] = self.indic_angle_end
d["bg_angle_start"] = self.bg_angle_start
d["bg_angle_end"] = self.bg_angle_end
d["value"] = self.value
d["min_value"] = self.min_value
d["max_value"] = self.max_value
d["dragging"] = self.dragging
d["type"] = self.type
d["min_close"] = self.min_close
d["in_out"] = self.in_out
d["chg_rate"] = self.chg_rate
d["last_tick"] = self.last_tick
d["last_angle"] = self.last_angle
d["knob_offset"] = self.knob_offset
s['widget_data'] = d
return s
@@ -0,0 +1,101 @@
"""
Auto-generated wrapper for lv_arclabel_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import safe_point, safe_string
class LVArclabel(LVObject):
"""LVGL arclabel widget (lv_arclabel_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_arclabel_t", ptr=True)
@property
def text(self):
return safe_string(self._wv, "text")
@property
def dot_begin(self):
"""Offset where bytes have been replaced with dots"""
return int(self._wv.safe_field("dot_begin", 0))
@property
def angle_start(self):
return int(self._wv.safe_field("angle_start", 0))
@property
def angle_size(self):
return int(self._wv.safe_field("angle_size", 0))
@property
def offset(self):
return int(self._wv.safe_field("offset", 0))
@property
def radius(self):
return int(self._wv.safe_field("radius", 0))
@property
def center_offset(self):
return safe_point(self._wv, "center_offset")
@property
def dir(self):
return int(self._wv.safe_field("dir", 0))
@property
def text_align_v(self):
"""Vertical text alignment"""
return int(self._wv.safe_field("text_align_v", 0))
@property
def text_align_h(self):
"""Horizontal text alignment"""
return int(self._wv.safe_field("text_align_h", 0))
@property
def static_txt(self):
"""Flag to indicate the text is static"""
return int(self._wv.safe_field("static_txt", 0))
@property
def recolor(self):
"""Enable in-line letter re-coloring"""
return int(self._wv.safe_field("recolor", 0))
@property
def overflow(self):
"""Overflow mode: 0=visible, 1=ellipsis, 2=clip"""
return int(self._wv.safe_field("overflow", 0))
@property
def end_overlap(self):
"""End overlap flag, false if prevent end overlap"""
return int(self._wv.safe_field("end_overlap", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["text"] = self.text
d["dot_begin"] = self.dot_begin
d["angle_start"] = self.angle_start
d["angle_size"] = self.angle_size
d["offset"] = self.offset
d["radius"] = self.radius
d["center_offset"] = self.center_offset
d["dir"] = self.dir
d["text_align_v"] = self.text_align_v
d["text_align_h"] = self.text_align_h
d["static_txt"] = self.static_txt
d["recolor"] = self.recolor
d["overflow"] = self.overflow
d["end_overlap"] = self.end_overlap
s['widget_data'] = d
return s
@@ -0,0 +1,82 @@
"""
Auto-generated wrapper for lv_bar_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import safe_area
class LVBar(LVObject):
"""LVGL bar widget (lv_bar_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_bar_t", ptr=True)
@property
def cur_value(self):
"""Current value of the bar"""
return int(self._wv.safe_field("cur_value", 0))
@property
def min_value(self):
"""Minimum value of the bar"""
return int(self._wv.safe_field("min_value", 0))
@property
def max_value(self):
"""Maximum value of the bar"""
return int(self._wv.safe_field("max_value", 0))
@property
def start_value(self):
"""Start value of the bar"""
return int(self._wv.safe_field("start_value", 0))
@property
def indic_area(self):
"""Save the indicator area. Might be used by derived types"""
return safe_area(self._wv, "indic_area")
@property
def val_reversed(self):
"""Whether value been reversed"""
return int(self._wv.safe_field("val_reversed", 0))
@property
def cur_value_anim(self):
return int(self._wv.safe_field("cur_value_anim", 0))
@property
def start_value_anim(self):
return int(self._wv.safe_field("start_value_anim", 0))
@property
def mode(self):
"""Type of bar"""
return int(self._wv.safe_field("mode", 0))
@property
def orientation(self):
"""Orientation of bar"""
return int(self._wv.safe_field("orientation", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["cur_value"] = self.cur_value
d["min_value"] = self.min_value
d["max_value"] = self.max_value
d["start_value"] = self.start_value
d["indic_area"] = self.indic_area
d["val_reversed"] = self.val_reversed
d["cur_value_anim"] = self.cur_value_anim
d["start_value_anim"] = self.start_value_anim
d["mode"] = self.mode
d["orientation"] = self.orientation
s['widget_data'] = d
return s
@@ -0,0 +1,21 @@
"""
Auto-generated wrapper for lv_button_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LVButton(LVObject):
"""LVGL button widget (lv_button_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_button_t", ptr=True)
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
return s
@@ -0,0 +1,72 @@
"""
Auto-generated wrapper for lv_buttonmatrix_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import ptr_or_none, safe_string
class LVButtonmatrix(LVObject):
"""LVGL buttonmatrix widget (lv_buttonmatrix_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_buttonmatrix_t", ptr=True)
@property
def const(self):
"""Pointer to the current map"""
return safe_string(self._wv, "const")
@property
def button_areas(self):
"""Array of areas of buttons"""
return ptr_or_none(self._wv.safe_field("button_areas"))
@property
def ctrl_bits(self):
"""Array of control bytes"""
return ptr_or_none(self._wv.safe_field("ctrl_bits"))
@property
def btn_cnt(self):
"""Number of button in 'map_p'(Handled by the library)"""
return int(self._wv.safe_field("btn_cnt", 0))
@property
def row_cnt(self):
"""Number of rows in 'map_p'(Handled by the library)"""
return int(self._wv.safe_field("row_cnt", 0))
@property
def btn_id_sel(self):
"""Index of the active button (being pressed/released etc) or LV_BUTTONMATRIX_BUTTON_NONE"""
return int(self._wv.safe_field("btn_id_sel", 0))
@property
def one_check(self):
"""1: Single button toggled at once"""
return int(self._wv.safe_field("one_check", 0))
@property
def auto_free_map(self):
"""1: Automatically free the map when the widget is deleted"""
return int(self._wv.safe_field("auto_free_map", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["const"] = self.const
d["button_areas"] = self.button_areas
d["ctrl_bits"] = self.ctrl_bits
d["btn_cnt"] = self.btn_cnt
d["row_cnt"] = self.row_cnt
d["btn_id_sel"] = self.btn_id_sel
d["one_check"] = self.one_check
d["auto_free_map"] = self.auto_free_map
s['widget_data'] = d
return s
@@ -0,0 +1,58 @@
"""
Auto-generated wrapper for lv_calendar_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import ptr_or_none
class LVCalendar(LVObject):
"""LVGL calendar widget (lv_calendar_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_calendar_t", ptr=True)
@property
def btnm(self):
return ptr_or_none(self._wv.safe_field("btnm"))
@property
def today(self):
"""Date of today"""
return int(self._wv.safe_field("today", 0))
@property
def showed_date(self):
"""Currently visible month (day is ignored)"""
return int(self._wv.safe_field("showed_date", 0))
@property
def highlighted_dates(self):
"""Apply different style on these days (pointer to user-defined array)"""
return ptr_or_none(self._wv.safe_field("highlighted_dates"))
@property
def highlighted_dates_num(self):
"""Number of elements in `highlighted_days`"""
return int(self._wv.safe_field("highlighted_dates_num", 0))
@property
def use_chinese_calendar(self):
return int(self._wv.safe_field("use_chinese_calendar", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["btnm"] = self.btnm
d["today"] = self.today
d["showed_date"] = self.showed_date
d["highlighted_dates"] = self.highlighted_dates
d["highlighted_dates_num"] = self.highlighted_dates_num
d["use_chinese_calendar"] = self.use_chinese_calendar
s['widget_data'] = d
return s
@@ -0,0 +1,34 @@
"""
Auto-generated wrapper for lv_canvas_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from .lv_image import LVImage
from ._helpers import ptr_or_none
class LVCanvas(LVImage):
"""LVGL canvas widget (lv_canvas_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_canvas_t", ptr=True)
@property
def draw_buf(self):
return ptr_or_none(self._wv.safe_field("draw_buf"))
@property
def static_buf(self):
return int(self._wv.safe_field("static_buf", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["draw_buf"] = self.draw_buf
d["static_buf"] = self.static_buf
s['widget_data'] = d
return s
@@ -0,0 +1,69 @@
"""
Auto-generated wrapper for lv_chart_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LVChart(LVObject):
"""LVGL chart widget (lv_chart_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_chart_t", ptr=True)
@property
def series_ll(self):
"""Linked list for series (stores lv_chart_series_t)"""
return int(self._wv.safe_field("series_ll", 0))
@property
def cursor_ll(self):
"""Linked list for cursors (stores lv_chart_cursor_t)"""
return int(self._wv.safe_field("cursor_ll", 0))
@property
def pressed_point_id(self):
return int(self._wv.safe_field("pressed_point_id", 0))
@property
def hdiv_cnt(self):
"""Number of horizontal division lines"""
return int(self._wv.safe_field("hdiv_cnt", 0))
@property
def vdiv_cnt(self):
"""Number of vertical division lines"""
return int(self._wv.safe_field("vdiv_cnt", 0))
@property
def point_cnt(self):
"""Number of points in all series"""
return int(self._wv.safe_field("point_cnt", 0))
@property
def type(self):
"""Chart type"""
return int(self._wv.safe_field("type", 0))
@property
def update_mode(self):
return int(self._wv.safe_field("update_mode", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["series_ll"] = self.series_ll
d["cursor_ll"] = self.cursor_ll
d["pressed_point_id"] = self.pressed_point_id
d["hdiv_cnt"] = self.hdiv_cnt
d["vdiv_cnt"] = self.vdiv_cnt
d["point_cnt"] = self.point_cnt
d["type"] = self.type
d["update_mode"] = self.update_mode
s['widget_data'] = d
return s
@@ -0,0 +1,34 @@
"""
Auto-generated wrapper for lv_checkbox_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import safe_string
class LVCheckbox(LVObject):
"""LVGL checkbox widget (lv_checkbox_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_checkbox_t", ptr=True)
@property
def txt(self):
return safe_string(self._wv, "txt")
@property
def static_txt(self):
return int(self._wv.safe_field("static_txt", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["txt"] = self.txt
d["static_txt"] = self.static_txt
s['widget_data'] = d
return s
@@ -0,0 +1,96 @@
"""
Auto-generated wrapper for lv_dropdown_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import ptr_or_none, safe_string
class LVDropdown(LVObject):
"""LVGL dropdown widget (lv_dropdown_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_dropdown_t", ptr=True)
@property
def list(self):
"""The dropped down list"""
return ptr_or_none(self._wv.safe_field("list"))
@property
def text(self):
"""Text to display on the dropdown's button"""
return safe_string(self._wv, "text")
@property
def symbol(self):
"""Arrow or other icon when the drop-down list is closed"""
return ptr_or_none(self._wv.safe_field("symbol"))
@property
def options(self):
"""Options in a '\n' separated list"""
return safe_string(self._wv, "options")
@property
def option_cnt(self):
"""Number of options"""
return int(self._wv.safe_field("option_cnt", 0))
@property
def sel_opt_id(self):
"""Index of the currently selected option"""
return int(self._wv.safe_field("sel_opt_id", 0))
@property
def sel_opt_id_orig(self):
"""Store the original index on focus"""
return int(self._wv.safe_field("sel_opt_id_orig", 0))
@property
def pr_opt_id(self):
"""Index of the currently pressed option"""
return int(self._wv.safe_field("pr_opt_id", 0))
@property
def dir(self):
"""Direction in which the list should open"""
return int(self._wv.safe_field("dir", 0))
@property
def static_options(self):
"""1: Only a pointer is saved in `options`"""
return int(self._wv.safe_field("static_options", 0))
@property
def selected_highlight(self):
"""1: Make the selected option highlighted in the list"""
return int(self._wv.safe_field("selected_highlight", 0))
@property
def static_text(self):
"""1: Only a pointer is saved in `text`"""
return int(self._wv.safe_field("static_text", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["list"] = self.list
d["text"] = self.text
d["symbol"] = self.symbol
d["options"] = self.options
d["option_cnt"] = self.option_cnt
d["sel_opt_id"] = self.sel_opt_id
d["sel_opt_id_orig"] = self.sel_opt_id_orig
d["pr_opt_id"] = self.pr_opt_id
d["dir"] = self.dir
d["static_options"] = self.static_options
d["selected_highlight"] = self.selected_highlight
d["static_text"] = self.static_text
s['widget_data'] = d
return s
@@ -0,0 +1,29 @@
"""
Auto-generated wrapper for lv_dropdown_list_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import ptr_or_none
class LVDropdownList(LVObject):
"""LVGL dropdown widget (lv_dropdown_list_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_dropdown_list_t", ptr=True)
@property
def dropdown(self):
return ptr_or_none(self._wv.safe_field("dropdown"))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["dropdown"] = self.dropdown
s['widget_data'] = d
return s
@@ -0,0 +1,107 @@
"""
Auto-generated wrapper for lv_image_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import ptr_or_none, safe_point
class LVImage(LVObject):
"""LVGL image widget (lv_image_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_image_t", ptr=True)
@property
def src(self):
"""Image source: Pointer to an array or a file or a symbol"""
return ptr_or_none(self._wv.safe_field("src"))
@property
def bitmap_mask_src(self):
"""Pointer to an A8 bitmap mask"""
return ptr_or_none(self._wv.safe_field("bitmap_mask_src"))
@property
def offset(self):
return safe_point(self._wv, "offset")
@property
def w(self):
"""Width of the image (Handled by the library)"""
return int(self._wv.safe_field("w", 0))
@property
def h(self):
"""Height of the image (Handled by the library)"""
return int(self._wv.safe_field("h", 0))
@property
def rotation(self):
"""Rotation angle of the image"""
return int(self._wv.safe_field("rotation", 0))
@property
def scale_x(self):
"""256 means no zoom, 512 double size, 128 half size"""
return int(self._wv.safe_field("scale_x", 0))
@property
def scale_y(self):
"""256 means no zoom, 512 double size, 128 half size"""
return int(self._wv.safe_field("scale_y", 0))
@property
def pivot(self):
"""Rotation center of the image"""
return safe_point(self._wv, "pivot")
@property
def src_type(self):
"""See: lv_image_src_t"""
return int(self._wv.safe_field("src_type", 0))
@property
def cf(self):
"""Color format from `lv_color_format_t`"""
return int(self._wv.safe_field("cf", 0))
@property
def antialias(self):
"""Apply anti-aliasing in transformations (rotate, zoom)"""
return int(self._wv.safe_field("antialias", 0))
@property
def align(self):
"""Image size mode when image size and object size is different. See lv_image_align_t"""
return int(self._wv.safe_field("align", 0))
@property
def blend_mode(self):
"""Element of `lv_blend_mode_t`"""
return int(self._wv.safe_field("blend_mode", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["src"] = self.src
d["bitmap_mask_src"] = self.bitmap_mask_src
d["offset"] = self.offset
d["w"] = self.w
d["h"] = self.h
d["rotation"] = self.rotation
d["scale_x"] = self.scale_x
d["scale_y"] = self.scale_y
d["pivot"] = self.pivot
d["src_type"] = self.src_type
d["cf"] = self.cf
d["antialias"] = self.antialias
d["align"] = self.align
d["blend_mode"] = self.blend_mode
s['widget_data'] = d
return s
@@ -0,0 +1,21 @@
"""
Auto-generated wrapper for lv_imagebutton_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LVImagebutton(LVObject):
"""LVGL imagebutton widget (lv_imagebutton_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_imagebutton_t", ptr=True)
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
return s
@@ -0,0 +1,92 @@
"""
Auto-generated wrapper for lv_ime_pinyin_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import ptr_or_none, safe_string
class LVImePinyin(LVObject):
"""LVGL ime widget (lv_ime_pinyin_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_ime_pinyin_t", ptr=True)
@property
def kb(self):
return ptr_or_none(self._wv.safe_field("kb"))
@property
def cand_panel(self):
return ptr_or_none(self._wv.safe_field("cand_panel"))
@property
def dict(self):
return ptr_or_none(self._wv.safe_field("dict"))
@property
def k9_legal_py_ll(self):
return int(self._wv.safe_field("k9_legal_py_ll", 0))
@property
def cand_str(self):
"""Candidate string"""
return safe_string(self._wv, "cand_str")
@property
def k9_py_ll_pos(self):
"""Current pinyin map pages(k9)"""
return int(self._wv.safe_field("k9_py_ll_pos", 0))
@property
def k9_legal_py_count(self):
"""Count of legal Pinyin numbers(k9)"""
return int(self._wv.safe_field("k9_legal_py_count", 0))
@property
def k9_input_str_len(self):
"""9-key input(k9) mode input string max len"""
return int(self._wv.safe_field("k9_input_str_len", 0))
@property
def ta_count(self):
"""The number of characters entered in the text box this time"""
return int(self._wv.safe_field("ta_count", 0))
@property
def cand_num(self):
"""Number of candidates"""
return int(self._wv.safe_field("cand_num", 0))
@property
def py_page(self):
"""Current pinyin map pages(k26)"""
return int(self._wv.safe_field("py_page", 0))
@property
def mode(self):
"""Set mode, 1: 26-key input(k26), 0: 9-key input(k9). Default: 1."""
return int(self._wv.safe_field("mode", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["kb"] = self.kb
d["cand_panel"] = self.cand_panel
d["dict"] = self.dict
d["k9_legal_py_ll"] = self.k9_legal_py_ll
d["cand_str"] = self.cand_str
d["k9_py_ll_pos"] = self.k9_py_ll_pos
d["k9_legal_py_count"] = self.k9_legal_py_count
d["k9_input_str_len"] = self.k9_input_str_len
d["ta_count"] = self.ta_count
d["cand_num"] = self.cand_num
d["py_page"] = self.py_page
d["mode"] = self.mode
s['widget_data'] = d
return s
@@ -0,0 +1,42 @@
"""
Auto-generated wrapper for lv_keyboard_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from .lv_buttonmatrix import LVButtonmatrix
from ._helpers import ptr_or_none
class LVKeyboard(LVButtonmatrix):
"""LVGL keyboard widget (lv_keyboard_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_keyboard_t", ptr=True)
@property
def ta(self):
"""Pointer to the assigned text area"""
return ptr_or_none(self._wv.safe_field("ta"))
@property
def mode(self):
"""Key map type"""
return int(self._wv.safe_field("mode", 0))
@property
def popovers(self):
"""Show button titles in popovers on press"""
return int(self._wv.safe_field("popovers", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["ta"] = self.ta
d["mode"] = self.mode
d["popovers"] = self.popovers
s['widget_data'] = d
return s
@@ -0,0 +1,108 @@
"""
Auto-generated wrapper for lv_label_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import safe_point, safe_string
class LVLabel(LVObject):
"""LVGL label widget (lv_label_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_label_t", ptr=True)
@property
def text(self):
return safe_string(self._wv, "text")
@property
def translation_tag(self):
return safe_string(self._wv, "translation_tag")
@property
def dot_begin(self):
"""Offset where bytes have been replaced with dots"""
return int(self._wv.safe_field("dot_begin", 0))
@property
def hint(self):
return int(self._wv.safe_field("hint", 0))
@property
def sel_start(self):
return int(self._wv.safe_field("sel_start", 0))
@property
def sel_end(self):
return int(self._wv.safe_field("sel_end", 0))
@property
def size_cache(self):
"""Text size cache"""
return safe_point(self._wv, "size_cache")
@property
def offset(self):
"""Text draw position offset"""
return safe_point(self._wv, "offset")
@property
def long_mode(self):
"""Determine what to do with the long texts"""
return int(self._wv.safe_field("long_mode", 0))
@property
def static_txt(self):
"""Flag to indicate the text is static"""
return int(self._wv.safe_field("static_txt", 0))
@property
def recolor(self):
"""Enable in-line letter re-coloring"""
return int(self._wv.safe_field("recolor", 0))
@property
def expand(self):
"""Ignore real width (used by the library with LV_LABEL_LONG_MODE_SCROLL)"""
return int(self._wv.safe_field("expand", 0))
@property
def invalid_size_cache(self):
"""1: Recalculate size and update cache"""
return int(self._wv.safe_field("invalid_size_cache", 0))
@property
def need_refr_text(self):
"""1: Refresh text after layout update completion"""
return int(self._wv.safe_field("need_refr_text", 0))
@property
def text_size(self):
return safe_point(self._wv, "text_size")
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["text"] = self.text
d["translation_tag"] = self.translation_tag
d["dot_begin"] = self.dot_begin
d["hint"] = self.hint
d["sel_start"] = self.sel_start
d["sel_end"] = self.sel_end
d["size_cache"] = self.size_cache
d["offset"] = self.offset
d["long_mode"] = self.long_mode
d["static_txt"] = self.static_txt
d["recolor"] = self.recolor
d["expand"] = self.expand
d["invalid_size_cache"] = self.invalid_size_cache
d["need_refr_text"] = self.need_refr_text
d["text_size"] = self.text_size
s['widget_data'] = d
return s
@@ -0,0 +1,35 @@
"""
Auto-generated wrapper for lv_led_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import safe_color
class LVLed(LVObject):
"""LVGL led widget (lv_led_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_led_t", ptr=True)
@property
def color(self):
return safe_color(self._wv, "color")
@property
def bright(self):
"""Current brightness of the LED (0..255)"""
return int(self._wv.safe_field("bright", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["color"] = self.color
d["bright"] = self.bright
s['widget_data'] = d
return s
@@ -0,0 +1,21 @@
"""
Auto-generated wrapper for lv_line_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LVLine(LVObject):
"""LVGL line widget (lv_line_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_line_t", ptr=True)
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
return s
+105
View File
@@ -0,0 +1,105 @@
"""
Auto-generated wrapper for lv_menu_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import ptr_or_none
class LVMenu(LVObject):
"""LVGL menu widget (lv_menu_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_menu_t", ptr=True)
@property
def storage(self):
"""a pointer to obj that is the parent of all pages not displayed"""
return ptr_or_none(self._wv.safe_field("storage"))
@property
def main(self):
return ptr_or_none(self._wv.safe_field("main"))
@property
def main_page(self):
return ptr_or_none(self._wv.safe_field("main_page"))
@property
def main_header(self):
return ptr_or_none(self._wv.safe_field("main_header"))
@property
def main_header_title(self):
return ptr_or_none(self._wv.safe_field("main_header_title"))
@property
def sidebar(self):
return ptr_or_none(self._wv.safe_field("sidebar"))
@property
def sidebar_page(self):
return ptr_or_none(self._wv.safe_field("sidebar_page"))
@property
def sidebar_header(self):
return ptr_or_none(self._wv.safe_field("sidebar_header"))
@property
def sidebar_header_title(self):
return ptr_or_none(self._wv.safe_field("sidebar_header_title"))
@property
def selected_tab(self):
return ptr_or_none(self._wv.safe_field("selected_tab"))
@property
def history_ll(self):
return int(self._wv.safe_field("history_ll", 0))
@property
def cur_depth(self):
return int(self._wv.safe_field("cur_depth", 0))
@property
def prev_depth(self):
return int(self._wv.safe_field("prev_depth", 0))
@property
def sidebar_generated(self):
return int(self._wv.safe_field("sidebar_generated", 0))
@property
def mode_header(self):
return int(self._wv.safe_field("mode_header", 0))
@property
def mode_root_back_btn(self):
return int(self._wv.safe_field("mode_root_back_btn", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["storage"] = self.storage
d["main"] = self.main
d["main_page"] = self.main_page
d["main_header"] = self.main_header
d["main_header_title"] = self.main_header_title
d["sidebar"] = self.sidebar
d["sidebar_page"] = self.sidebar_page
d["sidebar_header"] = self.sidebar_header
d["sidebar_header_title"] = self.sidebar_header_title
d["selected_tab"] = self.selected_tab
d["history_ll"] = self.history_ll
d["cur_depth"] = self.cur_depth
d["prev_depth"] = self.prev_depth
d["sidebar_generated"] = self.sidebar_generated
d["mode_header"] = self.mode_header
d["mode_root_back_btn"] = self.mode_root_back_btn
s['widget_data'] = d
return s
@@ -0,0 +1,34 @@
"""
Auto-generated wrapper for lv_menu_page_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import safe_string
class LVMenuPage(LVObject):
"""LVGL menu widget (lv_menu_page_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_menu_page_t", ptr=True)
@property
def title(self):
return safe_string(self._wv, "title")
@property
def static_title(self):
return int(self._wv.safe_field("static_title", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["title"] = self.title
d["static_title"] = self.static_title
s['widget_data'] = d
return s
@@ -0,0 +1,44 @@
"""
Auto-generated wrapper for lv_msgbox_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import ptr_or_none
class LVMsgbox(LVObject):
"""LVGL msgbox widget (lv_msgbox_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_msgbox_t", ptr=True)
@property
def header(self):
return ptr_or_none(self._wv.safe_field("header"))
@property
def content(self):
return ptr_or_none(self._wv.safe_field("content"))
@property
def footer(self):
return ptr_or_none(self._wv.safe_field("footer"))
@property
def title(self):
return ptr_or_none(self._wv.safe_field("title"))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["header"] = self.header
d["content"] = self.content
d["footer"] = self.footer
d["title"] = self.title
s['widget_data'] = d
return s
@@ -0,0 +1,57 @@
"""
Auto-generated wrapper for lv_roller_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LVRoller(LVObject):
"""LVGL roller widget (lv_roller_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_roller_t", ptr=True)
@property
def option_cnt(self):
"""Number of options"""
return int(self._wv.safe_field("option_cnt", 0))
@property
def sel_opt_id(self):
"""Index of the current option"""
return int(self._wv.safe_field("sel_opt_id", 0))
@property
def sel_opt_id_ori(self):
"""Store the original index on focus"""
return int(self._wv.safe_field("sel_opt_id_ori", 0))
@property
def inf_page_cnt(self):
"""Number of extra pages added to make the roller look infinite"""
return int(self._wv.safe_field("inf_page_cnt", 0))
@property
def mode(self):
return int(self._wv.safe_field("mode", 0))
@property
def moved(self):
return int(self._wv.safe_field("moved", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["option_cnt"] = self.option_cnt
d["sel_opt_id"] = self.sel_opt_id
d["sel_opt_id_ori"] = self.sel_opt_id_ori
d["inf_page_cnt"] = self.inf_page_cnt
d["mode"] = self.mode
d["moved"] = self.moved
s['widget_data'] = d
return s
@@ -0,0 +1,112 @@
"""
Auto-generated wrapper for lv_scale_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LVScale(LVObject):
"""LVGL scale widget (lv_scale_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_scale_t", ptr=True)
@property
def section_ll(self):
"""Linked list for the sections (stores lv_scale_section_t)"""
return int(self._wv.safe_field("section_ll", 0))
@property
def mode(self):
"""Orientation and layout of scale."""
return int(self._wv.safe_field("mode", 0))
@property
def range_min(self):
"""Scale's minimum value"""
return int(self._wv.safe_field("range_min", 0))
@property
def range_max(self):
"""Scale's maximum value"""
return int(self._wv.safe_field("range_max", 0))
@property
def total_tick_count(self):
"""Total number of ticks (major and minor)"""
return int(self._wv.safe_field("total_tick_count", 0))
@property
def major_tick_every(self):
"""Frequency of major ticks to minor ticks"""
return int(self._wv.safe_field("major_tick_every", 0))
@property
def label_enabled(self):
"""Draw labels for major ticks?"""
return int(self._wv.safe_field("label_enabled", 0))
@property
def post_draw(self):
return int(self._wv.safe_field("post_draw", 0))
@property
def draw_ticks_on_top(self):
"""Draw ticks on top of main line?"""
return int(self._wv.safe_field("draw_ticks_on_top", 0))
@property
def angle_range(self):
"""Degrees between low end and high end of scale"""
return int(self._wv.safe_field("angle_range", 0))
@property
def rotation(self):
"""Clockwise angular offset from 3-o'clock position of low end of scale"""
return int(self._wv.safe_field("rotation", 0))
@property
def custom_label_cnt(self):
"""Number of custom labels provided in `txt_src`"""
return int(self._wv.safe_field("custom_label_cnt", 0))
@property
def last_tick_width(self):
"""Width of last tick in pixels"""
return int(self._wv.safe_field("last_tick_width", 0))
@property
def first_tick_width(self):
"""Width of first tick in pixels"""
return int(self._wv.safe_field("first_tick_width", 0))
@property
def needles(self):
"""Needle list of this scale"""
return int(self._wv.safe_field("needles", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["section_ll"] = self.section_ll
d["mode"] = self.mode
d["range_min"] = self.range_min
d["range_max"] = self.range_max
d["total_tick_count"] = self.total_tick_count
d["major_tick_every"] = self.major_tick_every
d["label_enabled"] = self.label_enabled
d["post_draw"] = self.post_draw
d["draw_ticks_on_top"] = self.draw_ticks_on_top
d["angle_range"] = self.angle_range
d["rotation"] = self.rotation
d["custom_label_cnt"] = self.custom_label_cnt
d["last_tick_width"] = self.last_tick_width
d["first_tick_width"] = self.first_tick_width
d["needles"] = self.needles
s['widget_data'] = d
return s
@@ -0,0 +1,57 @@
"""
Auto-generated wrapper for lv_slider_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from .lv_bar import LVBar
from ._helpers import ptr_or_none, safe_area, safe_point
class LVSlider(LVBar):
"""LVGL slider widget (lv_slider_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_slider_t", ptr=True)
@property
def left_knob_area(self):
return safe_area(self._wv, "left_knob_area")
@property
def right_knob_area(self):
return safe_area(self._wv, "right_knob_area")
@property
def pressed_point(self):
return safe_point(self._wv, "pressed_point")
@property
def value_to_set(self):
"""Which bar value to set"""
return ptr_or_none(self._wv.safe_field("value_to_set"))
@property
def dragging(self):
"""1: the slider is being dragged"""
return int(self._wv.safe_field("dragging", 0))
@property
def left_knob_focus(self):
"""1: with encoder now the right knob can be adjusted"""
return int(self._wv.safe_field("left_knob_focus", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["left_knob_area"] = self.left_knob_area
d["right_knob_area"] = self.right_knob_area
d["pressed_point"] = self.pressed_point
d["value_to_set"] = self.value_to_set
d["dragging"] = self.dragging
d["left_knob_focus"] = self.left_knob_focus
s['widget_data'] = d
return s
@@ -0,0 +1,63 @@
"""
Auto-generated wrapper for lv_spangroup_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LVSpangroup(LVObject):
"""LVGL span widget (lv_spangroup_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_spangroup_t", ptr=True)
@property
def lines(self):
return int(self._wv.safe_field("lines", 0))
@property
def indent(self):
"""first line indent"""
return int(self._wv.safe_field("indent", 0))
@property
def cache_w(self):
"""the cache automatically calculates the width"""
return int(self._wv.safe_field("cache_w", 0))
@property
def cache_h(self):
"""similar cache_w"""
return int(self._wv.safe_field("cache_h", 0))
@property
def child_ll(self):
return int(self._wv.safe_field("child_ll", 0))
@property
def overflow(self):
"""details see lv_span_overflow_t"""
return int(self._wv.safe_field("overflow", 0))
@property
def refresh(self):
"""the spangroup need refresh cache_w and cache_h"""
return int(self._wv.safe_field("refresh", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["lines"] = self.lines
d["indent"] = self.indent
d["cache_w"] = self.cache_w
d["cache_h"] = self.cache_h
d["child_ll"] = self.child_ll
d["overflow"] = self.overflow
d["refresh"] = self.refresh
s['widget_data'] = d
return s
@@ -0,0 +1,66 @@
"""
Auto-generated wrapper for lv_spinbox_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from .lv_textarea import LVTextarea
class LVSpinbox(LVTextarea):
"""LVGL spinbox widget (lv_spinbox_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_spinbox_t", ptr=True)
@property
def value(self):
return int(self._wv.safe_field("value", 0))
@property
def range_max(self):
return int(self._wv.safe_field("range_max", 0))
@property
def range_min(self):
return int(self._wv.safe_field("range_min", 0))
@property
def step(self):
return int(self._wv.safe_field("step", 0))
@property
def digit_count(self):
return int(self._wv.safe_field("digit_count", 0))
@property
def dec_point_pos(self):
"""if 0, there is no separator and the number is an integer"""
return int(self._wv.safe_field("dec_point_pos", 0))
@property
def rollover(self):
"""Set to true for rollover functionality"""
return int(self._wv.safe_field("rollover", 0))
@property
def digit_step_dir(self):
"""the direction the digit will step on encoder button press when editing"""
return int(self._wv.safe_field("digit_step_dir", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["value"] = self.value
d["range_max"] = self.range_max
d["range_min"] = self.range_min
d["step"] = self.step
d["digit_count"] = self.digit_count
d["dec_point_pos"] = self.dec_point_pos
d["rollover"] = self.rollover
d["digit_step_dir"] = self.digit_step_dir
s['widget_data'] = d
return s
@@ -0,0 +1,35 @@
"""
Auto-generated wrapper for lv_spinner_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from .lv_arc import LVArc
class LVSpinner(LVArc):
"""LVGL spinner widget (lv_spinner_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_spinner_t", ptr=True)
@property
def duration(self):
"""Anim duration in ms"""
return int(self._wv.safe_field("duration", 0))
@property
def angle(self):
"""Anim angle in degrees"""
return int(self._wv.safe_field("angle", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["duration"] = self.duration
d["angle"] = self.angle
s['widget_data'] = d
return s
@@ -0,0 +1,34 @@
"""
Auto-generated wrapper for lv_switch_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LVSwitch(LVObject):
"""LVGL switch widget (lv_switch_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_switch_t", ptr=True)
@property
def anim_state(self):
return int(self._wv.safe_field("anim_state", 0))
@property
def orientation(self):
"""Orientation of switch"""
return int(self._wv.safe_field("orientation", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["anim_state"] = self.anim_state
d["orientation"] = self.orientation
s['widget_data'] = d
return s
@@ -0,0 +1,54 @@
"""
Auto-generated wrapper for lv_table_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import ptr_or_none
class LVTable(LVObject):
"""LVGL table widget (lv_table_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_table_t", ptr=True)
@property
def col_cnt(self):
return int(self._wv.safe_field("col_cnt", 0))
@property
def row_cnt(self):
return int(self._wv.safe_field("row_cnt", 0))
@property
def row_h(self):
return ptr_or_none(self._wv.safe_field("row_h"))
@property
def col_w(self):
return ptr_or_none(self._wv.safe_field("col_w"))
@property
def col_act(self):
return int(self._wv.safe_field("col_act", 0))
@property
def row_act(self):
return int(self._wv.safe_field("row_act", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["col_cnt"] = self.col_cnt
d["row_cnt"] = self.row_cnt
d["row_h"] = self.row_h
d["col_w"] = self.col_w
d["col_act"] = self.col_act
d["row_act"] = self.row_act
s['widget_data'] = d
return s
@@ -0,0 +1,38 @@
"""
Auto-generated wrapper for lv_tabview_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LVTabview(LVObject):
"""LVGL tabview widget (lv_tabview_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_tabview_t", ptr=True)
@property
def tab_cur(self):
return int(self._wv.safe_field("tab_cur", 0))
@property
def tab_pos(self):
return int(self._wv.safe_field("tab_pos", 0))
@property
def tab_bar_size(self):
return int(self._wv.safe_field("tab_bar_size", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["tab_cur"] = self.tab_cur
d["tab_pos"] = self.tab_pos
d["tab_bar_size"] = self.tab_bar_size
s['widget_data'] = d
return s
@@ -0,0 +1,66 @@
"""
Auto-generated wrapper for lv_textarea_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import ptr_or_none, safe_string
class LVTextarea(LVObject):
"""LVGL textarea widget (lv_textarea_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_textarea_t", ptr=True)
@property
def label(self):
"""Label of the text area"""
return ptr_or_none(self._wv.safe_field("label"))
@property
def placeholder_txt(self):
"""Place holder label. only visible if text is an empty string"""
return safe_string(self._wv, "placeholder_txt")
@property
def pwd_tmp(self):
"""Used to store the original text in password mode"""
return safe_string(self._wv, "pwd_tmp")
@property
def pwd_bullet(self):
"""Replacement characters displayed in password mode"""
return safe_string(self._wv, "pwd_bullet")
@property
def accepted_chars(self):
"""Only these characters will be accepted. NULL: accept all"""
return safe_string(self._wv, "accepted_chars")
@property
def max_length(self):
"""The max. number of characters. 0: no limit"""
return int(self._wv.safe_field("max_length", 0))
@property
def pwd_show_time(self):
"""Time to show characters in password mode before change them to '*'"""
return int(self._wv.safe_field("pwd_show_time", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["label"] = self.label
d["placeholder_txt"] = self.placeholder_txt
d["pwd_tmp"] = self.pwd_tmp
d["pwd_bullet"] = self.pwd_bullet
d["accepted_chars"] = self.accepted_chars
d["max_length"] = self.max_length
d["pwd_show_time"] = self.pwd_show_time
s['widget_data'] = d
return s
@@ -0,0 +1,29 @@
"""
Auto-generated wrapper for lv_tileview_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
from ._helpers import ptr_or_none
class LVTileview(LVObject):
"""LVGL tileview widget (lv_tileview_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_tileview_t", ptr=True)
@property
def tile_act(self):
return ptr_or_none(self._wv.safe_field("tile_act"))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["tile_act"] = self.tile_act
s['widget_data'] = d
return s
@@ -0,0 +1,28 @@
"""
Auto-generated wrapper for lv_tileview_tile_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LVTileviewTile(LVObject):
"""LVGL tileview widget (lv_tileview_tile_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_tileview_tile_t", ptr=True)
@property
def dir(self):
return int(self._wv.safe_field("dir", 0))
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
d = s.get('widget_data') or {}
d["dir"] = self.dir
s['widget_data'] = d
return s
@@ -0,0 +1,21 @@
"""
Auto-generated wrapper for lv_win_t.
Do not edit manually. Regenerate from the GDB script root with:
python3 scripts/generate_all.py
"""
from lvglgdb.lvgl.core.lv_obj import LVObject
class LVWin(LVObject):
"""LVGL win widget (lv_win_t)."""
def __init__(self, obj):
super().__init__(obj)
self._wv = self.cast("lv_win_t", ptr=True)
def snapshot(self, include_children=False, include_styles=False):
"""Snapshot with widget-specific fields in widget_data."""
s = super().snapshot(include_children=include_children, include_styles=include_styles)
return s
@@ -0,0 +1,431 @@
#!/usr/bin/env python3
"""
Generate widget wrapper classes from LVGL widget _private.h headers.
Each widget gets its own Python file under lvglgdb/lvgl/widgets/,
with a class inheriting from LVObject (or its parent widget wrapper).
Output structure:
lvglgdb/lvgl/widgets/
__init__.py re-exports + WIDGET_REGISTRY + wrap_widget()
_helpers.py shared field-reading helpers
lv_label.py class LVLabel(LVObject)
lv_slider.py class LVSlider(LVBar)
...
Usage (from the GDB script root):
python3 scripts/generators/gen_widget_wrappers.py
"""
import re
import sys
from pathlib import Path
from dataclasses import dataclass, field as dc_field
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
LVGL_SRC = Path(__file__).parent.parent.parent.parent.parent / "src"
WIDGETS_DIR = LVGL_SRC / "widgets"
OUTPUT_DIR = Path(__file__).parent.parent.parent / "lvglgdb" / "lvgl" / "widgets"
SIMPLE_INT_TYPES = {
"int8_t", "int16_t", "int32_t", "int64_t",
"uint8_t", "uint16_t", "uint32_t", "uint64_t",
"int", "bool", "size_t",
"lv_value_precise_t",
}
@dataclass
class StructField:
name: str
c_type: str
is_bitfield: bool = False
bitfield_width: int = 0
is_pointer: bool = False
is_obj_pointer: bool = False
is_string: bool = False
is_array: bool = False
comment: str = ""
@dataclass
class WidgetDef:
struct_name: str
c_type: str
parent_type: str
fields: list[StructField] = dc_field(default_factory=list)
widget_dir: str = ""
@property
def class_name(self) -> str:
inner = self.c_type.removeprefix("lv_").removesuffix("_t")
return "LV" + "".join(w.capitalize() for w in inner.split("_"))
@property
def parent_class_name(self) -> str:
if self.parent_type == "lv_obj_t":
return "LVObject"
inner = self.parent_type.removeprefix("lv_").removesuffix("_t")
return "LV" + "".join(w.capitalize() for w in inner.split("_"))
@property
def module_name(self) -> str:
"""Python module filename without .py, e.g. 'lv_label'."""
return self.c_type.removesuffix("_t")
@property
def c_class_name(self) -> str:
"""C class name for registry, e.g. 'lv_label'."""
return self.c_type.removesuffix("_t")
def parse_struct_fields(body: str) -> list[StructField]:
fields = []
depth = 0
clean_lines = []
for line in body.splitlines():
stripped = line.strip()
if re.match(r"(struct|union)\s*\{", stripped):
depth += 1
continue
if stripped.startswith("}") and depth > 0:
depth -= 1
continue
if depth > 0:
continue
clean_lines.append(stripped)
for line in clean_lines:
line = line.rstrip(";").strip()
if not line or line.startswith("/*") or line.startswith("//") or line.startswith("*") or line.startswith("#"):
continue
comment = ""
cm = re.search(r"/\*\*?<?\s*(.*?)\s*\*/", line)
if cm:
comment = cm.group(1)
line = line[:cm.start()].strip().rstrip(";").strip()
if not line:
continue
if re.search(r"\[.*\]", line):
am = re.match(r"(?:const\s+)?(\w[\w\s\*]*?)\s+(\w+)\s*\[", line)
if am:
fields.append(StructField(
name=am.group(2), c_type=am.group(1).strip(),
is_array=True, comment=comment,
))
continue
bm = re.match(r"(?:const\s+)?(\w[\w\s\*]*?)\s+(\w+)\s*:\s*(\d+)", line)
if bm:
fields.append(StructField(
name=bm.group(2), c_type=bm.group(1).strip(),
is_bitfield=True, bitfield_width=int(bm.group(3)),
comment=comment,
))
continue
fm = re.match(r"((?:const\s+)?\w[\w\s]*?\s*\*?)\s+(\*?\w+)", line)
if fm:
c_type = fm.group(1).strip()
name = fm.group(2).strip().lstrip("*")
is_ptr = "*" in fm.group(1) or fm.group(2).startswith("*")
fields.append(StructField(
name=name, c_type=c_type,
is_pointer=is_ptr,
is_obj_pointer=is_ptr and "lv_obj_t" in c_type,
is_string=is_ptr and "char" in c_type,
comment=comment,
))
return fields
def parse_widgets() -> dict[str, WidgetDef]:
widgets = {}
for private_h in sorted(WIDGETS_DIR.glob("*/lv_*_private.h")):
text = private_h.read_text()
widget_dir = private_h.parent.name
for m in re.finditer(r"struct\s+_lv_(\w+)_t\s*\{([^}]+)\}", text, re.DOTALL):
struct_name = f"lv_{m.group(1)}_t"
body = m.group(2)
first_line = ""
for line in body.splitlines():
line = line.strip()
if line and not line.startswith(("/*", "//", "*", "#")):
first_line = line.rstrip(";").strip()
break
parent_match = re.match(r"(lv_\w+_t)\s+\w+", first_line)
if not parent_match:
continue
parent_type = parent_match.group(1)
if parent_type == "lv_obj_t" or parent_type in widgets or parent_type in (
"lv_bar_t", "lv_image_t", "lv_arc_t", "lv_textarea_t", "lv_buttonmatrix_t",
):
all_fields = parse_struct_fields(body)
widgets[struct_name] = WidgetDef(
struct_name=struct_name, c_type=struct_name,
parent_type=parent_type,
fields=all_fields[1:] if all_fields else [],
widget_dir=widget_dir,
)
return widgets
def _field_expr(f: StructField) -> str | None:
"""Return snapshot expression for a field, or None to skip."""
if f.is_array:
return None
if f.is_obj_pointer:
return f'ptr_or_none(self._wv.safe_field("{f.name}"))'
if f.is_string:
return f'safe_string(self._wv, "{f.name}")'
if f.is_pointer:
return f'ptr_or_none(self._wv.safe_field("{f.name}"))'
if f.c_type == "lv_color_t":
return f'safe_color(self._wv, "{f.name}")'
if f.c_type == "lv_area_t":
return f'safe_area(self._wv, "{f.name}")'
if f.c_type == "lv_point_t":
return f'safe_point(self._wv, "{f.name}")'
if f.is_bitfield or f.c_type in SIMPLE_INT_TYPES or f.c_type.startswith(("uint", "int")):
return f'int(self._wv.safe_field("{f.name}", 0))'
if f.c_type.startswith("lv_") and f.c_type.endswith("_t"):
return f'int(self._wv.safe_field("{f.name}", 0))'
return None
def _topo_sort(widgets: dict[str, WidgetDef]) -> list[WidgetDef]:
result, visited = [], set()
def visit(w):
if w.c_type in visited:
return
visited.add(w.c_type)
if w.parent_type in widgets:
visit(widgets[w.parent_type])
result.append(w)
for w in widgets.values():
visit(w)
return result
def gen_helpers() -> str:
return '''\
"""Shared field-reading helpers for widget wrappers.
All helpers return a safe value (or None) without raising exceptions.
Protection is provided by Value.safe_field() and Value.string(fallback=).
"""
from lvglgdb.lvgl.data_utils import ptr_or_none # noqa: F401
def safe_string(obj, field_name):
"""Read a char* field as string, or None."""
val = obj.safe_field(field_name)
if val is None or not getattr(val, 'is_ok', True) or not int(val):
return None
return val.string(fallback=None)
def safe_color(obj, field_name):
"""Read lv_color_t as hex string."""
val = obj.safe_field(field_name)
if val is None or not getattr(val, 'is_ok', True):
return None
return f"#{int(val):06x}"
def safe_area(obj, field_name):
"""Read lv_area_t as dict."""
val = obj.safe_field(field_name)
if val is None or not getattr(val, 'is_ok', True):
return None
return {
"x1": int(val.safe_field("x1", 0)),
"y1": int(val.safe_field("y1", 0)),
"x2": int(val.safe_field("x2", 0)),
"y2": int(val.safe_field("y2", 0)),
}
def safe_point(obj, field_name):
"""Read lv_point_t as dict."""
val = obj.safe_field(field_name)
if val is None or not getattr(val, 'is_ok', True):
return None
return {
"x": int(val.safe_field("x", 0)),
"y": int(val.safe_field("y", 0)),
}
'''
def gen_widget_file(wdef: WidgetDef, widgets: dict[str, WidgetDef]) -> str:
"""Generate a single widget module file."""
lines = [
'"""',
f"Auto-generated wrapper for {wdef.c_type}.",
"",
"Do not edit manually. Regenerate from the GDB script root with:",
" python3 scripts/generate_all.py",
'"""',
"",
]
# Import parent
if wdef.parent_type == "lv_obj_t":
lines.append("from lvglgdb.lvgl.core.lv_obj import LVObject")
else:
parent_def = widgets[wdef.parent_type]
lines.append(f"from .{parent_def.module_name} import {parent_def.class_name}")
# Import helpers only if needed
needs = set()
for f in wdef.fields:
expr = _field_expr(f)
if expr is None:
continue
if "ptr_or_none" in expr:
needs.add("ptr_or_none")
if "safe_string" in expr:
needs.add("safe_string")
if "safe_color" in expr:
needs.add("safe_color")
if "safe_area" in expr:
needs.add("safe_area")
if "safe_point" in expr:
needs.add("safe_point")
if needs:
imports = ", ".join(sorted(needs))
lines.append(f"from ._helpers import {imports}")
lines.append("")
lines.append("")
parent_cls = wdef.parent_class_name
lines.append(f"class {wdef.class_name}({parent_cls}):")
lines.append(f' """LVGL {wdef.widget_dir} widget ({wdef.c_type})."""')
lines.append("")
lines.append(f" def __init__(self, obj):")
lines.append(f" super().__init__(obj)")
lines.append(f' self._wv = self.cast("{wdef.c_type}", ptr=True)')
lines.append("")
# Properties
snapshot_fields = []
for f in wdef.fields:
expr = _field_expr(f)
if expr is None:
continue
lines.append(f" @property")
lines.append(f" def {f.name}(self):")
if f.comment:
lines.append(f' """{f.comment}"""')
lines.append(f" return {expr}")
lines.append("")
snapshot_fields.append(f.name)
# snapshot — override to include widget-specific fields in widget_data
lines.append(f" def snapshot(self, include_children=False, include_styles=False):")
lines.append(f' """Snapshot with widget-specific fields in widget_data."""')
lines.append(f" s = super().snapshot(include_children=include_children, include_styles=include_styles)")
if snapshot_fields:
lines.append(f" d = s.get('widget_data') or {{}}")
for fname in snapshot_fields:
lines.append(f' d["{fname}"] = self.{fname}')
lines.append(f" s['widget_data'] = d")
lines.append(f" return s")
lines.append("")
return "\n".join(lines)
def gen_init(ordered: list[WidgetDef]) -> str:
"""Generate __init__.py with imports, registry, and wrap_widget()."""
lines = [
'"""',
"Auto-generated LVGL widget wrappers.",
"",
"Do not edit manually. Regenerate from the GDB script root with:",
" python3 scripts/generate_all.py",
'"""',
"",
]
# Imports
for w in ordered:
lines.append(f"from .{w.module_name} import {w.class_name}")
lines.append("")
# Registry
lines.append("WIDGET_REGISTRY: dict[str, type] = {")
for w in ordered:
lines.append(f' "{w.c_class_name}": {w.class_name},')
lines.append("}")
lines.append("")
lines.append("")
# wrap_widget
lines.append("import gdb")
lines.append("")
lines.append("")
lines.append("def wrap_widget(obj):")
lines.append(' """Wrap an LVObject into its widget class if known."""')
lines.append(" cls = WIDGET_REGISTRY.get(obj.class_name)")
lines.append(" if cls:")
lines.append(" try:")
lines.append(" return cls(obj)")
lines.append(" except gdb.error:")
lines.append(" pass # type not available in debug info")
lines.append(" return None")
lines.append("")
# __all__
names = [w.class_name for w in ordered]
lines.append("__all__ = [")
for n in names:
lines.append(f' "{n}",')
lines.append(' "WIDGET_REGISTRY",')
lines.append(' "wrap_widget",')
lines.append("]")
lines.append("")
return "\n".join(lines)
def main():
widgets = parse_widgets()
ordered = _topo_sort(widgets)
print(f"Parsed {len(widgets)} widget types")
for w in ordered:
print(f" {w.module_name}.py: {w.class_name}({w.parent_class_name}) — {len(w.fields)} fields")
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
# _helpers.py
(OUTPUT_DIR / "_helpers.py").write_text(gen_helpers())
# Per-widget files
for w in ordered:
src = gen_widget_file(w, widgets)
(OUTPUT_DIR / f"{w.module_name}.py").write_text(src)
# __init__.py
(OUTPUT_DIR / "__init__.py").write_text(gen_init(ordered))
print(f"\nGenerated {len(ordered) + 2} files in {OUTPUT_DIR}/")
if __name__ == "__main__":
main()