0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-13 19:21:50 +02:00

refactor(toolbar): split toolbar into components, bundles and connections

This commit is contained in:
2025-06-25 10:49:39 +02:00
committed by Jan Wyzula
parent f10140e0f3
commit db720e8fa4
48 changed files with 3415 additions and 2567 deletions

View File

@ -97,13 +97,15 @@ def test_new_dock_raises_for_invalid_name(bec_dock_area):
# Toolbar Actions
###################################
def test_toolbar_add_plot_waveform(bec_dock_area):
bec_dock_area.toolbar.widgets["menu_plots"].widgets["waveform"].trigger()
bec_dock_area.toolbar.components.get_action("menu_plots").actions["waveform"].action.trigger()
assert "waveform_0" in bec_dock_area.panels
assert bec_dock_area.panels["waveform_0"].widgets[0].config.widget_class == "Waveform"
def test_toolbar_add_plot_scatter_waveform(bec_dock_area):
bec_dock_area.toolbar.widgets["menu_plots"].widgets["scatter_waveform"].trigger()
bec_dock_area.toolbar.components.get_action("menu_plots").actions[
"scatter_waveform"
].action.trigger()
assert "scatter_waveform_0" in bec_dock_area.panels
assert (
bec_dock_area.panels["scatter_waveform_0"].widgets[0].config.widget_class
@ -112,19 +114,22 @@ def test_toolbar_add_plot_scatter_waveform(bec_dock_area):
def test_toolbar_add_plot_image(bec_dock_area):
bec_dock_area.toolbar.widgets["menu_plots"].widgets["image"].trigger()
bec_dock_area.toolbar.components.get_action("menu_plots").actions["image"].action.trigger()
assert "image_0" in bec_dock_area.panels
assert bec_dock_area.panels["image_0"].widgets[0].config.widget_class == "Image"
def test_toolbar_add_plot_motor_map(bec_dock_area):
bec_dock_area.toolbar.widgets["menu_plots"].widgets["motor_map"].trigger()
bec_dock_area.toolbar.components.get_action("menu_plots").actions["motor_map"].action.trigger()
assert "motor_map_0" in bec_dock_area.panels
assert bec_dock_area.panels["motor_map_0"].widgets[0].config.widget_class == "MotorMap"
def test_toolbar_add_multi_waveform(bec_dock_area):
bec_dock_area.toolbar.widgets["menu_plots"].widgets["multi_waveform"].trigger()
bec_dock_area.toolbar.components.get_action("menu_plots").actions[
"multi_waveform"
].action.trigger()
# Check if the MultiWaveform panel is created
assert "multi_waveform_0" in bec_dock_area.panels
assert (
bec_dock_area.panels["multi_waveform_0"].widgets[0].config.widget_class == "MultiWaveform"
@ -132,7 +137,9 @@ def test_toolbar_add_multi_waveform(bec_dock_area):
def test_toolbar_add_device_positioner_box(bec_dock_area):
bec_dock_area.toolbar.widgets["menu_devices"].widgets["positioner_box"].trigger()
bec_dock_area.toolbar.components.get_action("menu_devices").actions[
"positioner_box"
].action.trigger()
assert "positioner_box_0" in bec_dock_area.panels
assert (
bec_dock_area.panels["positioner_box_0"].widgets[0].config.widget_class == "PositionerBox"
@ -143,19 +150,21 @@ def test_toolbar_add_utils_queue(bec_dock_area, bec_queue_msg_full):
bec_dock_area.client.connector.set_and_publish(
MessageEndpoints.scan_queue_status(), bec_queue_msg_full
)
bec_dock_area.toolbar.widgets["menu_utils"].widgets["queue"].trigger()
bec_dock_area.toolbar.components.get_action("menu_utils").actions["queue"].action.trigger()
assert "bec_queue_0" in bec_dock_area.panels
assert bec_dock_area.panels["bec_queue_0"].widgets[0].config.widget_class == "BECQueue"
def test_toolbar_add_utils_status(bec_dock_area):
bec_dock_area.toolbar.widgets["menu_utils"].widgets["status"].trigger()
bec_dock_area.toolbar.components.get_action("menu_utils").actions["status"].action.trigger()
assert "bec_status_box_0" in bec_dock_area.panels
assert bec_dock_area.panels["bec_status_box_0"].widgets[0].config.widget_class == "BECStatusBox"
def test_toolbar_add_utils_progress_bar(bec_dock_area):
bec_dock_area.toolbar.widgets["menu_utils"].widgets["progress_bar"].trigger()
bec_dock_area.toolbar.components.get_action("menu_utils").actions[
"progress_bar"
].action.trigger()
assert "ring_progress_bar_0" in bec_dock_area.panels
assert (
bec_dock_area.panels["ring_progress_bar_0"].widgets[0].config.widget_class

View File

@ -157,10 +157,10 @@ def test_curve_tree_init(curve_tree_fixture):
assert curve_tree.color_palette == "plasma"
assert curve_tree.tree.columnCount() == 7
assert "add" in curve_tree.toolbar.widgets
assert "expand_all" in curve_tree.toolbar.widgets
assert "collapse_all" in curve_tree.toolbar.widgets
assert "renormalize_colors" in curve_tree.toolbar.widgets
assert curve_tree.toolbar.components.exists("add")
assert curve_tree.toolbar.components.exists("expand")
assert curve_tree.toolbar.components.exists("collapse")
assert curve_tree.toolbar.components.exists("renormalize_colors")
def test_add_new_curve(curve_tree_fixture):

View File

@ -39,9 +39,11 @@ def test_initialization(roi_tree, image_widget):
assert len(roi_tree.tree.findItems("", Qt.MatchContains)) == 0 # Empty tree initially
# Check toolbar actions
assert hasattr(roi_tree, "add_rect_action")
assert hasattr(roi_tree, "add_circle_action")
assert hasattr(roi_tree, "expand_toggle")
assert roi_tree.toolbar.components.get_action("roi_rectangle")
assert roi_tree.toolbar.components.get_action("roi_circle")
assert roi_tree.toolbar.components.get_action("roi_ellipse")
assert roi_tree.toolbar.components.get_action("expand_toggle")
assert roi_tree.toolbar.components.get_action("lock_unlock_all")
# Check tree view setup
assert roi_tree.tree.columnCount() == 3
@ -216,23 +218,25 @@ def test_draw_mode_toggle(roi_tree, qtbot):
assert roi_tree._roi_draw_mode is None
# Toggle rect mode on
roi_tree.add_rect_action.action.toggle()
rect_action = roi_tree.toolbar.components.get_action("roi_rectangle").action
circle_action = roi_tree.toolbar.components.get_action("roi_circle").action
rect_action.toggle()
assert roi_tree._roi_draw_mode == "rect"
assert roi_tree.add_rect_action.action.isChecked()
assert not roi_tree.add_circle_action.action.isChecked()
assert rect_action.isChecked()
assert not circle_action.isChecked()
# Toggle circle mode on (should turn off rect mode)
roi_tree.add_circle_action.action.toggle()
circle_action.toggle()
qtbot.wait(200)
assert roi_tree._roi_draw_mode == "circle"
assert not roi_tree.add_rect_action.action.isChecked()
assert roi_tree.add_circle_action.action.isChecked()
assert not rect_action.isChecked()
assert circle_action.isChecked()
# Toggle circle mode off
roi_tree.add_circle_action.action.toggle()
circle_action.toggle()
assert roi_tree._roi_draw_mode is None
assert not roi_tree.add_rect_action.action.isChecked()
assert not roi_tree.add_circle_action.action.isChecked()
assert not circle_action.isChecked()
assert not rect_action.isChecked()
def test_add_roi_from_toolbar(qtbot, mocked_client):
@ -250,7 +254,7 @@ def test_add_roi_from_toolbar(qtbot, mocked_client):
# Test rectangle ROI creation
# 1. Activate rectangle drawing mode
roi_tree.add_rect_action.action.setChecked(True)
roi_tree.toolbar.components.get_action("roi_rectangle").action.setChecked(True)
assert roi_tree._roi_draw_mode == "rect"
# Get plot widget and view
@ -294,8 +298,8 @@ def test_add_roi_from_toolbar(qtbot, mocked_client):
# Test circle ROI creation
# Reset ROI draw mode
roi_tree.add_rect_action.action.setChecked(False)
roi_tree.add_circle_action.action.setChecked(True)
roi_tree.toolbar.components.get_action("roi_rectangle").action.setChecked(False)
roi_tree.toolbar.components.get_action("roi_circle").action.setChecked(True)
assert roi_tree._roi_draw_mode == "circle"
# Define new positions for circle ROI

View File

@ -242,10 +242,11 @@ def test_image_data_update_1d(qtbot, mocked_client):
def test_toolbar_actions_presence(qtbot, mocked_client):
bec_image_view = create_widget(qtbot, Image, client=mocked_client)
assert "autorange_image" in bec_image_view.toolbar.widgets
assert "lock_aspect_ratio" in bec_image_view.toolbar.bundles["mouse_interaction"]
assert "processing" in bec_image_view.toolbar.bundles
assert "selection" in bec_image_view.toolbar.bundles
assert bec_image_view.toolbar.components.exists("image_autorange")
assert bec_image_view.toolbar.components.exists("lock_aspect_ratio")
assert bec_image_view.toolbar.components.exists("image_processing_fft")
assert bec_image_view.toolbar.components.exists("image_device_combo")
assert bec_image_view.toolbar.components.exists("image_dim_combo")
def test_image_processing_fft_toggle(qtbot, mocked_client):
@ -304,8 +305,8 @@ def test_setting_vrange_with_colorbar(qtbot, mocked_client, colorbar_type):
def test_setup_image_from_toolbar(qtbot, mocked_client):
bec_image_view = create_widget(qtbot, Image, client=mocked_client)
bec_image_view.selection_bundle.device_combo_box.setCurrentText("eiger")
bec_image_view.selection_bundle.dim_combo_box.setCurrentText("2d")
bec_image_view.device_combo_box.setCurrentText("eiger")
bec_image_view.dim_combo_box.setCurrentText("2d")
assert bec_image_view.monitor == "eiger"
assert bec_image_view.subscriptions["main"].source == "device_monitor_2d"
@ -318,17 +319,17 @@ def test_image_actions_interactions(qtbot, mocked_client):
bec_image_view = create_widget(qtbot, Image, client=mocked_client)
bec_image_view.autorange = False # Change the initial state to False
bec_image_view.autorange_mean_action.action.trigger()
bec_image_view.toolbar.components.get_action("image_autorange_mean").action.trigger()
assert bec_image_view.autorange is True
assert bec_image_view.main_image.autorange is True
assert bec_image_view.autorange_mode == "mean"
bec_image_view.autorange_max_action.action.trigger()
bec_image_view.toolbar.components.get_action("image_autorange_max").action.trigger()
assert bec_image_view.autorange is True
assert bec_image_view.main_image.autorange is True
assert bec_image_view.autorange_mode == "max"
bec_image_view.toolbar.widgets["lock_aspect_ratio"].action.trigger()
bec_image_view.toolbar.components.get_action("lock_aspect_ratio").action.trigger()
assert bec_image_view.lock_aspect_ratio is False
assert bool(bec_image_view.plot_item.getViewBox().state["aspectLocked"]) is False
@ -336,7 +337,7 @@ def test_image_actions_interactions(qtbot, mocked_client):
def test_image_toggle_action_fft(qtbot, mocked_client):
bec_image_view = create_widget(qtbot, Image, client=mocked_client)
bec_image_view.processing_bundle.fft.action.trigger()
bec_image_view.toolbar.components.get_action("image_processing_fft").action.trigger()
assert bec_image_view.fft is True
assert bec_image_view.main_image.fft is True
@ -346,7 +347,7 @@ def test_image_toggle_action_fft(qtbot, mocked_client):
def test_image_toggle_action_log(qtbot, mocked_client):
bec_image_view = create_widget(qtbot, Image, client=mocked_client)
bec_image_view.processing_bundle.log.action.trigger()
bec_image_view.toolbar.components.get_action("image_processing_log").action.trigger()
assert bec_image_view.log is True
assert bec_image_view.main_image.log is True
@ -356,7 +357,7 @@ def test_image_toggle_action_log(qtbot, mocked_client):
def test_image_toggle_action_transpose(qtbot, mocked_client):
bec_image_view = create_widget(qtbot, Image, client=mocked_client)
bec_image_view.processing_bundle.transpose.action.trigger()
bec_image_view.toolbar.components.get_action("image_processing_transpose").action.trigger()
assert bec_image_view.transpose is True
assert bec_image_view.main_image.transpose is True
@ -366,7 +367,7 @@ def test_image_toggle_action_transpose(qtbot, mocked_client):
def test_image_toggle_action_rotate_right(qtbot, mocked_client):
bec_image_view = create_widget(qtbot, Image, client=mocked_client)
bec_image_view.processing_bundle.right.action.trigger()
bec_image_view.toolbar.components.get_action("image_processing_rotate_right").action.trigger()
assert bec_image_view.num_rotation_90 == 3
assert bec_image_view.main_image.num_rotation_90 == 3
@ -376,7 +377,7 @@ def test_image_toggle_action_rotate_right(qtbot, mocked_client):
def test_image_toggle_action_rotate_left(qtbot, mocked_client):
bec_image_view = create_widget(qtbot, Image, client=mocked_client)
bec_image_view.processing_bundle.left.action.trigger()
bec_image_view.toolbar.components.get_action("image_processing_rotate_left").action.trigger()
assert bec_image_view.num_rotation_90 == 1
assert bec_image_view.main_image.num_rotation_90 == 1
@ -392,7 +393,7 @@ def test_image_toggle_action_reset(qtbot, mocked_client):
bec_image_view.transpose = True
bec_image_view.num_rotation_90 = 2
bec_image_view.processing_bundle.reset.action.trigger()
bec_image_view.toolbar.components.get_action("image_processing_reset").action.trigger()
assert bec_image_view.num_rotation_90 == 0
assert bec_image_view.main_image.num_rotation_90 == 0
@ -473,8 +474,8 @@ def test_show_roi_manager_popup(qtbot, mocked_client):
view = create_widget(qtbot, Image, client=mocked_client, popups=True)
# ROI-manager toggle is exposed via the toolbar.
assert "roi_mgr" in view.toolbar.widgets
roi_action = view.toolbar.widgets["roi_mgr"].action
assert view.toolbar.components.exists("roi_mgr")
roi_action = view.toolbar.components.get_action("roi_mgr").action
assert roi_action.isChecked() is False, "Should start unchecked"
# Open the popup.
@ -497,10 +498,10 @@ def test_show_roi_manager_popup(qtbot, mocked_client):
def test_crosshair_roi_panels_visibility(qtbot, mocked_client):
"""
Verify that enabling the ROIcrosshair shows ROI panels and disabling hides them.
Verify that enabling the ROI-crosshair shows ROI panels and disabling hides them.
"""
bec_image_view = create_widget(qtbot, Image, client=mocked_client)
switch = bec_image_view.toolbar.widgets["switch_crosshair"]
switch = bec_image_view.toolbar.components.get_action("image_switch_crosshair")
# Initially panels should be hidden
assert bec_image_view.side_panel_x.panel_height == 0
@ -548,7 +549,7 @@ def test_roi_plot_data_from_image(qtbot, mocked_client):
bec_image_view.on_image_update_2d({"data": test_data}, {})
# Activate ROI crosshair
switch = bec_image_view.toolbar.widgets["switch_crosshair"]
switch = bec_image_view.toolbar.components.get_action("image_switch_crosshair")
switch.actions["crosshair_roi"].action.trigger()
qtbot.wait(50)
@ -579,11 +580,10 @@ def test_roi_plot_data_from_image(qtbot, mocked_client):
def test_monitor_selection_reverse_device_items(qtbot, mocked_client):
"""
Verify that _reverse_device_items correctly reverses the order of items in the
device combobox while preserving the current selection.
device combobox while preserving the current selection.
"""
view = create_widget(qtbot, Image, client=mocked_client)
bundle = view.selection_bundle
combo = bundle.device_combo_box
combo = view.device_combo_box
# Replace existing items with a deterministic list
combo.clear()
@ -593,7 +593,7 @@ def test_monitor_selection_reverse_device_items(qtbot, mocked_client):
combo.setCurrentText("samy")
# Reverse the items
bundle._reverse_device_items()
view._reverse_device_items()
# Order should be reversed and selection preserved
assert [combo.itemText(i) for i in range(combo.count())] == ["samz", "samy", "samx"]
@ -606,7 +606,6 @@ def test_monitor_selection_populate_preview_signals(qtbot, mocked_client, monkey
with the correct userData.
"""
view = create_widget(qtbot, Image, client=mocked_client)
bundle = view.selection_bundle
# Provide a deterministic fake device_manager with get_bec_signals
class _FakeDM:
@ -618,27 +617,26 @@ def test_monitor_selection_populate_preview_signals(qtbot, mocked_client, monkey
monkeypatch.setattr(view.client, "device_manager", _FakeDM())
initial_count = bundle.device_combo_box.count()
initial_count = view.device_combo_box.count()
bundle._populate_preview_signals()
view._populate_preview_signals()
# Two new entries should have been added
assert bundle.device_combo_box.count() == initial_count + 2
assert view.device_combo_box.count() == initial_count + 2
# The first newly added item should carry tuple userData describing the device/signal
data = bundle.device_combo_box.itemData(initial_count)
data = view.device_combo_box.itemData(initial_count)
assert isinstance(data, tuple) and data[0] == "eiger"
def test_monitor_selection_adjust_and_connect(qtbot, mocked_client, monkeypatch):
"""
Verify that _adjust_and_connect performs the full setup:
fills the combobox with preview signals,
reverses their order,
and resets the currentText to an empty string.
Verify that _adjust_and_connect performs the full set-up:
- fills the combobox with preview signals,
- reverses their order,
- and resets the currentText to an empty string.
"""
view = create_widget(qtbot, Image, client=mocked_client)
bundle = view.selection_bundle
# Deterministic fake device_manager
class _FakeDM:
@ -647,14 +645,14 @@ def test_monitor_selection_adjust_and_connect(qtbot, mocked_client, monkeypatch)
monkeypatch.setattr(view.client, "device_manager", _FakeDM())
combo = bundle.device_combo_box
combo = view.device_combo_box
# Start from a clean state
combo.clear()
combo.addItem("", None)
combo.setCurrentText("")
# Execute the method under test
bundle._adjust_and_connect()
view._adjust_and_connect()
# Expect exactly two items: preview label followed by the empty default
assert combo.count() == 2

View File

@ -5,19 +5,18 @@ from qtpy.QtCore import QPoint, Qt
from qtpy.QtGui import QContextMenuEvent
from qtpy.QtWidgets import QComboBox, QLabel, QMenu, QStyle, QToolButton, QWidget
from bec_widgets.utils.toolbar import (
from bec_widgets.utils.toolbars.actions import (
DeviceSelectionAction,
ExpandableMenuAction,
IconAction,
LongPressToolButton,
MaterialIconAction,
ModularToolBar,
QtIconAction,
SeparatorAction,
SwitchableToolBarAction,
ToolbarBundle,
WidgetAction,
)
from bec_widgets.utils.toolbars.bundles import ToolbarBundle
from bec_widgets.utils.toolbars.toolbar import ModularToolBar
@pytest.fixture
@ -34,14 +33,12 @@ def toolbar_fixture(qtbot, request, dummy_widget):
"""Parametrized fixture to create a ModularToolBar with different orientations."""
orientation: Literal["horizontal", "vertical"] = request.param
toolbar = ModularToolBar(
target_widget=dummy_widget,
orientation=orientation,
background_color="rgba(255, 255, 255, 255)", # White background for testing
)
qtbot.addWidget(toolbar)
qtbot.waitExposed(toolbar)
yield toolbar
toolbar.close()
@pytest.fixture
@ -50,12 +47,6 @@ def separator_action():
return SeparatorAction()
@pytest.fixture
def icon_action():
"""Fixture to create an IconAction."""
return IconAction(icon_path="assets/BEC-Icon.png", tooltip="Test Icon Action", checkable=True)
@pytest.fixture
def material_icon_action():
"""Fixture to create a MaterialIconAction."""
@ -64,6 +55,14 @@ def material_icon_action():
)
@pytest.fixture
def material_icon_action_2():
"""Fixture to create another MaterialIconAction."""
return MaterialIconAction(
icon_name="home", tooltip="Test Material Icon Action 2", checkable=False
)
@pytest.fixture
def qt_icon_action():
"""Fixture to create a QtIconAction."""
@ -121,7 +120,7 @@ def test_initialization(toolbar_fixture):
else:
pytest.fail("Toolbar orientation is neither horizontal nor vertical.")
assert toolbar.background_color == "rgba(255, 255, 255, 255)"
assert toolbar.widgets == {}
assert len(toolbar.components._components) == 1 # only the separator
assert not toolbar.isMovable()
assert not toolbar.isFloatable()
@ -152,80 +151,60 @@ def test_set_orientation(toolbar_fixture, qtbot, dummy_widget):
assert toolbar.orientation() == Qt.Vertical
def test_add_action(
toolbar_fixture,
icon_action,
separator_action,
material_icon_action,
qt_icon_action,
dummy_widget,
):
"""Test adding different types of actions to the toolbar."""
def test_add_action(toolbar_fixture, material_icon_action, qt_icon_action):
"""Test adding different types of actions to the toolbar components."""
toolbar = toolbar_fixture
# Add IconAction
toolbar.add_action("icon_action", icon_action, dummy_widget)
assert "icon_action" in toolbar.widgets
assert toolbar.widgets["icon_action"] == icon_action
assert icon_action.action in toolbar.actions()
# Add SeparatorAction
toolbar.add_action("separator_action", separator_action, dummy_widget)
assert "separator_action" in toolbar.widgets
assert toolbar.widgets["separator_action"] == separator_action
# Add MaterialIconAction
toolbar.add_action("material_icon_action", material_icon_action, dummy_widget)
assert "material_icon_action" in toolbar.widgets
assert toolbar.widgets["material_icon_action"] == material_icon_action
assert material_icon_action.action in toolbar.actions()
toolbar.add_action("material_icon_action", material_icon_action)
assert toolbar.components.exists("material_icon_action")
assert toolbar.components.get_action("material_icon_action") == material_icon_action
# Add QtIconAction
toolbar.add_action("qt_icon_action", qt_icon_action, dummy_widget)
assert "qt_icon_action" in toolbar.widgets
assert toolbar.widgets["qt_icon_action"] == qt_icon_action
assert qt_icon_action.action in toolbar.actions()
toolbar.add_action("qt_icon_action", qt_icon_action)
assert toolbar.components.exists("qt_icon_action")
assert toolbar.components.get_action("qt_icon_action") == qt_icon_action
def test_hide_show_action(toolbar_fixture, icon_action, qtbot, dummy_widget):
def test_hide_show_action(toolbar_fixture, qt_icon_action, qtbot):
"""Test hiding and showing actions on the toolbar."""
toolbar = toolbar_fixture
# Add an action
toolbar.add_action("icon_action", icon_action, dummy_widget)
assert icon_action.action.isVisible()
toolbar.add_action("icon_action", qt_icon_action)
assert qt_icon_action.action.isVisible()
# Hide the action
toolbar.hide_action("icon_action")
qtbot.wait(100)
assert not icon_action.action.isVisible()
assert not qt_icon_action.action.isVisible()
# Show the action
toolbar.show_action("icon_action")
qtbot.wait(100)
assert icon_action.action.isVisible()
assert qt_icon_action.action.isVisible()
def test_add_duplicate_action(toolbar_fixture, icon_action, dummy_widget):
def test_add_duplicate_action(toolbar_fixture, qt_icon_action):
"""Test that adding an action with a duplicate action_id raises a ValueError."""
toolbar = toolbar_fixture
# Add an action
toolbar.add_action("icon_action", icon_action, dummy_widget)
assert "icon_action" in toolbar.widgets
toolbar.add_action("qt_icon_action", qt_icon_action)
assert toolbar.components.exists("qt_icon_action")
# Attempt to add another action with the same ID
with pytest.raises(ValueError) as excinfo:
toolbar.add_action("icon_action", icon_action, dummy_widget)
assert "Action with ID 'icon_action' already exists." in str(excinfo.value)
toolbar.add_action("qt_icon_action", qt_icon_action)
assert "Bundle with name 'qt_icon_action' already exists." in str(excinfo.value)
def test_update_material_icon_colors(toolbar_fixture, material_icon_action, dummy_widget):
def test_update_material_icon_colors(toolbar_fixture, material_icon_action):
"""Test updating the color of MaterialIconAction icons."""
toolbar = toolbar_fixture
# Add MaterialIconAction
toolbar.add_action("material_icon_action", material_icon_action, dummy_widget)
toolbar.add_action("material_icon_action", material_icon_action)
assert material_icon_action.action is not None
# Initial icon
@ -242,11 +221,12 @@ def test_update_material_icon_colors(toolbar_fixture, material_icon_action, dumm
assert initial_icon != updated_icon
def test_device_selection_action(toolbar_fixture, device_selection_action, dummy_widget):
def test_device_selection_action(toolbar_fixture, device_selection_action):
"""Test adding a DeviceSelectionAction to the toolbar."""
toolbar = toolbar_fixture
toolbar.add_action("device_selection", device_selection_action, dummy_widget)
assert "device_selection" in toolbar.widgets
toolbar.add_action("device_selection", device_selection_action)
assert toolbar.components.exists("device_selection")
toolbar.show_bundles(["device_selection"])
# DeviceSelectionAction adds a QWidget, so it should be present in the toolbar's widgets
# Check if the widget is added
widget = device_selection_action.device_combobox.parentWidget()
@ -256,11 +236,12 @@ def test_device_selection_action(toolbar_fixture, device_selection_action, dummy
assert label.text() == "Select Device:"
def test_widget_action(toolbar_fixture, widget_action, dummy_widget):
def test_widget_action(toolbar_fixture, widget_action):
"""Test adding a WidgetAction to the toolbar."""
toolbar = toolbar_fixture
toolbar.add_action("widget_action", widget_action, dummy_widget)
assert "widget_action" in toolbar.widgets
toolbar.add_action("widget_action", widget_action)
assert toolbar.components.exists("widget_action")
toolbar.show_bundles(["widget_action"])
# WidgetAction adds a QWidget to the toolbar
container = widget_action.widget.parentWidget()
assert container in toolbar.findChildren(QWidget)
@ -269,11 +250,12 @@ def test_widget_action(toolbar_fixture, widget_action, dummy_widget):
assert label.text() == "Sample Label:"
def test_expandable_menu_action(toolbar_fixture, expandable_menu_action, dummy_widget):
def test_expandable_menu_action(toolbar_fixture, expandable_menu_action):
"""Test adding an ExpandableMenuAction to the toolbar."""
toolbar = toolbar_fixture
toolbar.add_action("expandable_menu", expandable_menu_action, dummy_widget)
assert "expandable_menu" in toolbar.widgets
toolbar.add_action("expandable_menu", expandable_menu_action)
assert toolbar.components.exists("expandable_menu")
toolbar.show_bundles(["expandable_menu"])
# ExpandableMenuAction adds a QToolButton with a QMenu
# Find the QToolButton
tool_buttons = toolbar.findChildren(QToolButton)
@ -300,44 +282,47 @@ def test_update_material_icon_colors_no_material_actions(toolbar_fixture, dummy_
def test_hide_action_nonexistent(toolbar_fixture):
"""Test hiding an action that does not exist raises a ValueError."""
"""Test hiding an action that does not exist raises a KeyError."""
toolbar = toolbar_fixture
with pytest.raises(ValueError) as excinfo:
with pytest.raises(KeyError) as excinfo:
toolbar.hide_action("nonexistent_action")
assert "Action with ID 'nonexistent_action' does not exist." in str(excinfo.value)
excinfo.match("Component with name 'nonexistent_action' does not exist.")
def test_show_action_nonexistent(toolbar_fixture):
"""Test showing an action that does not exist raises a ValueError."""
"""Test showing an action that does not exist raises a KeyError."""
toolbar = toolbar_fixture
with pytest.raises(ValueError) as excinfo:
with pytest.raises(KeyError) as excinfo:
toolbar.show_action("nonexistent_action")
assert "Action with ID 'nonexistent_action' does not exist." in str(excinfo.value)
excinfo.match("Component with name 'nonexistent_action' does not exist.")
def test_add_bundle(toolbar_fixture, dummy_widget, icon_action, material_icon_action):
def test_add_bundle(toolbar_fixture, material_icon_action):
"""Test adding a bundle of actions to the toolbar."""
toolbar = toolbar_fixture
bundle = ToolbarBundle(
bundle_id="test_bundle",
actions=[
("icon_action_in_bundle", icon_action),
("material_icon_in_bundle", material_icon_action),
],
)
toolbar.add_bundle(bundle, dummy_widget)
assert "test_bundle" in toolbar.bundles
assert "icon_action_in_bundle" in toolbar.widgets
assert "material_icon_in_bundle" in toolbar.widgets
assert icon_action.action in toolbar.actions()
toolbar.add_action("material_icon_in_bundle", material_icon_action)
bundle = ToolbarBundle("test_bundle", toolbar.components)
bundle.add_action("material_icon_in_bundle")
toolbar.add_bundle(bundle)
assert toolbar.get_bundle("test_bundle")
assert toolbar.components.exists("material_icon_in_bundle")
toolbar.show_bundles(["test_bundle"])
assert material_icon_action.action in toolbar.actions()
def test_invalid_orientation(dummy_widget):
def test_invalid_orientation():
"""Test that an invalid orientation raises a ValueError."""
toolbar = ModularToolBar(target_widget=dummy_widget, orientation="horizontal")
with pytest.raises(ValueError):
toolbar.set_orientation("diagonal")
try:
toolbar = ModularToolBar(orientation="horizontal")
with pytest.raises(ValueError):
toolbar.set_orientation("diagonal")
finally:
toolbar.close()
toolbar.deleteLater()
def test_widget_action_calculate_minimum_width(qtbot):
@ -353,24 +338,26 @@ def test_widget_action_calculate_minimum_width(qtbot):
def test_add_action_to_bundle(toolbar_fixture, dummy_widget, material_icon_action):
# Create an initial bundle with one action
bundle = ToolbarBundle(
bundle_id="test_bundle", actions=[("initial_action", material_icon_action)]
)
toolbar_fixture.add_bundle(bundle, dummy_widget)
toolbar_fixture.add_action("initial_action", material_icon_action)
bundle = ToolbarBundle("test_bundle", toolbar_fixture.components)
bundle.add_action("initial_action")
toolbar_fixture.add_bundle(bundle)
# Create a new action to add to the existing bundle
new_action = MaterialIconAction(
icon_name="counter_1", tooltip="New Action", checkable=True, parent=dummy_widget
)
toolbar_fixture.add_action_to_bundle("test_bundle", "new_action", new_action, dummy_widget)
toolbar_fixture.components.add_safe("new_action", new_action)
toolbar_fixture.get_bundle("test_bundle").add_action("new_action")
toolbar_fixture.show_bundles(["test_bundle"])
# Verify the new action is registered in the toolbar's widgets
assert "new_action" in toolbar_fixture.widgets
assert toolbar_fixture.widgets["new_action"] == new_action
assert toolbar_fixture.components.exists("new_action")
assert toolbar_fixture.components.get_action("new_action") == new_action
# Verify the new action is included in the bundle tracking
assert "new_action" in toolbar_fixture.bundles["test_bundle"]
assert toolbar_fixture.bundles["test_bundle"][-1] == "new_action"
assert toolbar_fixture.bundles["test_bundle"].bundle_actions["new_action"]() == new_action
# Verify the new action's QAction is present in the toolbar's action list
actions_list = toolbar_fixture.actions()
@ -384,7 +371,7 @@ def test_add_action_to_bundle(toolbar_fixture, dummy_widget, material_icon_actio
def test_context_menu_contains_added_actions(
toolbar_fixture, icon_action, material_icon_action, dummy_widget, monkeypatch
toolbar_fixture, material_icon_action, material_icon_action_2, monkeypatch
):
"""
Test that the toolbar's context menu lists all added toolbar actions.
@ -392,9 +379,13 @@ def test_context_menu_contains_added_actions(
toolbar = toolbar_fixture
# Add two different actions
toolbar.add_action("icon_action", icon_action, dummy_widget)
toolbar.add_action("material_icon_action", material_icon_action, dummy_widget)
toolbar.components.add_safe("material_icon_action", material_icon_action)
toolbar.components.add_safe("material_icon_action_2", material_icon_action_2)
bundle = toolbar.new_bundle("test_bundle")
bundle.add_action("material_icon_action")
bundle.add_action("material_icon_action_2")
toolbar.show_bundles(["test_bundle"])
# Mock the QMenu.exec_ method to prevent the context menu from being displayed and block CI pipeline
monkeypatch.setattr(QMenu, "exec_", lambda self, pos=None: None)
event = QContextMenuEvent(QContextMenuEvent.Mouse, QPoint(10, 10))
@ -404,23 +395,26 @@ def test_context_menu_contains_added_actions(
assert len(menus) > 0
menu = menus[-1]
menu_action_texts = [action.text() for action in menu.actions()]
assert any(icon_action.tooltip in text or "icon_action" in text for text in menu_action_texts)
assert any(
material_icon_action.tooltip in text or "material_icon_action" in text
for text in menu_action_texts
)
tooltips = [
action.action.tooltip
for action in toolbar.components._components.values()
if not isinstance(action.action, SeparatorAction)
]
menu_actions_tooltips = [
action.toolTip() for action in menu.actions() if action.toolTip() != ""
]
assert menu_action_texts == tooltips
def test_context_menu_toggle_action_visibility(
toolbar_fixture, icon_action, dummy_widget, monkeypatch
):
def test_context_menu_toggle_action_visibility(toolbar_fixture, material_icon_action, monkeypatch):
"""
Test that toggling action visibility works correctly through the toolbar's context menu.
"""
toolbar = toolbar_fixture
# Add an action
toolbar.add_action("icon_action", icon_action, dummy_widget)
assert icon_action.action.isVisible()
toolbar.add_action("material_icon_action", material_icon_action)
toolbar.show_bundles(["material_icon_action"])
assert material_icon_action.action.isVisible()
# Manually trigger the context menu event
monkeypatch.setattr(QMenu, "exec_", lambda self, pos=None: None)
@ -433,7 +427,7 @@ def test_context_menu_toggle_action_visibility(
menu = menus[-1]
# Locate the QAction in the menu
matching_actions = [m for m in menu.actions() if m.text() == icon_action.tooltip]
matching_actions = [m for m in menu.actions() if m.text() == material_icon_action.tooltip]
assert len(matching_actions) == 1
action_in_menu = matching_actions[0]
@ -441,23 +435,24 @@ def test_context_menu_toggle_action_visibility(
action_in_menu.setChecked(False)
menu.triggered.emit(action_in_menu)
# The action on the toolbar should now be hidden
assert not icon_action.action.isVisible()
assert not material_icon_action.action.isVisible()
# Toggle it on (check)
action_in_menu.setChecked(True)
menu.triggered.emit(action_in_menu)
# The action on the toolbar should be visible again
assert icon_action.action.isVisible()
assert material_icon_action.action.isVisible()
def test_switchable_toolbar_action_add(toolbar_fixture, dummy_widget, switchable_toolbar_action):
def test_switchable_toolbar_action_add(toolbar_fixture, switchable_toolbar_action):
"""Test that a switchable toolbar action can be added to the toolbar correctly."""
toolbar = toolbar_fixture
toolbar.add_action("switch_action", switchable_toolbar_action, dummy_widget)
toolbar.add_action("switch_action", switchable_toolbar_action)
toolbar.show_bundles(["switch_action"])
# Verify the action was added correctly
assert "switch_action" in toolbar.widgets
assert toolbar.widgets["switch_action"] == switchable_toolbar_action
assert toolbar.components.exists("switch_action")
assert toolbar.components.get_action("switch_action") == switchable_toolbar_action
# Verify the button is present and is the correct type
button = switchable_toolbar_action.main_button
@ -468,11 +463,10 @@ def test_switchable_toolbar_action_add(toolbar_fixture, dummy_widget, switchable
assert button.toolTip() == "Action 1"
def test_switchable_toolbar_action_switching(
toolbar_fixture, dummy_widget, switchable_toolbar_action, qtbot
):
def test_switchable_toolbar_action_switching(toolbar_fixture, switchable_toolbar_action, qtbot):
toolbar = toolbar_fixture
toolbar.add_action("switch_action", switchable_toolbar_action, dummy_widget)
toolbar.add_action("switch_action", switchable_toolbar_action)
toolbar.show_bundles(["switch_action"])
# Verify initial state is set to action1
assert switchable_toolbar_action.current_key == "action1"
assert switchable_toolbar_action.main_button.toolTip() == "Action 1"
@ -494,9 +488,10 @@ def test_switchable_toolbar_action_switching(
assert switchable_toolbar_action.main_button.toolTip() == "Action 2"
def test_long_pressbutton(toolbar_fixture, dummy_widget, switchable_toolbar_action, qtbot):
def test_long_pressbutton(toolbar_fixture, switchable_toolbar_action, qtbot):
toolbar = toolbar_fixture
toolbar.add_action("switch_action", switchable_toolbar_action, dummy_widget)
toolbar.add_action("switch_action", switchable_toolbar_action)
toolbar.show_bundles(["switch_action"])
# Verify the button is a LongPressToolButton
button = switchable_toolbar_action.main_button
@ -521,92 +516,73 @@ def test_long_pressbutton(toolbar_fixture, dummy_widget, switchable_toolbar_acti
# Additional tests for action/bundle removal
def test_remove_standalone_action(toolbar_fixture, icon_action, dummy_widget):
def test_remove_standalone_action(toolbar_fixture, material_icon_action):
"""
Ensure that a standalone action is fully removed and no longer accessible.
"""
toolbar = toolbar_fixture
# Add the action and check it is present
toolbar.add_action("icon_action", icon_action, dummy_widget)
assert "icon_action" in toolbar.widgets
assert icon_action.action in toolbar.actions()
toolbar.add_action("icon_action", material_icon_action)
assert toolbar.components.exists("icon_action")
toolbar.show_bundles(["icon_action"])
assert material_icon_action.action in toolbar.actions()
# Now remove it
toolbar.remove_action("icon_action")
toolbar.components.remove_action("icon_action")
# Action bookkeeping
assert "icon_action" not in toolbar.widgets
assert not toolbar.components.exists("icon_action")
# QAction list
assert icon_action.action not in toolbar.actions()
assert material_icon_action.action not in toolbar.actions()
# Attempting to hide / show it should raise
with pytest.raises(ValueError):
with pytest.raises(KeyError):
toolbar.hide_action("icon_action")
with pytest.raises(ValueError):
with pytest.raises(KeyError):
toolbar.show_action("icon_action")
def test_remove_action_from_bundle(
toolbar_fixture, dummy_widget, icon_action, material_icon_action
):
def test_remove_action_from_bundle(toolbar_fixture, material_icon_action, material_icon_action_2):
"""
Remove a single action that is part of a bundle and verify cleanup.
Remove a single action that is part of a bundle. This should not remove the action
from the toolbar's components, but only from the bundle tracking.
"""
toolbar = toolbar_fixture
bundle = ToolbarBundle(
bundle_id="test_bundle",
actions=[("icon_action", icon_action), ("material_action", material_icon_action)],
)
toolbar.add_bundle(bundle, dummy_widget)
bundle = toolbar.new_bundle("test_bundle")
# Add two actions to the bundle
toolbar.components.add_safe("material_action", material_icon_action)
toolbar.components.add_safe("material_action_2", material_icon_action_2)
bundle.add_action("material_action")
bundle.add_action("material_action_2")
toolbar.show_bundles(["test_bundle"])
# Initial assertions
assert "test_bundle" in toolbar.bundles
assert "icon_action" in toolbar.widgets
assert "material_action" in toolbar.widgets
assert toolbar.components.exists("material_action")
assert toolbar.components.exists("material_action_2")
# Remove one action from the bundle
toolbar.remove_action("icon_action")
toolbar.get_bundle("test_bundle").remove_action("material_action")
# icon_action should be fully gone
assert "icon_action" not in toolbar.widgets
assert icon_action.action not in toolbar.actions()
# Bundle tracking should be updated
assert "icon_action" not in toolbar.bundles["test_bundle"]
# The other action must still exist
assert "material_action" in toolbar.widgets
assert material_icon_action.action in toolbar.actions()
# The bundle should still exist
assert "test_bundle" in toolbar.bundles
# The removed action should still exist in the components
assert toolbar.components.exists("material_action")
# The removed action should not be in the bundle anymore
assert "material_action" not in toolbar.bundles["test_bundle"].bundle_actions
def test_remove_last_action_from_bundle_removes_bundle(toolbar_fixture, dummy_widget, icon_action):
"""
Removing the final action from a bundle should delete the bundle entry itself.
"""
def test_remove_entire_bundle(toolbar_fixture, material_icon_action, material_icon_action_2):
toolbar = toolbar_fixture
bundle = ToolbarBundle(bundle_id="single_action_bundle", actions=[("only_action", icon_action)])
toolbar.add_bundle(bundle, dummy_widget)
# Sanity check
assert "single_action_bundle" in toolbar.bundles
assert "only_action" in toolbar.widgets
# Remove the sole action
toolbar.remove_action("only_action")
# Bundle should be gone
assert "single_action_bundle" not in toolbar.bundles
# QAction removed
assert icon_action.action not in toolbar.actions()
def test_remove_entire_bundle(toolbar_fixture, dummy_widget, icon_action, material_icon_action):
"""
Ensure that removing a bundle deletes all its actions and separators.
"""
toolbar = toolbar_fixture
bundle = ToolbarBundle(
bundle_id="to_remove",
actions=[("icon_action", icon_action), ("material_action", material_icon_action)],
)
toolbar.add_bundle(bundle, dummy_widget)
toolbar.components.add_safe("material_action", material_icon_action)
toolbar.components.add_safe("material_action_2", material_icon_action_2)
# Create a bundle with two actions
bundle = toolbar.new_bundle("to_remove")
bundle.add_action("material_action")
bundle.add_action("material_action_2")
# Confirm bundle presence
assert "to_remove" in toolbar.bundles
@ -616,58 +592,23 @@ def test_remove_entire_bundle(toolbar_fixture, dummy_widget, icon_action, materi
# Bundle mapping gone
assert "to_remove" not in toolbar.bundles
# All actions gone
for aid, act in [("icon_action", icon_action), ("material_action", material_icon_action)]:
assert aid not in toolbar.widgets
assert act.action not in toolbar.actions()
def test_trigger_removed_action_raises(toolbar_fixture, icon_action, dummy_widget, qtbot):
"""
Add an action, connect a mock slot, then remove the action and verify that
attempting to trigger it afterwards raises RuntimeError (since the underlying
QAction has been deleted).
"""
toolbar = toolbar_fixture
# Add the action and connect a mock slot
toolbar.add_action("icon_action", icon_action, dummy_widget)
called = []
def mock_slot():
called.append(True)
icon_action.action.triggered.connect(mock_slot)
# Trigger once to confirm connection works
icon_action.action.trigger()
assert called == [True]
# Now remove the action
toolbar.remove_action("icon_action")
# Allow deleteLater event to process
qtbot.wait(50)
# The underlying C++ object should be deleted; triggering should raise
with pytest.raises(RuntimeError):
icon_action.action.trigger()
def test_remove_nonexistent_action(toolbar_fixture):
"""
Attempting to remove an action that does not exist should raise ValueError.
Attempting to remove an action that does not exist should raise KeyError.
"""
toolbar = toolbar_fixture
with pytest.raises(ValueError) as excinfo:
toolbar.remove_action("nonexistent_action")
with pytest.raises(KeyError) as excinfo:
toolbar.components.remove_action("nonexistent_action")
assert "Action with ID 'nonexistent_action' does not exist." in str(excinfo.value)
def test_remove_nonexistent_bundle(toolbar_fixture):
"""
Attempting to remove a bundle that does not exist should raise ValueError.
Attempting to remove a bundle that does not exist should raise KeyError.
"""
toolbar = toolbar_fixture
with pytest.raises(ValueError) as excinfo:
with pytest.raises(KeyError) as excinfo:
toolbar.remove_bundle("nonexistent_bundle")
assert "Bundle 'nonexistent_bundle' does not exist." in str(excinfo.value)
excinfo.match("Bundle with name 'nonexistent_bundle' does not exist.")

View File

@ -272,16 +272,15 @@ def test_motor_map_toolbar_selection(qtbot, mocked_client):
mm = create_widget(qtbot, MotorMap, client=mocked_client)
# Verify toolbar bundle was created during initialization
assert hasattr(mm, "motor_selection_bundle")
assert mm.motor_selection_bundle is not None
motor_selection = mm.toolbar.components.get_action("motor_selection")
mm.motor_selection_bundle.motor_x.setCurrentText("samx")
mm.motor_selection_bundle.motor_y.setCurrentText("samy")
motor_selection.motor_x.setCurrentText("samx")
motor_selection.motor_y.setCurrentText("samy")
assert mm.config.x_motor.name == "samx"
assert mm.config.y_motor.name == "samy"
mm.motor_selection_bundle.motor_y.setCurrentText("samz")
motor_selection.motor_y.setCurrentText("samz")
assert mm.config.x_motor.name == "samx"
assert mm.config.y_motor.name == "samz"
@ -291,9 +290,9 @@ def test_motor_map_settings_dialog(qtbot, mocked_client):
"""Test the settings dialog for the motor map."""
mm = create_widget(qtbot, MotorMap, client=mocked_client, popups=True)
assert "popup_bundle" in mm.toolbar.bundles
for action_id in mm.toolbar.bundles["popup_bundle"]:
assert mm.toolbar.widgets[action_id].action.isVisible() is True
assert "axis_popup" in mm.toolbar.bundles
for action_ref in mm.toolbar.bundles["axis_popup"].bundle_actions.values():
assert action_ref().action.isVisible()
# set properties to be fetched by dialog
mm.map(x_name="samx", y_name="samy")

View File

@ -244,15 +244,14 @@ def test_selection_toolbar_updates_widget(qtbot, mocked_client):
updates the widget properties.
"""
mw = create_widget(qtbot, MultiWaveform, client=mocked_client)
toolbar = mw.monitor_selection_bundle
monitor_combo = toolbar.monitor
colormap_widget = toolbar.colormap_widget
monitor_selection_action = mw.toolbar.components.get_action("monitor_selection")
cmap_action = mw.toolbar.components.get_action("color_map")
monitor_combo.addItem("waveform1d")
monitor_combo.setCurrentText("waveform1d")
monitor_selection_action.combobox.addItem("waveform1d")
monitor_selection_action.combobox.setCurrentText("waveform1d")
assert mw.monitor == "waveform1d"
colormap_widget.colormap = "viridis"
cmap_action.widget.colormap = "viridis"
assert mw.color_palette == "viridis"
@ -290,11 +289,10 @@ def test_control_panel_opacity_slider_spinbox(qtbot, mocked_client):
def test_control_panel_highlight_slider_spinbox(qtbot, mocked_client):
"""
Test that the slider and spinbox for curve highlighting update
the widgets highlighted_index property, and are disabled if
the widget's highlighted_index property, and are disabled if
highlight_last_curve is True.
"""
mw = create_widget(qtbot, MultiWaveform, client=mocked_client)
slider_index = mw.controls.ui.highlighted_index
spinbox_index = mw.controls.ui.spinbox_index
checkbox_highlight_last = mw.controls.ui.highlight_last_curve

View File

@ -265,54 +265,56 @@ def test_ui_mode_popup(qtbot, mocked_client):
pb = create_widget(qtbot, PlotBase, client=mocked_client)
pb.ui_mode = UIMode.POPUP
# The popup bundle should be created and its actions made visible.
assert "popup_bundle" in pb.toolbar.bundles
for action_id in pb.toolbar.bundles["popup_bundle"]:
assert pb.toolbar.widgets[action_id].action.isVisible() is True
assert "axis_popup" in pb.toolbar.bundles
for action_ref in pb.toolbar.bundles["axis_popup"].bundle_actions.values():
assert action_ref().action.isVisible() is True
# The side panel should be hidden.
assert not pb.side_panel.isVisible()
def test_ui_mode_side(qtbot, mocked_client):
"""
Test that setting ui_mode to SIDE shows the side panel and ensures any popup actions
are hidden.
"""
pb = create_widget(qtbot, PlotBase, client=mocked_client)
pb.ui_mode = UIMode.SIDE
# If a popup bundle exists, its actions should be hidden.
if "popup_bundle" in pb.toolbar.bundles:
for action_id in pb.toolbar.bundles["popup_bundle"]:
assert pb.toolbar.widgets[action_id].action.isVisible() is False
# Side panels are not properly implemented yet. Once the logic is fixed, we can re-enable this test.
# See issue #742
# def test_ui_mode_side(qtbot, mocked_client):
# """
# Test that setting ui_mode to SIDE shows the side panel and ensures any popup actions
# are hidden.
# """
# pb = create_widget(qtbot, PlotBase, client=mocked_client)
# pb.ui_mode = UIMode.SIDE
# # If a popup bundle exists, its actions should be hidden.
# if "axis_popup" in pb.toolbar.bundles:
# for action_ref in pb.toolbar.bundles["axis_popup"].bundle_actions.values():
# assert action_ref().action.isVisible() is False
def test_enable_popups_property(qtbot, mocked_client):
"""
Test the enable_popups property: when enabled, ui_mode should be POPUP,
and when disabled, ui_mode should change to NONE.
"""
pb = create_widget(qtbot, PlotBase, client=mocked_client)
pb.enable_popups = True
assert pb.ui_mode == UIMode.POPUP
# The popup bundle actions should be visible.
assert "popup_bundle" in pb.toolbar.bundles
for action_id in pb.toolbar.bundles["popup_bundle"]:
assert pb.toolbar.widgets[action_id].action.isVisible() is True
# def test_enable_popups_property(qtbot, mocked_client):
# """
# Test the enable_popups property: when enabled, ui_mode should be POPUP,
# and when disabled, ui_mode should change to NONE.
# """
# pb = create_widget(qtbot, PlotBase, client=mocked_client)
# pb.enable_popups = True
# assert pb.ui_mode == UIMode.POPUP
# # The popup bundle actions should be visible.
# assert "popup_bundle" in pb.toolbar.bundles
# for action_id in pb.toolbar.bundles["popup_bundle"]:
# assert pb.toolbar.widgets[action_id].action.isVisible() is True
pb.enable_popups = False
assert pb.ui_mode == UIMode.NONE
# pb.enable_popups = False
# assert pb.ui_mode == UIMode.NONE
def test_enable_side_panel_property(qtbot, mocked_client):
"""
Test the enable_side_panel property: when enabled, ui_mode should be SIDE,
and when disabled, ui_mode should change to NONE.
"""
pb = create_widget(qtbot, PlotBase, client=mocked_client)
pb.enable_side_panel = True
assert pb.ui_mode == UIMode.SIDE
# def test_enable_side_panel_property(qtbot, mocked_client):
# """
# Test the enable_side_panel property: when enabled, ui_mode should be SIDE,
# and when disabled, ui_mode should change to NONE.
# """
# pb = create_widget(qtbot, PlotBase, client=mocked_client)
# pb.enable_side_panel = True
# assert pb.ui_mode == UIMode.SIDE
pb.enable_side_panel = False
assert pb.ui_mode == UIMode.NONE
# pb.enable_side_panel = False
# assert pb.ui_mode == UIMode.NONE
def test_switching_between_popup_and_side_panel_closes_dialog(qtbot, mocked_client):
@ -323,18 +325,19 @@ def test_switching_between_popup_and_side_panel_closes_dialog(qtbot, mocked_clie
pb = create_widget(qtbot, PlotBase, client=mocked_client)
pb.ui_mode = UIMode.POPUP
# Open the axis settings popup.
pb.show_axis_settings_popup()
pb_connection = pb.toolbar.bundles["axis_popup"].get_connection("plot_base")
pb_connection.show_axis_settings_popup()
qtbot.wait(100)
# The dialog should now exist and be visible.
assert pb.axis_settings_dialog is not None
assert pb.axis_settings_dialog.isVisible() is True
assert pb_connection.axis_settings_dialog is not None
assert pb_connection.axis_settings_dialog.isVisible() is True
# Switch to side panel mode.
pb.ui_mode = UIMode.SIDE
qtbot.wait(100)
# The axis settings dialog should be closed (and reference cleared).
qtbot.waitUntil(lambda: pb.axis_settings_dialog is None, timeout=5000)
qtbot.waitUntil(lambda: pb_connection.axis_settings_dialog is None, timeout=5000)
def test_enable_fps_monitor_property(qtbot, mocked_client):

View File

@ -136,7 +136,7 @@ def test_add_menu(side_panel_fixture, menu_widget, qtbot):
assert panel.stack_widget.count() == initial_count + 1
# Verify the action is added to the toolbar
action = panel.toolbar.widgets.get("test_action")
action = panel.toolbar.components.get_action("test_action")
assert action is not None
assert action.tooltip == "Test Tooltip"
assert action.action in panel.toolbar.actions()
@ -155,7 +155,7 @@ def test_toggle_action_show_panel(side_panel_fixture, menu_widget, qtbot):
)
qtbot.wait(100)
action = panel.toolbar.widgets.get("toggle_action")
action = panel.toolbar.components.get_action("toggle_action")
assert action is not None
# Initially, panel should be hidden
@ -199,8 +199,8 @@ def test_switch_actions(side_panel_fixture, menu_widget, qtbot):
)
qtbot.wait(100)
action1 = panel.toolbar.widgets.get("action1")
action2 = panel.toolbar.widgets.get("action2")
action1 = panel.toolbar.components.get_action("action1")
action2 = panel.toolbar.components.get_action("action2")
assert action1 is not None
assert action2 is not None
@ -241,7 +241,7 @@ def test_multiple_add_menu(side_panel_fixture, menu_widget, qtbot):
)
qtbot.wait(100)
assert panel.stack_widget.count() == initial_count + i + 1
action = panel.toolbar.widgets.get(f"action{i}")
action = panel.toolbar.components.get_action(f"action{i}")
assert action is not None
assert action.tooltip == f"Tooltip{i}"
assert action.action in panel.toolbar.actions()
@ -360,7 +360,7 @@ def test_add_multiple_menus(side_panel_fixture, menu_widget, qtbot):
)
qtbot.wait(100)
assert panel.stack_widget.count() == initial_count + i + 1
action = panel.toolbar.widgets.get(f"action{i}")
action = panel.toolbar.components.get_action(f"action{i}")
assert action is not None
assert action.tooltip == f"Tooltip{i}"
assert action.action in panel.toolbar.actions()

View File

@ -797,7 +797,7 @@ def test_show_curve_settings_popup(qtbot, mocked_client):
"""
wf = create_widget(qtbot, Waveform, client=mocked_client)
curve_action = wf.toolbar.widgets["curve"].action
curve_action = wf.toolbar.components.get_action("curve").action
assert not curve_action.isChecked(), "Should start unchecked"
wf.show_curve_settings_popup()
@ -807,8 +807,9 @@ def test_show_curve_settings_popup(qtbot, mocked_client):
assert curve_action.isChecked()
# add a new row to the curve tree
wf.curve_settings_dialog.widget.curve_manager.toolbar.widgets["add"].action.trigger()
wf.curve_settings_dialog.widget.curve_manager.toolbar.widgets["add"].action.trigger()
add_action = wf.curve_settings_dialog.widget.curve_manager.toolbar.components.get_action("add")
add_action.action.trigger()
add_action.action.trigger()
qtbot.wait(100)
# Check that the new row is added
assert wf.curve_settings_dialog.widget.curve_manager.tree.model().rowCount() == 2
@ -824,9 +825,9 @@ def test_show_dap_summary_popup(qtbot, mocked_client):
"""
wf = create_widget(qtbot, Waveform, client=mocked_client, popups=True)
assert "fit_params" in wf.toolbar.widgets
assert wf.toolbar.components.exists("fit_params")
fit_action = wf.toolbar.widgets["fit_params"].action
fit_action = wf.toolbar.components.get_action("fit_params").action
assert fit_action.isChecked() is False
wf.show_dap_summary_popup()