mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-05-13 10:05:44 +02:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c0600a0cba | |||
| 1057db9d76 | |||
| be35e249f9 | |||
| cdd833dfc2 | |||
| 3c7834b492 | |||
| acd35a2786 |
@@ -1,6 +1,17 @@
|
||||
# CHANGELOG
|
||||
|
||||
|
||||
## v3.9.1 (2026-05-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Logpanel fixture overwriting xread
|
||||
([`3c7834b`](https://github.com/bec-project/bec_widgets/commit/3c7834b492a5d2da13689f58b20caf38dda9ac1d))
|
||||
|
||||
- **scan_control**: Restore scan parameters from history are fetched on demand with button
|
||||
([`acd35a2`](https://github.com/bec-project/bec_widgets/commit/acd35a278660ce4962167af6237b5d12007f0774))
|
||||
|
||||
|
||||
## v3.9.0 (2026-05-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -143,6 +143,15 @@ def get_plugin_designer_registry() -> dict[str, tuple[str, str]]:
|
||||
return {}
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_plugin_widget_icons() -> dict[str, str]:
|
||||
"""If there is a plugin repository installed, return the designer widget icon registry."""
|
||||
designer_module = get_plugin_designer_module()
|
||||
if designer_module and hasattr(designer_module, "widget_icons"):
|
||||
return designer_module.widget_icons
|
||||
return {}
|
||||
|
||||
|
||||
def get_all_plugin_widgets() -> BECClassContainer:
|
||||
"""If there is a plugin repository installed, load all widgets from it."""
|
||||
if plugin := user_widget_plugin():
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from functools import lru_cache
|
||||
from typing import Literal, Mapping, Sequence
|
||||
|
||||
import slugify
|
||||
@@ -20,7 +21,12 @@ from qtpy.QtWidgets import (
|
||||
import bec_widgets.widgets.containers.qt_ads as QtAds
|
||||
from bec_widgets import BECWidget, SafeProperty, SafeSlot
|
||||
from bec_widgets.cli.designer_plugins import widget_icons
|
||||
from bec_widgets.utils.bec_plugin_helper import (
|
||||
get_plugin_rpc_widget_registry,
|
||||
get_plugin_widget_icons,
|
||||
)
|
||||
from bec_widgets.utils.colors import apply_theme
|
||||
from bec_widgets.utils.plugin_utils import get_rpc_widget_registry
|
||||
from bec_widgets.utils.rpc_decorator import rpc_timeout
|
||||
from bec_widgets.utils.rpc_widget_handler import widget_handler
|
||||
from bec_widgets.utils.toolbars.actions import (
|
||||
@@ -74,6 +80,19 @@ PROFILE_STATE_KEYS = {key: SETTINGS_KEYS[key] for key in ("geom", "state", "ads_
|
||||
StartupProfile = Literal["restore", "skip"] | str | None
|
||||
|
||||
|
||||
@lru_cache
|
||||
def _plugin_toolbar_actions() -> dict[str, tuple[str, str, str]]:
|
||||
plugin_registry = get_plugin_rpc_widget_registry()
|
||||
internal_registry = get_rpc_widget_registry()
|
||||
plugin_icons = get_plugin_widget_icons()
|
||||
|
||||
return {
|
||||
widget_name: (plugin_icons.get(widget_name, "widgets"), f"Add {widget_name}", widget_name)
|
||||
for widget_name in sorted(plugin_registry)
|
||||
if widget_name not in internal_registry
|
||||
}
|
||||
|
||||
|
||||
class BECDockArea(DockAreaWidget):
|
||||
RPC = True
|
||||
PLUGIN = False
|
||||
@@ -390,6 +409,10 @@ class BECDockArea(DockAreaWidget):
|
||||
_build_menu("menu_devices", "Add Device Control ", device_actions)
|
||||
_build_menu("menu_utils", "Add Utils ", util_actions)
|
||||
|
||||
plugin_actions = _plugin_toolbar_actions()
|
||||
if plugin_actions:
|
||||
_build_menu("menu_plugins", "Add Plugins ", plugin_actions)
|
||||
|
||||
# Create flat toolbar bundles for each widget type
|
||||
def _build_flat_bundles(category: str, mapping: dict[str, tuple[str, str, str]]):
|
||||
bundle = ToolbarBundle(f"flat_{category}", self.toolbar.components)
|
||||
@@ -460,14 +483,16 @@ class BECDockArea(DockAreaWidget):
|
||||
bda.add_action("dark_mode")
|
||||
self.toolbar.add_bundle(bda)
|
||||
|
||||
self._apply_toolbar_layout()
|
||||
|
||||
# Store mappings on self for use in _hook_toolbar
|
||||
# Store mappings on self for use in _hook_toolbar and _apply_toolbar_layout
|
||||
self._ACTION_MAPPINGS = {
|
||||
"menu_plots": plot_actions,
|
||||
"menu_devices": device_actions,
|
||||
"menu_utils": util_actions,
|
||||
}
|
||||
if plugin_actions:
|
||||
self._ACTION_MAPPINGS["menu_plugins"] = plugin_actions
|
||||
|
||||
self._apply_toolbar_layout()
|
||||
|
||||
def _hook_toolbar(self):
|
||||
def _connect_menu(menu_key: str):
|
||||
@@ -476,7 +501,8 @@ class BECDockArea(DockAreaWidget):
|
||||
|
||||
# first two items not needed for this part
|
||||
for key, (_, _, widget_type) in mapping.items():
|
||||
act = menu.actions[key].action
|
||||
toolbar_action = menu.actions[key]
|
||||
act = toolbar_action.action
|
||||
if key == "terminal":
|
||||
act.triggered.connect(
|
||||
lambda _, t=widget_type: self.new(widget=t, closable=True, startup_cmd=None)
|
||||
@@ -487,12 +513,18 @@ class BECDockArea(DockAreaWidget):
|
||||
widget=t, closable=True, show_settings_action=False
|
||||
)
|
||||
)
|
||||
elif menu_key == "menu_plugins":
|
||||
act.triggered.connect(
|
||||
lambda _, t=widget_type, a=toolbar_action: self._new_plugin_widget(t, a)
|
||||
)
|
||||
else:
|
||||
act.triggered.connect(lambda _, t=widget_type: self.new(widget=t))
|
||||
|
||||
_connect_menu("menu_plots")
|
||||
_connect_menu("menu_devices")
|
||||
_connect_menu("menu_utils")
|
||||
if "menu_plugins" in self._ACTION_MAPPINGS:
|
||||
_connect_menu("menu_plugins")
|
||||
|
||||
def _connect_flat_actions(mapping: dict[str, tuple[str, str, str]]):
|
||||
for action_id, (_, _, widget_type) in mapping.items():
|
||||
@@ -507,6 +539,10 @@ class BECDockArea(DockAreaWidget):
|
||||
self.toolbar.components.get_action("attach_all").action.triggered.connect(self.attach_all)
|
||||
self.toolbar.components.get_action("screenshot").action.triggered.connect(self.screenshot)
|
||||
|
||||
def _new_plugin_widget(self, widget_type: str, toolbar_action: MaterialIconAction) -> None:
|
||||
# Created as helper method for simple tests
|
||||
self.new(widget=widget_type, dock_icon=toolbar_action.get_icon())
|
||||
|
||||
def _set_editable(self, editable: bool) -> None:
|
||||
self.workspace_is_locked = not editable
|
||||
self._editable = editable
|
||||
@@ -1108,14 +1144,10 @@ class BECDockArea(DockAreaWidget):
|
||||
if mode_key == "user":
|
||||
bundles = ["spacer_bundle", "workspace", "dock_actions"]
|
||||
elif mode_key == "creator":
|
||||
bundles = [
|
||||
"menu_plots",
|
||||
"menu_devices",
|
||||
"menu_utils",
|
||||
"spacer_bundle",
|
||||
"workspace",
|
||||
"dock_actions",
|
||||
]
|
||||
bundles = ["menu_plots", "menu_devices", "menu_utils"]
|
||||
if "menu_plugins" in getattr(self, "_ACTION_MAPPINGS", {}):
|
||||
bundles.append("menu_plugins")
|
||||
bundles += ["spacer_bundle", "workspace", "dock_actions"]
|
||||
elif mode_key == "plot":
|
||||
bundles = ["flat_plots", "spacer_bundle", "workspace", "dock_actions"]
|
||||
elif mode_key == "device":
|
||||
|
||||
@@ -26,7 +26,6 @@ from bec_widgets.utils.error_popups import SafeProperty, SafeSlot
|
||||
from bec_widgets.widgets.control.buttons.stop_button.stop_button import StopButton
|
||||
from bec_widgets.widgets.control.scan_control.scan_group_box import ScanGroupBox
|
||||
from bec_widgets.widgets.editors.scan_metadata.scan_metadata import ScanMetadata
|
||||
from bec_widgets.widgets.utility.toggle.toggle import ToggleSwitch
|
||||
|
||||
|
||||
class ScanParameterConfig(BaseModel):
|
||||
@@ -84,7 +83,6 @@ class ScanControl(BECWidget, QWidget):
|
||||
self.kwarg_boxes = []
|
||||
self.expert_mode = False # TODO implement in the future versions
|
||||
self.previous_scan = None
|
||||
self.last_scan_found = None
|
||||
|
||||
# Widget Default Parameters
|
||||
self.config.default_scan = default_scan
|
||||
@@ -123,17 +121,12 @@ class ScanControl(BECWidget, QWidget):
|
||||
scan_selection_layout.addWidget(self.comboBox_scan_selection, 1)
|
||||
self.scan_selection_group.layout().addLayout(scan_selection_layout)
|
||||
|
||||
# Label to reload the last scan parameters within scan selection group box
|
||||
self.toggle_layout = QHBoxLayout()
|
||||
self.toggle_layout.addSpacerItem(
|
||||
QSpacerItem(0, 0, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
# Button to reload the last scan parameters on demand.
|
||||
self.last_scan_button = QPushButton(
|
||||
"Restore last scan parameters", self.scan_selection_group
|
||||
)
|
||||
self.last_scan_label = QLabel("Restore last scan parameters", self.scan_selection_group)
|
||||
self.toggle = ToggleSwitch(parent=self.scan_selection_group, checked=False)
|
||||
self.toggle.enabled.connect(self.request_last_executed_scan_parameters)
|
||||
self.toggle_layout.addWidget(self.last_scan_label)
|
||||
self.toggle_layout.addWidget(self.toggle)
|
||||
self.scan_selection_group.layout().addLayout(self.toggle_layout)
|
||||
self.last_scan_button.clicked.connect(self.request_last_executed_scan_parameters)
|
||||
self.scan_selection_group.layout().addWidget(self.last_scan_button)
|
||||
self.scan_selection_group.setSizePolicy(
|
||||
QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed
|
||||
)
|
||||
@@ -206,7 +199,6 @@ class ScanControl(BECWidget, QWidget):
|
||||
"""Callback for scan selection combo box"""
|
||||
selected_scan_name = self.comboBox_scan_selection.currentText()
|
||||
self.scan_selected.emit(selected_scan_name)
|
||||
self.request_last_executed_scan_parameters()
|
||||
self.restore_scan_parameters(selected_scan_name)
|
||||
|
||||
@SafeSlot()
|
||||
@@ -215,10 +207,6 @@ class ScanControl(BECWidget, QWidget):
|
||||
"""
|
||||
Requests the last executed scan parameters from BEC and restores them to the scan control widget.
|
||||
"""
|
||||
self.last_scan_found = False
|
||||
if not self.toggle.checked:
|
||||
return
|
||||
|
||||
current_scan = self.comboBox_scan_selection.currentText()
|
||||
history = (
|
||||
self.client.connector.xread(
|
||||
@@ -246,8 +234,6 @@ class ScanControl(BECWidget, QWidget):
|
||||
if merged and self.kwarg_boxes:
|
||||
for box in self.kwarg_boxes:
|
||||
box.set_parameters(merged)
|
||||
|
||||
self.last_scan_found = True
|
||||
break
|
||||
|
||||
@SafeProperty(str)
|
||||
@@ -496,8 +482,6 @@ class ScanControl(BECWidget, QWidget):
|
||||
Args:
|
||||
scan_name(str): Name of the scan to restore the parameters for.
|
||||
"""
|
||||
if self.last_scan_found is True:
|
||||
return
|
||||
scan_params = self.config.scans.get(scan_name, None)
|
||||
if scan_params is None and self.previous_scan is None:
|
||||
return
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "bec_widgets"
|
||||
version = "3.9.0"
|
||||
version = "3.9.1"
|
||||
description = "BEC Widgets"
|
||||
requires-python = ">=3.11"
|
||||
classifiers = [
|
||||
@@ -60,6 +60,7 @@ qtermwidget = ["pyside6_qtermwidget"]
|
||||
|
||||
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
@@ -2,7 +2,10 @@ from importlib.machinery import FileFinder, SourceFileLoader
|
||||
from types import ModuleType
|
||||
from unittest import mock
|
||||
|
||||
from bec_widgets.utils.bec_plugin_helper import _all_widgets_from_all_submods
|
||||
from bec_widgets.utils.bec_plugin_helper import (
|
||||
_all_widgets_from_all_submods,
|
||||
get_plugin_widget_icons,
|
||||
)
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.utils.plugin_utils import BECClassContainer, BECClassInfo
|
||||
|
||||
@@ -69,3 +72,29 @@ def test_all_widgets_from_module_no_widgets():
|
||||
widgets = _all_widgets_from_all_submods(module).as_dict()
|
||||
|
||||
assert widgets == {}
|
||||
|
||||
|
||||
def test_get_plugin_widget_icons_from_designer_module():
|
||||
designer_module = mock.MagicMock(spec=ModuleType)
|
||||
designer_module.widget_icons = {"PluginWidget": "star"}
|
||||
|
||||
get_plugin_widget_icons.cache_clear()
|
||||
try:
|
||||
with mock.patch(
|
||||
"bec_widgets.utils.bec_plugin_helper.get_plugin_designer_module",
|
||||
return_value=designer_module,
|
||||
):
|
||||
assert get_plugin_widget_icons() == {"PluginWidget": "star"}
|
||||
finally:
|
||||
get_plugin_widget_icons.cache_clear()
|
||||
|
||||
|
||||
def test_get_plugin_widget_icons_without_designer_module():
|
||||
get_plugin_widget_icons.cache_clear()
|
||||
try:
|
||||
with mock.patch(
|
||||
"bec_widgets.utils.bec_plugin_helper.get_plugin_designer_module", return_value=None
|
||||
):
|
||||
assert get_plugin_widget_icons() == {}
|
||||
finally:
|
||||
get_plugin_widget_icons.cache_clear()
|
||||
|
||||
@@ -11,6 +11,7 @@ from qtpy.QtGui import QPixmap
|
||||
from qtpy.QtWidgets import QDialog, QMessageBox, QWidget
|
||||
|
||||
import bec_widgets.widgets.containers.dock_area.basic_dock_area as basic_dock_module
|
||||
import bec_widgets.widgets.containers.dock_area.dock_area as dock_area_module
|
||||
import bec_widgets.widgets.containers.dock_area.profile_utils as profile_utils
|
||||
from bec_widgets.widgets.containers.dock_area.basic_dock_area import (
|
||||
DockAreaWidget,
|
||||
@@ -68,6 +69,13 @@ def temp_profile_dir():
|
||||
return os.environ["BECWIDGETS_PROFILE_DIR"]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def clear_plugin_toolbar_actions_cache():
|
||||
dock_area_module._plugin_toolbar_actions.cache_clear()
|
||||
yield
|
||||
dock_area_module._plugin_toolbar_actions.cache_clear()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def module_profile_factory(monkeypatch, tmp_path):
|
||||
"""Provide a helper to create synthetic module-level (read-only) profiles."""
|
||||
@@ -985,6 +993,64 @@ class TestToolbarFunctionality:
|
||||
# Verify save was called with the filename
|
||||
mock_screenshot.save.assert_called_once_with(str(screenshot_path))
|
||||
|
||||
def test_plugin_toolbar_actions_empty_when_no_plugins(self, clear_plugin_toolbar_actions_cache):
|
||||
"""Test that no plugin toolbar actions are produced when no plugin widgets exist."""
|
||||
with patch(
|
||||
"bec_widgets.widgets.containers.dock_area.dock_area.get_plugin_rpc_widget_registry",
|
||||
return_value={},
|
||||
):
|
||||
plugin_actions = dock_area_module._plugin_toolbar_actions()
|
||||
|
||||
assert plugin_actions == {}
|
||||
|
||||
def test_plugin_toolbar_actions_include_available_plugins(
|
||||
self, clear_plugin_toolbar_actions_cache
|
||||
):
|
||||
"""Test that plugin toolbar actions are built from RPC widgets and generated icons."""
|
||||
plugin_registry = {
|
||||
"FakePluginWidget": ("fake_plugin.widgets.fake_plugin_widget", "FakePluginWidget")
|
||||
}
|
||||
with (
|
||||
patch(
|
||||
"bec_widgets.widgets.containers.dock_area.dock_area.get_plugin_rpc_widget_registry",
|
||||
return_value=plugin_registry,
|
||||
),
|
||||
patch(
|
||||
"bec_widgets.widgets.containers.dock_area.dock_area.get_plugin_widget_icons",
|
||||
return_value={"FakePluginWidget": "star"},
|
||||
),
|
||||
):
|
||||
plugin_actions = dock_area_module._plugin_toolbar_actions()
|
||||
|
||||
assert plugin_actions == {
|
||||
"FakePluginWidget": ("star", "Add FakePluginWidget", "FakePluginWidget")
|
||||
}
|
||||
|
||||
def test_plugin_toolbar_actions_ignore_builtin_name_collisions(
|
||||
self, clear_plugin_toolbar_actions_cache
|
||||
):
|
||||
"""Test that plugin widgets shadowed by built-ins are not added to the plugin menu."""
|
||||
plugin_registry = {"Waveform": ("fake_plugin.widgets.waveform", "Waveform")}
|
||||
with patch(
|
||||
"bec_widgets.widgets.containers.dock_area.dock_area.get_plugin_rpc_widget_registry",
|
||||
return_value=plugin_registry,
|
||||
):
|
||||
plugin_actions = dock_area_module._plugin_toolbar_actions()
|
||||
|
||||
assert plugin_actions == {}
|
||||
|
||||
def test_new_plugin_widget_passes_toolbar_icon_to_new(self):
|
||||
"""Test that plugin widget creation passes the toolbar icon to dock creation."""
|
||||
dock_area = MagicMock()
|
||||
toolbar_action = MagicMock()
|
||||
dock_icon = object()
|
||||
toolbar_action.get_icon.return_value = dock_icon
|
||||
|
||||
BECDockArea._new_plugin_widget(dock_area, "FakePluginWidget", toolbar_action)
|
||||
|
||||
toolbar_action.get_icon.assert_called_once_with()
|
||||
dock_area.new.assert_called_once_with(widget="FakePluginWidget", dock_icon=dock_icon)
|
||||
|
||||
|
||||
class TestDockSettingsDialog:
|
||||
"""Test dock settings dialog functionality."""
|
||||
|
||||
@@ -62,16 +62,16 @@ TEST_LOG_MESSAGES = [
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def log_panel(qtbot, mocked_client):
|
||||
with patch.object(mocked_client.connector, "xread", lambda *_, **__: TEST_LOG_MESSAGES):
|
||||
widget = LogPanel()
|
||||
qtbot.addWidget(widget)
|
||||
qtbot.waitExposed(widget)
|
||||
yield widget
|
||||
widget._model.log_queue.cleanup()
|
||||
widget.close()
|
||||
widget.deleteLater()
|
||||
qtbot.wait(100)
|
||||
def log_panel(qtbot, mocked_client, monkeypatch):
|
||||
monkeypatch.setattr(mocked_client.connector, "xread", lambda *_, **__: TEST_LOG_MESSAGES)
|
||||
widget = LogPanel()
|
||||
qtbot.addWidget(widget)
|
||||
qtbot.waitExposed(widget)
|
||||
yield widget
|
||||
widget._model.log_queue.cleanup()
|
||||
widget.close()
|
||||
widget.deleteLater()
|
||||
qtbot.wait(100)
|
||||
|
||||
|
||||
def test_log_panel_init(qtbot, log_panel: LogPanel):
|
||||
|
||||
@@ -503,12 +503,47 @@ def test_changing_scans_remember_parameters(scan_control, mocked_client):
|
||||
assert grid_kwargs["burst_at_each_point"] == kwargs["burst_at_each_point"]
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Unreliable - GH issue #1134")
|
||||
def test_get_scan_parameters_from_redis(scan_control, mocked_client):
|
||||
def test_scan_selection_does_not_fetch_last_scan_parameters(
|
||||
scan_control, mocked_client, monkeypatch
|
||||
):
|
||||
xread = MagicMock(wraps=mocked_client.connector.xread)
|
||||
monkeypatch.setattr(mocked_client.connector, "xread", xread)
|
||||
|
||||
scan_control.comboBox_scan_selection.setCurrentText("line_scan")
|
||||
assert scan_control.comboBox_scan_selection.currentText() == "line_scan"
|
||||
|
||||
scan_control.comboBox_scan_selection.setCurrentText("grid_scan")
|
||||
|
||||
xread.assert_not_called()
|
||||
|
||||
|
||||
def test_restore_last_scan_parameters_button_fetches_on_demand(
|
||||
scan_control, mocked_client, monkeypatch
|
||||
):
|
||||
xread = MagicMock(wraps=mocked_client.connector.xread)
|
||||
monkeypatch.setattr(mocked_client.connector, "xread", xread)
|
||||
|
||||
scan_control.comboBox_scan_selection.setCurrentText("grid_scan")
|
||||
scan_control.comboBox_scan_selection.setCurrentText("line_scan")
|
||||
xread.assert_not_called()
|
||||
|
||||
scan_control.last_scan_button.click()
|
||||
|
||||
xread.assert_called_once_with(
|
||||
MessageEndpoints.scan_history(), from_start=True, user_id=scan_control.object_name
|
||||
)
|
||||
args, kwargs = scan_control.get_scan_parameters(bec_object=False)
|
||||
assert args == ["samx", 0.0, 2.0]
|
||||
assert kwargs["steps"] == 10
|
||||
assert kwargs["relative"] is False
|
||||
assert kwargs["exp_time"] == 2
|
||||
|
||||
|
||||
def test_get_scan_parameters_from_redis(scan_control):
|
||||
scan_name = "line_scan"
|
||||
scan_control.comboBox_scan_selection.setCurrentText(scan_name)
|
||||
|
||||
scan_control.toggle.checked = True
|
||||
scan_control.last_scan_button.click()
|
||||
|
||||
args, kwargs = scan_control.get_scan_parameters(bec_object=False)
|
||||
|
||||
@@ -588,8 +623,7 @@ def test_scan_metadata_is_passed_to_scan_function(scan_control: ScanControl):
|
||||
scans.grid_scan.assert_called_once_with(metadata=TEST_MD)
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Unreliable - GH issue #1134")
|
||||
def test_restore_parameters_with_fewer_arg_bundles(scan_control, qtbot):
|
||||
def test_restore_parameters_with_fewer_arg_bundles(scan_control):
|
||||
"""
|
||||
Ensure that when more argument bundles are present than exist in the
|
||||
stored history, restoring parameters regenerates the arg box to the
|
||||
@@ -605,8 +639,7 @@ def test_restore_parameters_with_fewer_arg_bundles(scan_control, qtbot):
|
||||
assert scan_control.arg_box.count_arg_rows() == 3
|
||||
|
||||
# Trigger restore of parameters from history
|
||||
scan_control.toggle.checked = True
|
||||
qtbot.wait(200)
|
||||
scan_control.last_scan_button.click()
|
||||
|
||||
# After restore, arg_box should have only one bundle (the history size)
|
||||
assert scan_control.arg_box.count_arg_rows() == 1
|
||||
|
||||
Reference in New Issue
Block a user