diff --git a/bec_widgets/cli/rpc/rpc_base.py b/bec_widgets/cli/rpc/rpc_base.py index c1d7bdf1..70606143 100644 --- a/bec_widgets/cli/rpc/rpc_base.py +++ b/bec_widgets/cli/rpc/rpc_base.py @@ -14,10 +14,8 @@ if TYPE_CHECKING: # pragma: no cover from bec_lib import messages from bec_lib.connector import MessageObject - from bec_widgets.cli.client_utils import BECGuiClient - - import bec_widgets.cli.client as client + from bec_widgets.cli.client_utils import BECGuiClient else: client = lazy_import("bec_widgets.cli.client") # avoid circular import messages = lazy_import("bec_lib.messages") diff --git a/bec_widgets/utils/bec_widget.py b/bec_widgets/utils/bec_widget.py index ae3456c5..b7eb1916 100644 --- a/bec_widgets/utils/bec_widget.py +++ b/bec_widgets/utils/bec_widget.py @@ -77,13 +77,6 @@ class BECWidget(BECConnector): logger.debug(f"Subscribing to theme updates for {self.__class__.__name__}") self._connect_to_theme_change() - def _ensure_bec_app(self): - # pylint: disable=import-outside-toplevel - from bec_widgets.utils.bec_qapp import BECApplication - - app = BECApplication.from_qapplication() - return app - def _connect_to_theme_change(self): """Connect to the theme change signal.""" qapp = QApplication.instance() @@ -113,6 +106,7 @@ class BECWidget(BECConnector): """Cleanup the widget.""" with RPCRegister.delayed_broadcast(): # All widgets need to call super().cleanup() in their cleanup method + logger.info(f"Registry cleanup for widget {self.__class__.__name__}") self.rpc_register.remove_rpc(self) def closeEvent(self, event): diff --git a/bec_widgets/utils/settings_dialog.py b/bec_widgets/utils/settings_dialog.py index ad7ad49b..df28176d 100644 --- a/bec_widgets/utils/settings_dialog.py +++ b/bec_widgets/utils/settings_dialog.py @@ -1,7 +1,11 @@ +from bec_lib.logger import bec_logger +from PySide6.QtGui import QCloseEvent from qtpy.QtWidgets import QDialog, QDialogButtonBox, QHBoxLayout, QPushButton, QVBoxLayout, QWidget from bec_widgets.utils.error_popups import SafeSlot +logger = bec_logger.logger + class SettingWidget(QWidget): """ @@ -37,6 +41,15 @@ class SettingWidget(QWidget): """ pass + def cleanup(self): + """ + Cleanup the settings widget. + """ + + def closeEvent(self, event: QCloseEvent) -> None: + self.cleanup() + return super().closeEvent(event) + class SettingsDialog(QDialog): """ @@ -99,8 +112,17 @@ class SettingsDialog(QDialog): Accept the changes made in the settings widget and close the dialog. """ self.widget.accept_changes() + self.cleanup() super().accept() + @SafeSlot() + def reject(self): + """ + Reject the changes made in the settings widget and close the dialog. + """ + self.cleanup() + super().reject() + @SafeSlot() def apply_changes(self): """ @@ -114,7 +136,10 @@ class SettingsDialog(QDialog): """ self.button_box.close() self.button_box.deleteLater() + self.widget.close() + self.widget.deleteLater() def closeEvent(self, event): + logger.info("Closing settings dialog") self.cleanup() super().closeEvent(event) diff --git a/bec_widgets/widgets/plots/image/image.py b/bec_widgets/widgets/plots/image/image.py index c10f812d..0e6583c0 100644 --- a/bec_widgets/widgets/plots/image/image.py +++ b/bec_widgets/widgets/plots/image/image.py @@ -133,7 +133,7 @@ class Image(PlotBase): super().__init__( parent=parent, config=config, client=client, gui_id=gui_id, popups=popups, **kwargs ) - self._main_image.parent_image = self + self._main_image = ImageItem(parent_image=self, parent_id=self.gui_id) self.plot_item.addItem(self._main_image) self.scan_id = None @@ -913,10 +913,14 @@ class Image(PlotBase): """ Disconnect the image update signals and clean up the image. """ + # Main Image cleanup if self._main_image.config.monitor is not None: self.disconnect_monitor(self._main_image.config.monitor) self._main_image.config.monitor = None + self.plot_item.removeItem(self._main_image) + self._main_image = None + # Colorbar Cleanup if self._color_bar: if self.config.color_bar == "full": self.cleanup_histogram_lut_item(self._color_bar) @@ -925,6 +929,10 @@ class Image(PlotBase): self._color_bar.deleteLater() self._color_bar = None + # Toolbar cleanup + self.toolbar.widgets["monitor"].widget.close() + self.toolbar.widgets["monitor"].widget.deleteLater() + super().cleanup() diff --git a/bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py b/bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py index fcda57b8..033ed439 100644 --- a/bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py +++ b/bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py @@ -28,7 +28,9 @@ class MonitorSelectionToolbarBundle(ToolbarBundle): # 1) Device combo box self.device_combo_box = DeviceComboBox( - device_filter=BECDeviceFilter.DEVICE, readout_priority_filter=[ReadoutPriority.ASYNC] + parent=self.target_widget, + device_filter=BECDeviceFilter.DEVICE, + readout_priority_filter=[ReadoutPriority.ASYNC], ) self.device_combo_box.addItem("", None) self.device_combo_box.setCurrentText("") diff --git a/bec_widgets/widgets/plots/motor_map/motor_map.py b/bec_widgets/widgets/plots/motor_map/motor_map.py index 6ff54e0b..85fe5b7f 100644 --- a/bec_widgets/widgets/plots/motor_map/motor_map.py +++ b/bec_widgets/widgets/plots/motor_map/motor_map.py @@ -791,6 +791,10 @@ class MotorMap(PlotBase): data = {"x": self._buffer["x"], "y": self._buffer["y"]} return data + def cleanup(self): + self.motor_selection_bundle.cleanup() + super().cleanup() + class DemoApp(QMainWindow): # pragma: no cover def __init__(self): diff --git a/bec_widgets/widgets/plots/motor_map/toolbar_bundles/motor_selection.py b/bec_widgets/widgets/plots/motor_map/toolbar_bundles/motor_selection.py index 8874823d..da9f956c 100644 --- a/bec_widgets/widgets/plots/motor_map/toolbar_bundles/motor_selection.py +++ b/bec_widgets/widgets/plots/motor_map/toolbar_bundles/motor_selection.py @@ -27,14 +27,18 @@ class MotorSelectionToolbarBundle(ToolbarBundle): self.target_widget = target_widget # Motor X - self.motor_x = DeviceComboBox(device_filter=[BECDeviceFilter.POSITIONER]) + self.motor_x = DeviceComboBox( + parent=self.target_widget, device_filter=[BECDeviceFilter.POSITIONER] + ) self.motor_x.addItem("", None) self.motor_x.setCurrentText("") self.motor_x.setToolTip("Select Motor X") self.motor_x.setItemDelegate(NoCheckDelegate(self.motor_x)) # Motor X - self.motor_y = DeviceComboBox(device_filter=[BECDeviceFilter.POSITIONER]) + self.motor_y = DeviceComboBox( + parent=self.target_widget, device_filter=[BECDeviceFilter.POSITIONER] + ) self.motor_y.addItem("", None) self.motor_y.setCurrentText("") self.motor_y.setToolTip("Select Motor Y") @@ -58,3 +62,9 @@ class MotorSelectionToolbarBundle(ToolbarBundle): or motor_y != self.target_widget.config.y_motor.name ): self.target_widget.map(motor_x, motor_y) + + def cleanup(self): + self.motor_x.close() + self.motor_x.deleteLater() + self.motor_y.close() + self.motor_y.deleteLater() diff --git a/bec_widgets/widgets/plots/multi_waveform/multi_waveform.py b/bec_widgets/widgets/plots/multi_waveform/multi_waveform.py index fe195219..efcc037f 100644 --- a/bec_widgets/widgets/plots/multi_waveform/multi_waveform.py +++ b/bec_widgets/widgets/plots/multi_waveform/multi_waveform.py @@ -496,3 +496,9 @@ class MultiWaveform(PlotBase): self.monitor_selection_bundle.colormap_widget.blockSignals(True) self.monitor_selection_bundle.colormap_widget.colormap = self.config.color_palette self.monitor_selection_bundle.colormap_widget.blockSignals(False) + + def cleanup(self): + self._disconnect_monitor() + self.clear_curves() + self.monitor_selection_bundle.cleanup() + super().cleanup() diff --git a/bec_widgets/widgets/plots/multi_waveform/toolbar_bundles/monitor_selection.py b/bec_widgets/widgets/plots/multi_waveform/toolbar_bundles/monitor_selection.py index 434da400..09d745c6 100644 --- a/bec_widgets/widgets/plots/multi_waveform/toolbar_bundles/monitor_selection.py +++ b/bec_widgets/widgets/plots/multi_waveform/toolbar_bundles/monitor_selection.py @@ -58,3 +58,10 @@ class MultiWaveformSelectionToolbarBundle(ToolbarBundle): @SafeSlot(str) def change_colormap(self, colormap: str): self.target_widget.color_palette = colormap + + def cleanup(self): + """ + Cleanup the toolbar bundle. + """ + self.monitor.close() + self.monitor.deleteLater() diff --git a/bec_widgets/widgets/plots/plot_base.py b/bec_widgets/widgets/plots/plot_base.py index 1ffb1e81..2cddb08d 100644 --- a/bec_widgets/widgets/plots/plot_base.py +++ b/bec_widgets/widgets/plots/plot_base.py @@ -69,7 +69,7 @@ class PlotBase(BECWidget, QWidget): config: ConnectionConfig | None = None, client=None, gui_id: str | None = None, - popups: bool = False, + popups: bool = True, **kwargs, ) -> None: if config is None: @@ -170,6 +170,9 @@ class PlotBase(BECWidget, QWidget): # hide some options by default self.toolbar.toggle_action_visibility("fps_monitor", False) + # Get default viewbox state + self.mouse_bundle.get_viewbox_mode() + def add_side_menus(self): """Adds multiple menus to the side panel.""" # Setting Axis Widget diff --git a/bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.py b/bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.py index 42f40e82..5057c74a 100644 --- a/bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.py +++ b/bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.py @@ -107,14 +107,15 @@ class ScatterWaveform(PlotBase): ): if config is None: config = ScatterWaveformConfig(widget_class=self.__class__.__name__) + # Specific GUI elements + self.scatter_dialog = None + self.scatter_curve_settings = None + super().__init__( parent=parent, config=config, client=client, gui_id=gui_id, popups=popups, **kwargs ) self._main_curve = ScatterCurve(parent_item=self) - # Specific GUI elements - self.scatter_dialog = None - # Scan Data self.old_scan_id = None self.scan_id = None @@ -128,24 +129,26 @@ class ScatterWaveform(PlotBase): self.proxy_update_sync = pg.SignalProxy( self.sync_signal_update, rateLimit=25, slot=self.update_sync_curves ) - self._init_scatter_curve_settings() self.update_with_scan_history(-1) ################################################################################ # Widget Specific GUI interactions ################################################################################ + def _init_scatter_curve_settings(self): """ Initialize the scatter curve settings menu. """ - scatter_curve_settings = ScatterCurveSettings(parent=self, target_widget=self, popup=False) + self.scatter_curve_settings = ScatterCurveSettings( + parent=self, target_widget=self, popup=False + ) self.side_panel.add_menu( action_id="scatter_curve", icon_name="scatter_plot", tooltip="Show Scatter Curve Settings", - widget=scatter_curve_settings, + widget=self.scatter_curve_settings, title="Scatter Curve Settings", ) @@ -461,17 +464,30 @@ class ScatterWaveform(PlotBase): logger.warning(f"Neither scan_id or scan_number was provided, fetching the latest scan") scan_index = -1 - if scan_index is not None: - if len(self.client.history) == 0: - logger.info("No scans executed so far. Skipping scan history update.") - return - - self.scan_item = self.client.history[scan_index] - metadata = self.scan_item.metadata - self.scan_id = metadata["bec"]["scan_id"] - else: + if scan_index is None: self.scan_id = scan_id self.scan_item = self.client.history.get_by_scan_id(scan_id) + self.sync_signal_update.emit() + return + + if scan_index == -1: + scan_item = self.client.queue.scan_storage.current_scan + if scan_item is not None: + if scan_item.status_message is None: + logger.warning(f"Scan item with {scan_item.scan_id} has no status message.") + return + self.scan_item = scan_item + self.scan_id = scan_item.scan_id + self.sync_signal_update.emit() + return + + if len(self.client.history) == 0: + logger.info("No scans executed so far. Skipping scan history update.") + return + + self.scan_item = self.client.history[scan_index] + metadata = self.scan_item.metadata + self.scan_id = metadata["bec"]["scan_id"] self.sync_signal_update.emit() @@ -487,6 +503,22 @@ class ScatterWaveform(PlotBase): self.crosshair.clear_markers() self._main_curve.clear() + def cleanup(self): + """ + Cleanup the widget and disconnect all signals. + """ + if self.scatter_dialog is not None: + self.scatter_dialog.close() + self.scatter_dialog.deleteLater() + if self.scatter_curve_settings is not None: + self.scatter_curve_settings.cleanup() + print("scatter_curve_settings celanup called") + self.bec_dispatcher.disconnect_slot(self.on_scan_status, MessageEndpoints.scan_status()) + self.bec_dispatcher.disconnect_slot(self.on_scan_progress, MessageEndpoints.scan_progress()) + self.plot_item.removeItem(self._main_curve) + self._main_curve = None + super().cleanup() + class DemoApp(QMainWindow): # pragma: no cover def __init__(self): diff --git a/bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_setting.py b/bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_setting.py index 2dd05ce4..703266af 100644 --- a/bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_setting.py +++ b/bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_setting.py @@ -122,3 +122,17 @@ class ScatterCurveSettings(SettingWidget): color_map=color_map, validate_bec=validate_bec, ) + + def cleanup(self): + self.ui.x_name.close() + self.ui.x_name.deleteLater() + self.ui.x_entry.close() + self.ui.x_entry.deleteLater() + self.ui.y_name.close() + self.ui.y_name.deleteLater() + self.ui.y_entry.close() + self.ui.y_entry.deleteLater() + self.ui.z_name.close() + self.ui.z_name.deleteLater() + self.ui.z_entry.close() + self.ui.z_entry.deleteLater() diff --git a/bec_widgets/widgets/plots/toolbar_bundles/mouse_interactions.py b/bec_widgets/widgets/plots/toolbar_bundles/mouse_interactions.py index c47aab01..390d9585 100644 --- a/bec_widgets/widgets/plots/toolbar_bundles/mouse_interactions.py +++ b/bec_widgets/widgets/plots/toolbar_bundles/mouse_interactions.py @@ -56,9 +56,6 @@ class MouseInteractionToolbarBundle(ToolbarBundle): rect.action.toggled.connect(self.enable_mouse_rectangle_mode) auto.action.triggered.connect(self.autorange_plot) - # Give some time to check the state - QTimer.singleShot(10, self.get_viewbox_mode) - def get_viewbox_mode(self): """ Returns the current interaction mode of a PyQtGraph ViewBox and sets the corresponding action. diff --git a/bec_widgets/widgets/plots/waveform/curve.py b/bec_widgets/widgets/plots/waveform/curve.py index 261b73f9..da323b11 100644 --- a/bec_widgets/widgets/plots/waveform/curve.py +++ b/bec_widgets/widgets/plots/waveform/curve.py @@ -93,7 +93,8 @@ class Curve(BECConnector, pg.PlotDataItem): self.config = config self.parent_item = parent_item self.parent_id = self.parent_item.gui_id - super().__init__(name=name, config=config, gui_id=gui_id, **kwargs) + object_name = name.replace("-", "_") if name else None + super().__init__(name=name, object_name=object_name, config=config, gui_id=gui_id, **kwargs) self.apply_config() self.dap_params = None diff --git a/bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_setting.py b/bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_setting.py index 4337e07d..24d94dfe 100644 --- a/bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_setting.py +++ b/bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_setting.py @@ -28,7 +28,6 @@ class CurveSetting(SettingWidget): def __init__(self, parent=None, target_widget: Waveform = None, *args, **kwargs): super().__init__(parent=parent, *args, **kwargs) self.setProperty("skip_settings", True) - self.setObjectName("CurveSetting") self.target_widget = target_widget self.layout = QVBoxLayout(self) @@ -52,7 +51,7 @@ class CurveSetting(SettingWidget): self.spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.device_x_label = QLabel("Device") - self.device_x = DeviceLineEdit() + self.device_x = DeviceLineEdit(parent=self) self.signal_x_label = QLabel("Signal") self.signal_x = QLineEdit() @@ -117,3 +116,10 @@ class CurveSetting(SettingWidget): """Refresh the curve tree and the x axis combo box in the case Waveform is modified from rpc.""" self.curve_manager.refresh_from_waveform() self._get_x_mode_from_waveform() + + def cleanup(self): + """Cleanup the widget.""" + self.device_x.close() + self.device_x.deleteLater() + self.curve_manager.close() + self.curve_manager.deleteLater() diff --git a/bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py b/bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py index f16c864e..9805c3eb 100644 --- a/bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py +++ b/bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py @@ -3,6 +3,7 @@ from __future__ import annotations import json from typing import TYPE_CHECKING +from bec_lib.logger import bec_logger from bec_qthemes._icon.material_icons import material_icon from qtpy.QtGui import QColor from qtpy.QtWidgets import ( @@ -36,6 +37,9 @@ if TYPE_CHECKING: # pragma: no cover from bec_widgets.widgets.plots.waveform.waveform import Waveform +logger = bec_logger.logger + + class ColorButton(QPushButton): """A QPushButton subclass that displays a color. @@ -110,11 +114,16 @@ class CurveRow(QTreeWidgetItem): self.curve_tree = tree.parent() # The CurveTree widget self.curve_tree.all_items.append(self) # Track stable ordering + # BEC user input + self.device_edit = None + self.dap_combo = None + self.dev = device_manager self.entry_validator = EntryValidator(self.dev) self.config = config or CurveConfig() self.source = self.config.source + self.dap_rows = [] # Create column 0 (Actions) self._init_actions() @@ -155,8 +164,8 @@ class CurveRow(QTreeWidgetItem): """Create columns 1 and 2. For device rows, we have device/entry edits; for dap rows, label/model combo.""" if self.source == "device": # Device row: columns 1..2 are device line edits - self.device_edit = DeviceLineEdit() - self.entry_edit = QLineEdit() # TODO in future will be signal line edit + self.device_edit = DeviceLineEdit(parent=self.tree) + self.entry_edit = QLineEdit(parent=self.tree) # TODO in future will be signal line edit if self.config.signal: self.device_edit.setText(self.config.signal.name or "") self.entry_edit.setText(self.config.signal.entry or "") @@ -168,7 +177,7 @@ class CurveRow(QTreeWidgetItem): # DAP row: column1= "Model" label, column2= DapComboBox self.label_widget = QLabel("Model") self.tree.setItemWidget(self, 1, self.label_widget) - self.dap_combo = DapComboBox() + self.dap_combo = DapComboBox(parent=self.tree) self.dap_combo.populate_fit_model_combobox() # If config.signal has a dap if self.config.signal and self.config.signal.dap: @@ -258,15 +267,31 @@ class CurveRow(QTreeWidgetItem): def remove_self(self): """Remove this row from the tree and from the parent's item list.""" - # If top-level: + # Recursively remove all child rows first + for i in reversed(range(self.childCount())): + child = self.child(i) + if isinstance(child, CurveRow): + child.remove_self() + + # Clean up the widget references if they still exist + if getattr(self, "device_edit", None) is not None: + self.device_edit.close() + self.device_edit.deleteLater() + self.device_edit = None + + if getattr(self, "dap_combo", None) is not None: + self.dap_combo.close() + self.dap_combo.deleteLater() + self.dap_combo = None + + # Remove the item from the tree widget index = self.tree.indexOfTopLevelItem(self) if index != -1: self.tree.takeTopLevelItem(index) - else: - # If child item - if self.parent_item: - self.parent_item.removeChild(self) - # Also remove from all_items + elif self.parent_item: + self.parent_item.removeChild(self) + + # Finally, remove self from the registration list in the curve tree curve_tree = self.tree.parent() if self in curve_tree.all_items: curve_tree.all_items.remove(self) @@ -320,6 +345,10 @@ class CurveRow(QTreeWidgetItem): return self.config.model_dump() + def closeEvent(self, event) -> None: + logger.info(f"CurveRow closeEvent: {self.config.label}") + return super().closeEvent(event) + class CurveTree(BECWidget, QWidget): """A tree widget that manages device and DAP curves.""" @@ -535,3 +564,13 @@ class CurveTree(BECWidget, QWidget): for dap in dap_curves: if dap.config.parent_label == dev.config.label: CurveRow(self.tree, parent_item=dr, config=dap.config, device_manager=self.dev) + + def cleanup(self): + """Cleanup the widget.""" + all_items = list(self.all_items) + for item in all_items: + item.remove_self() + + def closeEvent(self, event): + self.cleanup() + return super().closeEvent(event) diff --git a/bec_widgets/widgets/plots/waveform/waveform.py b/bec_widgets/widgets/plots/waveform/waveform.py index f090da3d..b342e386 100644 --- a/bec_widgets/widgets/plots/waveform/waveform.py +++ b/bec_widgets/widgets/plots/waveform/waveform.py @@ -10,14 +10,7 @@ from bec_lib import bec_logger, messages from bec_lib.endpoints import MessageEndpoints from pydantic import Field, ValidationError, field_validator from qtpy.QtCore import QTimer, Signal -from qtpy.QtWidgets import ( - QApplication, - QDialog, - QHBoxLayout, - QMainWindow, - QVBoxLayout, - QWidget, -) +from qtpy.QtWidgets import QApplication, QDialog, QHBoxLayout, QMainWindow, QVBoxLayout, QWidget from bec_widgets.utils import ConnectionConfig from bec_widgets.utils.bec_signal_proxy import BECSignalProxy @@ -320,6 +313,8 @@ class Waveform(PlotBase): """ Slot for when the axis settings dialog is closed. """ + self.curve_settings_dialog.close() + self.curve_settings_dialog.deleteLater() self.curve_settings_dialog = None self.toolbar.widgets["curve"].action.setChecked(False) @@ -863,6 +858,9 @@ class Waveform(PlotBase): Clear all curves from the plot widget. """ curve_list = self.curves + self._dap_curves = [] + self._sync_curves = [] + self._async_curves = [] for curve in curve_list: self.remove_curve(curve.name()) if self.crosshair is not None: @@ -943,6 +941,7 @@ class Waveform(PlotBase): self.on_async_readback, MessageEndpoints.device_async_readback(self.scan_id, curve.name()), ) + curve.rpc_register.remove_rpc(curve) # Remove itself from the DAP summary only for side panels if ( @@ -1330,7 +1329,9 @@ class Waveform(PlotBase): # find the device curve parent_curve = self._find_curve_by_label(parent_label) if parent_curve is None: - logger.warning(f"No device curve found for DAP curve '{dap_curve.name()}'!") + logger.warning( + f"No device curve found for DAP curve '{dap_curve.name()}'!" + ) # TODO triggerd when DAP curve is removed from the curve dialog, why? continue x_data, y_data = parent_curve.get_data() @@ -1565,7 +1566,6 @@ class Waveform(PlotBase): found_sync = True else: logger.warning("Device {dev_name} not found in readout priority list.") - # Determine the mode of the scan if found_async and found_sync: mode = "mixed" @@ -1599,18 +1599,31 @@ class Waveform(PlotBase): logger.warning(f"Neither scan_id or scan_number was provided, fetching the latest scan") scan_index = -1 - if scan_index is not None: - if len(self.client.history) == 0: - logger.info("No scans executed so far. Skipping scan history update.") - return - - self.scan_item = self.client.history[scan_index] - metadata = self.scan_item.metadata - self.scan_id = metadata["bec"]["scan_id"] - else: + if scan_index is None: self.scan_id = scan_id self.scan_item = self.client.history.get_by_scan_id(scan_id) + self._emit_signal_update() + return + if scan_index == -1: + scan_item = self.client.queue.scan_storage.current_scan + if scan_item is not None: + self.scan_item = scan_item + self.scan_id = scan_item.scan_id + self._emit_signal_update() + return + + if len(self.client.history) == 0: + logger.info("No scans executed so far. Skipping scan history update.") + return + + self.scan_item = self.client.history[scan_index] + metadata = self.scan_item.metadata + self.scan_id = metadata["bec"]["scan_id"] + + self._emit_signal_update() + + def _emit_signal_update(self): self._categorise_device_curves() self.setup_dap_for_scan() @@ -1733,9 +1746,11 @@ class Waveform(PlotBase): self.clear_all() if self.curve_settings_dialog is not None: self.curve_settings_dialog.close() + self.curve_settings_dialog.deleteLater() self.curve_settings_dialog = None if self.dap_summary_dialog is not None: self.dap_summary_dialog.close() + self.dap_summary_dialog.deleteLater() self.dap_summary_dialog = None super().cleanup() diff --git a/tests/unit_tests/test_axis_settings.py b/tests/unit_tests/test_axis_settings.py index 4f8c8ee1..a896d0c6 100644 --- a/tests/unit_tests/test_axis_settings.py +++ b/tests/unit_tests/test_axis_settings.py @@ -29,8 +29,6 @@ def test_axis_settings_init(axis_settings_fixture): assert axis_settings.layout.count() == 1 # scroll area # Check the target assert axis_settings.target_widget == plot_base - # Check the object name - assert axis_settings.objectName() == "AxisSettings" def test_change_ui_updates_plot_base(axis_settings_fixture, qtbot): diff --git a/tests/unit_tests/test_curve_settings.py b/tests/unit_tests/test_curve_settings.py index ad239790..ff2f63d3 100644 --- a/tests/unit_tests/test_curve_settings.py +++ b/tests/unit_tests/test_curve_settings.py @@ -33,8 +33,6 @@ def test_curve_setting_init(curve_setting_fixture): """ curve_setting, wf = curve_setting_fixture - # Basic checks - assert curve_setting.objectName() == "CurveSetting" # The layout should be QVBoxLayout assert isinstance(curve_setting.layout, QVBoxLayout) diff --git a/tests/unit_tests/test_waveform_next_gen.py b/tests/unit_tests/test_waveform_next_gen.py index 903f4b2b..2aa934d7 100644 --- a/tests/unit_tests/test_waveform_next_gen.py +++ b/tests/unit_tests/test_waveform_next_gen.py @@ -134,12 +134,6 @@ def test_curve_access_pattern(qtbot, mocked_client): assert wf.get_curve(0) == c1 assert wf.get_curve(1) == c2 - # Check that the curve is accessible by label - assert wf["bpm4i-bpm4i"] == c1 - assert wf["bpm3a-bpm3a"] == c2 - assert wf[0] == c1 - assert wf[1] == c2 - assert wf.curves[0] == c1 assert wf.curves[1] == c2