diff --git a/bec_widgets/cli/client_utils.py b/bec_widgets/cli/client_utils.py index 8afe967f..6c48da8f 100644 --- a/bec_widgets/cli/client_utils.py +++ b/bec_widgets/cli/client_utils.py @@ -207,7 +207,7 @@ class BECGuiClient(RPCBase): self._process = None self._process_output_processing_thread = None self._exposed_widgets = [] - self._registry_state = {} + self._server_registry = {} self._ipython_registry = {} self.available_widgets = AvailableWidgetsNamespace() @@ -329,7 +329,7 @@ class BECGuiClient(RPCBase): ) # Remove all reference from top level self._top_level.clear() - self._registry_state.clear() + self._server_registry.clear() def close(self): """Deprecated. Use kill_server() instead.""" @@ -353,7 +353,7 @@ class BECGuiClient(RPCBase): # Wait for 'bec' gui to be registered, this may take some time # After 60s timeout. Should this raise an exception on timeout? while time.time() < time.time() + timeout: - if len(list(self._registry_state.keys())) == 0: + if len(list(self._server_registry.keys())) == 0: time.sleep(0.1) else: break @@ -403,9 +403,10 @@ class BECGuiClient(RPCBase): return self._start_server(wait=wait) def _handle_registry_update(self, msg: StreamMessage) -> None: - with self._lock: - self._registry_state = msg["data"].state - self._update_dynamic_namespace() + # This was causing a deadlock during shutdown, not sure why. + # with self._lock: + self._server_registry = msg["data"].state + self._update_dynamic_namespace() def _do_show_all(self): rpc_client = RPCBase(gui_id=f"{self._gui_id}:window", parent=self) @@ -437,12 +438,13 @@ class BECGuiClient(RPCBase): def _cleanup_ipython_registry(self): """Cleanup the ipython registry""" names_in_registry = list(self._ipython_registry.keys()) - remove_ids = list(set(names_in_registry) - set(self._exposed_widgets)) + names_in_server_state = list(self._server_registry.keys()) + remove_ids = list(set(names_in_registry) - set(names_in_server_state)) for widget_id in remove_ids: self._ipython_registry.pop(widget_id) self._cleanup_rpc_references_on_rpc_base(remove_ids) # Clear the exposed widgets - self._exposed_widgets.clear() + self._exposed_widgets.clear() # No longer needed I think def _cleanup_rpc_references_on_rpc_base(self, remove_ids: list[str]) -> None: """Cleanup the rpc references on the RPCBase object""" @@ -473,7 +475,7 @@ class BECGuiClient(RPCBase): # Add dock areas dock_area_states = [ state - for state in self._registry_state.values() + for state in self._server_registry.values() if state["widget_class"] == "BECDockArea" ] for state in dock_area_states: @@ -491,7 +493,7 @@ class BECGuiClient(RPCBase): # Add docks dock_states = [ state - for state in self._registry_state.values() + for state in self._server_registry.values() if state["config"].get("parent_id", "") == dock_area_ref._gui_id ] for state in dock_states: @@ -506,7 +508,7 @@ class BECGuiClient(RPCBase): # Add widgets widget_states = [ state - for state in self._registry_state.values() + for state in self._server_registry.values() if state["config"].get("parent_id", "") == dock_ref._gui_id ] for state in widget_states: @@ -527,7 +529,7 @@ class BECGuiClient(RPCBase): """Add a widget to the namespace Args: - state (dict): The state of the widget from the _registry_state. + state (dict): The state of the widget from the _server_registry. parent (object): The parent object. """ name = state["name"] diff --git a/bec_widgets/utils/bec_connector.py b/bec_widgets/utils/bec_connector.py index ec3082c1..0019b98b 100644 --- a/bec_widgets/utils/bec_connector.py +++ b/bec_widgets/utils/bec_connector.py @@ -19,8 +19,9 @@ from bec_widgets.qt_utils.error_popups import SafeSlot as pyqtSlot from bec_widgets.utils.container_utils import WidgetContainerUtils from bec_widgets.utils.yaml_dialog import load_yaml, load_yaml_gui, save_yaml, save_yaml_gui -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover from bec_widgets.utils.bec_dispatcher import BECDispatcher + from bec_widgets.widgets.containers.dock import BECDock logger = bec_logger.logger BECDispatcher = lazy_import_from("bec_widgets.utils.bec_dispatcher", ("BECDispatcher",)) @@ -82,11 +83,13 @@ class BECConnector: config: ConnectionConfig | None = None, gui_id: str | None = None, name: str | None = None, + parent_dock: BECDock | None = None, parent_id: str | None = None, ): # BEC related connections self.bec_dispatcher = BECDispatcher(client=client) self.client = self.bec_dispatcher.client if client is None else client + self._parent_dock = parent_dock if not self.client in BECConnector.EXIT_HANDLERS: # register function to clean connections at exit; @@ -312,10 +315,14 @@ class BECConnector: def remove(self): """Cleanup the BECConnector""" - if hasattr(self, "close"): + # If the widget is attached to a dock, remove it from the dock. + if self._parent_dock is not None: + self._parent_dock.delete(self._name) + # If the widget is from Qt, trigger its close method. + elif hasattr(self, "close"): self.close() - if hasattr(self, "deleteLater"): - self.deleteLater() + # If the widget is neither from a Dock nor from Qt, remove it from the RPC registry. + # i.e. Curve Item from Waveform else: self.rpc_register.remove_rpc(self) diff --git a/bec_widgets/utils/bec_widget.py b/bec_widgets/utils/bec_widget.py index 551e557d..7a172a9c 100644 --- a/bec_widgets/utils/bec_widget.py +++ b/bec_widgets/utils/bec_widget.py @@ -58,9 +58,13 @@ class BECWidget(BECConnector): raise RuntimeError(f"{repr(self)} is not a subclass of QWidget") super().__init__( - client=client, config=config, gui_id=gui_id, name=name, parent_id=parent_id + client=client, + config=config, + gui_id=gui_id, + name=name, + parent_dock=parent_dock, + parent_id=parent_id, ) - self._parent_dock = parent_dock app = QApplication.instance() if not hasattr(app, "theme"): # DO NOT SET THE THEME TO AUTO! Otherwise, the qwebengineview will segfault