1
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2026-03-05 00:12:49 +01:00

feat(widget_io): widget hierarchy can grap all bec connectors from the widget recursively

This commit is contained in:
2025-08-06 20:24:40 +02:00
parent a7cf98cb58
commit 3b7bc2b25a

View File

@@ -2,8 +2,10 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Type, TypeVar, cast
import shiboken6 as shb
from bec_lib import bec_logger
from qtpy.QtWidgets import (
QApplication,
QCheckBox,
@@ -21,6 +23,13 @@ from qtpy.QtWidgets import (
from bec_widgets.widgets.utility.toggle.toggle import ToggleSwitch
if TYPE_CHECKING: # pragma: no cover
from bec_widgets.utils import BECConnector
logger = bec_logger.logger
TAncestor = TypeVar("TAncestor", bound=QWidget)
class WidgetHandler(ABC):
"""Abstract base class for all widget handlers."""
@@ -465,13 +474,19 @@ class WidgetHierarchy:
"""
from bec_widgets.utils import BECConnector
# Guard against deleted/invalid Qt wrappers
if not shb.isValid(widget):
return None
parent = widget.parent()
# Retrieve first parent
parent = widget.parent() if hasattr(widget, "parent") else None
# Walk up, validating each step
while parent is not None:
if not shb.isValid(parent):
return None
if isinstance(parent, BECConnector):
return parent
parent = parent.parent()
parent = parent.parent() if hasattr(parent, "parent") else None
return None
@staticmethod
@@ -553,6 +568,70 @@ class WidgetHierarchy:
WidgetIO.set_value(child, value)
WidgetHierarchy.import_config_from_dict(child, widget_config, set_values)
@staticmethod
def get_bec_connectors_from_parent(widget) -> list:
"""
Return all BECConnector instances whose closest BECConnector ancestor is the given widget,
including the widget itself if it is a BECConnector.
"""
from bec_widgets.utils import BECConnector
connectors: list[BECConnector] = []
if isinstance(widget, BECConnector):
connectors.append(widget)
for child in widget.findChildren(BECConnector):
if WidgetHierarchy._get_becwidget_ancestor(child) is widget:
connectors.append(child)
return connectors
@staticmethod
def find_ancestor(
widget: QWidget | BECConnector, ancestor_class: Type[TAncestor] | str
) -> TAncestor | None:
"""
Find the closest ancestor of the specified class (or class-name string).
Args:
widget(QWidget): The starting widget.
ancestor_class(Type[TAncestor] | str): The ancestor class or class-name string to search for.
Returns:
TAncestor | None: The closest ancestor of the specified class, or None if not found.
"""
if widget is None or not shb.isValid(widget):
return None
try:
from bec_widgets.utils import BECConnector # local import to avoid cycles
is_bec_target = False
if isinstance(ancestor_class, str):
is_bec_target = ancestor_class == "BECConnector"
elif isinstance(ancestor_class, type):
is_bec_target = issubclass(ancestor_class, BECConnector)
if is_bec_target:
ancestor = WidgetHierarchy._get_becwidget_ancestor(widget)
return cast(TAncestor, ancestor)
except Exception as e:
logger.error(f"Error importing BECConnector: {e}")
parent = widget.parent() if hasattr(widget, "parent") else None
while parent is not None:
if not shb.isValid(parent):
return None
try:
if isinstance(ancestor_class, str):
if parent.__class__.__name__ == ancestor_class:
return cast(TAncestor, parent)
else:
if isinstance(parent, ancestor_class):
return cast(TAncestor, parent)
except Exception as e:
logger.error(f"Error checking ancestor class: {e}")
parent = parent.parent() if hasattr(parent, "parent") else None
return None
# Example usage
def hierarchy_example(): # pragma: no cover