mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
feat: add Dap dialog widget
This commit is contained in:
@ -26,6 +26,7 @@ class Widgets(str, enum.Enum):
|
||||
DeviceBrowser = "DeviceBrowser"
|
||||
DeviceComboBox = "DeviceComboBox"
|
||||
DeviceLineEdit = "DeviceLineEdit"
|
||||
LMFitDialog = "LMFitDialog"
|
||||
PositionerBox = "PositionerBox"
|
||||
PositionerControlLine = "PositionerControlLine"
|
||||
ResetButton = "ResetButton"
|
||||
@ -1825,15 +1826,6 @@ class BECWaveform(RPCBase):
|
||||
x_entry(str): Entry of the x signal.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def get_dap_params(self) -> "dict":
|
||||
"""
|
||||
Get the DAP parameters of all DAP curves.
|
||||
|
||||
Returns:
|
||||
dict: DAP parameters of all DAP curves.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def remove_curve(self, *identifiers):
|
||||
"""
|
||||
@ -2096,10 +2088,10 @@ class BECWaveformWidget(RPCBase):
|
||||
self,
|
||||
x_name: "str",
|
||||
y_name: "str",
|
||||
dap: "str",
|
||||
x_entry: "str | None" = None,
|
||||
y_entry: "str | None" = None,
|
||||
color: "str | None" = None,
|
||||
dap: "str" = "GaussianModel",
|
||||
validate_bec: "bool" = True,
|
||||
**kwargs,
|
||||
) -> "BECCurve":
|
||||
@ -2120,15 +2112,6 @@ class BECWaveformWidget(RPCBase):
|
||||
BECCurve: The curve object.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def get_dap_params(self) -> "dict":
|
||||
"""
|
||||
Get the DAP parameters of all DAP curves.
|
||||
|
||||
Returns:
|
||||
dict: DAP parameters of all DAP curves.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def remove_curve(self, *identifiers):
|
||||
"""
|
||||
@ -2313,7 +2296,7 @@ class BECWaveformWidget(RPCBase):
|
||||
|
||||
class DarkModeButton(RPCBase):
|
||||
@rpc_call
|
||||
def toggle_dark_mode(self) -> None:
|
||||
def toggle_dark_mode(self) -> "None":
|
||||
"""
|
||||
Toggle the dark mode state. This will change the theme of the entire
|
||||
application to dark or light mode.
|
||||
@ -2392,6 +2375,24 @@ class DeviceLineEdit(RPCBase):
|
||||
"""
|
||||
|
||||
|
||||
class LMFitDialog(RPCBase):
|
||||
@property
|
||||
@rpc_call
|
||||
def _config_dict(self) -> "dict":
|
||||
"""
|
||||
Get the configuration of the widget.
|
||||
|
||||
Returns:
|
||||
dict: The configuration of the widget.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def _get_all_rpc(self) -> "dict":
|
||||
"""
|
||||
Get all registered RPC objects.
|
||||
"""
|
||||
|
||||
|
||||
class PositionerBox(RPCBase):
|
||||
@rpc_call
|
||||
def set_positioner(self, positioner: "str | Positioner"):
|
||||
|
@ -50,7 +50,6 @@ class BECWaveform(BECPlotBase):
|
||||
"plot",
|
||||
"add_dap",
|
||||
"set_x",
|
||||
"get_dap_params",
|
||||
"remove_curve",
|
||||
"scan_history",
|
||||
"curves",
|
||||
@ -74,8 +73,8 @@ class BECWaveform(BECPlotBase):
|
||||
]
|
||||
scan_signal_update = pyqtSignal()
|
||||
async_signal_update = pyqtSignal()
|
||||
dap_params_update = pyqtSignal(dict)
|
||||
dap_summary_update = pyqtSignal(dict)
|
||||
dap_params_update = pyqtSignal(dict, dict)
|
||||
dap_summary_update = pyqtSignal(dict, dict)
|
||||
autorange_signal = pyqtSignal()
|
||||
new_scan = pyqtSignal()
|
||||
|
||||
@ -1085,8 +1084,9 @@ class BECWaveform(BECPlotBase):
|
||||
curve.setData(x, y)
|
||||
curve.dap_params = msg["data"][1]["fit_parameters"]
|
||||
curve.dap_summary = msg["data"][1]["fit_summary"]
|
||||
self.dap_params_update.emit(curve.dap_params)
|
||||
self.dap_summary_update.emit(curve.dap_summary)
|
||||
metadata.update({"curve_id": curve_id_request})
|
||||
self.dap_params_update.emit(curve.dap_params, metadata)
|
||||
self.dap_summary_update.emit(curve.dap_summary, metadata)
|
||||
break
|
||||
|
||||
@Slot(dict, dict)
|
||||
|
0
bec_widgets/widgets/lmfit_dialog/__init__.py
Normal file
0
bec_widgets/widgets/lmfit_dialog/__init__.py
Normal file
1
bec_widgets/widgets/lmfit_dialog/lm_fit_dialog.pyproject
Normal file
1
bec_widgets/widgets/lmfit_dialog/lm_fit_dialog.pyproject
Normal file
@ -0,0 +1 @@
|
||||
{'files': ['lmfit_dialog.py']}
|
54
bec_widgets/widgets/lmfit_dialog/lm_fit_dialog_plugin.py
Normal file
54
bec_widgets/widgets/lmfit_dialog/lm_fit_dialog_plugin.py
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
|
||||
from bec_widgets.utils.bec_designer import designer_material_icon
|
||||
from bec_widgets.widgets.lmfit_dialog.lmfit_dialog import LMFitDialog
|
||||
|
||||
DOM_XML = """
|
||||
<ui language='c++'>
|
||||
<widget class='LMFitDialog' name='lm_fit_dialog'>
|
||||
</widget>
|
||||
</ui>
|
||||
"""
|
||||
|
||||
|
||||
class LMFitDialogPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._form_editor = None
|
||||
|
||||
def createWidget(self, parent):
|
||||
t = LMFitDialog(parent)
|
||||
return t
|
||||
|
||||
def domXml(self):
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return ""
|
||||
|
||||
def icon(self):
|
||||
return designer_material_icon(LMFitDialog.ICON_NAME)
|
||||
|
||||
def includeFile(self):
|
||||
return "lm_fit_dialog"
|
||||
|
||||
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 "LMFitDialog"
|
||||
|
||||
def toolTip(self):
|
||||
return "LMFitDialog"
|
||||
|
||||
def whatsThis(self):
|
||||
return self.toolTip()
|
185
bec_widgets/widgets/lmfit_dialog/lmfit_dialog.py
Normal file
185
bec_widgets/widgets/lmfit_dialog/lmfit_dialog.py
Normal file
@ -0,0 +1,185 @@
|
||||
import os
|
||||
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from bec_lib.logger import bec_logger
|
||||
from qtpy.QtCore import Property, Signal, Slot
|
||||
from qtpy.QtWidgets import QTreeWidgetItem, QVBoxLayout, QWidget
|
||||
|
||||
from bec_widgets.utils import UILoader
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
|
||||
class LMFitDialog(BECWidget, QWidget):
|
||||
|
||||
ICON_NAME = "bike_lane"
|
||||
selected_fit = Signal(str)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent=None,
|
||||
client=None,
|
||||
config=None,
|
||||
target_widget=None,
|
||||
gui_id: str | None = None,
|
||||
ui_file="lmfit_dialog_vertical.ui",
|
||||
):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
self._ui_file = ui_file
|
||||
self.target_widget = target_widget
|
||||
|
||||
current_path = os.path.dirname(__file__)
|
||||
self.ui = UILoader(self).loader(os.path.join(current_path, self._ui_file))
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.layout.addWidget(self.ui)
|
||||
self.summary_data = {}
|
||||
self._fit_curve_id = None
|
||||
self._deci_precision = 3
|
||||
self.ui.curve_list.currentItemChanged.connect(self.display_fit_details)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
@Property(bool)
|
||||
def hide_curve_selection(self):
|
||||
"""Property for showing the curve selection."""
|
||||
return not self.ui.group_curve_selection.isVisible()
|
||||
|
||||
@hide_curve_selection.setter
|
||||
def hide_curve_selection(self, show: bool):
|
||||
"""Setter for showing the curve selection.
|
||||
|
||||
Args:
|
||||
show (bool): Whether to show the curve selection.
|
||||
"""
|
||||
self.ui.group_curve_selection.setVisible(not show)
|
||||
|
||||
@property
|
||||
def fit_curve_id(self):
|
||||
"""Property for the currently displayed fit curve_id."""
|
||||
return self._fit_curve_id
|
||||
|
||||
@fit_curve_id.setter
|
||||
def fit_curve_id(self, curve_id: str):
|
||||
"""Setter for the currently displayed fit curve_id.
|
||||
|
||||
Args:
|
||||
fit_curve_id (str): The curve_id of the fit curve to be displayed.
|
||||
"""
|
||||
self._fit_curve_id = curve_id
|
||||
self.selected_fit.emit(curve_id)
|
||||
|
||||
@Slot(str)
|
||||
def remove_dap_data(self, curve_id: str):
|
||||
"""Remove the DAP data for the given curve_id.
|
||||
|
||||
Args:
|
||||
curve_id (str): The curve_id of the DAP data to be removed.
|
||||
"""
|
||||
self.summary_data.pop(curve_id, None)
|
||||
self.refresh_curve_list()
|
||||
|
||||
@Slot(str)
|
||||
def select_curve(self, curve_id: str):
|
||||
"""Select active curve_id in the curve list.
|
||||
|
||||
Args:
|
||||
curve_id (str): curve_id to be selected.
|
||||
"""
|
||||
self.fit_curve_id = curve_id
|
||||
|
||||
@Slot(dict, dict)
|
||||
def update_summary_tree(self, data: dict, metadata: dict):
|
||||
"""Update the summary tree with the given data.
|
||||
|
||||
Args:
|
||||
data (dict): Data for the DAP Summary.
|
||||
metadata (dict): Metadata of the fit curve.
|
||||
"""
|
||||
curve_id = metadata.get("curve_id", "")
|
||||
self.summary_data.update({curve_id: data})
|
||||
self.refresh_curve_list()
|
||||
if self.fit_curve_id is None:
|
||||
self.fit_curve_id = curve_id
|
||||
for index in range(self.ui.curve_list.count()):
|
||||
item = self.ui.curve_list.item(index)
|
||||
if item.text() == curve_id:
|
||||
self.ui.curve_list.setCurrentItem(item)
|
||||
if curve_id != self.fit_curve_id:
|
||||
return
|
||||
if data is None:
|
||||
return
|
||||
self.ui.summary_tree.clear()
|
||||
properties = [
|
||||
("Model", data.get("model", "")),
|
||||
("Method", data.get("method", "")),
|
||||
("Chi-Squared", f"{data.get('chisqr', 0.0):.{self._deci_precision}f}"),
|
||||
("Reduced Chi-Squared", f"{data.get('redchi', 0.0):.{self._deci_precision}f}"),
|
||||
("R-Squared", f"{data.get('rsquared', 0.0):.{self._deci_precision}f}"),
|
||||
("Message", data.get("message", "")),
|
||||
]
|
||||
for prop, val in properties:
|
||||
QTreeWidgetItem(self.ui.summary_tree, [prop, val])
|
||||
self.update_param_tree(data.get("params", []))
|
||||
|
||||
def _update_summary_data(self, curve_id: str, data: dict):
|
||||
"""Update the summary data with the given data.
|
||||
|
||||
Args:
|
||||
curve_id (str): The curve_id of the fit curve.
|
||||
data (dict): The data to be updated.
|
||||
"""
|
||||
self.summary_data.update({curve_id: data})
|
||||
if self.fit_curve_id is not None:
|
||||
return
|
||||
self.fit_curve_id = curve_id
|
||||
|
||||
def update_param_tree(self, params):
|
||||
"""Update the parameter tree with the given parameters.
|
||||
|
||||
Args:
|
||||
params (list): List of LMFit parameters for the fit curve.
|
||||
"""
|
||||
self.ui.param_tree.clear()
|
||||
for param in params:
|
||||
param_name, param_value, param_std = (
|
||||
param[0],
|
||||
f"{param[1]:.{self._deci_precision}f}",
|
||||
f"{param[7]:.{self._deci_precision}f}",
|
||||
)
|
||||
QTreeWidgetItem(self.ui.param_tree, [param_name, param_value, param_std])
|
||||
|
||||
def populate_curve_list(self):
|
||||
"""Populate the curve list with the available fit curves."""
|
||||
for curve_name in self.summary_data.keys():
|
||||
self.ui.curve_list.addItem(curve_name)
|
||||
|
||||
def refresh_curve_list(self):
|
||||
"""Refresh the curve list with the updated data."""
|
||||
self.ui.curve_list.clear()
|
||||
self.populate_curve_list()
|
||||
|
||||
def display_fit_details(self, current):
|
||||
"""Callback for displaying the fit details of the selected curve.
|
||||
|
||||
Args:
|
||||
current: The current item in the curve list.
|
||||
"""
|
||||
if current:
|
||||
curve_name = current.text()
|
||||
self.fit_curve_id = curve_name
|
||||
data = self.summary_data[curve_name]
|
||||
if data is None:
|
||||
return
|
||||
self.update_summary_tree(data, {"curve_id": curve_name})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
from qtpy.QtWidgets import QApplication
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
dialog = LMFitDialog()
|
||||
dialog.show()
|
||||
sys.exit(app.exec_())
|
120
bec_widgets/widgets/lmfit_dialog/lmfit_dialog_compact.ui
Normal file
120
bec_widgets/widgets/lmfit_dialog/lmfit_dialog_compact.ui
Normal file
@ -0,0 +1,120 @@
|
||||
<?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>655</width>
|
||||
<height>520</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QSplitter" name="splitter_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::VLine</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="opaqueResize">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QGroupBox" name="group_curve_selection">
|
||||
<property name="title">
|
||||
<string>Select Curve</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QListWidget" name="curve_list"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>2</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QGroupBox" name="group_summary">
|
||||
<property name="title">
|
||||
<string>Fit Summary</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="summary_tree">
|
||||
<property name="uniformRowHeights">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Property</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="group_parameters">
|
||||
<property name="title">
|
||||
<string>Parameter Details</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="param_tree">
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Parameter</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Std</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
147
bec_widgets/widgets/lmfit_dialog/lmfit_dialog_vertical.ui
Normal file
147
bec_widgets/widgets/lmfit_dialog/lmfit_dialog_vertical.ui
Normal file
@ -0,0 +1,147 @@
|
||||
<?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>274</width>
|
||||
<height>568</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="0,0,0">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="group_curve_selection">
|
||||
<property name="title">
|
||||
<string>Select Curve</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="3">
|
||||
<item>
|
||||
<widget class="QListWidget" name="curve_list"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="group_summary">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Fit Summary</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="summary_tree">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>80</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Property</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="group_parameters">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Parameter Details</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="param_tree">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>80</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Parameter</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Std</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
15
bec_widgets/widgets/lmfit_dialog/register_lm_fit_dialog.py
Normal file
15
bec_widgets/widgets/lmfit_dialog/register_lm_fit_dialog.py
Normal file
@ -0,0 +1,15 @@
|
||||
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.lmfit_dialog.lm_fit_dialog_plugin import LMFitDialogPlugin
|
||||
|
||||
QPyDesignerCustomWidgetCollection.addCustomWidget(LMFitDialogPlugin())
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
@ -1,127 +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>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QSplitter" name="splitter_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::VLine</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="opaqueResize">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QGroupBox" name="group_curve_selection">
|
||||
<property name="title">
|
||||
<string>Select Curve</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="refresh_button">
|
||||
<property name="text">
|
||||
<string>Refresh DAP Summary</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="curve_list"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>2</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QGroupBox" name="group_summary">
|
||||
<property name="title">
|
||||
<string>Fit Summary</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="summary_tree">
|
||||
<property name="uniformRowHeights">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Property</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="group_parameters">
|
||||
<property name="title">
|
||||
<string>Parameter Details</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="param_tree">
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Parameter</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Std</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -4,66 +4,26 @@ from qtpy.QtWidgets import QDialog, QTreeWidgetItem, QVBoxLayout
|
||||
|
||||
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
||||
from bec_widgets.utils import UILoader
|
||||
from bec_widgets.widgets.lmfit_dialog.lmfit_dialog import LMFitDialog
|
||||
|
||||
|
||||
class FitSummaryWidget(QDialog):
|
||||
|
||||
def __init__(self, parent=None, target_widget=None):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.target_widget = target_widget
|
||||
self.summary_data = self.target_widget.get_dap_summary()
|
||||
|
||||
self.setModal(True)
|
||||
|
||||
current_path = os.path.dirname(__file__)
|
||||
self.ui = UILoader(self).loader(os.path.join(current_path, "dap_summary.ui"))
|
||||
self.target_widget = target_widget
|
||||
self.dap_dialog = LMFitDialog(parent=self, ui_file="lmfit_dialog_compact.ui")
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.layout.addWidget(self.ui)
|
||||
self.layout.addWidget(self.dap_dialog)
|
||||
self.target_widget.dap_summary_update.connect(self.dap_dialog.update_summary_tree)
|
||||
self.setLayout(self.layout)
|
||||
self._get_dap_from_target_widget()
|
||||
|
||||
self.ui.curve_list.currentItemChanged.connect(self.display_fit_details)
|
||||
self.ui.refresh_button.clicked.connect(self.refresh_dap)
|
||||
|
||||
self.populate_curve_list()
|
||||
|
||||
def populate_curve_list(self):
|
||||
for curve_name in self.summary_data.keys():
|
||||
self.ui.curve_list.addItem(curve_name)
|
||||
|
||||
def display_fit_details(self, current):
|
||||
if current:
|
||||
curve_name = current.text()
|
||||
data = self.summary_data[curve_name]
|
||||
if data is None:
|
||||
return
|
||||
self.refresh_trees(data)
|
||||
|
||||
@Slot()
|
||||
def refresh_dap(self):
|
||||
self.ui.curve_list.clear()
|
||||
self.summary_data = self.target_widget.get_dap_summary()
|
||||
self.populate_curve_list()
|
||||
|
||||
def refresh_trees(self, data):
|
||||
self.update_summary_tree(data)
|
||||
self.update_param_tree(data["params"])
|
||||
|
||||
def update_summary_tree(self, data):
|
||||
self.ui.summary_tree.clear()
|
||||
properties = [
|
||||
("Model", data.get("model", "")),
|
||||
("Method", data.get("method", "")),
|
||||
("Chi-Squared", str(data.get("chisqr", ""))),
|
||||
("Reduced Chi-Squared", str(data.get("redchi", ""))),
|
||||
("AIC", str(data.get("aic", ""))),
|
||||
("BIC", str(data.get("bic", ""))),
|
||||
("R-Squared", str(data.get("rsquared", ""))),
|
||||
("Message", data.get("message", "")),
|
||||
]
|
||||
for prop, val in properties:
|
||||
QTreeWidgetItem(self.ui.summary_tree, [prop, val])
|
||||
|
||||
def update_param_tree(self, params):
|
||||
self.ui.param_tree.clear()
|
||||
for param in params:
|
||||
param_name, param_value, param_std = param[0], str(param[1]), str(param[7])
|
||||
QTreeWidgetItem(self.ui.param_tree, [param_name, param_value, param_std])
|
||||
def _get_dap_from_target_widget(self) -> None:
|
||||
"""Get the DAP data from the target widget and update the DAP dialog manually on creation."""
|
||||
dap_summary = self.target_widget.get_dap_summary()
|
||||
for curve_id, data in dap_summary.items():
|
||||
md = {"curve_id": curve_id}
|
||||
self.dap_dialog.update_summary_tree(data=data, metadata=md)
|
||||
|
@ -33,7 +33,6 @@ class BECWaveformWidget(BECWidget, QWidget):
|
||||
"curves",
|
||||
"plot",
|
||||
"add_dap",
|
||||
"get_dap_params",
|
||||
"remove_curve",
|
||||
"scan_history",
|
||||
"get_all_data",
|
||||
@ -55,8 +54,8 @@ class BECWaveformWidget(BECWidget, QWidget):
|
||||
]
|
||||
scan_signal_update = Signal()
|
||||
async_signal_update = Signal()
|
||||
dap_params_update = Signal(dict)
|
||||
dap_summary_update = Signal(dict)
|
||||
dap_summary_update = Signal(dict, dict)
|
||||
dap_params_update = Signal(dict, dict)
|
||||
autorange_signal = Signal()
|
||||
new_scan = Signal()
|
||||
crosshair_position_changed = Signal(tuple)
|
||||
@ -218,7 +217,7 @@ class BECWaveformWidget(BECWidget, QWidget):
|
||||
def show_fit_summary_dialog(self):
|
||||
dialog = FitSummaryWidget(target_widget=self)
|
||||
dialog.resize(800, 600)
|
||||
dialog.show()
|
||||
dialog.exec()
|
||||
|
||||
###################################
|
||||
# User Access Methods from Waveform
|
||||
@ -257,7 +256,7 @@ class BECWaveformWidget(BECWidget, QWidget):
|
||||
"""
|
||||
self.waveform.set_colormap(colormap)
|
||||
|
||||
@SafeSlot(popup_error=True)
|
||||
@SafeSlot(str, popup_error=True)
|
||||
def set_x(self, x_name: str, x_entry: str | None = None):
|
||||
"""
|
||||
Change the x axis of the plot widget.
|
||||
@ -272,7 +271,7 @@ class BECWaveformWidget(BECWidget, QWidget):
|
||||
"""
|
||||
self.waveform.set_x(x_name, x_entry)
|
||||
|
||||
@SafeSlot(popup_error=True)
|
||||
@SafeSlot(str, popup_error=True)
|
||||
def plot(
|
||||
self,
|
||||
arg1: list | np.ndarray | str | None = None,
|
||||
@ -331,15 +330,16 @@ class BECWaveformWidget(BECWidget, QWidget):
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@SafeSlot(popup_error=True)
|
||||
@SafeSlot(str, str, str, popup_error=True)
|
||||
def add_dap(
|
||||
self,
|
||||
x_name: str,
|
||||
y_name: str,
|
||||
dap: str,
|
||||
x_entry: str | None = None,
|
||||
y_entry: str | None = None,
|
||||
color: str | None = None,
|
||||
dap: str = "GaussianModel",
|
||||
# dap: str = "GaussianModel",
|
||||
validate_bec: bool = True,
|
||||
**kwargs,
|
||||
) -> BECCurve:
|
||||
|
BIN
docs/assets/widget_screenshots/lmfit_dialog.png
Normal file
BIN
docs/assets/widget_screenshots/lmfit_dialog.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 147 KiB |
BIN
docs/assets/widget_screenshots/lmfit_dialog_connect.png
Normal file
BIN
docs/assets/widget_screenshots/lmfit_dialog_connect.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 MiB |
50
docs/user/widgets/lmfit_dialog/lmfit_dialog.md
Normal file
50
docs/user/widgets/lmfit_dialog/lmfit_dialog.md
Normal file
@ -0,0 +1,50 @@
|
||||
(user.widgets.lmfit_dialog)=
|
||||
|
||||
# LMFit Dialog
|
||||
|
||||
````{tab} Overview
|
||||
|
||||
The [`LMFit Dialog`](/api_reference/_autosummary/bec_widgets.widgets.lmfit_dialog.lmfit_dialog.LMFitDialog) is a widget that is developed to be used togther with the [`BECWaveformWidget`](/api_reference/_autosummary/bec_widgets.widgets.waveform.waveform_widget.BECWaveformWidget). The `BECWaveformWidget` allows user to submit a fit request to BEC's [DAP server](https://bec.readthedocs.io/en/latest/developer/getting_started/architecture.html) choosing from a selection of [LMFit models](https://lmfit.github.io/lmfit-py/builtin_models.html#) to fit monitored data sources. The `LMFit Dialog` provides an interface to monitor these fits, including statistics and fit parameters in real time.
|
||||
Within the `BECWaveformWidget`, the dialog is accessible via the toolbar and will be automatically linked to the current waveform widget. For a more customised use, we can embed the `LMFit Dialog` in a larger GUI using the *BEC Designer*. In this case, one has to connect the [`update_summary_tree`](/api_reference/_autosummary/bec_widgets.widgets.lmfit_dialog.lmfit_dialog.LMFitDialog.rst#bec_widgets.widgets.lmfit_dialog.lmfit_dialog.LMFitDialog.update_summary_tree) slot of the LMFit Dialog to the [`dap_summary_update`](/api_reference/_autosummary/bec_widgets.widgets.waveform.waveform_widget.BECWaveformWidget.rst#bec_widgets.widgets.waveform.waveform_widget.BECWaveformWidget.dap_summary_update) signal of the BECWaveformWidget to ensure its functionality.
|
||||
|
||||
|
||||
## Key Features:
|
||||
- **Fit Summary**: Display updates on LMFit DAP processes and fit statistics.
|
||||
- **Fit Parameter**: Display current fit parameter.
|
||||
- **BECWaveformWidget Integration**: Directly connect to BECWaveformWidget to display fit statistics and parameters.
|
||||
```{figure} /assets/widget_screenshots/lmfit_dialog.png
|
||||
---
|
||||
name: lmfit_dialog
|
||||
---
|
||||
LMFit Dialog
|
||||
```
|
||||
````
|
||||
````{tab} Connect in BEC Designer
|
||||
The `LMFit Dialog` widget can be connected to a `BECWaveformWidget` to display fit statistics and parameters from the LMFit DAP process hooked up to the waveform widget. You can use the signal/slot editor from the BEC Designer to connect the `dap_summary_update` signal of the BECWaveformWidget to the `update_summary_tree` slot of the LMFit Dialog.
|
||||
|
||||
```{figure} /assets/widget_screenshots/lmfit_dialog_connect.png
|
||||
````
|
||||
````{tab} Connect in Python
|
||||
It is also possible to directly connect the `dap_summary_update` signal of the BECWaveformWidget to the `update_summary_tree` slot of the LMFit Dialog in Python.
|
||||
|
||||
```python
|
||||
waveform = BECWaveformWidget(...)
|
||||
lmfit_dialog = LMFitDialog(...)
|
||||
waveform.dap_summary_update.connect(lmfit_dialog.update_summary_tree)
|
||||
|
||||
```
|
||||
````
|
||||
````{tab} API
|
||||
```{eval-rst}
|
||||
.. include:: /api_reference/_autosummary/bec_widgets.cli.client.LMFitDialog.rst
|
||||
```
|
||||
````
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -190,6 +190,14 @@ Display spinner widget for loading or device movement.
|
||||
|
||||
Display position of motor withing its limits.
|
||||
```
|
||||
|
||||
```{grid-item-card} LMFit Dialog
|
||||
:link: user.widgets.lmfit_dialog
|
||||
:link-type: ref
|
||||
:img-top: /assets/widget_screenshots/lmfit_dialog.png
|
||||
|
||||
Display DAP summaries of LMFit models in a window.
|
||||
```
|
||||
````
|
||||
|
||||
```{toctree}
|
||||
@ -216,5 +224,6 @@ toggle/toggle.md
|
||||
spinner/spinner.md
|
||||
device_input/device_input.md
|
||||
position_indicator/position_indicator.md
|
||||
lmfit_dialog/lmfit_dialog.md
|
||||
|
||||
```
|
183
tests/unit_tests/test_lmfit_dialog.py
Normal file
183
tests/unit_tests/test_lmfit_dialog.py
Normal file
@ -0,0 +1,183 @@
|
||||
from unittest import mock
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from bec_widgets.widgets.lmfit_dialog.lmfit_dialog import LMFitDialog
|
||||
|
||||
from .client_mocks import mocked_client
|
||||
from .conftest import create_widget
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def lmfit_dialog(qtbot, mocked_client):
|
||||
"""Fixture for LMFitDialog widget"""
|
||||
db = create_widget(qtbot, LMFitDialog, client=mocked_client)
|
||||
yield db
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def lmfit_message():
|
||||
"""Fixture for lmfit summary message"""
|
||||
yield {
|
||||
"model": "Model(breit_wigner)",
|
||||
"method": "leastsq",
|
||||
"ndata": 4,
|
||||
"nvarys": 4,
|
||||
"nfree": 0,
|
||||
"chisqr": 1.2583132407517716,
|
||||
"redchi": 1.2583132407517716,
|
||||
"aic": 3.3739110606840716,
|
||||
"bic": 0.9190885051636339,
|
||||
"rsquared": 0.9650468544235619,
|
||||
"nfev": 2498,
|
||||
"max_nfev": 10000,
|
||||
"aborted": False,
|
||||
"errorbars": True,
|
||||
"success": True,
|
||||
"message": "Fit succeeded.",
|
||||
"lmdif_message": "Both actual and predicted relative reductions in the sum of squares\n are at most 0.000000",
|
||||
"ier": 1,
|
||||
"nan_policy": "raise",
|
||||
"scale_covar": True,
|
||||
"calc_covar": True,
|
||||
"ci_out": None,
|
||||
"col_deriv": False,
|
||||
"flatchain": None,
|
||||
"call_kws": {
|
||||
"Dfun": None,
|
||||
"full_output": 1,
|
||||
"col_deriv": 0,
|
||||
"ftol": 1.5e-08,
|
||||
"xtol": 1.5e-08,
|
||||
"gtol": 0.0,
|
||||
"maxfev": 20000,
|
||||
"epsfcn": 1e-10,
|
||||
"factor": 100,
|
||||
"diag": None,
|
||||
},
|
||||
"var_names": ["amplitude", "center", "sigma", "q"],
|
||||
"user_options": None,
|
||||
"kws": {},
|
||||
"init_values": {"amplitude": 1.0, "center": 0.0, "sigma": 1.0, "q": 1.0},
|
||||
"best_values": {
|
||||
"amplitude": 1.5824142042890903,
|
||||
"center": -2.8415356591834326,
|
||||
"sigma": 0.0002550847234503717,
|
||||
"q": -259.8514775889427,
|
||||
},
|
||||
"params": [
|
||||
[
|
||||
"amplitude",
|
||||
1.5824142042890903,
|
||||
True,
|
||||
None,
|
||||
-np.inf,
|
||||
np.inf,
|
||||
None,
|
||||
1.3249185295752495,
|
||||
{
|
||||
"center": 0.8429146627203449,
|
||||
"sigma": -0.8362891947010586,
|
||||
"q": -0.8362890089256452,
|
||||
},
|
||||
1.0,
|
||||
None,
|
||||
],
|
||||
[
|
||||
"center",
|
||||
-2.8415356591834326,
|
||||
True,
|
||||
None,
|
||||
-np.inf,
|
||||
np.inf,
|
||||
None,
|
||||
0.5077201488266584,
|
||||
{
|
||||
"amplitude": 0.8429146627203449,
|
||||
"sigma": -0.9987662050702767,
|
||||
"q": -0.9987662962832818,
|
||||
},
|
||||
0.0,
|
||||
None,
|
||||
],
|
||||
[
|
||||
"sigma",
|
||||
0.0002550847234503717,
|
||||
True,
|
||||
None,
|
||||
0.0,
|
||||
np.inf,
|
||||
None,
|
||||
113.2533064536711,
|
||||
{
|
||||
"amplitude": -0.8362891947010586,
|
||||
"center": -0.9987662050702767,
|
||||
"q": 0.999999999997876,
|
||||
},
|
||||
1.0,
|
||||
None,
|
||||
],
|
||||
[
|
||||
"q",
|
||||
-259.8514775889427,
|
||||
True,
|
||||
None,
|
||||
-np.inf,
|
||||
np.inf,
|
||||
None,
|
||||
114893884.64553572,
|
||||
{
|
||||
"amplitude": -0.8362890089256452,
|
||||
"center": -0.9987662962832818,
|
||||
"sigma": 0.999999999997876,
|
||||
},
|
||||
1.0,
|
||||
None,
|
||||
],
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_fit_curve_id(lmfit_dialog):
|
||||
"""Test hide_curve_selection property"""
|
||||
my_callback = mock.MagicMock()
|
||||
lmfit_dialog.selected_fit.connect(my_callback)
|
||||
assert lmfit_dialog.fit_curve_id is None
|
||||
lmfit_dialog.fit_curve_id = "test_curve_id"
|
||||
assert lmfit_dialog.fit_curve_id == "test_curve_id"
|
||||
assert my_callback.call_count == 1
|
||||
assert my_callback.call_args == mock.call("test_curve_id")
|
||||
|
||||
|
||||
def test_remove_dap_data(lmfit_dialog):
|
||||
"""Test remove_dap_data method"""
|
||||
lmfit_dialog.summary_data = {"test": "data", "test2": "data2"}
|
||||
lmfit_dialog.refresh_curve_list()
|
||||
# Only 2 items
|
||||
assert lmfit_dialog.ui.curve_list.count() == 2
|
||||
lmfit_dialog.remove_dap_data("test")
|
||||
assert lmfit_dialog.summary_data == {"test2": "data2"}
|
||||
assert lmfit_dialog.ui.curve_list.count() == 1
|
||||
# Test removing non-existing data
|
||||
# Nothing should happen
|
||||
lmfit_dialog.remove_dap_data("test_not_there")
|
||||
assert lmfit_dialog.summary_data == {"test2": "data2"}
|
||||
assert lmfit_dialog.ui.curve_list.count() == 1
|
||||
|
||||
|
||||
def test_update_summary_tree(lmfit_dialog, lmfit_message):
|
||||
"""Test display_fit_details method"""
|
||||
lmfit_dialog.update_summary_tree(data=lmfit_message, metadata={"curve_id": "test_curve_id"})
|
||||
# Check if the data is updated
|
||||
assert lmfit_dialog.summary_data == {"test_curve_id": lmfit_message}
|
||||
# Check if the curve list is updated
|
||||
assert lmfit_dialog.ui.curve_list.count() == 1
|
||||
# Check summary tree is updated
|
||||
assert lmfit_dialog.ui.summary_tree.topLevelItemCount() == 6
|
||||
assert lmfit_dialog.ui.summary_tree.topLevelItem(0).text(0) == "Model"
|
||||
assert lmfit_dialog.ui.summary_tree.topLevelItem(0).text(1) == "Model(breit_wigner)"
|
||||
# Check fit params tree is updated
|
||||
assert lmfit_dialog.ui.param_tree.topLevelItemCount() == 4
|
||||
assert lmfit_dialog.ui.param_tree.topLevelItem(0).text(0) == "amplitude"
|
||||
assert lmfit_dialog.ui.param_tree.topLevelItem(0).text(1) == "1.582"
|
@ -9,9 +9,13 @@ from bec_widgets.qt_utils.settings_dialog import SettingsDialog
|
||||
from bec_widgets.utils.colors import apply_theme, get_theme_palette, set_theme
|
||||
from bec_widgets.widgets.figure.plots.axis_settings import AxisSettings
|
||||
from bec_widgets.widgets.waveform.waveform_popups.curve_dialog.curve_dialog import CurveSettings
|
||||
from bec_widgets.widgets.waveform.waveform_popups.dap_summary_dialog.dap_summary_dialog import (
|
||||
FitSummaryWidget,
|
||||
)
|
||||
from bec_widgets.widgets.waveform.waveform_widget import BECWaveformWidget
|
||||
|
||||
from .client_mocks import mocked_client
|
||||
from .conftest import create_widget
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -97,7 +101,7 @@ def test_waveform_plot_scan_curves(waveform_widget, mock_waveform):
|
||||
|
||||
|
||||
def test_waveform_widget_add_dap(waveform_widget, mock_waveform):
|
||||
waveform_widget.add_dap(x_name="samx", y_name="bpm4i")
|
||||
waveform_widget.add_dap(x_name="samx", y_name="bpm4i", dap="GaussianModel")
|
||||
waveform_widget.waveform.add_dap.assert_called_once_with(
|
||||
x_name="samx",
|
||||
y_name="bpm4i",
|
||||
@ -252,7 +256,7 @@ def test_toolbar_fit_params_action_triggered(qtbot, waveform_widget):
|
||||
) as MockFitSummaryWidget:
|
||||
mock_dialog_instance = MockFitSummaryWidget.return_value
|
||||
action.trigger()
|
||||
mock_dialog_instance.show.assert_called_once()
|
||||
mock_dialog_instance.exec.assert_called_once()
|
||||
|
||||
|
||||
def test_enable_mouse_pan_mode(qtbot, waveform_widget):
|
||||
@ -378,6 +382,14 @@ def test_curve_dialog_dap(qtbot, waveform_widget):
|
||||
assert len(waveform_widget.curves) == 2
|
||||
|
||||
|
||||
def test_fit_dialog_summary(qtbot, waveform_widget):
|
||||
"""Test the fit dialog summary widget"""
|
||||
waveform_widget.plot(x_name="samx", y_name="bpm4i", dap="GaussianModel")
|
||||
fit_dialog_summary = create_widget(qtbot, FitSummaryWidget, target_widget=waveform_widget)
|
||||
assert fit_dialog_summary.dap_dialog.fit_curve_id == "bpm4i-bpm4i-GaussianModel"
|
||||
assert fit_dialog_summary.dap_dialog.ui.curve_list.count() == 1
|
||||
|
||||
|
||||
###################################
|
||||
# Axis Dialog Tests
|
||||
###################################
|
||||
|
Reference in New Issue
Block a user