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

feat(waveform_widget): dap parameter window

This commit is contained in:
2024-07-17 22:17:08 +02:00
parent 8e588d79c8
commit 1e551d6e96
8 changed files with 257 additions and 3 deletions

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#FFFFFF">
<path d="m78.89-112.59 263.02-367.17h202l303.5-354.22v721.39H78.89Zm62.24-262.74-54.7-39.78 166.59-233.02h201L640.46-864.8l51.45 44.26-205.82 240.78H287.33l-146.2 204.43Zm70.54 194.37h567.37v-468.78L574.98-411.63H376.22L211.67-180.96Zm567.37 0Z"/>
</svg>

After

Width:  |  Height:  |  Size: 366 B

View File

@ -75,6 +75,7 @@ class BECWaveform(BECPlotBase):
scan_signal_update = pyqtSignal()
async_signal_update = pyqtSignal()
dap_params_update = pyqtSignal(dict)
dap_summary_update = pyqtSignal(dict)
autorange_signal = pyqtSignal()
def __init__(
@ -655,6 +656,19 @@ class BECWaveform(BECPlotBase):
params[curve_id] = curve.dap_params
return params
@pyqtSlot()
def get_dap_summary(self) -> dict:
"""
Get the DAP summary of all DAP curves.
Returns:
dict: DAP summary of all DAP curves.
"""
summary = {}
for curve_id, curve in self._curves_data["DAP"].items():
summary[curve_id] = curve.dap_summary
return summary
def _add_curve_object(
self,
name: str,
@ -1071,7 +1085,9 @@ class BECWaveform(BECPlotBase):
y = msg["data"][0]["y"]
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)
break
@pyqtSlot(dict, dict)

View File

@ -101,6 +101,7 @@ class BECCurve(BECConnector, pg.PlotDataItem):
self.parent_item = parent_item
self.apply_config()
self.dap_params = None
self.dap_summary = None
if kwargs:
self.set(**kwargs)
@ -132,6 +133,14 @@ class BECCurve(BECConnector, pg.PlotDataItem):
def dap_params(self, value):
self._dap_params = value
@property
def dap_summary(self):
return self._dap_report
@dap_summary.setter
def dap_summary(self, value):
self._dap_report = value
def set_data(self, x, y):
if self.config.source == "custom":
self.setData(x, y)

View File

@ -0,0 +1,127 @@
<?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>

View File

@ -0,0 +1,69 @@
import os
from qtpy.QtCore import Slot
from qtpy.QtWidgets import QDialog, QTreeWidgetItem, QVBoxLayout
from bec_widgets.utils import UILoader
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.layout = QVBoxLayout(self)
self.layout.addWidget(self.ui)
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])

View File

@ -41,6 +41,17 @@ class CurveAction(ToolBarAction):
toolbar.addAction(self.action)
class FitParamsAction(ToolBarAction):
def add_to_toolbar(self, toolbar, target):
icon = QIcon()
icon.addFile(
os.path.join(MODULE_PATH, "assets", "toolbar_icons", "fitting_parameters.svg"),
size=QSize(20, 20),
)
self.action = QAction(icon, "Open Fitting Parameters", target)
toolbar.addAction(self.action)
class SettingsAction(ToolBarAction):
def add_to_toolbar(self, toolbar, target):
icon = QIcon()

View File

@ -6,7 +6,7 @@ from typing import Literal
import numpy as np
from qtpy.QtWidgets import QVBoxLayout, QWidget
from bec_widgets.qt_utils.error_popups import WarningPopupUtility, SafeSlot
from bec_widgets.qt_utils.error_popups import SafeSlot, WarningPopupUtility
from bec_widgets.qt_utils.settings_dialog import SettingsDialog
from bec_widgets.qt_utils.toolbar import ModularToolBar, SeparatorAction
from bec_widgets.utils import BECConnector
@ -15,6 +15,9 @@ from bec_widgets.widgets.figure.plots.axis_settings import AxisSettings
from bec_widgets.widgets.figure.plots.waveform.waveform import Waveform1DConfig
from bec_widgets.widgets.figure.plots.waveform.waveform_curve import BECCurve
from bec_widgets.widgets.waveform.waveform_toolbar.curve_dialog.curve_dialog import CurveSettings
from bec_widgets.widgets.waveform.waveform_toolbar.dap_summary_dialog.dap_summary_dialog import (
FitSummaryWidget,
)
from bec_widgets.widgets.waveform.waveform_toolbar.waveform_toolbar import *
try:
@ -73,6 +76,7 @@ class BECWaveformWidget(BECConnector, QWidget):
"matplotlib": MatplotlibAction(),
"separator_1": SeparatorAction(),
"curves": CurveAction(),
"fit_params": FitParamsAction(),
"axis_settings": SettingsAction(),
"separator_2": SeparatorAction(),
"import": ImportAction(),
@ -95,13 +99,14 @@ class BECWaveformWidget(BECConnector, QWidget):
# TEst actions
self.plot(x_name="samx", y_name="bpm4i", dap="GaussianModel")
self.plot(x_name="samx", y_name="bpm3a")
self.plot(x_name="samx", y_name="bpm3a", dap="GaussianModel")
self.plot(x_name="samx", y_name="bpm6i")
def _hook_actions(self):
self.toolbar.widgets["save"].action.triggered.connect(self.export)
self.toolbar.widgets["matplotlib"].action.triggered.connect(self.export_to_matplotlib)
self.toolbar.widgets["curves"].action.triggered.connect(self.show_curve_settings)
self.toolbar.widgets["fit_params"].action.triggered.connect(self.show_fit_summary_dialog)
self.toolbar.widgets["axis_settings"].action.triggered.connect(self.show_axis_settings)
self.toolbar.widgets["import"].action.triggered.connect(
lambda: self.load_config(path=None, gui=True)
@ -129,6 +134,11 @@ class BECWaveformWidget(BECConnector, QWidget):
dialog.resize(800, 600)
dialog.exec()
def show_fit_summary_dialog(self):
dialog = FitSummaryWidget(target_widget=self)
dialog.resize(800, 600)
dialog.show()
def _check_if_scans_have_same_x(
self, enabled=True, x_name_to_check: str = None
) -> bool: # TODO probably not needed anymore
@ -307,7 +317,16 @@ class BECWaveformWidget(BECConnector, QWidget):
dict: DAP parameters of all DAP curves.
"""
self.waveform.get_dap_params()
return self.waveform.get_dap_params()
def get_dap_summary(self) -> dict:
"""
Get the DAP summary of all DAP curves.
Returns:
dict: DAP summary of all DAP curves.
"""
return self.waveform.get_dap_summary()
def remove_curve(self, *identifiers):
"""