bec-widgets 0.72.2__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 CHANGED
@@ -1,5 +1,15 @@
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
+
3
13
  ## v0.72.2 (2024-06-25)
4
14
 
5
15
  ### Fix
@@ -143,13 +153,3 @@ in their parent process ([`ce37416`](https://gitlab.psi.ch/bec/bec_widgets/-/com
143
153
  ### Test
144
154
 
145
155
  * test: add test suite for bec_status_box and status_item ([`5d4ca81`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/5d4ca816cdedec4c88aba9eb326f85392504ea1c))
146
-
147
- ### Unknown
148
-
149
- * Update file requirements.txt ([`505a5ec`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/505a5ec8334ff4422913b3a7b79d39bcb42ad535))
150
-
151
- ## v0.66.1 (2024-06-20)
152
-
153
- ### Fix
154
-
155
- * fix: fixed shutdown for pyside ([`2718bc6`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/2718bc624731301756df524d0d5beef6cb1c1430))
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.72.2
3
+ Version: 0.73.0
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
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, vmin: "float" = None, vmax: "float" = None, vrange: "tuple[int, int]" = None
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
  """
@@ -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 ImageProcessor, ProcessorWorker
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[int, int]] = Field(
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 is not None:
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 set_vrange(self, vmin: float = None, vmax: float = None, vrange: tuple[int, int] = None):
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
- self.config.autorange = False
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):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.72.2
3
+ Version: 0.73.0
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
@@ -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=AaBdg_zGOP0iVHGQZJ_fTkb3RLAvQ-4uGoIoLvLJdxA,7452
5
+ CHANGELOG.md,sha256=ASQWccB46kB7TWP7qISIQCOrxP2-NhIzE87fyan10xs,7466
6
6
  LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
7
- PKG-INFO,sha256=mg37DoQMxkIsyA4lJXFyGNELbsvy76rdiBsAWETxMW8,1302
7
+ PKG-INFO,sha256=naAwZRYeVxYn6qmLeqPUjRxSzQ07BCmfxE55V5m3lYs,1302
8
8
  README.md,sha256=y4jB6wvArS7N8_iTbKWnSM_oRAqLA2GqgzUR-FMh5sU,2645
9
- pyproject.toml,sha256=dI3a7QcP6s6mxX5XGOExxFAIUVbVRrHwCJRM0KLVkxc,2215
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=IGtxHvLP6zkvzIEVloX9ygKRvrplNhXGmqQN-BnYlQE,58447
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
@@ -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=-rxCt1IXmS2XQu0dS0SSXAF8VaxacSmQ-_kDsFxbPm4,19653
87
- bec_widgets/widgets/figure/plots/image/image_item.py,sha256=1oytCY2IIgRbtS3GRrp9JV02KOif78O2-iaK0qYuHFU,9058
88
- bec_widgets/widgets/figure/plots/image/image_processor.py,sha256=TOnHbdq9rK5--L5JNshILLm_e5_LVwuQ2-MFV8JKL9I,4423
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.72.2.dist-info/METADATA,sha256=mg37DoQMxkIsyA4lJXFyGNELbsvy76rdiBsAWETxMW8,1302
214
- bec_widgets-0.72.2.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
215
- bec_widgets-0.72.2.dist-info/entry_points.txt,sha256=3otEkCdDB9LZJuBLzG4pFLK5Di0CVybN_12IsZrQ-58,166
216
- bec_widgets-0.72.2.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
217
- bec_widgets-0.72.2.dist-info/RECORD,,
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_widgets"
7
- version = "0.72.2"
7
+ version = "0.73.0"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [
@@ -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()