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

docs: updated docs for v2 (#531)

This commit is contained in:
2025-04-24 17:37:10 +02:00
parent 25bd905cef
commit b4af2cc77a
13 changed files with 105 additions and 77 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -28,7 +28,7 @@ from qtpy.QtWidgets import QWidget, QLabel, QDoubleSpinBox, QPushButton, QVBoxLa
class MotorControlWidget(QWidget):
def __init__(self, motor_name: str, parent=None):
def __init__(self, parent=None, motor_name: str = ""):
super().__init__(parent)
self.motor_name = motor_name
@ -70,18 +70,17 @@ from [`BECWidget`](https://bec.readthedocs.io/projects/bec-widgets/en/latest/api
pass the motor name to the widget, and use `get_bec_shortcuts` to access BEC services.
```python
from qtpy.QtCore import Slot
from qtpy.QtWidgets import QWidget, QLabel, QDoubleSpinBox, QPushButton, QVBoxLayout
from bec_lib.endpoints import MessageEndpoints
from qtpy.QtWidgets import QDoubleSpinBox, QLabel, QPushButton, QVBoxLayout, QWidget
from bec_widgets.utils.bec_widget import BECWidget
from bec_widgets.utils.error_popups import SafeSlot
class MotorControlWidget(BECWidget, QWidget):
def __init__(self, motor_name: str, parent=None, *args, **kwargs):
super().__init__(*args, **kwargs)
QWidget.__init__(self, parent)
def __init__(self, parent=None, motor_name: str = "", **kwargs):
super().__init__(parent=parent, **kwargs)
self.motor_name = motor_name
@ -114,13 +113,13 @@ class MotorControlWidget(BECWidget, QWidget):
self.on_motor_update, MessageEndpoints.device_readback(self.motor_name)
)
@Slot()
@SafeSlot()
def move_motor(self):
target_position = self.spin_box.value()
self.dev[self.motor_name].move(target_position)
print(f"Commanding motor {self.motor_name} to move to {target_position}")
@Slot(dict, dict)
@SafeSlot(dict, dict)
def on_motor_update(self, msg_content, metadata):
position = msg_content.get("signals", {}).get(self.motor_name, {}).get("value", "N/A")
self.label.setText(f"{self.motor_name} : {round(position, 2)}")
@ -132,19 +131,18 @@ Next, well set up an RPC interface to allow remote control of the widget from
the `BECIPythonClient`. Well expose a method that allows changing the motor name through CLI commands.
```python
from qtpy.QtCore import Slot
from qtpy.QtWidgets import QWidget, QLabel, QDoubleSpinBox, QPushButton, QVBoxLayout
from bec_lib.endpoints import MessageEndpoints
from qtpy.QtWidgets import QDoubleSpinBox, QLabel, QPushButton, QVBoxLayout, QWidget
from bec_widgets.utils.bec_widget import BECWidget
from bec_widgets.utils.error_popups import SafeSlot
class MotorControlWidget(BECWidget, QWidget):
USER_ACCESS = ["change_motor"]
def __init__(self, motor_name: str, parent=None, *args, **kwargs):
super().__init__(*args, **kwargs)
QWidget.__init__(self, parent)
def __init__(self, parent=None, motor_name: str = "", **kwargs):
super().__init__(parent=parent, **kwargs)
self.motor_name = motor_name
@ -177,13 +175,13 @@ class MotorControlWidget(BECWidget, QWidget):
self.on_motor_update, MessageEndpoints.device_readback(self.motor_name)
)
@Slot()
@SafeSlot()
def move_motor(self):
target_position = self.spin_box.value()
self.dev[self.motor_name].move(target_position)
print(f"Commanding motor {self.motor_name} to move to {target_position}")
@Slot(dict, dict)
@SafeSlot(dict, dict)
def on_motor_update(self, msg_content, metadata):
position = msg_content.get("signals", {}).get(self.motor_name, {}).get("value", "N/A")
self.label.setText(f"{self.motor_name} : {round(position, 2)}")
@ -203,11 +201,11 @@ class MotorControlWidget(BECWidget, QWidget):
```
```{warning}
After implementing an RPC method, you must run the `cli/generate_cli.py` script to update the CLI commands for `BECIPythonClient`. This script generates the necessary command-line interface bindings, ensuring that your RPC method can be accessed and controlled remotely.
After implementing an RPC method, you must run the `bw-generate-cli --target <your plugin repo name>` script to update the CLI commands for `BECIPythonClient`, e.g. `bw-generate-cli --target csaxs_bec`. This script generates the necessary command-line interface bindings, ensuring that your RPC method can be accessed and controlled remotely.
```
```{note}
In this tutorial, we used the @Slot decorator from QtCore to mark methods as slots for signals. This decorator ensures that the connected methods are treated as slots by the Qt framework, which can be connected to signals. Its a best practice to use the @Slot decorator to clearly indicate which methods are intended to handle signal events with correct argument signatures.
In this tutorial, we used the @SafeSlot decorator from BEC Widgets to mark methods as slots for signals. This decorator ensures that the connected methods are treated as slots by the Qt framework, which can be connected to signals. Its a best practice to use the @SafeSlot decorator to clearly indicate which methods are intended to handle signal events with correct argument signatures. @SafeSlot also provides error handling and logging capabilities, making it more robust and easier to debug.
```
## Step 4: Running the Widget

View File

@ -63,12 +63,12 @@ from qtpy.QtWidgets import QWidget, QVBoxLayout
from bec_widgets.widgets.buttons import DarkModeButton
class MyGui(QWidget):
def __init__(self):
super().__init__()
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setLayout(QVBoxLayout(self))
# Create and add the DarkModeButton to the layout
self.dark_mode_button = DarkModeButton()
self.dark_mode_button = DarkModeButton(parent=self)
self.layout().addWidget(self.dark_mode_button)
# Example of how this custom GUI might be used:
@ -83,12 +83,12 @@ from qtpy.QtWidgets import QWidget, QVBoxLayout
from bec_widgets.widgets.buttons import ColorButton
class MyGui(QWidget):
def __init__(self):
super().__init__()
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setLayout(QVBoxLayout(self))
# Create and add the ColorButton to the layout
self.color_button = ColorButton()
self.color_button = ColorButton(self)
self.layout().addWidget(self.color_button)
# Example of how this custom GUI might be used:
@ -103,12 +103,12 @@ from qtpy.QtWidgets import QWidget, QVBoxLayout
from bec_widgets.widgets.buttons import ColormapSelector
class MyGui(QWidget):
def __init__(self):
super().__init__()
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setLayout(QVBoxLayout(self))
# Create and add the ColormapSelector to the layout
self.colormap_selector = ColormapSelector()
self.colormap_selector = ColormapSelector(self)
self.layout().addWidget(self.colormap_selector)
# Example of how this custom GUI might be used:
@ -123,12 +123,12 @@ from qtpy.QtWidgets import QWidget, QVBoxLayout
from bec_widgets.widgets.buttons import ColormapButton
class MyGui(QWidget):
def __init__(self):
super().__init__()
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setLayout(QVBoxLayout(self))
# Create and add the ColormapButton to the layout
self.colormap_button = ColormapButton()
self.colormap_button = ColormapButton(self)
self.layout().addWidget(self.colormap_button)
# Connect the signal to handle colormap changes

View File

@ -45,12 +45,12 @@ from bec_lib.device import ReadoutPriority
from bec_widgets.widgets.base_classes.device_input_base import BECDeviceFilter
class MyGui(QWidget):
def __init__(self):
super().__init__()
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setLayout(QVBoxLayout(self)) # Initialize the layout for the widget
# Create and add the DeviceLineEdit to the layout
self.device_line_edit = DeviceLineEdit(device_filter=BECDeviceFilter.POSITIONER, readout_priority_filter=ReadoutPriority.BASELINE)
self.device_line_edit = DeviceLineEdit(parent=self, device_filter=BECDeviceFilter.POSITIONER, readout_priority_filter=ReadoutPriority.BASELINE)
self.layout().addWidget(self.device_line_edit)
# Example of how this custom GUI might be used:
@ -71,12 +71,12 @@ from bec_lib.device import ReadoutPriority
from bec_widgets.widgets.base_classes.device_input_base import BECDeviceFilter
class MyGui(QWidget):
def __init__(self):
super().__init__()
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setLayout(QVBoxLayout(self)) # Initialize the layout for the widget
# Create and add the DeviceComboBox to the layout
self.device_combobox = DeviceComboBox(device_filter=BECDeviceFilter.POSITIONER, readout_priority_filter=ReadoutPriority.BASELINE)
self.device_combobox = DeviceComboBox(parent=self, device_filter=BECDeviceFilter.POSITIONER, readout_priority_filter=ReadoutPriority.BASELINE)
self.layout().addWidget(self.device_combobox)
# Example of how this custom GUI might be used:

View File

@ -4,14 +4,14 @@
````{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 together 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.
The [`LMFit Dialog`](/api_reference/_autosummary/bec_widgets.widgets.dap.lmfit_dialog.lmfit_dialog.LMFitDialog) is a widget that is developed to be used together with the [`Waveform`](/api_reference/_autosummary/bec_widgets.widgets.plots.waveform.waveform_widget.Waveform) widget. The `Waveform` widget 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 `Waveform` widget, 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.dap.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.plots.waveform.waveform_widget.Waveform.rst#bec_widgets.widgets.plots.waveform.waveform_widget.Waveform.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.
- **`Waveform` Widget Integration**: Directly connect to `Waveform` widget to display fit statistics and parameters.
```{figure} /assets/widget_screenshots/lmfit_dialog.png
---
name: lmfit_dialog
@ -20,12 +20,12 @@ 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.
The `LMFit Dialog` widget can be connected to a `Waveform` widget 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 `Waveform` widget 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.
It is also possible to directly connect the `dap_summary_update` signal of the `Waveform` widget to the `update_summary_tree` slot of the LMFit Dialog in Python.
```python
waveform = BECWaveformWidget(...)

View File

@ -27,12 +27,12 @@ from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
from bec_widgets.widgets.positioner_box import PositionerBox
class MyGui(QWidget):
def __init__(self):
super().__init__()
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setLayout(QVBoxLayout(self)) # Initialize the layout for the widget
# Create and add the PositionerBox to the layout
self.positioner_box = PositionerBox(device="motor1")
self.positioner_box = PositionerBox(parent=self, device="motor1")
self.layout().addWidget(self.positioner_box)
# Example of how this custom GUI might be used:

View File

@ -1,10 +1,10 @@
(user.widgets.positioner_box_2d)=
# Positioner Box Widget
# Positioner Box 2D Widget
````{tab} Overview
The [`PositionerBox2D`](/api_reference/_autosummary/bec_widgets.cli.client.PositionerBox2D) widget is very similar to the ['PositionerBox'](/user/widgets/positioner_box/positioner_box) but allows controlling two positioners at the same time, in a horizontal and vertical orientation respectively. It is intended primarily for controlling axes which have a perpendicular relationship like that. In other cases, it may be better to use a `PositionerGroup` instead.
The [`PositionerBox2D`](/api_reference/_autosummary/bec_widgets.cli.client.PositionerBox2D) widget is very similar to the [`PositionerBox`](/user/widgets/positioner_box/positioner_box) but allows controlling two positioners at the same time, in a horizontal and vertical orientation respectively. It is intended primarily for controlling axes which have a perpendicular relationship like that. In other cases, it may be better to use a `PositionerGroup` instead.
The `PositionerBox2D` has the same features as the standard `PositionerBox`, but additionally, step buttons which move the positioner by the selected step size, and tweak buttons which move by a tenth of the selected step size.
@ -23,12 +23,12 @@ from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
from bec_widgets.widgets.positioner_box import PositionerBox2D
class MyGui(QWidget):
def __init__(self):
super().__init__()
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setLayout(QVBoxLayout(self)) # Initialize the layout for the widget
# Create and add the PositionerBox to the layout
self.positioner_box_2d = PositionerBox(device_hor="horizontal_motor", device_ver="vertical_motor")
self.positioner_box_2d = PositionerBox(parent=self, device_hor="horizontal_motor", device_ver="vertical_motor")
self.layout().addWidget(self.positioner_box_2d)
# Example of how this custom GUI might be used:

View File

@ -24,7 +24,8 @@ In this example, we demonstrate how to add a `RingProgressBar` widget to a `BECD
```python
# Add a new dock with a RingProgressBar widget
progress = gui.add_dock().add_widget("RingProgressBar")
dock_area = gui.new('my_new_dock_area') # Create a new dock area
progress = dock_area.new().new(gui.available_widgets.RingProgressBar)
# Customize the size of the progress ring
progress.set_line_widths(20)

View File

Before

Width:  |  Height:  |  Size: 13 MiB

After

Width:  |  Height:  |  Size: 13 MiB

View File

@ -0,0 +1,39 @@
(user.widgets.scatter_waveform_widget)=
# Scatter Waveform Widget
````{tab} Overview
The 2D scatter plot widget is designed for more complex data visualization. It employs a false color map to represent a third dimension (z-axis), making it an ideal tool for visualizing multidimensional data sets.
## Key Features:
- **Real-Time Data Visualization**: Display 2D scatter plots with a third dimension represented by color.
- **Flexible Integration**: Can be integrated into [`BECDockArea`](user.widgets.bec_dock_area), or used as an individual component in your application through `BECDesigner`.
````
````{tab} Examples - CLI
`ScatterWaveform` widget can be embedded in [`BECDockArea`](user.widgets.bec_dock_area), or used as an individual component in your application through `BECDesigner`. The command-line API is consistent across these contexts.
## Example
```python
# Add a new dock_area, a new dock and a BECWaveForm to the dock
plt = gui.new().new().new(gui.available_widgets.ScatterWaveform)
plt.plot(x_name='samx', y_name='samy', z_name='bpm4i')
```
![Scatter 2D](./scatter_2D.gif)
```{note}
The ScatterWaveform widget only plots the data points if both x and y axis motors are moving. Or more generally, if all signals are of readout type *monitored*.
```
````
````{tab} API
```{eval-rst}
.. include:: /api_reference/_autosummary/bec_widgets.cli.client.ScatterWaveform.rst
```
````

View File

@ -27,12 +27,12 @@ from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
from bec_widgets.widgets.toggle_switch import ToggleSwitch
class MyGui(QWidget):
def __init__(self):
super().__init__()
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setLayout(QVBoxLayout(self)) # Initialize the layout for the widget
# Create and add the ToggleSwitch to the layout
self.toggle_switch = ToggleSwitch()
self.toggle_switch = ToggleSwitch(parent=self)
self.layout().addWidget(self.toggle_switch)
# Example of how this custom GUI might be used:

View File

@ -36,9 +36,9 @@ plt1.plot(x_name='samx', y_name='bpm4i')
plt2.plot(x_name='samx', y_name='bpm3i')
# set axis labels
plt1.set_title("Gauss plots vs. samx")
plt1.set_x_label("Motor X")
plt1.set_y_label("Gauss Signal (A.U.")
plt1.title = "Gauss plots vs. samx"
plt1.x_label = "Motor X"
plt1.y_label = "Gauss Signal (A.U.)"
```
@ -97,25 +97,6 @@ print(dap_bpm3a.dap_params)
![Waveform 1D_DAP](./bec_figure_dap.gif)
## Example 3 - 2D ScatterWaveform plot
The 2D scatter plot widget is designed for more complex data visualization. It employs a false color map to represent a third dimension (z-axis), making it an ideal tool for visualizing multidimensional data sets.
```python
# Add a new dock_area, a new dock and a BECWaveForm to the dock
plt = gui.new().new().new(gui.available_widgets.ScatterWaveform)
plt.plot(x_name='samx', y_name='samy', z_name='bpm4i')
plt = gui.add_dock().add_widget('BECFigure').add_plot(x_name='samx', y_name='samy', z_name='bpm4i')
```
![Scatter 2D](./scatter_2D.gif)
```{note}
The ScatterWaveform widget only plots the data points if both x and y axis motors are moving. Or more generally, if all signals are of readout type *monitored*.
```
````
````{tab} API

View File

@ -45,7 +45,15 @@ Display 1D detector signals.
Display multiple 1D waveforms.
```
```{grid-item-card} Image widget
```{grid-item-card} Scatter Waveform Widget
:link: user.widgets.scatter_waveform_widget
:link-type: ref
:img-top: /assets/widget_screenshots/scatter_waveform.png
Display a 1D waveforms with a third device on the z-axis.
```
```{grid-item-card} Image Widget
:link: user.widgets.image_widget
:link-type: ref
:img-top: /assets/widget_screenshots/image_widget.png
@ -256,6 +264,7 @@ hidden: true
dock_area/bec_dock_area.md
waveform/waveform_widget.md
scatter_waveform/scatter_waveform.md
multi_waveform/multi_waveform.md
image/image_widget.md
motor_map/motor_map.md