0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-14 03:31:50 +02:00

WIP ensure sibling names are unique

This commit is contained in:
2025-04-05 20:50:22 +02:00
parent 89c3f7aa0b
commit bc2f26e376
5 changed files with 63 additions and 10 deletions

View File

@ -7,6 +7,8 @@ import uuid
from datetime import datetime
from typing import TYPE_CHECKING, Optional
from PySide6.QtCore import Qt
from bec_lib.logger import bec_logger
from bec_lib.utils.import_utils import lazy_import_from
from pydantic import BaseModel, Field, field_validator
@ -17,6 +19,7 @@ from bec_widgets.cli.rpc.rpc_register import RPCRegister
from bec_widgets.utils.container_utils import WidgetContainerUtils
from bec_widgets.utils.error_popups import ErrorPopupUtility
from bec_widgets.utils.error_popups import SafeSlot as pyqtSlot
from bec_widgets.utils.widget_io import WidgetHierarchy
from bec_widgets.utils.yaml_dialog import load_yaml, load_yaml_gui, save_yaml, save_yaml_gui
if TYPE_CHECKING: # pragma: no cover
@ -129,9 +132,13 @@ class BECConnector:
raise ValueError(f"Name {name} contains invalid characters.")
# TODO Hierarchy can be refreshed upon creation
if isinstance(self, QObject):
# 1) If no objectName is set, set the initial name
if not self.objectName():
self.setObjectName(name if name else self.__class__.__name__)
self._name = self.objectName()
self._name = self.objectName()
# 2) Enforce unique objectName among siblings with the same BECConnector parent
self._enforce_unique_sibling_name()
else:
self._name = name if name else self.__class__.__name__
self.rpc_register = RPCRegister()
@ -144,6 +151,49 @@ class BECConnector:
# Store references to running workers so they're not garbage collected prematurely.
self._workers = []
def _enforce_unique_sibling_name(self):
"""
Enforce that this BECConnector has a unique objectName among its siblings.
Sibling logic:
- If there's a nearest BECConnector parent, only compare with children of that parent.
- If parent is None (i.e., top-level object), compare with all other top-level BECConnectors.
"""
parent_bec = WidgetHierarchy._get_becwidget_ancestor(self)
if parent_bec:
# We have a parent => only compare with siblings under that parent
siblings = parent_bec.findChildren(BECConnector, options=Qt.FindDirectChildrenOnly)
else:
# No parent => treat all top-level BECConnectors as siblings
# 1) Gather all BECConnectors from QApplication
all_widgets = QApplication.allWidgets()
all_bec = [w for w in all_widgets if isinstance(w, BECConnector)]
# 2) "Top-level" means closest BECConnector parent is None
top_level_bec = [
w for w in all_bec if WidgetHierarchy._get_becwidget_ancestor(w) is None
]
# 3) We are among these top-level siblings
siblings = top_level_bec
# Collect used names among siblings
used_names = {sib.objectName() for sib in siblings if sib is not self}
base_name = self.objectName()
if base_name not in used_names:
# Name is already unique among siblings
return
# Need a suffix to avoid collision
counter = 0
while True:
trial_name = f"{base_name}_{counter}"
if trial_name not in used_names:
self.setObjectName(trial_name)
self._name = trial_name
break
counter += 1
def submit_task(self, fn, *args, on_complete: pyqtSlot = None, **kwargs) -> Worker:
"""
Submit a task to run in a separate thread. The task will run the specified

View File

@ -177,8 +177,9 @@ class _ErrorPopupUtility(QObject):
msg.setStandardButtons(QMessageBox.Ok)
msg.setDetailedText(detailed_text)
msg.setTextInteractionFlags(Qt.TextSelectableByMouse)
msg.setMinimumWidth(600)
msg.setMinimumHeight(400)
msg.resize(800, 800)
# msg.setMinimumWidth(600)
# msg.setMinimumHeight(400)
msg.exec_()
def show_property_error(self, title, message, widget):

View File

@ -23,8 +23,6 @@ from qtpy.QtWidgets import (
QWidget,
)
from bec_widgets.utils import BECConnector
from bec_widgets.utils.bec_widget import BECWidget
from bec_widgets.widgets.utility.toggle.toggle import ToggleSwitch
@ -299,6 +297,8 @@ class WidgetHierarchy:
"""
from bec_widgets.widgets.plots.waveform.waveform import Waveform
from bec_widgets.utils import BECConnector
# 1) Filter out widgets that are not BECConnectors (if 'only_bec_widgets' is True)
is_bec = isinstance(widget, BECConnector)
if only_bec_widgets and not is_bec:

View File

@ -92,7 +92,6 @@ class Curve(BECConnector, pg.PlotDataItem):
else:
self.config = config
pg.PlotDataItem.__init__(self, parent=parent, name=name)
BECConnector.__init__(self, config=config, gui_id=gui_id)
self.setObjectName(name.replace("-", "_"))
self.parent_id = parent_item.config.gui_id
@ -103,6 +102,8 @@ class Curve(BECConnector, pg.PlotDataItem):
if kwargs:
self.set(**kwargs)
BECConnector.__init__(self, config=config, gui_id=gui_id)
def parent(self):
return self.parent_item

View File

@ -1607,7 +1607,7 @@ class DemoApp(QMainWindow): # pragma: no cover
self.waveform_popup = Waveform(popups=True)
self.waveform_popup.plot(y_name="monitor_async")
self.waveform_side = Waveform(popups=False, parent=self.main_widget)
self.waveform_side = Waveform(popups=False)
self.waveform_side.plot(y_name="bpm4i", y_entry="bpm4i", dap="GaussianModel")
self.waveform_side.plot(y_name="bpm3a", y_entry="bpm3a")
@ -1622,9 +1622,10 @@ class DemoApp(QMainWindow): # pragma: no cover
def hierarchy(self):
print("getting app")
WidgetHierarchy.print_becconnector_hierarchy_from_app() # , only_bec_widgets=True)
print("getting of side waveform")
WidgetHierarchy.print_widget_hierarchy(self.waveform_side, only_bec_widgets=True)
print("Waveform popup")
print(self.waveform_popup.objectName())
print("Waveform side")
print(self.waveform_side.objectName())
if __name__ == "__main__": # pragma: no cover