mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-04-08 09:47:52 +02:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee02c13d5d | ||
|
|
9ccd4ea235 | ||
|
|
86416d50cb | ||
| 1d5442ac08 | |||
|
|
f3c7196921 | ||
|
|
14f901f1be | ||
|
|
9f93c01ff7 | ||
| 203ae09606 | |||
| 2d39c5e4d1 | |||
| 9049e0d27f | |||
|
|
d5d41fc759 | ||
|
|
d0f9bf1733 | ||
|
|
7d46d1160d | ||
|
|
b8d4e697ac | ||
|
|
4664661cfb | ||
|
|
d99fd76c0b | ||
|
|
fcf918c488 | ||
|
|
32747baa27 |
@@ -5,6 +5,8 @@ image: $CI_DOCKER_REGISTRY/python:3.10
|
||||
#commands to run in the Docker container before starting each job.
|
||||
variables:
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
BEC_CORE_BRANCH: "master"
|
||||
OPHYD_DEVICES_BRANCH: "master"
|
||||
|
||||
include:
|
||||
- template: Security/Secret-Detection.gitlab-ci.yml
|
||||
@@ -77,9 +79,13 @@ tests:
|
||||
variables:
|
||||
QT_QPA_PLATFORM: "offscreen"
|
||||
script:
|
||||
- git clone --branch $BEC_CORE_BRANCH https://gitlab.psi.ch/bec/bec.git
|
||||
- git clone --branch $OPHYD_DEVICES_BRANCH https://gitlab.psi.ch/bec/ophyd_devices.git
|
||||
- export OHPYD_DEVICES_PATH=$PWD/ophyd_devices
|
||||
- apt-get update
|
||||
- apt-get install -y libgl1-mesa-glx libegl1-mesa x11-utils libxkbcommon-x11-0 libdbus-1-3
|
||||
- pip install .[dev]
|
||||
- pip install -e ./bec/bec_lib[dev]
|
||||
- pip install -e .[dev]
|
||||
- coverage run --source=./bec_widgets -m pytest -v --junitxml=report.xml --random-order --full-trace ./tests
|
||||
- coverage report
|
||||
- coverage xml
|
||||
|
||||
38
CHANGELOG.md
38
CHANGELOG.md
@@ -2,6 +2,44 @@
|
||||
|
||||
<!--next-version-placeholder-->
|
||||
|
||||
## v0.44.2 (2024-03-20)
|
||||
|
||||
### Fix
|
||||
|
||||
* **utils/bec_dispatcher:** Try/except to start client, to avoid crash when redis is not running ([`9ccd4ea`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/9ccd4ea235be4c4332045b7a7f09d6cc6291f7ff))
|
||||
* **utils/bec_dispatcher:** Bec_dispatcher adjusted to the new BECClient; dropped support to inject bec_config.yaml, instead BECClient can be passed as arg ([`86416d5`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/86416d50cb850b42d312fe17fc46f0b4743dc940))
|
||||
|
||||
## v0.44.1 (2024-03-19)
|
||||
|
||||
### Fix
|
||||
|
||||
* **examples/motor_compilation:** Motor_control_compilations.py do not have any hardcoded config anymore ([`14f901f`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/14f901f1bea2ba7b79903c4743e37384e11533d3))
|
||||
|
||||
## v0.44.0 (2024-03-18)
|
||||
|
||||
### Feature
|
||||
|
||||
* **cli:** Added update script to BECFigure ([`9049e0d`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/9049e0d27fe9a3860e21ffc3b350eb69e567b71c))
|
||||
|
||||
### Fix
|
||||
|
||||
* **cli:** Removed hard-coded signal ([`203ae09`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/203ae0960688608fb609a742a23e5994bfe9805c))
|
||||
* **cli:** Fixed cleanup procedure ([`2d39c5e`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/2d39c5e4d18bbb66a5f3340fce7f8944dd4ba19f))
|
||||
|
||||
## v0.43.2 (2024-03-18)
|
||||
|
||||
### Fix
|
||||
|
||||
* **cli/server:** Added QApplications to enter separate QT event loop ensuring that QT objects are not deleted ([`d0f9bf1`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/d0f9bf17339296a60301e5e6ffe602db369c6c7c))
|
||||
|
||||
## v0.43.1 (2024-03-15)
|
||||
|
||||
### Fix
|
||||
|
||||
* **plots/image:** Same access pattern for image and image_item for setting up parameters, autorange of z scale disabled by default ([`b8d4e69`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/b8d4e697ac2a5929a1374ce1778046efc3f8187a))
|
||||
* **widget/various:** Corrected USER_ACCESS methods for children widgets to include inherited methods to RPC ([`4664661`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/4664661cfb4e8bd4a6adb71f2050b25d0b4f3d36))
|
||||
* **widgets/figure:** Added widgets can be accessed as a list (fig.axes) or as a dictionary (fig.widgets) ([`fcf918c`](https://gitlab.psi.ch/bec/bec-widgets/-/commit/fcf918c48862d069b9fe69cbba7dbecbe7429790))
|
||||
|
||||
## v0.43.0 (2024-03-14)
|
||||
|
||||
### Feature
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from .client import BECFigure
|
||||
|
||||
@@ -5,6 +5,16 @@ from typing import Literal, Optional, overload
|
||||
|
||||
|
||||
class BECPlotBase(RPCBase):
|
||||
@rpc_call
|
||||
def get_config(self, dict_output: "bool" = True) -> "dict | BaseModel":
|
||||
"""
|
||||
Get the configuration of the widget.
|
||||
Args:
|
||||
dict_output(bool): If True, return the configuration as a dictionary. If False, return the configuration as a pydantic model.
|
||||
Returns:
|
||||
dict: The configuration of the plot widget.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set(self, **kwargs) -> "None":
|
||||
"""
|
||||
@@ -241,12 +251,165 @@ class BECWaveform1D(RPCBase):
|
||||
dict | pd.DataFrame: Data of all curves in the specified format.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def get_config(self, dict_output: "bool" = True) -> "dict | BaseModel":
|
||||
"""
|
||||
Get the configuration of the widget.
|
||||
Args:
|
||||
dict_output(bool): If True, return the configuration as a dictionary. If False, return the configuration as a pydantic model.
|
||||
Returns:
|
||||
dict: The configuration of the plot widget.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set(self, **kwargs) -> "None":
|
||||
"""
|
||||
Set the properties of the plot widget.
|
||||
Args:
|
||||
**kwargs: Keyword arguments for the properties to be set.
|
||||
Possible properties:
|
||||
- title: str
|
||||
- x_label: str
|
||||
- y_label: str
|
||||
- x_scale: Literal["linear", "log"]
|
||||
- y_scale: Literal["linear", "log"]
|
||||
- x_lim: tuple
|
||||
- y_lim: tuple
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_title(self, title: "str"):
|
||||
"""
|
||||
Set the title of the plot widget.
|
||||
Args:
|
||||
title(str): Title of the plot widget.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_x_label(self, label: "str"):
|
||||
"""
|
||||
Set the label of the x-axis.
|
||||
Args:
|
||||
label(str): Label of the x-axis.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_y_label(self, label: "str"):
|
||||
"""
|
||||
Set the label of the y-axis.
|
||||
Args:
|
||||
label(str): Label of the y-axis.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_x_scale(self, scale: "Literal['linear', 'log']" = "linear"):
|
||||
"""
|
||||
Set the scale of the x-axis.
|
||||
Args:
|
||||
scale(Literal["linear", "log"]): Scale of the x-axis.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_y_scale(self, scale: "Literal['linear', 'log']" = "linear"):
|
||||
"""
|
||||
Set the scale of the y-axis.
|
||||
Args:
|
||||
scale(Literal["linear", "log"]): Scale of the y-axis.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_x_lim(self, *args) -> "None":
|
||||
"""
|
||||
Set the limits of the x-axis. This method can accept either two separate arguments
|
||||
for the minimum and maximum x-axis values, or a single tuple containing both limits.
|
||||
|
||||
Usage:
|
||||
set_x_lim(x_min, x_max)
|
||||
set_x_lim((x_min, x_max))
|
||||
|
||||
Args:
|
||||
*args: A variable number of arguments. Can be two integers (x_min and x_max)
|
||||
or a single tuple with two integers.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_y_lim(self, *args) -> "None":
|
||||
"""
|
||||
Set the limits of the y-axis. This method can accept either two separate arguments
|
||||
for the minimum and maximum y-axis values, or a single tuple containing both limits.
|
||||
|
||||
Usage:
|
||||
set_y_lim(y_min, y_max)
|
||||
set_y_lim((y_min, y_max))
|
||||
|
||||
Args:
|
||||
*args: A variable number of arguments. Can be two integers (y_min and y_max)
|
||||
or a single tuple with two integers.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_grid(self, x: "bool" = False, y: "bool" = False):
|
||||
"""
|
||||
Set the grid of the plot widget.
|
||||
Args:
|
||||
x(bool): Show grid on the x-axis.
|
||||
y(bool): Show grid on the y-axis.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def lock_aspect_ratio(self, lock):
|
||||
"""
|
||||
Lock aspect ratio.
|
||||
Args:
|
||||
lock(bool): True to lock, False to unlock.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def plot(self, data_x: "list | np.ndarray", data_y: "list | np.ndarray", **kwargs):
|
||||
"""
|
||||
Plot custom data on the plot widget. These data are not saved in config.
|
||||
Args:
|
||||
data_x(list|np.ndarray): x-axis data
|
||||
data_y(list|np.ndarray): y-axis data
|
||||
**kwargs: Keyword arguments for the plot.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def remove(self):
|
||||
"""
|
||||
Remove the plot widget from the figure.
|
||||
"""
|
||||
|
||||
|
||||
class BECFigure(RPCBase, BECFigureClientMixin):
|
||||
@property
|
||||
@rpc_call
|
||||
def axes(self) -> "list[BECPlotBase]":
|
||||
"""
|
||||
Access all widget in BECFigure as a list
|
||||
Returns:
|
||||
list[BECPlotBase]: List of all widgets in the figure.
|
||||
"""
|
||||
|
||||
@property
|
||||
@rpc_call
|
||||
def widgets(self) -> "dict":
|
||||
"""
|
||||
None
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def add_plot(
|
||||
self,
|
||||
widget_id: "str" = None,
|
||||
x_name: "str" = None,
|
||||
y_name: "str" = None,
|
||||
x_entry: "str" = None,
|
||||
y_entry: "str" = None,
|
||||
x: "list | np.ndarray" = None,
|
||||
y: "list | np.ndarray" = None,
|
||||
color: "Optional[str]" = None,
|
||||
label: "Optional[str]" = None,
|
||||
validate: "bool" = True,
|
||||
row: "int" = None,
|
||||
col: "int" = None,
|
||||
config=None,
|
||||
@@ -265,17 +428,31 @@ class BECFigure(RPCBase, BECFigureClientMixin):
|
||||
@rpc_call
|
||||
def add_image(
|
||||
self,
|
||||
widget_id: "str" = None,
|
||||
monitor: "str" = None,
|
||||
color_bar: "Literal['simple', 'full']" = "full",
|
||||
color_map: "str" = "magma",
|
||||
data: "np.ndarray" = None,
|
||||
vrange: "tuple[float, float]" = None,
|
||||
row: "int" = None,
|
||||
col: "int" = None,
|
||||
config=None,
|
||||
color_map: "str" = "magma",
|
||||
color_bar: "Literal['simple', 'full']" = "full",
|
||||
vrange: "tuple[float, float]" = None,
|
||||
**axis_kwargs
|
||||
) -> "BECImageShow":
|
||||
"""
|
||||
None
|
||||
Add an image to the figure at the specified position.
|
||||
Args:
|
||||
monitor(str): The name of the monitor to display.
|
||||
color_bar(Literal["simple","full"]): The type of color bar to display.
|
||||
color_map(str): The color map to use for the image.
|
||||
data(np.ndarray): Custom data to display.
|
||||
vrange(tuple[float, float]): The range of values to display.
|
||||
row(int): The row coordinate of the widget in the figure. If not provided, the next empty row will be used.
|
||||
col(int): The column coordinate of the widget in the figure. If not provided, the next empty column will be used.
|
||||
config(dict): Additional configuration for the widget.
|
||||
**axis_kwargs:
|
||||
|
||||
Returns:
|
||||
BECImageShow: The image widget.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
@@ -293,7 +470,7 @@ class BECFigure(RPCBase, BECFigureClientMixin):
|
||||
**axis_kwargs
|
||||
) -> "BECWaveform1D":
|
||||
"""
|
||||
Add a 1D waveform plot to the figure.
|
||||
Add a 1D waveform plot to the figure. Always access the first waveform widget in the figure.
|
||||
Args:
|
||||
x_name(str): The name of the device for the x-axis.
|
||||
y_name(str): The name of the device for the y-axis.
|
||||
@@ -321,7 +498,7 @@ class BECFigure(RPCBase, BECFigureClientMixin):
|
||||
**axis_kwargs
|
||||
) -> "BECImageShow":
|
||||
"""
|
||||
Add an image to the figure.
|
||||
Add an image to the figure. Always access the first image widget in the figure.
|
||||
Args:
|
||||
monitor(str): The name of the monitor to display.
|
||||
color_bar(Literal["simple","full"]): The type of color bar to display.
|
||||
@@ -559,6 +736,236 @@ class BECImageShow(RPCBase):
|
||||
name(str): The name of the image. If None, apply to all images.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_autorange(self, enable: "bool" = False, name: "str" = None):
|
||||
"""
|
||||
Set the autoscale of the image.
|
||||
Args:
|
||||
enable(bool): Whether to autoscale the color bar.
|
||||
name(str): The name of the image. If None, apply to all images.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_monitor(self, monitor: "str", name: "str" = None):
|
||||
"""
|
||||
Set the monitor of the image.
|
||||
If name is not specified, then set monitor for all images.
|
||||
Args:
|
||||
monitor(str): The name of the monitor.
|
||||
name(str): The name of the image. If None, apply to all images.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_processing(self, name: "str" = None, **kwargs):
|
||||
"""
|
||||
Set the post processing of the image.
|
||||
If name is not specified, then set post processing for all images.
|
||||
Args:
|
||||
name(str): The name of the image. If None, apply to all images.
|
||||
**kwargs: Keyword arguments for the properties to be set.
|
||||
Possible properties:
|
||||
- fft: bool
|
||||
- log: bool
|
||||
- rot: int
|
||||
- transpose: bool
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_image_properties(self, name: "str" = None, **kwargs):
|
||||
"""
|
||||
Set the properties of the image.
|
||||
Args:
|
||||
name(str): The name of the image. If None, apply to all images.
|
||||
**kwargs: Keyword arguments for the properties to be set.
|
||||
Possible properties:
|
||||
- downsample: bool
|
||||
- color_map: str
|
||||
- monitor: str
|
||||
- opacity: float
|
||||
- vrange: tuple[int,int]
|
||||
- fft: bool
|
||||
- log: bool
|
||||
- rot: int
|
||||
- transpose: bool
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_fft(self, enable: "bool" = False, name: "str" = None):
|
||||
"""
|
||||
Set the FFT of the image.
|
||||
If name is not specified, then set FFT for all images.
|
||||
Args:
|
||||
enable(bool): Whether to perform FFT on the monitor data.
|
||||
name(str): The name of the image. If None, apply to all images.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_log(self, enable: "bool" = False, name: "str" = None):
|
||||
"""
|
||||
Set the log of the image.
|
||||
If name is not specified, then set log for all images.
|
||||
Args:
|
||||
enable(bool): Whether to perform log on the monitor data.
|
||||
name(str): The name of the image. If None, apply to all images.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_rotation(self, deg_90: "int" = 0, name: "str" = None):
|
||||
"""
|
||||
Set the rotation of the image.
|
||||
If name is not specified, then set rotation for all images.
|
||||
Args:
|
||||
deg_90(int): The rotation angle of the monitor data before displaying.
|
||||
name(str): The name of the image. If None, apply to all images.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_transpose(self, enable: "bool" = False, name: "str" = None):
|
||||
"""
|
||||
Set the transpose of the image.
|
||||
If name is not specified, then set transpose for all images.
|
||||
Args:
|
||||
enable(bool): Whether to transpose the monitor data before displaying.
|
||||
name(str): The name of the image. If None, apply to all images.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def toggle_threading(self, use_threading: "bool"):
|
||||
"""
|
||||
Toggle threading for the widgets postprocessing and updating.
|
||||
Args:
|
||||
use_threading(bool): Whether to use threading.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def get_config(self, dict_output: "bool" = True) -> "dict | BaseModel":
|
||||
"""
|
||||
Get the configuration of the widget.
|
||||
Args:
|
||||
dict_output(bool): If True, return the configuration as a dictionary. If False, return the configuration as a pydantic model.
|
||||
Returns:
|
||||
dict: The configuration of the plot widget.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set(self, **kwargs) -> "None":
|
||||
"""
|
||||
Set the properties of the plot widget.
|
||||
Args:
|
||||
**kwargs: Keyword arguments for the properties to be set.
|
||||
Possible properties:
|
||||
- title: str
|
||||
- x_label: str
|
||||
- y_label: str
|
||||
- x_scale: Literal["linear", "log"]
|
||||
- y_scale: Literal["linear", "log"]
|
||||
- x_lim: tuple
|
||||
- y_lim: tuple
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_title(self, title: "str"):
|
||||
"""
|
||||
Set the title of the plot widget.
|
||||
Args:
|
||||
title(str): Title of the plot widget.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_x_label(self, label: "str"):
|
||||
"""
|
||||
Set the label of the x-axis.
|
||||
Args:
|
||||
label(str): Label of the x-axis.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_y_label(self, label: "str"):
|
||||
"""
|
||||
Set the label of the y-axis.
|
||||
Args:
|
||||
label(str): Label of the y-axis.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_x_scale(self, scale: "Literal['linear', 'log']" = "linear"):
|
||||
"""
|
||||
Set the scale of the x-axis.
|
||||
Args:
|
||||
scale(Literal["linear", "log"]): Scale of the x-axis.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_y_scale(self, scale: "Literal['linear', 'log']" = "linear"):
|
||||
"""
|
||||
Set the scale of the y-axis.
|
||||
Args:
|
||||
scale(Literal["linear", "log"]): Scale of the y-axis.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_x_lim(self, *args) -> "None":
|
||||
"""
|
||||
Set the limits of the x-axis. This method can accept either two separate arguments
|
||||
for the minimum and maximum x-axis values, or a single tuple containing both limits.
|
||||
|
||||
Usage:
|
||||
set_x_lim(x_min, x_max)
|
||||
set_x_lim((x_min, x_max))
|
||||
|
||||
Args:
|
||||
*args: A variable number of arguments. Can be two integers (x_min and x_max)
|
||||
or a single tuple with two integers.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_y_lim(self, *args) -> "None":
|
||||
"""
|
||||
Set the limits of the y-axis. This method can accept either two separate arguments
|
||||
for the minimum and maximum y-axis values, or a single tuple containing both limits.
|
||||
|
||||
Usage:
|
||||
set_y_lim(y_min, y_max)
|
||||
set_y_lim((y_min, y_max))
|
||||
|
||||
Args:
|
||||
*args: A variable number of arguments. Can be two integers (y_min and y_max)
|
||||
or a single tuple with two integers.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_grid(self, x: "bool" = False, y: "bool" = False):
|
||||
"""
|
||||
Set the grid of the plot widget.
|
||||
Args:
|
||||
x(bool): Show grid on the x-axis.
|
||||
y(bool): Show grid on the y-axis.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def lock_aspect_ratio(self, lock):
|
||||
"""
|
||||
Lock aspect ratio.
|
||||
Args:
|
||||
lock(bool): True to lock, False to unlock.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def plot(self, data_x: "list | np.ndarray", data_y: "list | np.ndarray", **kwargs):
|
||||
"""
|
||||
Plot custom data on the plot widget. These data are not saved in config.
|
||||
Args:
|
||||
data_x(list|np.ndarray): x-axis data
|
||||
data_y(list|np.ndarray): y-axis data
|
||||
**kwargs: Keyword arguments for the plot.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def remove(self):
|
||||
"""
|
||||
Remove the plot widget from the figure.
|
||||
"""
|
||||
|
||||
|
||||
class BECConnector(RPCBase):
|
||||
@rpc_call
|
||||
@@ -632,7 +1039,7 @@ class BECImageItem(RPCBase):
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def set_autorange(self, autorange: "bool" = True):
|
||||
def set_autorange(self, autorange: "bool" = False):
|
||||
"""
|
||||
Set the autorange of the color bar.
|
||||
Args:
|
||||
@@ -673,3 +1080,13 @@ class BECImageItem(RPCBase):
|
||||
vmin(float): Minimum value of the color bar.
|
||||
vmax(float): Maximum value of the color bar.
|
||||
"""
|
||||
|
||||
@rpc_call
|
||||
def get_config(self, dict_output: "bool" = True) -> "dict | BaseModel":
|
||||
"""
|
||||
Get the configuration of the widget.
|
||||
Args:
|
||||
dict_output(bool): If True, return the configuration as a dictionary. If False, return the configuration as a pydantic model.
|
||||
Returns:
|
||||
dict: The configuration of the plot widget.
|
||||
"""
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import select
|
||||
import subprocess
|
||||
import uuid
|
||||
|
||||
from functools import wraps
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from bec_lib import MessageEndpoints, messages
|
||||
from bec_lib.connector import MessageObject
|
||||
from bec_lib.device import DeviceBase
|
||||
from qtpy.QtCore import QCoreApplication
|
||||
|
||||
import bec_widgets.cli.client as client
|
||||
from bec_lib import MessageEndpoints, messages
|
||||
from bec_widgets.utils.bec_dispatcher import BECDispatcher
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bec_widgets.cli.client import BECFigure
|
||||
|
||||
|
||||
def rpc_call(func):
|
||||
"""
|
||||
@@ -30,10 +37,97 @@ def rpc_call(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
def get_selected_device(monitored_devices, selected_device):
|
||||
"""
|
||||
Get the selected device for the plot. If no device is selected, the first
|
||||
device in the monitored devices list is selected.
|
||||
"""
|
||||
if selected_device:
|
||||
return selected_device
|
||||
if len(monitored_devices) > 0:
|
||||
sel_device = monitored_devices[0]
|
||||
return sel_device
|
||||
return None
|
||||
|
||||
|
||||
def update_script(figure: BECFigure, msg):
|
||||
"""
|
||||
Update the script with the given data.
|
||||
"""
|
||||
info = msg.info
|
||||
status = msg.status
|
||||
scan_id = msg.scanID
|
||||
scan_number = info.get("scan_number", 0)
|
||||
scan_name = info.get("scan_name", "Unknown")
|
||||
scan_report_devices = info.get("scan_report_devices", [])
|
||||
monitored_devices = info.get("readout_priority", {}).get("monitored", [])
|
||||
monitored_devices = [dev for dev in monitored_devices if dev not in scan_report_devices]
|
||||
|
||||
if scan_name == "line_scan" and scan_report_devices:
|
||||
dev_x = scan_report_devices[0]
|
||||
dev_y = get_selected_device(monitored_devices, figure.selected_device)
|
||||
print(f"Selected device: {dev_y}")
|
||||
if not dev_y:
|
||||
return
|
||||
figure.clear_all()
|
||||
plt = figure.plot(dev_x, dev_y)
|
||||
plt.set(title=f"Scan {scan_number}", x_label=dev_x, y_label=dev_y)
|
||||
elif scan_name == "grid_scan" and scan_report_devices:
|
||||
print(f"Scan {scan_number} is running")
|
||||
dev_x = scan_report_devices[0]
|
||||
dev_y = scan_report_devices[1]
|
||||
figure.clear_all()
|
||||
plt = figure.plot(dev_x, dev_y, label=f"Scan {scan_number}")
|
||||
plt.set(title=f"Scan {scan_number}", x_label=dev_x, y_label=dev_y)
|
||||
elif scan_report_devices:
|
||||
dev_x = scan_report_devices[0]
|
||||
dev_y = get_selected_device(monitored_devices, figure.selected_device)
|
||||
if not dev_y:
|
||||
return
|
||||
figure.clear_all()
|
||||
plt = figure.plot(dev_x, dev_y, label=f"Scan {scan_number}")
|
||||
plt.set(title=f"Scan {scan_number}", x_label=dev_x, y_label=dev_y)
|
||||
|
||||
|
||||
class BECFigureClientMixin:
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self._process = None
|
||||
self.update_script = update_script
|
||||
self._target_endpoint = MessageEndpoints.scan_status()
|
||||
self._selected_device = None
|
||||
|
||||
@property
|
||||
def selected_device(self):
|
||||
"""
|
||||
Selected device for the plot.
|
||||
"""
|
||||
return self._selected_device
|
||||
|
||||
@selected_device.setter
|
||||
def selected_device(self, device: str | DeviceBase):
|
||||
if isinstance(device, DeviceBase):
|
||||
self._selected_device = device.name
|
||||
elif isinstance(device, str):
|
||||
self._selected_device = device
|
||||
else:
|
||||
raise ValueError("Device must be a string or a device object")
|
||||
|
||||
def _start_update_script(self) -> None:
|
||||
self._client.connector.register(
|
||||
self._target_endpoint, cb=self._handle_msg_update, parent=self
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _handle_msg_update(msg: MessageObject, parent: BECFigureClientMixin) -> None:
|
||||
if parent.update_script is not None:
|
||||
# pylint: disable=protected-access
|
||||
parent._update_script_msg_parser(msg.value)
|
||||
|
||||
def _update_script_msg_parser(self, msg: messages.BECMessage) -> None:
|
||||
if isinstance(msg, messages.ScanStatusMessage):
|
||||
if msg.status == "open":
|
||||
self.update_script(self, msg)
|
||||
|
||||
def show(self) -> None:
|
||||
"""
|
||||
@@ -56,6 +150,7 @@ class BECFigureClientMixin:
|
||||
"""
|
||||
Start the plot in a new process.
|
||||
"""
|
||||
self._start_update_script()
|
||||
# pylint: disable=subprocess-run-check
|
||||
monitor_module = importlib.import_module("bec_widgets.cli.server")
|
||||
monitor_path = monitor_module.__file__
|
||||
@@ -85,9 +180,6 @@ class BECFigureClientMixin:
|
||||
stderr_output.append(line.decode("utf-8"))
|
||||
return "".join(stderr_output)
|
||||
|
||||
def __del__(self) -> None:
|
||||
self.close()
|
||||
|
||||
|
||||
class RPCBase:
|
||||
def __init__(self, gui_id: str = None, config: dict = None, parent=None) -> None:
|
||||
@@ -96,7 +188,12 @@ class RPCBase:
|
||||
self._gui_id = gui_id if gui_id is not None else str(uuid.uuid4())
|
||||
self._parent = parent
|
||||
super().__init__()
|
||||
print(f"RPCBase: {self._gui_id}")
|
||||
# print(f"RPCBase: {self._gui_id}")
|
||||
|
||||
def __repr__(self):
|
||||
type_ = type(self)
|
||||
qualname = type_.__qualname__
|
||||
return f"<{qualname} object at {hex(id(self))}>"
|
||||
|
||||
@property
|
||||
def _root(self):
|
||||
@@ -129,7 +226,7 @@ class RPCBase:
|
||||
parameter={"args": args, "kwargs": kwargs, "gui_id": self._gui_id},
|
||||
metadata={"request_id": request_id},
|
||||
)
|
||||
print(f"RPCBase: {rpc_msg}")
|
||||
# print(f"RPCBase: {rpc_msg}")
|
||||
# pylint: disable=protected-access
|
||||
receiver = self._root._gui_id
|
||||
self._client.connector.set_and_publish(MessageEndpoints.gui_instructions(receiver), rpc_msg)
|
||||
@@ -150,7 +247,9 @@ class RPCBase:
|
||||
return [self._create_widget_from_msg_result(res) for res in msg_result]
|
||||
if isinstance(msg_result, dict):
|
||||
if "__rpc__" not in msg_result:
|
||||
return msg_result
|
||||
return {
|
||||
key: self._create_widget_from_msg_result(val) for key, val in msg_result.items()
|
||||
}
|
||||
cls = msg_result.pop("widget_class", None)
|
||||
msg_result.pop("__rpc__", None)
|
||||
|
||||
@@ -158,7 +257,7 @@ class RPCBase:
|
||||
return msg_result
|
||||
|
||||
cls = getattr(client, cls)
|
||||
print(msg_result)
|
||||
# print(msg_result)
|
||||
return cls(parent=self, **msg_result)
|
||||
return msg_result
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class BECWidgetsCLIServer:
|
||||
self.client.start()
|
||||
self.gui_id = gui_id
|
||||
self.fig = BECFigure(gui_id=self.gui_id)
|
||||
print(f"Server started with gui_id {self.gui_id}")
|
||||
# print(f"Server started with gui_id {self.gui_id}")
|
||||
|
||||
self.dispatcher.connect_slot(
|
||||
self.on_rpc_update, MessageEndpoints.gui_instructions(self.gui_id)
|
||||
@@ -54,17 +54,14 @@ class BECWidgetsCLIServer:
|
||||
if gui_id == self.fig.gui_id:
|
||||
return self.fig
|
||||
# check if the object is a widget
|
||||
if gui_id in self.fig.widgets:
|
||||
obj = self.fig.widgets[config["gui_id"]]
|
||||
if gui_id in self.fig._widgets:
|
||||
obj = self.fig._widgets[config["gui_id"]]
|
||||
return obj
|
||||
if self.fig.widgets:
|
||||
for widget in self.fig.widgets.values():
|
||||
if self.fig._widgets:
|
||||
for widget in self.fig._widgets.values():
|
||||
item = widget.find_widget_by_id(gui_id)
|
||||
if item:
|
||||
return item
|
||||
# raise NotImplementedError(
|
||||
# f"gui_id lookup for widget of type {widget.__class__.__name__} not implemented"
|
||||
# )
|
||||
|
||||
raise ValueError(f"Object with gui_id {gui_id} not found")
|
||||
|
||||
@@ -82,6 +79,8 @@ class BECWidgetsCLIServer:
|
||||
|
||||
if isinstance(res, list):
|
||||
res = [self.serialize_object(obj) for obj in res]
|
||||
elif isinstance(res, dict):
|
||||
res = {key: self.serialize_object(val) for key, val in res.items()}
|
||||
else:
|
||||
res = self.serialize_object(res)
|
||||
return res
|
||||
@@ -99,6 +98,10 @@ class BECWidgetsCLIServer:
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
import argparse
|
||||
import sys
|
||||
from qtpy.QtWidgets import QApplication
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
parser = argparse.ArgumentParser(description="BEC Widgets CLI Server")
|
||||
parser.add_argument("--id", type=str, help="The id of the server")
|
||||
@@ -108,3 +111,4 @@ if __name__ == "__main__": # pragma: no cover
|
||||
server = BECWidgetsCLIServer(gui_id=args.id)
|
||||
# server = BECWidgetsCLIServer(gui_id="test")
|
||||
server.start()
|
||||
sys.exit(app.exec())
|
||||
|
||||
@@ -242,19 +242,19 @@ if __name__ == "__main__": # pragma: no cover
|
||||
qdarktheme.setup_theme("auto")
|
||||
|
||||
if args.variant == "app":
|
||||
window = MotorControlApp(client=client, config=CONFIG_DEFAULT)
|
||||
window = MotorControlApp(client=client) # , config=CONFIG_DEFAULT)
|
||||
elif args.variant == "map":
|
||||
window = MotorControlMap(client=client, config=CONFIG_DEFAULT)
|
||||
window = MotorControlMap(client=client) # , config=CONFIG_DEFAULT)
|
||||
elif args.variant == "panel":
|
||||
window = MotorControlPanel(client=client, config=CONFIG_DEFAULT)
|
||||
window = MotorControlPanel(client=client) # , config=CONFIG_DEFAULT)
|
||||
elif args.variant == "panel_abs":
|
||||
window = MotorControlPanelAbsolute(client=client, config=CONFIG_DEFAULT)
|
||||
window = MotorControlPanelAbsolute(client=client) # , config=CONFIG_DEFAULT)
|
||||
elif args.variant == "panel_rel":
|
||||
window = MotorControlPanelRelative(client=client, config=CONFIG_DEFAULT)
|
||||
window = MotorControlPanelRelative(client=client) # , config=CONFIG_DEFAULT)
|
||||
else:
|
||||
print("Please specify a valid variant to run. Use -h for help.")
|
||||
print("Running the full application by default.")
|
||||
window = MotorControlApp(client=client, config=CONFIG_DEFAULT)
|
||||
window = MotorControlApp(client=client) # , config=CONFIG_DEFAULT)
|
||||
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
|
||||
@@ -35,19 +35,14 @@ class _Connection:
|
||||
class _BECDispatcher(QObject):
|
||||
"""Utility class to keep track of slots connected to a particular redis connector"""
|
||||
|
||||
def __init__(self, bec_config=None):
|
||||
def __init__(self, client=None):
|
||||
super().__init__()
|
||||
self.client = BECClient()
|
||||
|
||||
# TODO: this is a workaround for now to provide service config within qtdesigner, but is
|
||||
# it possible to provide config via a cli arg?
|
||||
if bec_config is None and os.path.isfile("bec_config.yaml"):
|
||||
bec_config = "bec_config.yaml"
|
||||
|
||||
self.client = BECClient() if client is None else client
|
||||
try:
|
||||
self.client.initialize(config=ServiceConfig(config_path=bec_config))
|
||||
except redis.exceptions.ConnectionError as e:
|
||||
print(f"Failed to initialize BECClient: {e}")
|
||||
self.client.start()
|
||||
except redis.exceptions.ConnectionError:
|
||||
print("Could not connect to Redis, skipping start of BECClient.")
|
||||
|
||||
self._connections = {}
|
||||
|
||||
def connect_slot(
|
||||
@@ -186,8 +181,8 @@ def BECDispatcher():
|
||||
global _bec_dispatcher
|
||||
if _bec_dispatcher is None:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--bec-config", default=None)
|
||||
parser.add_argument("--bec-client", default=None)
|
||||
args, _ = parser.parse_known_args()
|
||||
|
||||
_bec_dispatcher = _BECDispatcher(args.bec_config)
|
||||
_bec_dispatcher = _BECDispatcher(args.bec_client)
|
||||
return _bec_dispatcher
|
||||
|
||||
@@ -95,6 +95,8 @@ class WidgetHandler:
|
||||
|
||||
class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
USER_ACCESS = [
|
||||
"axes",
|
||||
"widgets",
|
||||
"add_plot",
|
||||
"add_image",
|
||||
"plot",
|
||||
@@ -127,13 +129,48 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
self.widget_handler = WidgetHandler()
|
||||
|
||||
# Widget container to reference widgets by 'widget_id'
|
||||
self.widgets = defaultdict(dict)
|
||||
self._widgets = defaultdict(dict)
|
||||
|
||||
# Container to keep track of the grid
|
||||
self.grid = []
|
||||
|
||||
@property
|
||||
def axes(self) -> list[BECPlotBase]:
|
||||
"""
|
||||
Access all widget in BECFigure as a list
|
||||
Returns:
|
||||
list[BECPlotBase]: List of all widgets in the figure.
|
||||
"""
|
||||
axes = [value for value in self._widgets.values() if isinstance(value, BECPlotBase)]
|
||||
return axes
|
||||
|
||||
@axes.setter
|
||||
def axes(self, value: list[BECPlotBase]):
|
||||
self._axes = value
|
||||
|
||||
@property
|
||||
def widgets(self) -> dict:
|
||||
return self._widgets
|
||||
|
||||
@widgets.setter
|
||||
def widgets(self, value: dict):
|
||||
self._widgets = value
|
||||
|
||||
def add_plot(
|
||||
self, widget_id: str = None, row: int = None, col: int = None, config=None, **axis_kwargs
|
||||
self,
|
||||
x_name: str = None,
|
||||
y_name: str = None,
|
||||
x_entry: str = None,
|
||||
y_entry: str = None,
|
||||
x: list | np.ndarray = None,
|
||||
y: list | np.ndarray = None,
|
||||
color: Optional[str] = None,
|
||||
label: Optional[str] = None,
|
||||
validate: bool = True,
|
||||
row: int = None,
|
||||
col: int = None,
|
||||
config=None,
|
||||
**axis_kwargs,
|
||||
) -> BECWaveform1D:
|
||||
"""
|
||||
Add a Waveform1D plot to the figure at the specified position.
|
||||
@@ -144,7 +181,8 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
config(dict): Additional configuration for the widget.
|
||||
**axis_kwargs(dict): Additional axis properties to set on the widget after creation.
|
||||
"""
|
||||
return self.add_widget(
|
||||
widget_id = self._generate_unique_widget_id()
|
||||
waveform = self.add_widget(
|
||||
widget_type="Waveform1D",
|
||||
widget_id=widget_id,
|
||||
row=row,
|
||||
@@ -153,6 +191,30 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
**axis_kwargs,
|
||||
)
|
||||
|
||||
# TODO remove repetition from .plot method
|
||||
|
||||
# User wants to add scan curve
|
||||
if x_name is not None and y_name is not None and x is None and y is None:
|
||||
waveform.add_curve_scan(
|
||||
x_name=x_name,
|
||||
y_name=y_name,
|
||||
x_entry=x_entry,
|
||||
y_entry=y_entry,
|
||||
validate=validate,
|
||||
color=color,
|
||||
label=label,
|
||||
)
|
||||
# User wants to add custom curve
|
||||
elif x is not None and y is not None and x_name is None and y_name is None:
|
||||
waveform.add_curve_custom(
|
||||
x=x,
|
||||
y=y,
|
||||
color=color,
|
||||
label=label,
|
||||
)
|
||||
|
||||
return waveform
|
||||
|
||||
def plot(
|
||||
self,
|
||||
x_name: str = None,
|
||||
@@ -167,7 +229,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
**axis_kwargs,
|
||||
) -> BECWaveform1D:
|
||||
"""
|
||||
Add a 1D waveform plot to the figure.
|
||||
Add a 1D waveform plot to the figure. Always access the first waveform widget in the figure.
|
||||
Args:
|
||||
x_name(str): The name of the device for the x-axis.
|
||||
y_name(str): The name of the device for the y-axis.
|
||||
@@ -225,7 +287,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
**axis_kwargs,
|
||||
) -> BECImageShow:
|
||||
"""
|
||||
Add an image to the figure.
|
||||
Add an image to the figure. Always access the first image widget in the figure.
|
||||
Args:
|
||||
monitor(str): The name of the monitor to display.
|
||||
color_bar(Literal["simple","full"]): The type of color bar to display.
|
||||
@@ -265,17 +327,35 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
|
||||
def add_image(
|
||||
self,
|
||||
widget_id: str = None,
|
||||
monitor: str = None,
|
||||
color_bar: Literal["simple", "full"] = "full",
|
||||
color_map: str = "magma",
|
||||
data: np.ndarray = None,
|
||||
vrange: tuple[float, float] = None,
|
||||
row: int = None,
|
||||
col: int = None,
|
||||
config=None,
|
||||
color_map: str = "magma", # TODO fix passing additional kwargs
|
||||
color_bar: Literal["simple", "full"] = "full",
|
||||
vrange: tuple[float, float] = None,
|
||||
**axis_kwargs,
|
||||
) -> BECImageShow:
|
||||
"""
|
||||
Add an image to the figure at the specified position.
|
||||
Args:
|
||||
monitor(str): The name of the monitor to display.
|
||||
color_bar(Literal["simple","full"]): The type of color bar to display.
|
||||
color_map(str): The color map to use for the image.
|
||||
data(np.ndarray): Custom data to display.
|
||||
vrange(tuple[float, float]): The range of values to display.
|
||||
row(int): The row coordinate of the widget in the figure. If not provided, the next empty row will be used.
|
||||
col(int): The column coordinate of the widget in the figure. If not provided, the next empty column will be used.
|
||||
config(dict): Additional configuration for the widget.
|
||||
**axis_kwargs:
|
||||
|
||||
Returns:
|
||||
BECImageShow: The image widget.
|
||||
"""
|
||||
|
||||
widget_id = self._generate_unique_widget_id()
|
||||
if config is None:
|
||||
widget_id = self._generate_unique_widget_id()
|
||||
config = ImageConfig(
|
||||
widget_class="BECImageShow",
|
||||
gui_id=widget_id,
|
||||
@@ -284,7 +364,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
color_bar=color_bar,
|
||||
vrange=vrange,
|
||||
)
|
||||
return self.add_widget(
|
||||
image = self.add_widget(
|
||||
widget_type="ImShow",
|
||||
widget_id=widget_id,
|
||||
row=row,
|
||||
@@ -292,6 +372,23 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
config=config,
|
||||
**axis_kwargs,
|
||||
)
|
||||
# TODO remove repetition from .image method
|
||||
if monitor is not None and data is None:
|
||||
image.add_monitor_image(
|
||||
monitor=monitor, color_map=color_map, vrange=vrange, color_bar=color_bar
|
||||
)
|
||||
elif data is not None and monitor is None:
|
||||
image.add_custom_image(
|
||||
name="custom", data=data, color_map=color_map, vrange=vrange, color_bar=color_bar
|
||||
)
|
||||
elif data is None and monitor is None:
|
||||
# Setting appearance
|
||||
if vrange is not None:
|
||||
image.set_vrange(vmin=vrange[0], vmax=vrange[1])
|
||||
if color_map is not None:
|
||||
image.set_color_map(color_map)
|
||||
|
||||
return image
|
||||
|
||||
def add_widget(
|
||||
self,
|
||||
@@ -314,7 +411,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
"""
|
||||
if not widget_id:
|
||||
widget_id = self._generate_unique_widget_id()
|
||||
if widget_id in self.widgets:
|
||||
if widget_id in self._widgets:
|
||||
raise ValueError(f"Widget with ID '{widget_id}' already exists.")
|
||||
|
||||
widget = self.widget_handler.create_widget(
|
||||
@@ -350,7 +447,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
|
||||
# Saving config for future referencing
|
||||
self.config.widgets[widget_id] = widget.config
|
||||
self.widgets[widget_id] = widget
|
||||
self._widgets[widget_id] = widget
|
||||
|
||||
# Reflect the grid coordinates
|
||||
self._change_grid(widget_id, row, col)
|
||||
@@ -402,7 +499,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
Returns:
|
||||
BECPlotBase: The widget of the given class.
|
||||
"""
|
||||
for widget_id, widget in self.widgets.items():
|
||||
for widget_id, widget in self._widgets.items():
|
||||
if isinstance(widget, widget_class):
|
||||
return widget
|
||||
if can_fail:
|
||||
@@ -420,7 +517,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
widget = self._get_widget_by_coordinates(row, col)
|
||||
if widget:
|
||||
widget_id = widget.config.gui_id
|
||||
if widget_id in self.widgets:
|
||||
if widget_id in self._widgets:
|
||||
self._remove_by_id(widget_id)
|
||||
|
||||
def _remove_by_id(self, widget_id: str) -> None:
|
||||
@@ -429,8 +526,8 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
Args:
|
||||
widget_id(str): The unique identifier of the widget to remove.
|
||||
"""
|
||||
if widget_id in self.widgets:
|
||||
widget = self.widgets.pop(widget_id)
|
||||
if widget_id in self._widgets:
|
||||
widget = self._widgets.pop(widget_id)
|
||||
widget.cleanup()
|
||||
self.removeItem(widget)
|
||||
self.grid[widget.config.row][widget.config.col] = None
|
||||
@@ -445,10 +542,10 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
if isinstance(key, tuple) and len(key) == 2:
|
||||
return self._get_widget_by_coordinates(*key)
|
||||
elif isinstance(key, str):
|
||||
widget = self.widgets.get(key)
|
||||
widget = self._widgets.get(key)
|
||||
if widget is None:
|
||||
raise KeyError(f"No widget with ID {key}")
|
||||
return self.widgets.get(key)
|
||||
return self._widgets.get(key)
|
||||
else:
|
||||
raise TypeError(
|
||||
"Key must be a string (widget id) or a tuple of two integers (grid coordinates)"
|
||||
@@ -478,7 +575,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
|
||||
def _generate_unique_widget_id(self):
|
||||
"""Generate a unique widget ID."""
|
||||
existing_ids = set(self.widgets.keys())
|
||||
existing_ids = set(self._widgets.keys())
|
||||
for i in itertools.count(1):
|
||||
widget_id = f"widget_{i}"
|
||||
if widget_id not in existing_ids:
|
||||
@@ -511,7 +608,10 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
# Update the config of each object to reflect its new position
|
||||
for row_idx, row in enumerate(new_grid):
|
||||
for col_idx, widget in enumerate(row):
|
||||
self.widgets[widget].config.row, self.widgets[widget].config.col = row_idx, col_idx
|
||||
self._widgets[widget].config.row, self._widgets[widget].config.col = (
|
||||
row_idx,
|
||||
col_idx,
|
||||
)
|
||||
|
||||
self.grid = new_grid
|
||||
self._replot_layout()
|
||||
@@ -521,7 +621,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
self.clear()
|
||||
for row_idx, row in enumerate(self.grid):
|
||||
for col_idx, widget in enumerate(row):
|
||||
self.addItem(self.widgets[widget], row=row_idx, col=col_idx)
|
||||
self.addItem(self._widgets[widget], row=row_idx, col=col_idx)
|
||||
|
||||
def change_layout(self, max_columns=None, max_rows=None):
|
||||
"""
|
||||
@@ -533,7 +633,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
max_rows (Optional[int]): The new maximum number of rows in the figure.
|
||||
"""
|
||||
# Calculate total number of widgets
|
||||
total_widgets = len(self.widgets)
|
||||
total_widgets = len(self._widgets)
|
||||
|
||||
if max_columns:
|
||||
# Calculate the required number of rows based on max_columns
|
||||
@@ -549,7 +649,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
|
||||
# Populate the new grid with widgets' IDs
|
||||
current_idx = 0
|
||||
for widget_id, widget in self.widgets.items():
|
||||
for widget_id, widget in self._widgets.items():
|
||||
row = current_idx // len(new_grid[0])
|
||||
col = current_idx % len(new_grid[0])
|
||||
new_grid[row][col] = widget_id
|
||||
@@ -565,10 +665,10 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
|
||||
def clear_all(self):
|
||||
"""Clear all widgets from the figure and reset to default state"""
|
||||
for widget in self.widgets.values():
|
||||
for widget in self._widgets.values():
|
||||
widget.cleanup()
|
||||
self.clear()
|
||||
self.widgets = defaultdict(dict)
|
||||
self._widgets = defaultdict(dict)
|
||||
self.grid = []
|
||||
theme = self.config.theme
|
||||
self.config = FigureConfig(
|
||||
|
||||
@@ -45,6 +45,9 @@ class MotorControlWidget(QWidget):
|
||||
self.motor_thread = motor_thread
|
||||
self.config = config
|
||||
|
||||
self.motor_x = None
|
||||
self.motor_y = None
|
||||
|
||||
if not self.client:
|
||||
bec_dispatcher = BECDispatcher()
|
||||
self.client = bec_dispatcher.client
|
||||
|
||||
@@ -69,6 +69,7 @@ class BECImageItem(BECConnector, pg.ImageItem):
|
||||
"set_auto_downsample",
|
||||
"set_monitor",
|
||||
"set_vrange",
|
||||
"get_config",
|
||||
]
|
||||
|
||||
def __init__(
|
||||
@@ -181,7 +182,7 @@ class BECImageItem(BECConnector, pg.ImageItem):
|
||||
self.setOpacity(opacity)
|
||||
self.config.opacity = opacity
|
||||
|
||||
def set_autorange(self, autorange: bool = True):
|
||||
def set_autorange(self, autorange: bool = False):
|
||||
"""
|
||||
Set the autorange of the color bar.
|
||||
Args:
|
||||
@@ -287,6 +288,28 @@ class BECImageShow(BECPlotBase):
|
||||
"add_custom_image",
|
||||
"set_vrange",
|
||||
"set_color_map",
|
||||
"set_autorange",
|
||||
"set_monitor",
|
||||
"set_processing",
|
||||
"set_image_properties",
|
||||
"set_fft",
|
||||
"set_log",
|
||||
"set_rotation",
|
||||
"set_transpose",
|
||||
"toggle_threading",
|
||||
"get_config",
|
||||
"set",
|
||||
"set_title",
|
||||
"set_x_label",
|
||||
"set_y_label",
|
||||
"set_x_scale",
|
||||
"set_y_scale",
|
||||
"set_x_lim",
|
||||
"set_y_lim",
|
||||
"set_grid",
|
||||
"lock_aspect_ratio",
|
||||
"plot",
|
||||
"remove",
|
||||
]
|
||||
|
||||
def __init__(
|
||||
@@ -555,6 +578,25 @@ class BECImageShow(BECPlotBase):
|
||||
"""
|
||||
self.apply_setting_to_images("set_color_map", args=[cmap], kwargs={}, image_id=name)
|
||||
|
||||
def set_autorange(self, enable: bool = False, name: str = None):
|
||||
"""
|
||||
Set the autoscale of the image.
|
||||
Args:
|
||||
enable(bool): Whether to autoscale the color bar.
|
||||
name(str): The name of the image. If None, apply to all images.
|
||||
"""
|
||||
self.apply_setting_to_images("set_autorange", args=[enable], kwargs={}, image_id=name)
|
||||
|
||||
def set_monitor(self, monitor: str, name: str = None):
|
||||
"""
|
||||
Set the monitor of the image.
|
||||
If name is not specified, then set monitor for all images.
|
||||
Args:
|
||||
monitor(str): The name of the monitor.
|
||||
name(str): The name of the image. If None, apply to all images.
|
||||
"""
|
||||
self.apply_setting_to_images("set_monitor", args=[monitor], kwargs={}, image_id=name)
|
||||
|
||||
def set_processing(self, name: str = None, **kwargs):
|
||||
"""
|
||||
Set the post processing of the image.
|
||||
|
||||
@@ -38,6 +38,7 @@ class WidgetConfig(ConnectionConfig):
|
||||
|
||||
class BECPlotBase(BECConnector, pg.GraphicsLayout):
|
||||
USER_ACCESS = [
|
||||
"get_config",
|
||||
"set",
|
||||
"set_title",
|
||||
"set_x_label",
|
||||
|
||||
@@ -219,11 +219,23 @@ class BECWaveform1D(BECPlotBase):
|
||||
"remove_curve",
|
||||
"scan_history",
|
||||
"curves",
|
||||
# "curves_data",
|
||||
"get_curve",
|
||||
"get_curve_config",
|
||||
"apply_config",
|
||||
"get_all_data",
|
||||
"get_config",
|
||||
"set",
|
||||
"set_title",
|
||||
"set_x_label",
|
||||
"set_y_label",
|
||||
"set_x_scale",
|
||||
"set_y_scale",
|
||||
"set_x_lim",
|
||||
"set_y_lim",
|
||||
"set_grid",
|
||||
"lock_aspect_ratio",
|
||||
"plot",
|
||||
"remove",
|
||||
]
|
||||
scan_signal_update = pyqtSignal()
|
||||
|
||||
@@ -356,19 +368,6 @@ class BECWaveform1D(BECPlotBase):
|
||||
def curves(self, value: list[BECCurve]):
|
||||
self._curves = value
|
||||
|
||||
@property
|
||||
def curves_data(self) -> dict[str, dict[str, BECCurve]]:
|
||||
"""
|
||||
Get the curves data of the plot widget as a dictionary
|
||||
Returns:
|
||||
dict: Dictionary of curves data.
|
||||
"""
|
||||
return self._curves_data
|
||||
|
||||
@curves_data.setter
|
||||
def curves_data(self, value: dict[str, dict[str, BECCurve]]):
|
||||
self._curves_data = value
|
||||
|
||||
def get_curve(self, identifier) -> BECCurve:
|
||||
"""
|
||||
Get the curve by its index or ID.
|
||||
|
||||
2
setup.py
2
setup.py
@@ -1,7 +1,7 @@
|
||||
# pylint: disable= missing-module-docstring
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
__version__ = "0.43.0"
|
||||
__version__ = "0.44.2"
|
||||
|
||||
# Default to PyQt6 if no other Qt binding is installed
|
||||
QT_DEPENDENCY = "PyQt6>=6.0"
|
||||
|
||||
@@ -30,6 +30,6 @@ def bec_dispatcher(threads_check):
|
||||
yield bec_dispatcher
|
||||
bec_dispatcher.disconnect_all()
|
||||
# clean BEC client
|
||||
BECService.shutdown(bec_dispatcher.client)
|
||||
bec_dispatcher.client.shutdown()
|
||||
# reinitialize singleton for next test
|
||||
bec_dispatcher_module._bec_dispatcher = None
|
||||
|
||||
@@ -36,37 +36,37 @@ def test_bec_figure_init_with_config(mocked_client):
|
||||
|
||||
|
||||
def test_bec_figure_add_remove_plot(bec_figure):
|
||||
initial_count = len(bec_figure.widgets)
|
||||
initial_count = len(bec_figure._widgets)
|
||||
|
||||
# Adding 3 widgets - 2 WaveformBase and 1 PlotBase
|
||||
w0 = bec_figure.add_plot()
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w2 = bec_figure.add_widget(widget_id="test_plot", widget_type="PlotBase")
|
||||
w1 = bec_figure.add_plot()
|
||||
w2 = bec_figure.add_widget(widget_type="PlotBase")
|
||||
|
||||
# Check if the widgets were added
|
||||
assert len(bec_figure.widgets) == initial_count + 3
|
||||
assert "widget_1" in bec_figure.widgets
|
||||
assert "test_plot" in bec_figure.widgets
|
||||
assert "test_waveform" in bec_figure.widgets
|
||||
assert bec_figure.widgets["widget_1"].config.widget_class == "BECWaveform1D"
|
||||
assert bec_figure.widgets["test_plot"].config.widget_class == "BECPlotBase"
|
||||
assert bec_figure.widgets["test_waveform"].config.widget_class == "BECWaveform1D"
|
||||
assert len(bec_figure._widgets) == initial_count + 3
|
||||
assert "widget_1" in bec_figure._widgets
|
||||
assert "widget_2" in bec_figure._widgets
|
||||
assert "widget_3" in bec_figure._widgets
|
||||
assert bec_figure._widgets["widget_1"].config.widget_class == "BECWaveform1D"
|
||||
assert bec_figure._widgets["widget_2"].config.widget_class == "BECWaveform1D"
|
||||
assert bec_figure._widgets["widget_3"].config.widget_class == "BECPlotBase"
|
||||
|
||||
# Check accessing positions by the grid in figure
|
||||
assert bec_figure[0, 0] == w0
|
||||
assert bec_figure[1, 0] == w1
|
||||
assert bec_figure[2, 0] == w2
|
||||
|
||||
# Removing 1 widget - PlotBase
|
||||
bec_figure.remove(widget_id="test_plot")
|
||||
assert len(bec_figure.widgets) == initial_count + 2
|
||||
assert "test_plot" not in bec_figure.widgets
|
||||
assert "test_waveform" in bec_figure.widgets
|
||||
assert bec_figure.widgets["test_waveform"].config.widget_class == "BECWaveform1D"
|
||||
# Removing 1 widget
|
||||
bec_figure.remove(widget_id="widget_1")
|
||||
assert len(bec_figure._widgets) == initial_count + 2
|
||||
assert "widget_1" not in bec_figure._widgets
|
||||
assert "widget_3" in bec_figure._widgets
|
||||
assert bec_figure._widgets["widget_2"].config.widget_class == "BECWaveform1D"
|
||||
|
||||
|
||||
def test_access_widgets_access_errors(bec_figure):
|
||||
bec_figure.add_plot(widget_id="test_waveform_1", row=0, col=0)
|
||||
bec_figure.add_plot(row=0, col=0)
|
||||
|
||||
# access widget by non-existent coordinates
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@@ -88,26 +88,18 @@ def test_access_widgets_access_errors(bec_figure):
|
||||
|
||||
|
||||
def test_add_plot_to_occupied_position(bec_figure):
|
||||
bec_figure.add_plot(widget_id="test_waveform_1", row=0, col=0)
|
||||
bec_figure.add_plot(row=0, col=0)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
bec_figure.add_plot(widget_id="test_waveform_2", row=0, col=0)
|
||||
bec_figure.add_plot(row=0, col=0)
|
||||
assert "Position at row 0 and column 0 is already occupied." in str(excinfo.value)
|
||||
|
||||
|
||||
def test_add_plot_to_occupied_id(bec_figure):
|
||||
bec_figure.add_plot(widget_id="test_waveform", row=0, col=0)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
bec_figure.add_plot(widget_id="test_waveform", row=0, col=1)
|
||||
assert "Widget with ID 'test_waveform' already exists" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_remove_plots(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform_1", row=0, col=0)
|
||||
w2 = bec_figure.add_plot(widget_id="test_waveform_2", row=0, col=1)
|
||||
w3 = bec_figure.add_plot(widget_id="test_waveform_3", row=1, col=0)
|
||||
w4 = bec_figure.add_plot(widget_id="test_waveform_4", row=1, col=1)
|
||||
w1 = bec_figure.add_plot(row=0, col=0)
|
||||
w2 = bec_figure.add_plot(row=0, col=1)
|
||||
w3 = bec_figure.add_plot(row=1, col=0)
|
||||
w4 = bec_figure.add_plot(row=1, col=1)
|
||||
|
||||
assert bec_figure[0, 0] == w1
|
||||
assert bec_figure[0, 1] == w2
|
||||
@@ -116,47 +108,47 @@ def test_remove_plots(bec_figure):
|
||||
|
||||
# remove by coordinates
|
||||
bec_figure[0, 0].remove()
|
||||
assert "test_waveform_1" not in bec_figure.widgets
|
||||
assert "widget_1" not in bec_figure._widgets
|
||||
|
||||
# remove by widget_id
|
||||
bec_figure.remove(widget_id="test_waveform_2")
|
||||
assert "test_waveform_2" not in bec_figure.widgets
|
||||
bec_figure.remove(widget_id="widget_2")
|
||||
assert "widget_2" not in bec_figure._widgets
|
||||
|
||||
# remove by widget object
|
||||
w3.remove()
|
||||
assert "test_waveform_3" not in bec_figure.widgets
|
||||
assert "widget_3" not in bec_figure._widgets
|
||||
|
||||
# check the remaining widget 4
|
||||
assert bec_figure[0, 0] == w4
|
||||
assert bec_figure["test_waveform_4"] == w4
|
||||
assert "test_waveform_4" in bec_figure.widgets
|
||||
assert len(bec_figure.widgets) == 1
|
||||
assert bec_figure["widget_4"] == w4
|
||||
assert "widget_4" in bec_figure._widgets
|
||||
assert len(bec_figure._widgets) == 1
|
||||
|
||||
|
||||
def test_remove_plots_by_coordinates_ints(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform_1", row=0, col=0)
|
||||
w2 = bec_figure.add_plot(widget_id="test_waveform_2", row=0, col=1)
|
||||
w1 = bec_figure.add_plot(row=0, col=0)
|
||||
w2 = bec_figure.add_plot(row=0, col=1)
|
||||
|
||||
bec_figure.remove(0, 0)
|
||||
assert "test_waveform_1" not in bec_figure.widgets
|
||||
assert "test_waveform_2" in bec_figure.widgets
|
||||
assert "widget_1" not in bec_figure._widgets
|
||||
assert "widget_2" in bec_figure._widgets
|
||||
assert bec_figure[0, 0] == w2
|
||||
assert len(bec_figure.widgets) == 1
|
||||
assert len(bec_figure._widgets) == 1
|
||||
|
||||
|
||||
def test_remove_plots_by_coordinates_tuple(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform_1", row=0, col=0)
|
||||
w2 = bec_figure.add_plot(widget_id="test_waveform_2", row=0, col=1)
|
||||
w1 = bec_figure.add_plot(row=0, col=0)
|
||||
w2 = bec_figure.add_plot(row=0, col=1)
|
||||
|
||||
bec_figure.remove(coordinates=(0, 0))
|
||||
assert "test_waveform_1" not in bec_figure.widgets
|
||||
assert "test_waveform_2" in bec_figure.widgets
|
||||
assert "widget_1" not in bec_figure._widgets
|
||||
assert "widget_2" in bec_figure._widgets
|
||||
assert bec_figure[0, 0] == w2
|
||||
assert len(bec_figure.widgets) == 1
|
||||
assert len(bec_figure._widgets) == 1
|
||||
|
||||
|
||||
def test_remove_plot_by_id_error(bec_figure):
|
||||
bec_figure.add_plot(widget_id="test_waveform_1", row=0, col=0)
|
||||
bec_figure.add_plot(row=0, col=0)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
bec_figure.remove(widget_id="non_existent_widget")
|
||||
@@ -164,7 +156,7 @@ def test_remove_plot_by_id_error(bec_figure):
|
||||
|
||||
|
||||
def test_remove_plot_by_coordinates_error(bec_figure):
|
||||
bec_figure.add_plot(widget_id="test_waveform_1", row=0, col=0)
|
||||
bec_figure.add_plot(row=0, col=0)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
bec_figure.remove(0, 1)
|
||||
@@ -172,7 +164,7 @@ def test_remove_plot_by_coordinates_error(bec_figure):
|
||||
|
||||
|
||||
def test_remove_plot_by_providing_nothing(bec_figure):
|
||||
bec_figure.add_plot(widget_id="test_waveform_1", row=0, col=0)
|
||||
bec_figure.add_plot(row=0, col=0)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
bec_figure.remove()
|
||||
@@ -192,10 +184,10 @@ def test_remove_plot_by_providing_nothing(bec_figure):
|
||||
|
||||
|
||||
def test_change_layout(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform_1", row=0, col=0)
|
||||
w2 = bec_figure.add_plot(widget_id="test_waveform_2", row=0, col=1)
|
||||
w3 = bec_figure.add_plot(widget_id="test_waveform_3", row=1, col=0)
|
||||
w4 = bec_figure.add_plot(widget_id="test_waveform_4", row=1, col=1)
|
||||
w1 = bec_figure.add_plot(row=0, col=0)
|
||||
w2 = bec_figure.add_plot(row=0, col=1)
|
||||
w3 = bec_figure.add_plot(row=1, col=0)
|
||||
w4 = bec_figure.add_plot(row=1, col=1)
|
||||
|
||||
bec_figure.change_layout(max_columns=1)
|
||||
|
||||
@@ -215,12 +207,12 @@ def test_change_layout(bec_figure):
|
||||
|
||||
|
||||
def test_clear_all(bec_figure):
|
||||
bec_figure.add_plot(widget_id="test_waveform_1", row=0, col=0)
|
||||
bec_figure.add_plot(widget_id="test_waveform_2", row=0, col=1)
|
||||
bec_figure.add_plot(widget_id="test_waveform_3", row=1, col=0)
|
||||
bec_figure.add_plot(widget_id="test_waveform_4", row=1, col=1)
|
||||
bec_figure.add_plot(row=0, col=0)
|
||||
bec_figure.add_plot(row=0, col=1)
|
||||
bec_figure.add_plot(row=1, col=0)
|
||||
bec_figure.add_plot(row=1, col=1)
|
||||
|
||||
bec_figure.clear_all()
|
||||
|
||||
assert len(bec_figure.widgets) == 0
|
||||
assert len(bec_figure._widgets) == 0
|
||||
assert np.shape(bec_figure.grid) == (0,)
|
||||
|
||||
@@ -10,7 +10,7 @@ from .test_bec_figure import bec_figure
|
||||
|
||||
|
||||
def test_adding_curve_to_waveform(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
|
||||
# adding curve which is in bec - only names
|
||||
c1 = w1.add_curve_scan(x_name="samx", y_name="bpm4i")
|
||||
@@ -38,7 +38,7 @@ def test_adding_curve_to_waveform(bec_figure):
|
||||
|
||||
|
||||
def test_adding_curve_with_same_id(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
c1 = w1.add_curve_scan(x_name="samx", y_name="bpm4i", gui_id="test_curve")
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@@ -101,7 +101,7 @@ def test_create_waveform1D_by_config(bec_figure):
|
||||
},
|
||||
}
|
||||
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform", config=w1_config_input)
|
||||
w1 = bec_figure.add_plot(config=w1_config_input)
|
||||
|
||||
w1_config_output = w1.get_config()
|
||||
|
||||
@@ -111,7 +111,7 @@ def test_create_waveform1D_by_config(bec_figure):
|
||||
|
||||
|
||||
def test_change_gui_id(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
c1 = w1.add_curve_scan(x_name="samx", y_name="bpm4i")
|
||||
w1.change_gui_id("new_id")
|
||||
|
||||
@@ -120,12 +120,12 @@ def test_change_gui_id(bec_figure):
|
||||
|
||||
|
||||
def test_getting_curve(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
c1 = w1.add_curve_scan(x_name="samx", y_name="bpm4i", gui_id="test_curve")
|
||||
c1_expected_config = CurveConfig(
|
||||
widget_class="BECCurve",
|
||||
gui_id="test_curve",
|
||||
parent_id="test_waveform",
|
||||
parent_id="widget_1",
|
||||
label="bpm4i-bpm4i",
|
||||
color="#cc4778",
|
||||
symbol="o",
|
||||
@@ -142,7 +142,7 @@ def test_getting_curve(bec_figure):
|
||||
)
|
||||
|
||||
assert w1.curves[0].config == c1_expected_config
|
||||
assert w1.curves_data["scan_segment"]["bpm4i-bpm4i"].config == c1_expected_config
|
||||
assert w1._curves_data["scan_segment"]["bpm4i-bpm4i"].config == c1_expected_config
|
||||
assert w1.get_curve(0).config == c1_expected_config
|
||||
assert w1.get_curve("bpm4i-bpm4i").config == c1_expected_config
|
||||
assert c1.get_config(False) == c1_expected_config
|
||||
@@ -150,7 +150,7 @@ def test_getting_curve(bec_figure):
|
||||
|
||||
|
||||
def test_getting_curve_errors(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
c1 = w1.add_curve_scan(x_name="samx", y_name="bpm4i", gui_id="test_curve")
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
@@ -167,18 +167,18 @@ def test_getting_curve_errors(bec_figure):
|
||||
|
||||
|
||||
def test_add_curve(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
|
||||
c1 = w1.add_curve_scan(x_name="samx", y_name="bpm4i")
|
||||
|
||||
assert len(w1.curves) == 1
|
||||
assert w1.curves_data["scan_segment"] == {"bpm4i-bpm4i": c1}
|
||||
assert w1._curves_data["scan_segment"] == {"bpm4i-bpm4i": c1}
|
||||
assert c1.config.label == "bpm4i-bpm4i"
|
||||
assert c1.config.source == "scan_segment"
|
||||
|
||||
|
||||
def test_remove_curve(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
|
||||
w1.add_curve_scan(x_name="samx", y_name="bpm4i")
|
||||
w1.add_curve_scan(x_name="samx", y_name="bpm3a")
|
||||
@@ -186,7 +186,7 @@ def test_remove_curve(bec_figure):
|
||||
w1.remove_curve("bpm3a-bpm3a")
|
||||
|
||||
assert len(w1.plot_item.curves) == 0
|
||||
assert w1.curves_data["scan_segment"] == {}
|
||||
assert w1._curves_data["scan_segment"] == {}
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
w1.remove_curve(1.2)
|
||||
@@ -196,7 +196,7 @@ def test_remove_curve(bec_figure):
|
||||
|
||||
|
||||
def test_change_curve_appearance_methods(bec_figure, qtbot):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
|
||||
c1 = w1.add_curve_scan(x_name="samx", y_name="bpm4i")
|
||||
|
||||
@@ -223,7 +223,7 @@ def test_change_curve_appearance_methods(bec_figure, qtbot):
|
||||
|
||||
|
||||
def test_change_curve_appearance_args(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
|
||||
c1 = w1.add_curve_scan(x_name="samx", y_name="bpm4i")
|
||||
|
||||
@@ -251,7 +251,7 @@ def test_change_curve_appearance_args(bec_figure):
|
||||
|
||||
|
||||
def test_set_custom_curve_data(bec_figure, qtbot):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
|
||||
c1 = w1.add_curve_custom(
|
||||
x=[1, 2, 3],
|
||||
@@ -287,7 +287,7 @@ def test_set_custom_curve_data(bec_figure, qtbot):
|
||||
|
||||
|
||||
def test_get_all_data(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
|
||||
c1 = w1.add_curve_custom(
|
||||
x=[1, 2, 3],
|
||||
@@ -322,7 +322,7 @@ def test_get_all_data(bec_figure):
|
||||
|
||||
|
||||
def test_curve_add_by_config(bec_figure):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
|
||||
c1_config_input = {
|
||||
"widget_class": "BECCurve",
|
||||
@@ -353,7 +353,7 @@ def test_curve_add_by_config(bec_figure):
|
||||
|
||||
|
||||
def test_scan_update(bec_figure, qtbot):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform")
|
||||
w1 = bec_figure.add_plot()
|
||||
|
||||
c1 = w1.add_curve_scan(x_name="samx", y_name="bpm4i")
|
||||
|
||||
@@ -387,7 +387,7 @@ def test_scan_update(bec_figure, qtbot):
|
||||
|
||||
|
||||
def test_scan_history_with_val_access(bec_figure, qtbot):
|
||||
w1 = bec_figure.add_plot(widget_id="test_waveform_history_val")
|
||||
w1 = bec_figure.add_plot()
|
||||
|
||||
c1 = w1.add_curve_scan(x_name="samx", y_name="bpm4i")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user