bec-widgets 0.72.1__py3-none-any.whl → 0.73.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- CHANGELOG.md +16 -16
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +26 -1
- bec_widgets/utils/bec_designer.py +50 -1
- bec_widgets/widgets/figure/plots/image/image.py +31 -1
- bec_widgets/widgets/figure/plots/image/image_item.py +44 -5
- bec_widgets/widgets/figure/plots/image/image_processor.py +30 -0
- {bec_widgets-0.72.1.dist-info → bec_widgets-0.73.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.72.1.dist-info → bec_widgets-0.73.0.dist-info}/RECORD +14 -13
- pyproject.toml +1 -1
- tests/unit_tests/test_bec_image.py +65 -0
- {bec_widgets-0.72.1.dist-info → bec_widgets-0.73.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.72.1.dist-info → bec_widgets-0.73.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-0.72.1.dist-info → bec_widgets-0.73.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## v0.73.0 (2024-06-25)
|
4
|
+
|
5
|
+
### Feature
|
6
|
+
|
7
|
+
* feat: add new default scaling of image_item ([`df812ea`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/df812eaad5989f2930dde41d87491868505af946))
|
8
|
+
|
9
|
+
### Test
|
10
|
+
|
11
|
+
* test: add test for imageitem ([`88ecd05`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/88ecd05b95974938ef1efff40e81854baf004cb4))
|
12
|
+
|
13
|
+
## v0.72.2 (2024-06-25)
|
14
|
+
|
15
|
+
### Fix
|
16
|
+
|
17
|
+
* fix(designer): fixed designer for pyenv and venv; closes #237 ([`e631fc1`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e631fc15d8707b73d58cb64316e115a7e43961ea))
|
18
|
+
|
3
19
|
## v0.72.1 (2024-06-24)
|
4
20
|
|
5
21
|
### Fix
|
@@ -137,19 +153,3 @@ in their parent process ([`ce37416`](https://gitlab.psi.ch/bec/bec_widgets/-/com
|
|
137
153
|
### Test
|
138
154
|
|
139
155
|
* test: add test suite for bec_status_box and status_item ([`5d4ca81`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/5d4ca816cdedec4c88aba9eb326f85392504ea1c))
|
140
|
-
|
141
|
-
### Unknown
|
142
|
-
|
143
|
-
* Update file requirements.txt ([`505a5ec`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/505a5ec8334ff4422913b3a7b79d39bcb42ad535))
|
144
|
-
|
145
|
-
## v0.66.1 (2024-06-20)
|
146
|
-
|
147
|
-
### Fix
|
148
|
-
|
149
|
-
* fix: fixed shutdown for pyside ([`2718bc6`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/2718bc624731301756df524d0d5beef6cb1c1430))
|
150
|
-
|
151
|
-
## v0.66.0 (2024-06-20)
|
152
|
-
|
153
|
-
### Feature
|
154
|
-
|
155
|
-
* feat(rpc): discover widgets automatically ([`ef25f56`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ef25f5638032f931ceb292540ada618508bb2aed))
|
PKG-INFO
CHANGED
bec_widgets/cli/client.py
CHANGED
@@ -711,6 +711,7 @@ class BECImageItem(RPCBase):
|
|
711
711
|
- log
|
712
712
|
- rot
|
713
713
|
- transpose
|
714
|
+
- autorange_mode
|
714
715
|
"""
|
715
716
|
|
716
717
|
@rpc_call
|
@@ -767,6 +768,15 @@ class BECImageItem(RPCBase):
|
|
767
768
|
autorange(bool): Whether to autorange the color bar.
|
768
769
|
"""
|
769
770
|
|
771
|
+
@rpc_call
|
772
|
+
def set_autorange_mode(self, mode: "Literal['max', 'mean']" = "mean"):
|
773
|
+
"""
|
774
|
+
Set the autorange mode to scale the vrange of the color bar. Choose between min/max or mean +/- std.
|
775
|
+
|
776
|
+
Args:
|
777
|
+
mode(Literal["max","mean"]): Max for min/max or mean for mean +/- std.
|
778
|
+
"""
|
779
|
+
|
770
780
|
@rpc_call
|
771
781
|
def set_color_map(self, cmap: "str" = "magma"):
|
772
782
|
"""
|
@@ -796,7 +806,11 @@ class BECImageItem(RPCBase):
|
|
796
806
|
|
797
807
|
@rpc_call
|
798
808
|
def set_vrange(
|
799
|
-
self,
|
809
|
+
self,
|
810
|
+
vmin: "float" = None,
|
811
|
+
vmax: "float" = None,
|
812
|
+
vrange: "tuple[float, float]" = None,
|
813
|
+
change_autorange: "bool" = True,
|
800
814
|
):
|
801
815
|
"""
|
802
816
|
Set the range of the color bar.
|
@@ -931,6 +945,17 @@ class BECImageShow(RPCBase):
|
|
931
945
|
name(str): The name of the image. If None, apply to all images.
|
932
946
|
"""
|
933
947
|
|
948
|
+
@rpc_call
|
949
|
+
def set_autorange_mode(self, mode: "Literal['max', 'mean']", name: "str" = None):
|
950
|
+
"""
|
951
|
+
Set the autoscale mode of the image, that decides how the vrange of the color bar is scaled.
|
952
|
+
Choose betwen 'max' -> min/max of the data, 'mean' -> mean +/- fudge_factor*std of the data (fudge_factor~2).
|
953
|
+
|
954
|
+
Args:
|
955
|
+
mode(str): The autoscale mode of the image.
|
956
|
+
name(str): The name of the image. If None, apply to all images.
|
957
|
+
"""
|
958
|
+
|
934
959
|
@rpc_call
|
935
960
|
def set_monitor(self, monitor: "str", name: "str" = None):
|
936
961
|
"""
|
@@ -1,4 +1,7 @@
|
|
1
|
+
import importlib.metadata
|
2
|
+
import json
|
1
3
|
import os
|
4
|
+
import site
|
2
5
|
import sys
|
3
6
|
import sysconfig
|
4
7
|
from pathlib import Path
|
@@ -9,15 +12,55 @@ if PYSIDE6:
|
|
9
12
|
from PySide6.scripts.pyside_tool import (
|
10
13
|
_extend_path_var,
|
11
14
|
init_virtual_env,
|
15
|
+
qt_tool_wrapper,
|
12
16
|
is_pyenv_python,
|
13
17
|
is_virtual_env,
|
14
|
-
qt_tool_wrapper,
|
15
18
|
ui_tool_binary,
|
16
19
|
)
|
17
20
|
|
18
21
|
import bec_widgets
|
19
22
|
|
20
23
|
|
24
|
+
def list_editable_packages() -> list[tuple[str, str]]:
|
25
|
+
"""
|
26
|
+
List all editable packages in the environment.
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
list[tuple[str, str]]: A list of tuples containing the package name and the path to the package.
|
30
|
+
"""
|
31
|
+
|
32
|
+
editable_packages = set()
|
33
|
+
|
34
|
+
# Get site-packages directories
|
35
|
+
site_packages = site.getsitepackages()
|
36
|
+
if hasattr(site, "getusersitepackages"):
|
37
|
+
site_packages.append(site.getusersitepackages())
|
38
|
+
|
39
|
+
for dist in importlib.metadata.distributions():
|
40
|
+
location = dist.locate_file("").resolve()
|
41
|
+
is_editable = all(not str(location).startswith(site_pkg) for site_pkg in site_packages)
|
42
|
+
|
43
|
+
if is_editable:
|
44
|
+
editable_packages.add(str(location))
|
45
|
+
|
46
|
+
for packages in site_packages:
|
47
|
+
# all dist-info directories in site-packages that contain a direct_url.json file
|
48
|
+
dist_info_dirs = Path(packages).rglob("*.dist-info")
|
49
|
+
for dist_info_dir in dist_info_dirs:
|
50
|
+
direct_url = dist_info_dir / "direct_url.json"
|
51
|
+
if not direct_url.exists():
|
52
|
+
continue
|
53
|
+
# load the json file and get the path to the package
|
54
|
+
with open(direct_url, "r", encoding="utf-8") as f:
|
55
|
+
data = json.load(f)
|
56
|
+
path = data.get("url", "")
|
57
|
+
if path.startswith("file://"):
|
58
|
+
path = path[7:]
|
59
|
+
editable_packages.add(path)
|
60
|
+
|
61
|
+
return editable_packages
|
62
|
+
|
63
|
+
|
21
64
|
def patch_designer(): # pragma: no cover
|
22
65
|
if not PYSIDE6:
|
23
66
|
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
|
@@ -40,6 +83,12 @@ def patch_designer(): # pragma: no cover
|
|
40
83
|
library_name = f"libpython{major_version}.{minor_version}.dylib"
|
41
84
|
lib_path = str(Path(sysconfig.get_config_var("LIBDIR")) / library_name)
|
42
85
|
os.environ["DYLD_INSERT_LIBRARIES"] = lib_path
|
86
|
+
|
87
|
+
if is_pyenv_python() or is_virtual_env():
|
88
|
+
# append all editable packages to the PYTHONPATH
|
89
|
+
editable_packages = list_editable_packages()
|
90
|
+
for pckg in editable_packages:
|
91
|
+
_extend_path_var("PYTHONPATH", pckg, True)
|
43
92
|
elif sys.platform == "win32":
|
44
93
|
if is_virtual_env():
|
45
94
|
_extend_path_var("PATH", os.fspath(Path(sys._base_executable).parent), True)
|
@@ -12,7 +12,11 @@ from qtpy.QtWidgets import QWidget
|
|
12
12
|
|
13
13
|
from bec_widgets.utils import EntryValidator
|
14
14
|
from bec_widgets.widgets.figure.plots.image.image_item import BECImageItem, ImageItemConfig
|
15
|
-
from bec_widgets.widgets.figure.plots.image.image_processor import
|
15
|
+
from bec_widgets.widgets.figure.plots.image.image_processor import (
|
16
|
+
ImageProcessor,
|
17
|
+
ImageStats,
|
18
|
+
ProcessorWorker,
|
19
|
+
)
|
16
20
|
from bec_widgets.widgets.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
17
21
|
|
18
22
|
|
@@ -35,6 +39,7 @@ class BECImageShow(BECPlotBase):
|
|
35
39
|
"set_vrange",
|
36
40
|
"set_color_map",
|
37
41
|
"set_autorange",
|
42
|
+
"set_autorange_mode",
|
38
43
|
"set_monitor",
|
39
44
|
"set_processing",
|
40
45
|
"set_image_properties",
|
@@ -86,6 +91,7 @@ class BECImageShow(BECPlotBase):
|
|
86
91
|
# Connect signals and slots
|
87
92
|
thread.started.connect(lambda: worker.process_image(device, image))
|
88
93
|
worker.processed.connect(self.update_image)
|
94
|
+
worker.stats.connect(self.update_vrange)
|
89
95
|
worker.finished.connect(thread.quit)
|
90
96
|
worker.finished.connect(thread.wait)
|
91
97
|
worker.finished.connect(worker.deleteLater)
|
@@ -341,6 +347,17 @@ class BECImageShow(BECPlotBase):
|
|
341
347
|
"""
|
342
348
|
self.apply_setting_to_images("set_autorange", args=[enable], kwargs={}, image_id=name)
|
343
349
|
|
350
|
+
def set_autorange_mode(self, mode: Literal["max", "mean"], name: str = None):
|
351
|
+
"""
|
352
|
+
Set the autoscale mode of the image, that decides how the vrange of the color bar is scaled.
|
353
|
+
Choose betwen 'max' -> min/max of the data, 'mean' -> mean +/- fudge_factor*std of the data (fudge_factor~2).
|
354
|
+
|
355
|
+
Args:
|
356
|
+
mode(str): The autoscale mode of the image.
|
357
|
+
name(str): The name of the image. If None, apply to all images.
|
358
|
+
"""
|
359
|
+
self.apply_setting_to_images("set_autorange_mode", args=[mode], kwargs={}, image_id=name)
|
360
|
+
|
344
361
|
def set_monitor(self, monitor: str, name: str = None):
|
345
362
|
"""
|
346
363
|
Set the monitor of the image.
|
@@ -461,6 +478,7 @@ class BECImageShow(BECPlotBase):
|
|
461
478
|
else:
|
462
479
|
data = self.processor.process_image(data)
|
463
480
|
self.update_image(device, data)
|
481
|
+
self.update_vrange(device, self.processor.config.stats)
|
464
482
|
|
465
483
|
@pyqtSlot(str, np.ndarray)
|
466
484
|
def update_image(self, device: str, data: np.ndarray):
|
@@ -474,6 +492,18 @@ class BECImageShow(BECPlotBase):
|
|
474
492
|
image_to_update = self._images["device_monitor"][device]
|
475
493
|
image_to_update.updateImage(data, autoLevels=image_to_update.config.autorange)
|
476
494
|
|
495
|
+
@pyqtSlot(str, ImageStats)
|
496
|
+
def update_vrange(self, device: str, stats: ImageStats):
|
497
|
+
"""
|
498
|
+
Update the scaling of the image.
|
499
|
+
|
500
|
+
Args:
|
501
|
+
stats(ImageStats): The statistics of the image.
|
502
|
+
"""
|
503
|
+
image_to_update = self._images["device_monitor"][device]
|
504
|
+
if image_to_update.config.autorange:
|
505
|
+
image_to_update.auto_update_vrange(stats)
|
506
|
+
|
477
507
|
def _connect_device_monitor(self, monitor: str):
|
478
508
|
"""
|
479
509
|
Connect to the device monitor.
|
@@ -7,7 +7,7 @@ import pyqtgraph as pg
|
|
7
7
|
from pydantic import Field
|
8
8
|
|
9
9
|
from bec_widgets.utils import BECConnector, ConnectionConfig
|
10
|
-
from bec_widgets.widgets.figure.plots.image.image_processor import ProcessingConfig
|
10
|
+
from bec_widgets.widgets.figure.plots.image.image_processor import ImageStats, ProcessingConfig
|
11
11
|
|
12
12
|
if TYPE_CHECKING:
|
13
13
|
from bec_widgets.widgets.figure.plots.image.image import BECImageShow
|
@@ -20,13 +20,16 @@ class ImageItemConfig(ConnectionConfig):
|
|
20
20
|
color_map: Optional[str] = Field("magma", description="The color map of the image.")
|
21
21
|
downsample: Optional[bool] = Field(True, description="Whether to downsample the image.")
|
22
22
|
opacity: Optional[float] = Field(1.0, description="The opacity of the image.")
|
23
|
-
vrange: Optional[tuple[
|
23
|
+
vrange: Optional[tuple[float, float]] = Field(
|
24
24
|
None, description="The range of the color bar. If None, the range is automatically set."
|
25
25
|
)
|
26
26
|
color_bar: Optional[Literal["simple", "full"]] = Field(
|
27
27
|
"simple", description="The type of the color bar."
|
28
28
|
)
|
29
29
|
autorange: Optional[bool] = Field(True, description="Whether to autorange the color bar.")
|
30
|
+
autorange_mode: Optional[Literal["max", "mean"]] = Field(
|
31
|
+
"mean", description="Whether to use the mean of the image for autoscaling."
|
32
|
+
)
|
30
33
|
processing: ProcessingConfig = Field(
|
31
34
|
default_factory=ProcessingConfig, description="The post processing of the image."
|
32
35
|
)
|
@@ -43,6 +46,7 @@ class BECImageItem(BECConnector, pg.ImageItem):
|
|
43
46
|
"set_transpose",
|
44
47
|
"set_opacity",
|
45
48
|
"set_autorange",
|
49
|
+
"set_autorange_mode",
|
46
50
|
"set_color_map",
|
47
51
|
"set_auto_downsample",
|
48
52
|
"set_monitor",
|
@@ -101,6 +105,7 @@ class BECImageItem(BECConnector, pg.ImageItem):
|
|
101
105
|
- log
|
102
106
|
- rot
|
103
107
|
- transpose
|
108
|
+
- autorange_mode
|
104
109
|
"""
|
105
110
|
method_map = {
|
106
111
|
"downsample": self.set_auto_downsample,
|
@@ -112,6 +117,7 @@ class BECImageItem(BECConnector, pg.ImageItem):
|
|
112
117
|
"log": self.set_log,
|
113
118
|
"rot": self.set_rotation,
|
114
119
|
"transpose": self.set_transpose,
|
120
|
+
"autorange_mode": self.set_autorange_mode,
|
115
121
|
}
|
116
122
|
for key, value in kwargs.items():
|
117
123
|
if key in method_map:
|
@@ -175,9 +181,18 @@ class BECImageItem(BECConnector, pg.ImageItem):
|
|
175
181
|
autorange(bool): Whether to autorange the color bar.
|
176
182
|
"""
|
177
183
|
self.config.autorange = autorange
|
178
|
-
if self.color_bar
|
184
|
+
if self.color_bar and autorange:
|
179
185
|
self.color_bar.autoHistogramRange()
|
180
186
|
|
187
|
+
def set_autorange_mode(self, mode: Literal["max", "mean"] = "mean"):
|
188
|
+
"""
|
189
|
+
Set the autorange mode to scale the vrange of the color bar. Choose between min/max or mean +/- std.
|
190
|
+
|
191
|
+
Args:
|
192
|
+
mode(Literal["max","mean"]): Max for min/max or mean for mean +/- std.
|
193
|
+
"""
|
194
|
+
self.config.autorange_mode = mode
|
195
|
+
|
181
196
|
def set_color_map(self, cmap: str = "magma"):
|
182
197
|
"""
|
183
198
|
Set the color map of the image.
|
@@ -212,7 +227,29 @@ class BECImageItem(BECConnector, pg.ImageItem):
|
|
212
227
|
"""
|
213
228
|
self.config.monitor = monitor
|
214
229
|
|
215
|
-
def
|
230
|
+
def auto_update_vrange(self, stats: ImageStats) -> None:
|
231
|
+
"""Auto update of the vrange base on the stats of the image.
|
232
|
+
|
233
|
+
Args:
|
234
|
+
stats(ImageStats): The stats of the image.
|
235
|
+
"""
|
236
|
+
fumble_factor = 2
|
237
|
+
if self.config.autorange_mode == "mean":
|
238
|
+
vmin = max(stats.mean - fumble_factor * stats.std, 0)
|
239
|
+
vmax = stats.mean + fumble_factor * stats.std
|
240
|
+
self.set_vrange(vmin, vmax, change_autorange=False)
|
241
|
+
return
|
242
|
+
if self.config.autorange_mode == "max":
|
243
|
+
self.set_vrange(max(stats.minimum, 0), stats.maximum, change_autorange=False)
|
244
|
+
return
|
245
|
+
|
246
|
+
def set_vrange(
|
247
|
+
self,
|
248
|
+
vmin: float = None,
|
249
|
+
vmax: float = None,
|
250
|
+
vrange: tuple[float, float] = None,
|
251
|
+
change_autorange: bool = True,
|
252
|
+
):
|
216
253
|
"""
|
217
254
|
Set the range of the color bar.
|
218
255
|
|
@@ -224,11 +261,13 @@ class BECImageItem(BECConnector, pg.ImageItem):
|
|
224
261
|
vmin, vmax = vrange
|
225
262
|
self.setLevels([vmin, vmax])
|
226
263
|
self.config.vrange = (vmin, vmax)
|
227
|
-
|
264
|
+
if change_autorange:
|
265
|
+
self.config.autorange = False
|
228
266
|
if self.color_bar is not None:
|
229
267
|
if self.config.color_bar == "simple":
|
230
268
|
self.color_bar.setLevels(low=vmin, high=vmax)
|
231
269
|
elif self.config.color_bar == "full":
|
270
|
+
# pylint: disable=unexpected-keyword-arg
|
232
271
|
self.color_bar.setLevels(min=vmin, max=vmax)
|
233
272
|
self.color_bar.setHistogramRange(vmin - 0.1 * vmin, vmax + 0.1 * vmax)
|
234
273
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
from dataclasses import dataclass
|
3
4
|
from typing import Optional
|
4
5
|
|
5
6
|
import numpy as np
|
@@ -7,6 +8,16 @@ from pydantic import BaseModel, Field
|
|
7
8
|
from qtpy.QtCore import QObject, Signal, Slot
|
8
9
|
|
9
10
|
|
11
|
+
@dataclass
|
12
|
+
class ImageStats:
|
13
|
+
"""Container to store stats of an image."""
|
14
|
+
|
15
|
+
maximum: float
|
16
|
+
minimum: float
|
17
|
+
mean: float
|
18
|
+
std: float
|
19
|
+
|
20
|
+
|
10
21
|
class ProcessingConfig(BaseModel):
|
11
22
|
fft: Optional[bool] = Field(False, description="Whether to perform FFT on the monitor data.")
|
12
23
|
log: Optional[bool] = Field(False, description="Whether to perform log on the monitor data.")
|
@@ -20,6 +31,10 @@ class ProcessingConfig(BaseModel):
|
|
20
31
|
None, description="The rotation angle of the monitor data before displaying."
|
21
32
|
)
|
22
33
|
model_config: dict = {"validate_assignment": True}
|
34
|
+
stats: ImageStats = Field(
|
35
|
+
ImageStats(maximum=0, minimum=0, mean=0, std=0),
|
36
|
+
description="The statistics of the image data.",
|
37
|
+
)
|
23
38
|
|
24
39
|
|
25
40
|
class ImageProcessor:
|
@@ -97,6 +112,18 @@ class ImageProcessor:
|
|
97
112
|
# def center_of_mass(self, data: np.ndarray) -> tuple: # TODO check functionality
|
98
113
|
# return np.unravel_index(np.argmax(data), data.shape)
|
99
114
|
|
115
|
+
def update_image_stats(self, data: np.ndarray) -> None:
|
116
|
+
"""Get the statistics of the image data.
|
117
|
+
|
118
|
+
Args:
|
119
|
+
data(np.ndarray): The image data.
|
120
|
+
|
121
|
+
"""
|
122
|
+
self.config.stats.maximum = np.max(data)
|
123
|
+
self.config.stats.minimum = np.min(data)
|
124
|
+
self.config.stats.mean = np.mean(data)
|
125
|
+
self.config.stats.std = np.std(data)
|
126
|
+
|
100
127
|
def process_image(self, data: np.ndarray) -> np.ndarray:
|
101
128
|
"""
|
102
129
|
Process the data according to the configuration.
|
@@ -115,6 +142,7 @@ class ImageProcessor:
|
|
115
142
|
data = self.transpose(data)
|
116
143
|
if self.config.log:
|
117
144
|
data = self.log(data)
|
145
|
+
self.update_image_stats(data)
|
118
146
|
return data
|
119
147
|
|
120
148
|
|
@@ -124,6 +152,7 @@ class ProcessorWorker(QObject):
|
|
124
152
|
"""
|
125
153
|
|
126
154
|
processed = Signal(str, np.ndarray)
|
155
|
+
stats = Signal(str, ImageStats)
|
127
156
|
stopRequested = Signal()
|
128
157
|
finished = Signal()
|
129
158
|
|
@@ -147,6 +176,7 @@ class ProcessorWorker(QObject):
|
|
147
176
|
self._isRunning = False
|
148
177
|
if not self._isRunning:
|
149
178
|
self.processed.emit(device, processed_image)
|
179
|
+
self.stats.emit(self.processor.config.stats)
|
150
180
|
self.finished.emit()
|
151
181
|
|
152
182
|
def stop(self):
|
@@ -2,11 +2,11 @@
|
|
2
2
|
.gitlab-ci.yml,sha256=RnYDz4zKXjlqltTryprlB1s5vLXxI2-seW-Vb70NNF0,8162
|
3
3
|
.pylintrc,sha256=OstrgmEyP0smNFBKoIN5_26-UmNZgMHnbjvAWX0UrLs,18535
|
4
4
|
.readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
|
5
|
-
CHANGELOG.md,sha256=
|
5
|
+
CHANGELOG.md,sha256=ASQWccB46kB7TWP7qISIQCOrxP2-NhIzE87fyan10xs,7466
|
6
6
|
LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
7
|
-
PKG-INFO,sha256=
|
7
|
+
PKG-INFO,sha256=naAwZRYeVxYn6qmLeqPUjRxSzQ07BCmfxE55V5m3lYs,1302
|
8
8
|
README.md,sha256=y4jB6wvArS7N8_iTbKWnSM_oRAqLA2GqgzUR-FMh5sU,2645
|
9
|
-
pyproject.toml,sha256=
|
9
|
+
pyproject.toml,sha256=CKV9AeTuofbkm8y4AWqXfsmgYhaT9PYRDftimPF1QGQ,2215
|
10
10
|
.git_hooks/pre-commit,sha256=n3RofIZHJl8zfJJIUomcMyYGFi_rwq4CC19z0snz3FI,286
|
11
11
|
.gitlab/issue_templates/bug_report_template.md,sha256=gAuyEwl7XlnebBrkiJ9AqffSNOywmr8vygUFWKTuQeI,386
|
12
12
|
.gitlab/issue_templates/documentation_update_template.md,sha256=FHLdb3TS_D9aL4CYZCjyXSulbaW5mrN2CmwTaeLPbNw,860
|
@@ -17,7 +17,7 @@ bec_widgets/assets/bec_widgets_icon.png,sha256=K8dgGwIjalDh9PRHUsSQBqgdX7a00nM3i
|
|
17
17
|
bec_widgets/assets/terminal_icon.png,sha256=bJl7Tft4Fi2uxvuXI8o14uMHnI9eAWKSU2uftXCH9ws,3889
|
18
18
|
bec_widgets/cli/__init__.py,sha256=d0Q6Fn44e7wFfLabDOBxpcJ1DPKWlFunGYDUBmO-4hA,22
|
19
19
|
bec_widgets/cli/auto_updates.py,sha256=DyBV3HnjMSH-cvVkYNcDiYKVf0Xut4Qy2qGQqkW47Bw,4833
|
20
|
-
bec_widgets/cli/client.py,sha256=
|
20
|
+
bec_widgets/cli/client.py,sha256=faWHDMG9mwD1fB5ptuZ-_QWka0Lrv9iJ94QupwmqCmY,59358
|
21
21
|
bec_widgets/cli/client_utils.py,sha256=tJwENsYTdWd4BKuoQ8fEkfp2JTuJqLJUImzRekd-Kos,12376
|
22
22
|
bec_widgets/cli/generate_cli.py,sha256=InKBVYM7DRfAVLNJhRJbWWSSPBQBHI8Ek6v7NCsK0ME,4997
|
23
23
|
bec_widgets/cli/rpc_register.py,sha256=QxXUZu5XNg00Yf5O3UHWOXg3-f_pzKjjoZYMOa-MOJc,2216
|
@@ -39,7 +39,7 @@ bec_widgets/examples/plugin_example_pyside/tictactoeplugin.py,sha256=BBt3MD8oDLU
|
|
39
39
|
bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py,sha256=LNwplI6deUdKY6FOhUuWBanotxk9asF2G-6k7lFfA8Y,2301
|
40
40
|
bec_widgets/utils/__init__.py,sha256=1930ji1Jj6dVuY81Wd2kYBhHYNV-2R0bN_L4o9zBj1U,533
|
41
41
|
bec_widgets/utils/bec_connector.py,sha256=3BNkb83HZDNL_fwbvMnG6FM28VTmlsndnc4z84E3v1w,7286
|
42
|
-
bec_widgets/utils/bec_designer.py,sha256=
|
42
|
+
bec_widgets/utils/bec_designer.py,sha256=2ay6c7dLozV06vsJcceoMRe78ePxQf_7pxnrEZjOrB8,4386
|
43
43
|
bec_widgets/utils/bec_dispatcher.py,sha256=yM9PG04O7ABhiA9Nzk38Rv9Qbjc5O93wi2xfSbOlOxc,6202
|
44
44
|
bec_widgets/utils/bec_table.py,sha256=nA2b8ukSeUfquFMAxGrUVOqdrzMoDYD6O_4EYbOG2zk,717
|
45
45
|
bec_widgets/utils/colors.py,sha256=GYSDe0ZxsJSwxvuy-yG2BH17qlf_Sjq8dhDcyp9IhBI,8532
|
@@ -83,9 +83,9 @@ bec_widgets/widgets/figure/figure.py,sha256=3bf1TyzIE8kVRDgjLqdlvCoE4wrozyfbeCWL
|
|
83
83
|
bec_widgets/widgets/figure/plots/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
84
84
|
bec_widgets/widgets/figure/plots/plot_base.py,sha256=QdDMMuWwTK0f6kNN8aAPBM7jah7TpUbYrMrRTdb2EZY,10419
|
85
85
|
bec_widgets/widgets/figure/plots/image/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
86
|
-
bec_widgets/widgets/figure/plots/image/image.py,sha256
|
87
|
-
bec_widgets/widgets/figure/plots/image/image_item.py,sha256=
|
88
|
-
bec_widgets/widgets/figure/plots/image/image_processor.py,sha256=
|
86
|
+
bec_widgets/widgets/figure/plots/image/image.py,sha256=1tZADgGQXDrFHS9CYsHD2r9KzszjHn9f2kuNqLfuwWI,20797
|
87
|
+
bec_widgets/widgets/figure/plots/image/image_item.py,sha256=Wnq6N6q4QwGHdZzZ_ibYzqoJk68FKU6WUCcYpINhZMU,10513
|
88
|
+
bec_widgets/widgets/figure/plots/image/image_processor.py,sha256=GeTtWjbldy6VejMwPGQgM-o3d6bmLglCjdoktu19xfA,5262
|
89
89
|
bec_widgets/widgets/figure/plots/motor_map/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
90
90
|
bec_widgets/widgets/figure/plots/motor_map/motor_map.py,sha256=Ff2WoNHxO_A3ggsbSd_AVUP1JeOWMuJs-0GLskxn-94,15267
|
91
91
|
bec_widgets/widgets/figure/plots/waveform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -182,6 +182,7 @@ tests/unit_tests/test_bec_connector.py,sha256=zGDfNHwLFZTbpyX6-yc7Pwzr2jWO_HGZ8T
|
|
182
182
|
tests/unit_tests/test_bec_dispatcher.py,sha256=rYPiRizHaswhGZw55IBMneDFxmPiCCLAZQBqjEkpdyY,3992
|
183
183
|
tests/unit_tests/test_bec_dock.py,sha256=BXKXpuyIYj-l6KSyhQtM_p3kRFCRECIoXLzvkcJZDlM,3611
|
184
184
|
tests/unit_tests/test_bec_figure.py,sha256=aEd2R8K6fU2ON8QvPemGWpql_LaaYLipRlvnjBY2qFA,8009
|
185
|
+
tests/unit_tests/test_bec_image.py,sha256=mjvcrHgOF_FCj6WbUyxvZH9HL63QGA5C0PNZ5dXYn50,2541
|
185
186
|
tests/unit_tests/test_bec_motor_map.py,sha256=AfD_9-x6VV3TPnkQgNfFYRndPHDsGx-a_YknFeDr6hc,4588
|
186
187
|
tests/unit_tests/test_bec_status_box.py,sha256=xR8c-hXFI9gKpNGhnnC5l_nzazfvPkWkhcAfJ77hxqY,4731
|
187
188
|
tests/unit_tests/test_client_utils.py,sha256=eViJ1Tz-HX9TkMvQH6W8cO-c3_1I8bUc4_Yen6LOc0E,830
|
@@ -210,8 +211,8 @@ tests/unit_tests/test_configs/config_device_no_entry.yaml,sha256=hdvue9KLc_kfNzG
|
|
210
211
|
tests/unit_tests/test_configs/config_scan.yaml,sha256=vo484BbWOjA_e-h6bTjSV9k7QaQHrlAvx-z8wtY-P4E,1915
|
211
212
|
tests/unit_tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
212
213
|
tests/unit_tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
|
213
|
-
bec_widgets-0.
|
214
|
-
bec_widgets-0.
|
215
|
-
bec_widgets-0.
|
216
|
-
bec_widgets-0.
|
217
|
-
bec_widgets-0.
|
214
|
+
bec_widgets-0.73.0.dist-info/METADATA,sha256=naAwZRYeVxYn6qmLeqPUjRxSzQ07BCmfxE55V5m3lYs,1302
|
215
|
+
bec_widgets-0.73.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
216
|
+
bec_widgets-0.73.0.dist-info/entry_points.txt,sha256=3otEkCdDB9LZJuBLzG4pFLK5Di0CVybN_12IsZrQ-58,166
|
217
|
+
bec_widgets-0.73.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
218
|
+
bec_widgets-0.73.0.dist-info/RECORD,,
|
pyproject.toml
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
# pylint: disable=missing-function-docstring, missing-module-docstring, unused-import
|
2
|
+
from unittest import mock
|
3
|
+
|
4
|
+
import numpy as np
|
5
|
+
import pytest
|
6
|
+
from bec_lib import messages
|
7
|
+
from qtpy.QtGui import QFontInfo
|
8
|
+
|
9
|
+
from .client_mocks import mocked_client
|
10
|
+
from .test_bec_figure import bec_figure
|
11
|
+
|
12
|
+
|
13
|
+
@pytest.fixture
|
14
|
+
def bec_image_show(bec_figure):
|
15
|
+
yield bec_figure.image("eiger")
|
16
|
+
|
17
|
+
|
18
|
+
def test_on_image_update(bec_image_show):
|
19
|
+
data = np.random.rand(100, 100)
|
20
|
+
msg = messages.DeviceMonitorMessage(
|
21
|
+
device="eiger", data=data, metadata={"scan_id": "12345"}
|
22
|
+
).model_dump()
|
23
|
+
bec_image_show.on_image_update(msg)
|
24
|
+
img = bec_image_show.images[0]
|
25
|
+
assert np.array_equal(img.get_data(), data)
|
26
|
+
|
27
|
+
|
28
|
+
def test_autorange_on_image_update(bec_image_show):
|
29
|
+
# Check if autorange mode "mean" works, should be default
|
30
|
+
data = np.random.rand(100, 100)
|
31
|
+
msg = messages.DeviceMonitorMessage(
|
32
|
+
device="eiger", data=data, metadata={"scan_id": "12345"}
|
33
|
+
).model_dump()
|
34
|
+
bec_image_show.on_image_update(msg)
|
35
|
+
img = bec_image_show.images[0]
|
36
|
+
assert np.array_equal(img.get_data(), data)
|
37
|
+
vmin = max(np.mean(data) - 2 * np.std(data), 0)
|
38
|
+
vmax = np.mean(data) + 2 * np.std(data)
|
39
|
+
assert np.isclose(img.color_bar.getLevels(), (vmin, vmax), rtol=(1e-5, 1e-5)).all()
|
40
|
+
# Test general update with autorange True, mode "max"
|
41
|
+
bec_image_show.set_autorange_mode("max")
|
42
|
+
bec_image_show.on_image_update(msg)
|
43
|
+
img = bec_image_show.images[0]
|
44
|
+
vmin = np.min(data)
|
45
|
+
vmax = np.max(data)
|
46
|
+
assert np.array_equal(img.get_data(), data)
|
47
|
+
assert np.isclose(img.color_bar.getLevels(), (vmin, vmax), rtol=(1e-5, 1e-5)).all()
|
48
|
+
# Change the input data, and switch to autorange False, colormap levels should stay untouched
|
49
|
+
data *= 100
|
50
|
+
msg = messages.DeviceMonitorMessage(
|
51
|
+
device="eiger", data=data, metadata={"scan_id": "12345"}
|
52
|
+
).model_dump()
|
53
|
+
bec_image_show.set_autorange(False)
|
54
|
+
bec_image_show.on_image_update(msg)
|
55
|
+
img = bec_image_show.images[0]
|
56
|
+
assert np.array_equal(img.get_data(), data)
|
57
|
+
assert np.isclose(img.color_bar.getLevels(), (vmin, vmax), rtol=(1e-3, 1e-3)).all()
|
58
|
+
# Reactivate autorange, should now scale the new data
|
59
|
+
bec_image_show.set_autorange(True)
|
60
|
+
bec_image_show.set_autorange_mode("mean")
|
61
|
+
bec_image_show.on_image_update(msg)
|
62
|
+
img = bec_image_show.images[0]
|
63
|
+
vmin = max(np.mean(data) - 2 * np.std(data), 0)
|
64
|
+
vmax = np.mean(data) + 2 * np.std(data)
|
65
|
+
assert np.isclose(img.color_bar.getLevels(), (vmin, vmax), rtol=(1e-5, 1e-5)).all()
|
File without changes
|
File without changes
|
File without changes
|