diff --git a/bec_widgets/widgets/__init__.py b/bec_widgets/widgets/__init__.py index 23ec8073..0e72a61b 100644 --- a/bec_widgets/widgets/__init__.py +++ b/bec_widgets/widgets/__init__.py @@ -1 +1 @@ -from .monitor import BECDeviceMonitor, ConfigDialog +from .monitor import BECMonitor, ConfigDialog diff --git a/bec_widgets/widgets/monitor/config_dialog.py b/bec_widgets/widgets/monitor/config_dialog.py index 8f87919a..dbee083f 100644 --- a/bec_widgets/widgets/monitor/config_dialog.py +++ b/bec_widgets/widgets/monitor/config_dialog.py @@ -17,8 +17,8 @@ from bec_widgets.qt_utils.yaml_dialog import load_yaml, save_yaml current_path = os.path.dirname(__file__) Ui_Form, BaseClass = uic.loadUiType(os.path.join(current_path, "config_dialog.ui")) Tab_Ui_Form, Tab_BaseClass = uic.loadUiType(os.path.join(current_path, "tab_template.ui")) -# test configs #TODO delete after loading works +# test configs for demonstration purpose config_default = { "plot_settings": { "background_color": "black", @@ -408,7 +408,9 @@ class ConfigDialog(QWidget, Ui_Form): self.comboBox_appearance.setCurrentText(plot_settings.get("background_color", "black")) self.spinBox_n_column.setValue(plot_settings.get("num_columns", 1)) - self.comboBox_colormap.setCurrentText(plot_settings.get("colormap", "magma")) + self.comboBox_colormap.setCurrentText( + plot_settings.get("colormap", "magma") + ) # TODO make logic to allow also different colormaps -> validation of incoming dict self.comboBox_scanTypes.setCurrentText( "Enabled" if plot_settings.get("scan_types", False) else "Disabled" ) diff --git a/bec_widgets/widgets/monitor/config_dialog.ui b/bec_widgets/widgets/monitor/config_dialog.ui index 09e403d6..58464f84 100644 --- a/bec_widgets/widgets/monitor/config_dialog.ui +++ b/bec_widgets/widgets/monitor/config_dialog.ui @@ -118,6 +118,11 @@ magma + + + plasma + + viridis diff --git a/bec_widgets/widgets/monitor/example_configs/config_device.yaml b/bec_widgets/widgets/monitor/example_configs/config_device.yaml index 0ee15b92..2cce78a4 100644 --- a/bec_widgets/widgets/monitor/example_configs/config_device.yaml +++ b/bec_widgets/widgets/monitor/example_configs/config_device.yaml @@ -1,7 +1,7 @@ plot_settings: background_color: "black" num_columns: 2 - colormap: "plasma" + colormap: "viridis" scan_types: False plot_data: diff --git a/tests/test_bec_monitor.py b/tests/test_bec_monitor.py index 7c6f258b..284ffd0d 100644 --- a/tests/test_bec_monitor.py +++ b/tests/test_bec_monitor.py @@ -1,155 +1,30 @@ -from unittest.mock import MagicMock +import os +import yaml import pytest +from unittest.mock import MagicMock from PyQt5.QtWidgets import QApplication from bec_widgets.widgets import BECMonitor -config_device = { - "plot_settings": { - "background_color": "black", - "num_columns": 1, - "colormap": "plasma", - "scan_types": False, - }, - "plot_data": [ - { - "plot_name": "BPM4i plots vs samx", - "x": { - "label": "Motor Y", - "signals": [{"name": "samx", "entry": "samx"}], - }, - "y": { - "label": "bpm4i", - "signals": [{"name": "bpm4i", "entry": "bpm4i"}], - }, - }, - { - "plot_name": "Gauss plots vs samx", - "x": { - "label": "Motor X", - "signals": [{"name": "samx", "entry": "samx"}], - }, - "y": { - "label": "Gauss", - "signals": [ - {"name": "gauss_adc1", "entry": "gauss_adc1"}, - {"name": "gauss_adc2", "entry": "gauss_adc2"}, - ], - }, - }, - ], -} +current_path = os.path.dirname(__file__) -config_device_no_entry = { - "plot_settings": { - "background_color": "white", - "num_columns": 5, # Number of columns higher than the actual number of plots - "colormap": "plasma", - "scan_types": False, - }, - "plot_data": [ - { - "plot_name": "BPM4i plots vs samx", - "x": { - "label": "Motor Y", - "signals": [{"name": "samx"}], # Entry is missing - }, - "y": { - "label": "bpm4i", - "signals": [{"name": "bpm4i"}], # Entry is missing - }, - }, - { - "plot_name": "Gauss plots vs samx", - "x": { - "label": "Motor X", - "signals": [{"name": "samx"}], # Entry is missing - }, - "y": { - "label": "Gauss", - "signals": [{"name": "gauss_bpm"}], # Entry is missing - }, - }, - ], -} - -config_scan = { - "plot_settings": { - "background_color": "white", - "num_columns": 3, - "colormap": "plasma", - "scan_types": True, - }, - "plot_data": { - "grid_scan": [ - { - "plot_name": "Grid plot 1", - "x": {"label": "Motor X", "signals": [{"name": "samx", "entry": "samx"}]}, - "y": { - "label": "BPM", - "signals": [ - {"name": "gauss_bpm", "entry": "gauss_bpm"}, - ], - }, - }, - { - "plot_name": "Grid plot 2", - "x": {"label": "Motor X", "signals": [{"name": "samx", "entry": "samx"}]}, - "y": { - "label": "BPM", - "signals": [ - {"name": "gauss_adc1", "entry": "gauss_adc1"}, - ], - }, - }, - { - "plot_name": "Grid plot 3", - "x": {"label": "Motor Y", "signals": [{"name": "samx", "entry": "samx"}]}, - "y": { - "label": "BPM", - "signals": [{"name": "gauss_adc2", "entry": "gauss_adc2"}], - }, - }, - { - "plot_name": "Grid plot 4", - "x": {"label": "Motor Y", "signals": [{"name": "samx", "entry": "samx"}]}, - "y": { - "label": "BPM", - "signals": [{"name": "bpm4i", "entry": "bpm4i"}], - }, - }, - ], - "line_scan": [ - { - "plot_name": "Multiple Gauss Plot", - "x": {"label": "Motor X", "signals": [{"name": "samx"}]}, - "y": { - "label": "BPM", - "signals": [ - {"name": "gauss_bpm", "entry": "gauss_bpm"}, - {"name": "gauss_adc1", "entry": "gauss_adc1"}, - {"name": "gauss_adc2", "entry": "gauss_adc2"}, - ], - }, - }, - { - "plot_name": "BPM Plot", - "x": {"label": "Motor X", "signals": [{"name": "samx", "entry": "samx"}]}, - "y": { - "label": "Multi", - "signals": [ - {"name": "bpm4i", "entry": "bpm4i"}, - ], - }, - }, - ], - }, -} +def load_config(config_path): + """Helper function to load config from yaml file.""" + with open(config_path, "r") as f: + config = yaml.safe_load(f) + return config -def setup_monitor(qtbot, config): +config_device = load_config(os.path.join(current_path, "test_configs/config_device.yaml")) +config_device_no_entry = load_config( + os.path.join(current_path, "test_configs/config_device_no_entry.yaml") +) +config_scan = load_config(os.path.join(current_path, "test_configs/config_scan.yaml")) + + +def setup_monitor(qtbot, config): # TODO fixture or helper function? """Helper function to set up the BECDeviceMonitor widget.""" client = MagicMock() widget = BECMonitor(config=config, client=client) @@ -246,7 +121,7 @@ def mock_getitem(dev_name): return mock_instance -# mocked messages and metadatas +# mocked messages and metadata msg_1 = { "data": { "samx": {"samx": {"value": 10}}, @@ -316,7 +191,6 @@ def test_on_scan_segment(qtbot, config, msg, metadata, expected_data): plot_app = setup_monitor(qtbot, config) # Initialize and run test - # plot_app.init_curves = MagicMock() plot_app.data = {} plot_app.scanID = 0 diff --git a/tests/test_config_dialog.py b/tests/test_config_dialog.py new file mode 100644 index 00000000..93d4bbf2 --- /dev/null +++ b/tests/test_config_dialog.py @@ -0,0 +1,179 @@ +import os +import yaml + +import pytest +from PyQt5.QtWidgets import QTabWidget, QTableWidgetItem + +from bec_widgets.widgets import ConfigDialog + +current_path = os.path.dirname(__file__) + + +def load_config(config_path): + """Helper function to load config from yaml file.""" + with open(config_path, "r") as f: + config = yaml.safe_load(f) + return config + + +# save configs as for test_bec_monitor.py +config_device = load_config(os.path.join(current_path, "test_configs/config_device.yaml")) +config_device_no_entry = load_config( + os.path.join(current_path, "test_configs/config_device_no_entry.yaml") +) +config_scan = load_config(os.path.join(current_path, "test_configs/config_scan.yaml")) + + +@pytest.fixture +def config_dialog(qtbot): + widget = ConfigDialog() + qtbot.addWidget(widget) + qtbot.waitExposed(widget) + return widget + + +@pytest.mark.parametrize("config", [config_device, config_scan]) +def test_load_config(config_dialog, config): + config_dialog.load_config(config) + + assert ( + config_dialog.comboBox_appearance.currentText() + == config["plot_settings"]["background_color"] + ) + assert config_dialog.spinBox_n_column.value() == config["plot_settings"]["num_columns"] + assert config_dialog.comboBox_colormap.currentText() == config["plot_settings"]["colormap"] + + +@pytest.mark.parametrize( + "config, scan_mode", + [ + (config_device, False), + (config_scan, True), + (config_device_no_entry, False), + ], +) +def test_initialization(config_dialog, config, scan_mode): + config_dialog.load_config(config) + + assert isinstance(config_dialog, ConfigDialog) + assert ( + config_dialog.comboBox_appearance.currentText() + == config["plot_settings"]["background_color"] + ) + assert config_dialog.spinBox_n_column.value() == config["plot_settings"]["num_columns"] + assert (config_dialog.comboBox_scanTypes.currentText() == "Enabled") == scan_mode + assert ( + config_dialog.tabWidget_scan_types.count() > 0 + ) # Ensures there's at least one tab created + + # If there's a need to check the contents of the first tab (there has to be always at least one tab) + first_tab = config_dialog.tabWidget_scan_types.widget(0) + if scan_mode: + assert ( + first_tab.findChild(QTabWidget, "tabWidget_plots") is not None + ) # Ensures plot tab widget exists in scan mode + else: + assert ( + first_tab.findChild(QTabWidget) is not None + ) # Ensures plot tab widget exists in default mode + + +def test_edit_and_apply_config(config_dialog): + config_dialog.load_config(config_device) + + config_dialog.comboBox_appearance.setCurrentText("white") + config_dialog.spinBox_n_column.setValue(2) + config_dialog.comboBox_colormap.setCurrentText("viridis") + + applied_config = config_dialog.apply_config() + + assert applied_config["plot_settings"]["background_color"] == "white" + assert applied_config["plot_settings"]["num_columns"] == 2 + assert applied_config["plot_settings"]["colormap"] == "viridis" + + +def test_edit_and_apply_config_scan_mode(config_dialog): + config_dialog.load_config(config_scan) + + config_dialog.comboBox_appearance.setCurrentText("white") + config_dialog.spinBox_n_column.setValue(2) + config_dialog.comboBox_colormap.setCurrentText("viridis") + config_dialog.comboBox_scanTypes.setCurrentText("Enabled") + + applied_config = config_dialog.apply_config() + + assert applied_config["plot_settings"]["background_color"] == "white" + assert applied_config["plot_settings"]["num_columns"] == 2 + assert applied_config["plot_settings"]["colormap"] == "viridis" + assert applied_config["plot_settings"]["scan_types"] is True + + +def test_add_new_scan(config_dialog): + # Ensure the tab count is initially 1 (from the default config) + assert config_dialog.tabWidget_scan_types.count() == 1 + + # Add a new scan tab + config_dialog.add_new_scan(config_dialog.tabWidget_scan_types, "Test Scan Tab") + + # Ensure the tab count is now 2 + assert config_dialog.tabWidget_scan_types.count() == 2 + + # Ensure the new tab has the correct name + assert config_dialog.tabWidget_scan_types.tabText(1) == "Test Scan Tab" + + +def test_add_new_plot_and_modify(config_dialog): + # Ensure the tab count is initially 1 and it is called "Default" + assert config_dialog.tabWidget_scan_types.count() == 1 + assert config_dialog.tabWidget_scan_types.tabText(0) == "Default" + # Get the first tab (which should be a scan tab) + scan_tab = config_dialog.tabWidget_scan_types.widget(0) + + # Ensure the plot tab count is initially 1 and it is called "Plot 1" + tabWidget_plots = scan_tab.findChild(QTabWidget) + assert tabWidget_plots.count() == 1 + assert tabWidget_plots.tabText(0) == "Plot 1" + + # Add a new plot tab + config_dialog.add_new_plot(scan_tab) + + # Ensure the plot tab count is now 2 + assert tabWidget_plots.count() == 2 + + # Ensure the new tab has the correct name + assert tabWidget_plots.tabText(1) == "Plot 2" + + # Access the new plot tab + new_plot_tab = tabWidget_plots.widget(1) + + # Modify the line edits within the new plot tab + new_plot_tab.ui.lineEdit_plot_title.setText("Modified Plot Title") + new_plot_tab.ui.lineEdit_x_label.setText("Modified X Label") + new_plot_tab.ui.lineEdit_y_label.setText("Modified Y Label") + new_plot_tab.ui.lineEdit_x_name.setText("Modified X Name") + new_plot_tab.ui.lineEdit_x_entry.setText("Modified X Entry") + + # Modify the table for signals + new_plot_tab.ui.pushButton_y_new.click() # Press button to add a new row + table = new_plot_tab.ui.tableWidget_y_signals + assert table.rowCount() == 1 # Ensure the new row is added + + row_position = table.rowCount() - 1 + + # Modify the first row + table.setItem(row_position, 0, QTableWidgetItem("New Signal Name")) + table.setItem(row_position, 1, QTableWidgetItem("New Signal Entry")) + # Apply the configuration + config = config_dialog.apply_config() + + # Check if the modifications are reflected in the configuration + modified_plot_config = config["plot_data"][ + 1 + ] # Assuming the new plot is the second item in the plot_data list + assert modified_plot_config["plot_name"] == "Modified Plot Title" + assert modified_plot_config["x"]["label"] == "Modified X Label" + assert modified_plot_config["y"]["label"] == "Modified Y Label" + assert modified_plot_config["x"]["signals"][0]["name"] == "Modified X Name" + assert modified_plot_config["x"]["signals"][0]["entry"] == "Modified X Entry" + assert modified_plot_config["y"]["signals"][0]["name"] == "New Signal Name" + assert modified_plot_config["y"]["signals"][0]["entry"] == "New Signal Entry"