0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-14 11:41:49 +02:00

refactor(motor_map_widget): BECMotorMapWidget removed

This commit is contained in:
2025-03-14 18:05:46 +01:00
parent fec26d793e
commit f878e87ad5
14 changed files with 13 additions and 779 deletions

View File

@ -18,7 +18,6 @@ class Widgets(str, enum.Enum):
AbortButton = "AbortButton" AbortButton = "AbortButton"
BECColorMapWidget = "BECColorMapWidget" BECColorMapWidget = "BECColorMapWidget"
BECDockArea = "BECDockArea" BECDockArea = "BECDockArea"
BECMotorMapWidget = "BECMotorMapWidget"
BECMultiWaveformWidget = "BECMultiWaveformWidget" BECMultiWaveformWidget = "BECMultiWaveformWidget"
BECProgressBar = "BECProgressBar" BECProgressBar = "BECProgressBar"
BECQueue = "BECQueue" BECQueue = "BECQueue"
@ -1316,94 +1315,6 @@ class BECMotorMap(RPCBase):
""" """
class BECMotorMapWidget(RPCBase):
@rpc_call
def change_motors(
self,
motor_x: "str",
motor_y: "str",
motor_x_entry: "str" = None,
motor_y_entry: "str" = None,
validate_bec: "bool" = True,
) -> "None":
"""
Change the active motors for the plot.
Args:
motor_x(str): Motor name for the X axis.
motor_y(str): Motor name for the Y axis.
motor_x_entry(str): Motor entry for the X axis.
motor_y_entry(str): Motor entry for the Y axis.
validate_bec(bool, optional): If True, validate the signal with BEC. Defaults to True.
"""
@rpc_call
def set_max_points(self, max_points: "int") -> "None":
"""
Set the maximum number of points to display on the motor map.
Args:
max_points(int): Maximum number of points to display.
"""
@rpc_call
def set_precision(self, precision: "int") -> "None":
"""
Set the precision of the motor map.
Args:
precision(int): Precision to set.
"""
@rpc_call
def set_num_dim_points(self, num_dim_points: "int") -> "None":
"""
Set the number of points to display on the motor map.
Args:
num_dim_points(int): Number of points to display.
"""
@rpc_call
def set_background_value(self, background_value: "int") -> "None":
"""
Set the background value of the motor map.
Args:
background_value(int): Background value of the motor map.
"""
@rpc_call
def set_scatter_size(self, scatter_size: "int") -> "None":
"""
Set the scatter size of the motor map.
Args:
scatter_size(int): Scatter size of the motor map.
"""
@rpc_call
def get_data(self) -> "dict":
"""
Get the data of the motor map.
Returns:
dict: Data of the motor map.
"""
@rpc_call
def reset_history(self) -> "None":
"""
Reset the history of the motor map.
"""
@rpc_call
def export(self):
"""
Show the export dialog for the motor map.
"""
class BECMultiWaveform(RPCBase): class BECMultiWaveform(RPCBase):
@property @property
@rpc_call @rpc_call

View File

@ -21,6 +21,7 @@ from bec_widgets.widgets.containers.figure import BECFigure
from bec_widgets.widgets.containers.layout_manager.layout_manager import LayoutManagerWidget from bec_widgets.widgets.containers.layout_manager.layout_manager import LayoutManagerWidget
from bec_widgets.widgets.editors.jupyter_console.jupyter_console import BECJupyterConsole from bec_widgets.widgets.editors.jupyter_console.jupyter_console import BECJupyterConsole
from bec_widgets.widgets.plots_next_gen.image.image import Image from bec_widgets.widgets.plots_next_gen.image.image import Image
from bec_widgets.widgets.plots_next_gen.motor_map.motor_map import MotorMap
from bec_widgets.widgets.plots_next_gen.plot_base import PlotBase from bec_widgets.widgets.plots_next_gen.plot_base import PlotBase
from bec_widgets.widgets.plots_next_gen.scatter_waveform.scatter_waveform import ScatterWaveform from bec_widgets.widgets.plots_next_gen.scatter_waveform.scatter_waveform import ScatterWaveform
from bec_widgets.widgets.plots_next_gen.waveform.waveform import Waveform from bec_widgets.widgets.plots_next_gen.waveform.waveform import Waveform
@ -53,11 +54,9 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
"w8": self.w8, "w8": self.w8,
"w9": self.w9, "w9": self.w9,
"w10": self.w10, "w10": self.w10,
"d0": self.d0,
"im": self.im, "im": self.im,
"mi": self.mi, "mi": self.mi,
"mm": self.mm, "mm": self.mm,
"mw": self.mw,
"lm": self.lm, "lm": self.lm,
"btn1": self.btn1, "btn1": self.btn1,
"btn2": self.btn2, "btn2": self.btn2,
@ -146,15 +145,19 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
tab_widget.addTab(seventh_tab, "Scatter Waveform") tab_widget.addTab(seventh_tab, "Scatter Waveform")
tab_widget.setCurrentIndex(6) tab_widget.setCurrentIndex(6)
eighth_tab = QWidget()
eighth_tab_layout = QVBoxLayout(eighth_tab)
self.mm = MotorMap()
eighth_tab_layout.addWidget(self.mm)
tab_widget.addTab(eighth_tab, "Motor Map")
tab_widget.setCurrentIndex(7)
# add stuff to the new Waveform widget # add stuff to the new Waveform widget
self._init_waveform() self._init_waveform()
# add stuff to figure # add stuff to figure
self._init_figure() self._init_figure()
# init dock for testing
self._init_dock()
self.setWindowTitle("Jupyter Console Window") self.setWindowTitle("Jupyter Console Window")
def _init_waveform(self): def _init_waveform(self):
@ -219,16 +222,6 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
col=2, col=2,
) )
def _init_dock(self):
self.d0 = self.dock.new(name="dock_0")
self.mm = self.d0.new("BECMotorMapWidget")
self.mm.change_motors("samx", "samy")
self.mw = None # self.wf.multi_waveform(monitor="waveform") # , config=config)
self.dock.save_state()
def closeEvent(self, event): def closeEvent(self, event):
"""Override to handle things when main window is closed.""" """Override to handle things when main window is closed."""
self.dock.cleanup() self.dock.cleanup()

View File

@ -25,9 +25,9 @@ from bec_widgets.widgets.containers.dock.dock import BECDock, DockConfig
from bec_widgets.widgets.control.device_control.positioner_box import PositionerBox from bec_widgets.widgets.control.device_control.positioner_box import PositionerBox
from bec_widgets.widgets.control.scan_control.scan_control import ScanControl from bec_widgets.widgets.control.scan_control.scan_control import ScanControl
from bec_widgets.widgets.editors.vscode.vscode import VSCodeEditor from bec_widgets.widgets.editors.vscode.vscode import VSCodeEditor
from bec_widgets.widgets.plots.motor_map.motor_map_widget import BECMotorMapWidget
from bec_widgets.widgets.plots.multi_waveform.multi_waveform_widget import BECMultiWaveformWidget from bec_widgets.widgets.plots.multi_waveform.multi_waveform_widget import BECMultiWaveformWidget
from bec_widgets.widgets.plots_next_gen.image.image import Image from bec_widgets.widgets.plots_next_gen.image.image import Image
from bec_widgets.widgets.plots_next_gen.motor_map.motor_map import MotorMap
from bec_widgets.widgets.plots_next_gen.scatter_waveform.scatter_waveform import ScatterWaveform from bec_widgets.widgets.plots_next_gen.scatter_waveform.scatter_waveform import ScatterWaveform
from bec_widgets.widgets.plots_next_gen.waveform.waveform import Waveform from bec_widgets.widgets.plots_next_gen.waveform.waveform import Waveform
from bec_widgets.widgets.progress.ring_progress_bar.ring_progress_bar import RingProgressBar from bec_widgets.widgets.progress.ring_progress_bar.ring_progress_bar import RingProgressBar
@ -111,9 +111,7 @@ class BECDockArea(BECWidget, QWidget):
icon_name=Image.ICON_NAME, tooltip="Add Image", filled=True icon_name=Image.ICON_NAME, tooltip="Add Image", filled=True
), ),
"motor_map": MaterialIconAction( "motor_map": MaterialIconAction(
icon_name=BECMotorMapWidget.ICON_NAME, icon_name=MotorMap.ICON_NAME, tooltip="Add Motor Map", filled=True
tooltip="Add Motor Map",
filled=True,
), ),
}, },
), ),
@ -192,7 +190,7 @@ class BECDockArea(BECWidget, QWidget):
lambda: self._create_widget_from_toolbar(widget_name="Image") lambda: self._create_widget_from_toolbar(widget_name="Image")
) )
self.toolbar.widgets["menu_plots"].widgets["motor_map"].triggered.connect( self.toolbar.widgets["menu_plots"].widgets["motor_map"].triggered.connect(
lambda: self._create_widget_from_toolbar(widget_name="BECMotorMapWidget") lambda: self._create_widget_from_toolbar(widget_name="MotorMap")
) )
# Menu Devices # Menu Devices

View File

@ -1 +0,0 @@
{'files': ['motor_map_widget.py','motor_map_widget_plugin.py']}

View File

@ -1,56 +0,0 @@
import os
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
import bec_widgets
from bec_widgets.utils.bec_designer import designer_material_icon
from bec_widgets.widgets.plots.motor_map.motor_map_widget import BECMotorMapWidget
DOM_XML = """
<ui language='c++'>
<widget class='BECMotorMapWidget' name='bec_motor_map_widget'>
</widget>
</ui>
"""
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
class BECMotorMapWidgetPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
def __init__(self):
super().__init__()
self._form_editor = None
def createWidget(self, parent):
t = BECMotorMapWidget(parent)
return t
def domXml(self):
return DOM_XML
def group(self):
return "BEC Plots"
def icon(self):
return designer_material_icon(BECMotorMapWidget.ICON_NAME)
def includeFile(self):
return "bec_motor_map_widget"
def initialize(self, form_editor):
self._form_editor = form_editor
def isContainer(self):
return False
def isInitialized(self):
return self._form_editor is not None
def name(self):
return "BECMotorMapWidget"
def toolTip(self):
return "BECMotorMapWidget"
def whatsThis(self):
return self.toolTip()

View File

@ -1,56 +0,0 @@
import os
from qtpy.QtWidgets import QVBoxLayout
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
from bec_widgets.qt_utils.settings_dialog import SettingWidget
from bec_widgets.utils import UILoader
from bec_widgets.utils.widget_io import WidgetIO
class MotorMapSettings(SettingWidget):
def __init__(self, parent=None, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
current_path = os.path.dirname(__file__)
self.ui = UILoader(self).loader(os.path.join(current_path, "motor_map_settings.ui"))
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.ui)
@Slot(dict)
def display_current_settings(self, config: dict):
WidgetIO.set_value(self.ui.max_points, config["max_points"])
WidgetIO.set_value(self.ui.trace_dim, config["num_dim_points"])
WidgetIO.set_value(self.ui.precision, config["precision"])
WidgetIO.set_value(self.ui.scatter_size, config["scatter_size"])
background_intensity = int((config["background_value"] / 255) * 100)
WidgetIO.set_value(self.ui.background_value, background_intensity)
color = config["color"]
self.ui.color.set_color(color)
@Slot()
def accept_changes(self):
max_points = WidgetIO.get_value(self.ui.max_points)
num_dim_points = WidgetIO.get_value(self.ui.trace_dim)
precision = WidgetIO.get_value(self.ui.precision)
scatter_size = WidgetIO.get_value(self.ui.scatter_size)
background_intensity = int(WidgetIO.get_value(self.ui.background_value) * 0.01 * 255)
color = self.ui.color.get_color("RGBA")
if self.target_widget is not None:
self.target_widget.set_max_points(max_points)
self.target_widget.set_num_dim_points(num_dim_points)
self.target_widget.set_precision(precision)
self.target_widget.set_scatter_size(scatter_size)
self.target_widget.set_background_value(background_intensity)
self.target_widget.set_color(color)
def cleanup(self):
self.ui.color.cleanup()
self.ui.color.close()
self.ui.color.deleteLater()
def closeEvent(self, event):
self.cleanup()
super().closeEvent(event)

View File

@ -1,108 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>243</width>
<height>233</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="1">
<widget class="QSpinBox" name="scatter_size">
<property name="maximum">
<number>20</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="trace_label">
<property name="text">
<string>Trace Dim</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="precision_label">
<property name="text">
<string>Precision</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="background_label">
<property name="text">
<string>Background Intensity</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="precision">
<property name="maximum">
<number>15</number>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="background_value">
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="max_point_label">
<property name="text">
<string>Max Points</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="scatter_size_label">
<property name="text">
<string>Scatter Size</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="max_points">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="trace_dim">
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="color_label">
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="ColorButton" name="color"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ColorButton</class>
<extends>QPushButton</extends>
<header>color_button</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -1,234 +0,0 @@
from __future__ import annotations
import sys
from qtpy.QtWidgets import QVBoxLayout, QWidget
from bec_widgets.qt_utils.settings_dialog import SettingsDialog
from bec_widgets.qt_utils.toolbar import DeviceSelectionAction, MaterialIconAction, ModularToolBar
from bec_widgets.utils.bec_widget import BECWidget
from bec_widgets.widgets.containers.figure import BECFigure
from bec_widgets.widgets.containers.figure.plots.motor_map.motor_map import MotorMapConfig
from bec_widgets.widgets.control.device_input.base_classes.device_input_base import BECDeviceFilter
from bec_widgets.widgets.control.device_input.device_combobox.device_combobox import DeviceComboBox
from bec_widgets.widgets.plots.motor_map.motor_map_dialog.motor_map_settings import MotorMapSettings
class BECMotorMapWidget(BECWidget, QWidget):
PLUGIN = True
ICON_NAME = "my_location"
USER_ACCESS = [
"change_motors",
"set_max_points",
"set_precision",
"set_num_dim_points",
"set_background_value",
"set_scatter_size",
"get_data",
"reset_history",
"export",
]
def __init__(
self,
parent: QWidget | None = None,
config: MotorMapConfig | None = None,
client=None,
gui_id: str | None = None,
**kwargs,
) -> None:
if config is None:
config = MotorMapConfig(widget_class=self.__class__.__name__)
else:
if isinstance(config, dict):
config = MotorMapConfig(**config)
super().__init__(client=client, gui_id=gui_id, **kwargs)
QWidget.__init__(self, parent)
self.layout = QVBoxLayout(self)
self.layout.setSpacing(0)
self.layout.setContentsMargins(0, 0, 0, 0)
self.fig = BECFigure()
self.toolbar = ModularToolBar(
actions={
"motor_x": DeviceSelectionAction(
"Motor X:", DeviceComboBox(device_filter=[BECDeviceFilter.POSITIONER])
),
"motor_y": DeviceSelectionAction(
"Motor Y:", DeviceComboBox(device_filter=[BECDeviceFilter.POSITIONER])
),
"connect": MaterialIconAction(icon_name="link", tooltip="Connect Motors"),
"history": MaterialIconAction(icon_name="history", tooltip="Reset Trace History"),
"config": MaterialIconAction(
icon_name="settings", tooltip="Open Configuration Dialog"
),
},
target_widget=self,
)
self.layout.addWidget(self.toolbar)
self.layout.addWidget(self.fig)
self.map = self.fig.motor_map()
self.map.apply_config(config)
self._hook_actions()
self.config = config
def _hook_actions(self):
self.toolbar.widgets["connect"].action.triggered.connect(self._action_motors)
self.toolbar.widgets["config"].action.triggered.connect(self.show_settings)
self.toolbar.widgets["history"].action.triggered.connect(self.reset_history)
if self.map.motor_x is None and self.map.motor_y is None:
self._enable_actions(False)
def _enable_actions(self, enable: bool):
self.toolbar.widgets["config"].action.setEnabled(enable)
self.toolbar.widgets["history"].action.setEnabled(enable)
def _action_motors(self):
toolbar_x = self.toolbar.widgets["motor_x"].device_combobox
toolbar_y = self.toolbar.widgets["motor_y"].device_combobox
motor_x = toolbar_x.currentText()
motor_y = toolbar_y.currentText()
self.change_motors(motor_x, motor_y, None, None, True)
toolbar_x.setStyleSheet("QComboBox {{ background-color: " "; }}")
toolbar_y.setStyleSheet("QComboBox {{ background-color: " "; }}")
def show_settings(self) -> None:
dialog = SettingsDialog(
self, settings_widget=MotorMapSettings(), window_title="Motor Map Settings"
)
dialog.exec()
###################################
# User Access Methods from MotorMap
###################################
def change_motors(
self,
motor_x: str,
motor_y: str,
motor_x_entry: str = None,
motor_y_entry: str = None,
validate_bec: bool = True,
) -> None:
"""
Change the active motors for the plot.
Args:
motor_x(str): Motor name for the X axis.
motor_y(str): Motor name for the Y axis.
motor_x_entry(str): Motor entry for the X axis.
motor_y_entry(str): Motor entry for the Y axis.
validate_bec(bool, optional): If True, validate the signal with BEC. Defaults to True.
"""
self.map.change_motors(motor_x, motor_y, motor_x_entry, motor_y_entry, validate_bec)
if self.map.motor_x is not None and self.map.motor_y is not None:
self._enable_actions(True)
toolbar_x = self.toolbar.widgets["motor_x"].device_combobox
toolbar_y = self.toolbar.widgets["motor_y"].device_combobox
if toolbar_x.currentText() != motor_x:
toolbar_x.setCurrentText(motor_x)
toolbar_x.setStyleSheet("QComboBox {{ background-color: " "; }}")
if toolbar_y.currentText() != motor_y:
toolbar_y.setCurrentText(motor_y)
toolbar_y.setStyleSheet("QComboBox {{ background-color: " "; }}")
def get_data(self) -> dict:
"""
Get the data of the motor map.
Returns:
dict: Data of the motor map.
"""
return self.map.get_data()
def reset_history(self) -> None:
"""
Reset the history of the motor map.
"""
self.map.reset_history()
def set_color(self, color: str | tuple):
"""
Set the color of the motor map.
Args:
color(str, tuple): Color to set.
"""
self.map.set_color(color)
def set_max_points(self, max_points: int) -> None:
"""
Set the maximum number of points to display on the motor map.
Args:
max_points(int): Maximum number of points to display.
"""
self.map.set_max_points(max_points)
def set_precision(self, precision: int) -> None:
"""
Set the precision of the motor map.
Args:
precision(int): Precision to set.
"""
self.map.set_precision(precision)
def set_num_dim_points(self, num_dim_points: int) -> None:
"""
Set the number of points to display on the motor map.
Args:
num_dim_points(int): Number of points to display.
"""
self.map.set_num_dim_points(num_dim_points)
def set_background_value(self, background_value: int) -> None:
"""
Set the background value of the motor map.
Args:
background_value(int): Background value of the motor map.
"""
self.map.set_background_value(background_value)
def set_scatter_size(self, scatter_size: int) -> None:
"""
Set the scatter size of the motor map.
Args:
scatter_size(int): Scatter size of the motor map.
"""
self.map.set_scatter_size(scatter_size)
def export(self):
"""
Show the export dialog for the motor map.
"""
self.map.export()
def cleanup(self):
self.fig.cleanup()
self.toolbar.widgets["motor_x"].device_combobox.cleanup()
self.toolbar.widgets["motor_y"].device_combobox.cleanup()
return super().cleanup()
def main(): # pragma: no cover
from qtpy.QtWidgets import QApplication
app = QApplication(sys.argv)
widget = BECMotorMapWidget()
widget.show()
sys.exit(app.exec_())
if __name__ == "__main__": # pragma: no cover
main()

View File

@ -1,17 +0,0 @@
def main(): # pragma: no cover
from qtpy import PYSIDE6
if not PYSIDE6:
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
return
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
from bec_widgets.widgets.plots.motor_map.bec_motor_map_widget_plugin import (
BECMotorMapWidgetPlugin,
)
QPyDesignerCustomWidgetCollection.addCustomWidget(BECMotorMapWidgetPlugin())
if __name__ == "__main__": # pragma: no cover
main()

View File

@ -135,11 +135,8 @@ def test_toolbar_add_plot_image(bec_dock_area):
def test_toolbar_add_plot_motor_map(bec_dock_area): 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.widgets["menu_plots"].widgets["motor_map"].trigger()
assert "BECMotorMapWidget_0" in bec_dock_area.panels assert "MotorMap_0" in bec_dock_area.panels
assert ( assert bec_dock_area.panels["MotorMap_0"].widgets[0].config.widget_class == "MotorMap"
bec_dock_area.panels["BECMotorMapWidget_0"].widgets[0].config.widget_class
== "BECMotorMapWidget"
)
def test_toolbar_add_multi_waveform(bec_dock_area): def test_toolbar_add_multi_waveform(bec_dock_area):

View File

@ -1,193 +0,0 @@
from unittest.mock import MagicMock, patch
import pytest
from bec_widgets.widgets.control.device_input.base_classes.device_input_base import BECDeviceFilter
from bec_widgets.widgets.plots.motor_map.motor_map_dialog.motor_map_settings import MotorMapSettings
from bec_widgets.widgets.plots.motor_map.motor_map_widget import BECMotorMapWidget
from .client_mocks import mocked_client
@pytest.fixture
def motor_map_widget(qtbot, mocked_client):
widget = BECMotorMapWidget(client=mocked_client())
widget.toolbar.widgets["motor_x"].device_combobox.set_device_filter(BECDeviceFilter.POSITIONER)
widget.toolbar.widgets["motor_y"].device_combobox.set_device_filter(BECDeviceFilter.POSITIONER)
qtbot.addWidget(widget)
qtbot.waitExposed(widget)
yield widget
@pytest.fixture
def mock_motor_map(motor_map_widget):
motor_map_mock = MagicMock()
motor_map_widget.map = motor_map_mock
return motor_map_mock
def test_motor_map_widget_init(motor_map_widget):
assert motor_map_widget is not None
assert motor_map_widget.client is not None
assert isinstance(motor_map_widget, BECMotorMapWidget)
assert motor_map_widget.config.widget_class == "BECMotorMapWidget"
# check initial state of toolbar actions
assert motor_map_widget.toolbar.widgets["connect"].action.isEnabled() == True
assert motor_map_widget.toolbar.widgets["config"].action.isEnabled() == False
assert motor_map_widget.toolbar.widgets["history"].action.isEnabled() == False
assert motor_map_widget.toolbar.widgets["motor_x"].device_combobox.config.device_filter == [
BECDeviceFilter.POSITIONER
]
assert motor_map_widget.toolbar.widgets["motor_y"].device_combobox.config.device_filter == [
BECDeviceFilter.POSITIONER
]
assert motor_map_widget.map.motor_x is None
assert motor_map_widget.map.motor_y is None
###################################
# Toolbar Actions
###################################
def test_motor_map_widget_change_motors_enable_toolbar(motor_map_widget):
motor_map_widget.change_motors("samx", "samy")
assert motor_map_widget.map.motor_x == "samx"
assert motor_map_widget.map.motor_y == "samy"
assert motor_map_widget.toolbar.widgets["motor_x"].device_combobox.currentText() == "samx"
assert motor_map_widget.toolbar.widgets["motor_y"].device_combobox.currentText() == "samy"
assert motor_map_widget.toolbar.widgets["config"].action.isEnabled() == True
assert motor_map_widget.toolbar.widgets["history"].action.isEnabled() == True
###################################
# Wrapper methods for MotorMap
###################################
def test_change_motors(motor_map_widget, mock_motor_map):
motor_map_widget.change_motors("motor_x", "motor_y", "motor_x_entry", "motor_y_entry", True)
mock_motor_map.change_motors.assert_called_once_with(
"motor_x", "motor_y", "motor_x_entry", "motor_y_entry", True
)
def test_get_data(motor_map_widget, mock_motor_map):
motor_map_widget.get_data()
mock_motor_map.get_data.assert_called_once()
def test_reset_history(motor_map_widget, mock_motor_map):
motor_map_widget.reset_history()
mock_motor_map.reset_history.assert_called_once()
def test_set_color(motor_map_widget, mock_motor_map):
motor_map_widget.set_color("blue")
mock_motor_map.set_color.assert_called_once_with("blue")
def test_set_max_points(motor_map_widget, mock_motor_map):
motor_map_widget.set_max_points(100)
mock_motor_map.set_max_points.assert_called_once_with(100)
def test_set_precision(motor_map_widget, mock_motor_map):
motor_map_widget.set_precision(2)
mock_motor_map.set_precision.assert_called_once_with(2)
def test_set_num_dim_points(motor_map_widget, mock_motor_map):
motor_map_widget.set_num_dim_points(50)
mock_motor_map.set_num_dim_points.assert_called_once_with(50)
def test_set_background_value(motor_map_widget, mock_motor_map):
motor_map_widget.set_background_value(128)
mock_motor_map.set_background_value.assert_called_once_with(128)
def test_set_scatter_size(motor_map_widget, mock_motor_map):
motor_map_widget.set_scatter_size(10)
mock_motor_map.set_scatter_size.assert_called_once_with(10)
###################################
# MotorMap Dialog
###################################
def test_motor_map_widget_clicked(motor_map_widget, qtbot):
motor_map_widget.toolbar.widgets["motor_x"].device_combobox.setCurrentText("samx")
motor_map_widget.toolbar.widgets["motor_y"].device_combobox.setCurrentText("samy")
motor_map_widget.toolbar.widgets["connect"].action.trigger()
qtbot.wait(200)
assert motor_map_widget.map.motor_x == "samx"
assert motor_map_widget.map.motor_y == "samy"
assert motor_map_widget.toolbar.widgets["config"].action.isEnabled() == True
assert motor_map_widget.toolbar.widgets["history"].action.isEnabled() == True
@pytest.fixture
def motor_map_settings(qtbot):
widget = MotorMapSettings()
qtbot.addWidget(widget)
qtbot.waitExposed(widget)
yield widget
def test_display_current_settings(motor_map_settings):
config = {
"max_points": 100,
"num_dim_points": 50,
"precision": 2,
"scatter_size": 10,
"background_value": 128,
"color": (255, 0, 0, 255),
}
with patch("bec_widgets.utils.widget_io.WidgetIO.set_value") as mock_set_value:
with patch.object(motor_map_settings.ui.color, "set_color") as mock_set_color:
motor_map_settings.display_current_settings(config)
mock_set_value.assert_any_call(motor_map_settings.ui.max_points, config["max_points"])
mock_set_value.assert_any_call(
motor_map_settings.ui.trace_dim, config["num_dim_points"]
)
mock_set_value.assert_any_call(motor_map_settings.ui.precision, config["precision"])
mock_set_value.assert_any_call(
motor_map_settings.ui.scatter_size, config["scatter_size"]
)
mock_set_value.assert_any_call(
motor_map_settings.ui.background_value, 50
) # 128/255*100 = 50
mock_set_color.assert_called_once_with(config["color"])
def test_accept_changes(motor_map_settings):
with patch(
"bec_widgets.utils.widget_io.WidgetIO.get_value", side_effect=[100, 50, 2, 10, 50]
) as mock_get_value:
with patch.object(
motor_map_settings.ui.color, "get_color", return_value=(255, 0, 0, 255)
) as mock_get_color:
mock_target_widget = MagicMock()
motor_map_settings.set_target_widget(mock_target_widget)
motor_map_settings.accept_changes()
mock_get_value.assert_any_call(motor_map_settings.ui.max_points)
mock_get_value.assert_any_call(motor_map_settings.ui.trace_dim)
mock_get_value.assert_any_call(motor_map_settings.ui.precision)
mock_get_value.assert_any_call(motor_map_settings.ui.scatter_size)
mock_get_value.assert_any_call(motor_map_settings.ui.background_value)
mock_get_color.assert_called_once()
mock_target_widget.set_max_points.assert_called_once_with(100)
mock_target_widget.set_num_dim_points.assert_called_once_with(50)
mock_target_widget.set_precision.assert_called_once_with(2)
mock_target_widget.set_scatter_size.assert_called_once_with(10)
mock_target_widget.set_background_value.assert_called_once_with(127)
mock_target_widget.set_color.assert_called_once_with((255, 0, 0, 255))