mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-10 04:37:55 +08:00
chore(gdb): cast fallback, NULL string handling, order-independent widget discovery
- Widget __init__ cast fallback to self when type missing in debug info - safe_string: NULL returns None (normal), CorruptedValue returns marker - parse_widgets: two-pass inheritance resolution, remove hardcoded list
This commit is contained in:
@@ -8,16 +8,16 @@ from lvglgdb.lvgl.data_utils import ptr_or_none # noqa: F401
|
||||
|
||||
|
||||
def safe_string(obj, field_name):
|
||||
"""Read a char* field as string or corrupted marker. Never returns None."""
|
||||
"""Read a char* field as string, corrupted marker, or None (NULL/missing)."""
|
||||
from lvglgdb.value import CorruptedValue
|
||||
val = obj.safe_field(field_name)
|
||||
if val is None:
|
||||
return str(CorruptedValue(0, ValueError("field not found")))
|
||||
return None
|
||||
if not getattr(val, 'is_ok', True):
|
||||
return str(val)
|
||||
addr = int(val)
|
||||
if not addr:
|
||||
return str(CorruptedValue(0, ValueError("NULL pointer")))
|
||||
return None
|
||||
return val.string(fallback=str(CorruptedValue(addr, MemoryError("unreadable"))))
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class LV3dtexture(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_3dtexture_t", ptr=True)
|
||||
self._wv = self.cast("lv_3dtexture_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVAnimimg(LVImage):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_animimg_t", ptr=True)
|
||||
self._wv = self.cast("lv_animimg_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def anim(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ class LVArc(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_arc_t", ptr=True)
|
||||
self._wv = self.cast("lv_arc_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def rotation(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVArclabel(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_arclabel_t", ptr=True)
|
||||
self._wv = self.cast("lv_arclabel_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVBar(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_bar_t", ptr=True)
|
||||
self._wv = self.cast("lv_bar_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def cur_value(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ class LVButton(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_button_t", ptr=True)
|
||||
self._wv = self.cast("lv_button_t", ptr=True) or self
|
||||
|
||||
def snapshot(self, include_children=False, include_styles=False):
|
||||
"""Snapshot with widget-specific fields in widget_data."""
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVButtonmatrix(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_buttonmatrix_t", ptr=True)
|
||||
self._wv = self.cast("lv_buttonmatrix_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def map_p(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVCalendar(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_calendar_t", ptr=True)
|
||||
self._wv = self.cast("lv_calendar_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def btnm(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVCanvas(LVImage):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_canvas_t", ptr=True)
|
||||
self._wv = self.cast("lv_canvas_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def draw_buf(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVChart(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_chart_t", ptr=True)
|
||||
self._wv = self.cast("lv_chart_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def series_ll(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVCheckbox(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_checkbox_t", ptr=True)
|
||||
self._wv = self.cast("lv_checkbox_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def txt(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVDropdown(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_dropdown_t", ptr=True)
|
||||
self._wv = self.cast("lv_dropdown_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def list(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVDropdownList(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_dropdown_list_t", ptr=True)
|
||||
self._wv = self.cast("lv_dropdown_list_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def dropdown(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVImage(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_image_t", ptr=True)
|
||||
self._wv = self.cast("lv_image_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def src(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ class LVImagebutton(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_imagebutton_t", ptr=True)
|
||||
self._wv = self.cast("lv_imagebutton_t", ptr=True) or self
|
||||
|
||||
def snapshot(self, include_children=False, include_styles=False):
|
||||
"""Snapshot with widget-specific fields in widget_data."""
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVImePinyin(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_ime_pinyin_t", ptr=True)
|
||||
self._wv = self.cast("lv_ime_pinyin_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def kb(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVKeyboard(LVButtonmatrix):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_keyboard_t", ptr=True)
|
||||
self._wv = self.cast("lv_keyboard_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def ta(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVLabel(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_label_t", ptr=True)
|
||||
self._wv = self.cast("lv_label_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVLed(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_led_t", ptr=True)
|
||||
self._wv = self.cast("lv_led_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def color(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ class LVLine(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_line_t", ptr=True)
|
||||
self._wv = self.cast("lv_line_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def point_num(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVMenu(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_menu_t", ptr=True)
|
||||
self._wv = self.cast("lv_menu_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def storage(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVMenuPage(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_menu_page_t", ptr=True)
|
||||
self._wv = self.cast("lv_menu_page_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVMsgbox(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_msgbox_t", ptr=True)
|
||||
self._wv = self.cast("lv_msgbox_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def header(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ class LVRoller(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_roller_t", ptr=True)
|
||||
self._wv = self.cast("lv_roller_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def option_cnt(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVScale(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_scale_t", ptr=True)
|
||||
self._wv = self.cast("lv_scale_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def section_ll(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVSlider(LVBar):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_slider_t", ptr=True)
|
||||
self._wv = self.cast("lv_slider_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def left_knob_area(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVSpangroup(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_spangroup_t", ptr=True)
|
||||
self._wv = self.cast("lv_spangroup_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def lines(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ class LVSpinbox(LVTextarea):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_spinbox_t", ptr=True)
|
||||
self._wv = self.cast("lv_spinbox_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ class LVSpinner(LVArc):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_spinner_t", ptr=True)
|
||||
self._wv = self.cast("lv_spinner_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def duration(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ class LVSwitch(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_switch_t", ptr=True)
|
||||
self._wv = self.cast("lv_switch_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def anim_state(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVTable(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_table_t", ptr=True)
|
||||
self._wv = self.cast("lv_table_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def col_cnt(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ class LVTabview(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_tabview_t", ptr=True)
|
||||
self._wv = self.cast("lv_tabview_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def tab_cur(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVTextarea(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_textarea_t", ptr=True)
|
||||
self._wv = self.cast("lv_textarea_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class LVTileview(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_tileview_t", ptr=True)
|
||||
self._wv = self.cast("lv_tileview_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def tile_act(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ class LVTileviewTile(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_tileview_tile_t", ptr=True)
|
||||
self._wv = self.cast("lv_tileview_tile_t", ptr=True) or self
|
||||
|
||||
@property
|
||||
def dir(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ class LVWin(LVObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
self._wv = self.cast("lv_win_t", ptr=True)
|
||||
self._wv = self.cast("lv_win_t", ptr=True) or self
|
||||
|
||||
def snapshot(self, include_children=False, include_styles=False):
|
||||
"""Snapshot with widget-specific fields in widget_data."""
|
||||
|
||||
@@ -190,36 +190,44 @@ def _find_structs(text: str):
|
||||
|
||||
|
||||
def parse_widgets() -> dict[str, WidgetDef]:
|
||||
widgets = {}
|
||||
# Pass 1: collect all structs with parent type
|
||||
candidates = []
|
||||
for private_h in sorted(WIDGETS_DIR.glob("*/lv_*_private.h")):
|
||||
text = private_h.read_text()
|
||||
widget_dir = private_h.parent.name
|
||||
|
||||
for raw_name, body in _find_structs(text):
|
||||
struct_name = f"lv_{raw_name}_t"
|
||||
|
||||
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
|
||||
if parent_match:
|
||||
candidates.append((struct_name, parent_match.group(1), body, widget_dir))
|
||||
|
||||
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,
|
||||
)
|
||||
# Pass 2: resolve inheritance from lv_obj_t (order-independent)
|
||||
widget_types = {"lv_obj_t"}
|
||||
changed = True
|
||||
while changed:
|
||||
changed = False
|
||||
for name, parent, _, _ in candidates:
|
||||
if name not in widget_types and parent in widget_types:
|
||||
widget_types.add(name)
|
||||
changed = True
|
||||
|
||||
# Pass 3: build WidgetDef for discovered widgets
|
||||
widgets = {}
|
||||
for struct_name, parent_type, body, widget_dir in candidates:
|
||||
if struct_name in widget_types and struct_name != "lv_obj_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
|
||||
|
||||
|
||||
@@ -284,16 +292,16 @@ from lvglgdb.lvgl.data_utils import ptr_or_none # noqa: F401
|
||||
|
||||
|
||||
def safe_string(obj, field_name):
|
||||
"""Read a char* field as string or corrupted marker. Never returns None."""
|
||||
"""Read a char* field as string, corrupted marker, or None (NULL/missing)."""
|
||||
from lvglgdb.value import CorruptedValue
|
||||
val = obj.safe_field(field_name)
|
||||
if val is None:
|
||||
return str(CorruptedValue(0, ValueError("field not found")))
|
||||
return None
|
||||
if not getattr(val, 'is_ok', True):
|
||||
return str(val)
|
||||
addr = int(val)
|
||||
if not addr:
|
||||
return str(CorruptedValue(0, ValueError("NULL pointer")))
|
||||
return None
|
||||
return val.string(fallback=str(CorruptedValue(addr, MemoryError("unreadable"))))
|
||||
|
||||
|
||||
@@ -396,7 +404,7 @@ def gen_widget_file(wdef: WidgetDef, widgets: dict[str, WidgetDef]) -> str:
|
||||
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(f' self._wv = self.cast("{wdef.c_type}", ptr=True) or self')
|
||||
lines.append("")
|
||||
|
||||
# Properties
|
||||
|
||||
Reference in New Issue
Block a user