bec-widgets 0.53.3__py3-none-any.whl → 0.55.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 +24 -26
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +265 -13
- bec_widgets/cli/client_utils.py +0 -3
- bec_widgets/cli/generate_cli.py +10 -5
- bec_widgets/cli/rpc_wigdet_handler.py +2 -1
- bec_widgets/cli/server.py +5 -7
- bec_widgets/examples/jupyter_console/jupyter_console_window.py +11 -5
- bec_widgets/examples/motor_movement/motor_control_compilations.py +17 -16
- bec_widgets/widgets/__init__.py +1 -10
- bec_widgets/widgets/figure/figure.py +40 -23
- bec_widgets/widgets/figure/plots/__init__.py +0 -0
- bec_widgets/widgets/figure/plots/image/__init__.py +0 -0
- bec_widgets/widgets/{plots → figure/plots/image}/image.py +6 -416
- bec_widgets/widgets/figure/plots/image/image_item.py +277 -0
- bec_widgets/widgets/figure/plots/image/image_processor.py +152 -0
- bec_widgets/widgets/figure/plots/motor_map/__init__.py +0 -0
- bec_widgets/widgets/{plots → figure/plots/motor_map}/motor_map.py +2 -2
- bec_widgets/widgets/figure/plots/waveform/__init__.py +0 -0
- bec_widgets/widgets/{plots → figure/plots/waveform}/waveform.py +9 -222
- bec_widgets/widgets/figure/plots/waveform/waveform_curve.py +227 -0
- bec_widgets/widgets/motor_control/__init__.py +0 -7
- bec_widgets/widgets/motor_control/motor_control.py +2 -948
- bec_widgets/widgets/motor_control/motor_table/__init__.py +0 -0
- bec_widgets/widgets/motor_control/motor_table/motor_table.py +483 -0
- bec_widgets/widgets/motor_control/movement_absolute/__init__.py +0 -0
- bec_widgets/widgets/motor_control/movement_absolute/movement_absolute.py +157 -0
- bec_widgets/widgets/motor_control/movement_relative/__init__.py +0 -0
- bec_widgets/widgets/motor_control/movement_relative/movement_relative.py +227 -0
- bec_widgets/widgets/motor_control/selection/__init__.py +0 -0
- bec_widgets/widgets/motor_control/selection/selection.py +110 -0
- bec_widgets/widgets/spiral_progress_bar/__init__.py +1 -0
- bec_widgets/widgets/spiral_progress_bar/ring.py +184 -0
- bec_widgets/widgets/spiral_progress_bar/spiral_progress_bar.py +594 -0
- {bec_widgets-0.53.3.dist-info → bec_widgets-0.55.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.53.3.dist-info → bec_widgets-0.55.0.dist-info}/RECORD +56 -53
- docs/requirements.txt +1 -0
- pyproject.toml +1 -1
- tests/end-2-end/test_bec_dock_rpc_e2e.py +82 -1
- tests/end-2-end/test_bec_figure_rpc_e2e.py +4 -4
- tests/end-2-end/test_rpc_register_e2e.py +1 -1
- tests/unit_tests/test_bec_dock.py +1 -1
- tests/unit_tests/test_bec_figure.py +6 -4
- tests/unit_tests/test_bec_motor_map.py +2 -3
- tests/unit_tests/test_motor_control.py +6 -5
- tests/unit_tests/test_spiral_progress_bar.py +338 -0
- tests/unit_tests/test_waveform1d.py +13 -1
- bec_widgets/validation/__init__.py +0 -2
- bec_widgets/validation/monitor_config_validator.py +0 -258
- bec_widgets/widgets/monitor/__init__.py +0 -1
- bec_widgets/widgets/monitor/config_dialog.py +0 -574
- bec_widgets/widgets/monitor/config_dialog.ui +0 -210
- bec_widgets/widgets/monitor/example_configs/config_device.yaml +0 -60
- bec_widgets/widgets/monitor/example_configs/config_scans.yaml +0 -92
- bec_widgets/widgets/monitor/monitor.py +0 -845
- bec_widgets/widgets/monitor/tab_template.ui +0 -180
- bec_widgets/widgets/motor_map/__init__.py +0 -1
- bec_widgets/widgets/motor_map/motor_map.py +0 -594
- bec_widgets/widgets/plots/__init__.py +0 -4
- tests/unit_tests/test_bec_monitor.py +0 -220
- tests/unit_tests/test_config_dialog.py +0 -178
- tests/unit_tests/test_motor_map.py +0 -171
- tests/unit_tests/test_validator_errors.py +0 -110
- /bec_widgets/{cli → assets}/bec_widgets_icon.png +0 -0
- /bec_widgets/{examples/jupyter_console → assets}/terminal_icon.png +0 -0
- /bec_widgets/widgets/{plots → figure/plots}/plot_base.py +0 -0
- /bec_widgets/widgets/motor_control/{motor_control_table.ui → motor_table/motor_table.ui} +0 -0
- /bec_widgets/widgets/motor_control/{motor_control_absolute.ui → movement_absolute/movement_absolute.ui} +0 -0
- /bec_widgets/widgets/motor_control/{motor_control_relative.ui → movement_relative/movement_relative.ui} +0 -0
- /bec_widgets/widgets/motor_control/{motor_control_selection.ui → selection/selection.ui} +0 -0
- {bec_widgets-0.53.3.dist-info → bec_widgets-0.55.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.53.3.dist-info → bec_widgets-0.55.0.dist-info}/licenses/LICENSE +0 -0
@@ -11,18 +11,13 @@ import qdarktheme
|
|
11
11
|
from pydantic import Field
|
12
12
|
from qtpy.QtCore import Signal as pyqtSignal
|
13
13
|
from qtpy.QtWidgets import QWidget
|
14
|
+
from typeguard import typechecked
|
14
15
|
|
15
16
|
from bec_widgets.utils import BECConnector, ConnectionConfig, WidgetContainerUtils
|
16
|
-
from bec_widgets.widgets.plots import
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
BECWaveform,
|
21
|
-
SubplotConfig,
|
22
|
-
Waveform1DConfig,
|
23
|
-
)
|
24
|
-
from bec_widgets.widgets.plots.image import ImageConfig
|
25
|
-
from bec_widgets.widgets.plots.motor_map import MotorMapConfig
|
17
|
+
from bec_widgets.widgets.figure.plots.image.image import BECImageShow, ImageConfig
|
18
|
+
from bec_widgets.widgets.figure.plots.motor_map.motor_map import BECMotorMap, MotorMapConfig
|
19
|
+
from bec_widgets.widgets.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
20
|
+
from bec_widgets.widgets.figure.plots.waveform.waveform import BECWaveform, Waveform1DConfig
|
26
21
|
|
27
22
|
|
28
23
|
class FigureConfig(ConnectionConfig):
|
@@ -267,19 +262,20 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
267
262
|
|
268
263
|
return waveform
|
269
264
|
|
265
|
+
@typechecked
|
270
266
|
def plot(
|
271
267
|
self,
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
color:
|
281
|
-
color_map_z:
|
282
|
-
label:
|
268
|
+
x: list | np.ndarray | None = None,
|
269
|
+
y: list | np.ndarray | None = None,
|
270
|
+
x_name: str | None = None,
|
271
|
+
y_name: str | None = None,
|
272
|
+
z_name: str | None = None,
|
273
|
+
x_entry: str | None = None,
|
274
|
+
y_entry: str | None = None,
|
275
|
+
z_entry: str | None = None,
|
276
|
+
color: str | None = None,
|
277
|
+
color_map_z: str | None = "plasma",
|
278
|
+
label: str | None = None,
|
283
279
|
validate: bool = True,
|
284
280
|
**axis_kwargs,
|
285
281
|
) -> BECWaveform:
|
@@ -287,14 +283,14 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
287
283
|
Add a 1D waveform plot to the figure. Always access the first waveform widget in the figure.
|
288
284
|
|
289
285
|
Args:
|
286
|
+
x(list | np.ndarray): Custom x data to plot.
|
287
|
+
y(list | np.ndarray): Custom y data to plot.
|
290
288
|
x_name(str): The name of the device for the x-axis.
|
291
289
|
y_name(str): The name of the device for the y-axis.
|
292
290
|
z_name(str): The name of the device for the z-axis.
|
293
291
|
x_entry(str): The name of the entry for the x-axis.
|
294
292
|
y_entry(str): The name of the entry for the y-axis.
|
295
293
|
z_entry(str): The name of the entry for the z-axis.
|
296
|
-
x(list | np.ndarray): Custom x data to plot.
|
297
|
-
y(list | np.ndarray): Custom y data to plot.
|
298
294
|
color(str): The color of the curve.
|
299
295
|
color_map_z(str): The color map to use for the z-axis.
|
300
296
|
label(str): The label of the curve.
|
@@ -313,6 +309,27 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
313
309
|
else:
|
314
310
|
waveform = self.add_plot(**axis_kwargs)
|
315
311
|
|
312
|
+
if x is not None and y is None:
|
313
|
+
if isinstance(x, np.ndarray):
|
314
|
+
if x.ndim == 1:
|
315
|
+
y = np.arange(x.size)
|
316
|
+
waveform.add_curve_custom(x=np.arange(x.size), y=x, color=color, label=label)
|
317
|
+
return waveform
|
318
|
+
if x.ndim == 2:
|
319
|
+
waveform.add_curve_custom(x=x[:, 0], y=x[:, 1], color=color, label=label)
|
320
|
+
return waveform
|
321
|
+
elif isinstance(x, list):
|
322
|
+
y = np.arange(len(x))
|
323
|
+
waveform.add_curve_custom(x=np.arange(len(x)), y=x, color=color, label=label)
|
324
|
+
return waveform
|
325
|
+
else:
|
326
|
+
raise ValueError(
|
327
|
+
"Invalid input. Provide either device names (x_name, y_name) or custom data."
|
328
|
+
)
|
329
|
+
if x is not None and y is not None:
|
330
|
+
waveform.add_curve_custom(x=x, y=y, color=color, label=label)
|
331
|
+
return waveform
|
332
|
+
|
316
333
|
# User wants to add scan curve -> 1D Waveform
|
317
334
|
if x_name is not None and y_name is not None and z_name is None and x is None and y is None:
|
318
335
|
waveform.add_curve_scan(
|
File without changes
|
File without changes
|
@@ -4,50 +4,16 @@ from collections import defaultdict
|
|
4
4
|
from typing import Any, Literal, Optional
|
5
5
|
|
6
6
|
import numpy as np
|
7
|
-
import pyqtgraph as pg
|
8
7
|
from bec_lib.endpoints import MessageEndpoints
|
9
|
-
from pydantic import
|
10
|
-
from qtpy.QtCore import
|
11
|
-
from qtpy.QtCore import Signal as pyqtSignal
|
8
|
+
from pydantic import Field, ValidationError
|
9
|
+
from qtpy.QtCore import QThread
|
12
10
|
from qtpy.QtCore import Slot as pyqtSlot
|
13
11
|
from qtpy.QtWidgets import QWidget
|
14
12
|
|
15
|
-
from bec_widgets.utils import
|
16
|
-
|
17
|
-
from .
|
18
|
-
|
19
|
-
|
20
|
-
class ProcessingConfig(BaseModel):
|
21
|
-
fft: Optional[bool] = Field(False, description="Whether to perform FFT on the monitor data.")
|
22
|
-
log: Optional[bool] = Field(False, description="Whether to perform log on the monitor data.")
|
23
|
-
center_of_mass: Optional[bool] = Field(
|
24
|
-
False, description="Whether to calculate the center of mass of the monitor data."
|
25
|
-
)
|
26
|
-
transpose: Optional[bool] = Field(
|
27
|
-
False, description="Whether to transpose the monitor data before displaying."
|
28
|
-
)
|
29
|
-
rotation: Optional[int] = Field(
|
30
|
-
None, description="The rotation angle of the monitor data before displaying."
|
31
|
-
)
|
32
|
-
|
33
|
-
|
34
|
-
class ImageItemConfig(ConnectionConfig):
|
35
|
-
parent_id: Optional[str] = Field(None, description="The parent plot of the image.")
|
36
|
-
monitor: Optional[str] = Field(None, description="The name of the monitor.")
|
37
|
-
source: Optional[str] = Field(None, description="The source of the curve.")
|
38
|
-
color_map: Optional[str] = Field("magma", description="The color map of the image.")
|
39
|
-
downsample: Optional[bool] = Field(True, description="Whether to downsample the image.")
|
40
|
-
opacity: Optional[float] = Field(1.0, description="The opacity of the image.")
|
41
|
-
vrange: Optional[tuple[int, int]] = Field(
|
42
|
-
None, description="The range of the color bar. If None, the range is automatically set."
|
43
|
-
)
|
44
|
-
color_bar: Optional[Literal["simple", "full"]] = Field(
|
45
|
-
"simple", description="The type of the color bar."
|
46
|
-
)
|
47
|
-
autorange: Optional[bool] = Field(True, description="Whether to autorange the color bar.")
|
48
|
-
processing: ProcessingConfig = Field(
|
49
|
-
default_factory=ProcessingConfig, description="The post processing of the image."
|
50
|
-
)
|
13
|
+
from bec_widgets.utils import EntryValidator
|
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
|
16
|
+
from bec_widgets.widgets.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
51
17
|
|
52
18
|
|
53
19
|
class ImageConfig(SubplotConfig):
|
@@ -57,251 +23,6 @@ class ImageConfig(SubplotConfig):
|
|
57
23
|
)
|
58
24
|
|
59
25
|
|
60
|
-
class BECImageItem(BECConnector, pg.ImageItem):
|
61
|
-
USER_ACCESS = [
|
62
|
-
"rpc_id",
|
63
|
-
"config_dict",
|
64
|
-
"set",
|
65
|
-
"set_fft",
|
66
|
-
"set_log",
|
67
|
-
"set_rotation",
|
68
|
-
"set_transpose",
|
69
|
-
"set_opacity",
|
70
|
-
"set_autorange",
|
71
|
-
"set_color_map",
|
72
|
-
"set_auto_downsample",
|
73
|
-
"set_monitor",
|
74
|
-
"set_vrange",
|
75
|
-
"get_data",
|
76
|
-
]
|
77
|
-
|
78
|
-
def __init__(
|
79
|
-
self,
|
80
|
-
config: Optional[ImageItemConfig] = None,
|
81
|
-
gui_id: Optional[str] = None,
|
82
|
-
parent_image: Optional[BECImageItem] = None,
|
83
|
-
**kwargs,
|
84
|
-
):
|
85
|
-
if config is None:
|
86
|
-
config = ImageItemConfig(widget_class=self.__class__.__name__)
|
87
|
-
self.config = config
|
88
|
-
else:
|
89
|
-
self.config = config
|
90
|
-
super().__init__(config=config, gui_id=gui_id)
|
91
|
-
pg.ImageItem.__init__(self)
|
92
|
-
|
93
|
-
self.parent_image = parent_image
|
94
|
-
self.colorbar_bar = None
|
95
|
-
|
96
|
-
self._add_color_bar(
|
97
|
-
self.config.color_bar, self.config.vrange
|
98
|
-
) # TODO can also support None to not have any colorbar
|
99
|
-
self.apply_config()
|
100
|
-
if kwargs:
|
101
|
-
self.set(**kwargs)
|
102
|
-
|
103
|
-
def apply_config(self):
|
104
|
-
"""
|
105
|
-
Apply current configuration.
|
106
|
-
"""
|
107
|
-
self.set_color_map(self.config.color_map)
|
108
|
-
self.set_auto_downsample(self.config.downsample)
|
109
|
-
if self.config.vrange is not None:
|
110
|
-
self.set_vrange(vrange=self.config.vrange)
|
111
|
-
|
112
|
-
def set(self, **kwargs):
|
113
|
-
"""
|
114
|
-
Set the properties of the image.
|
115
|
-
|
116
|
-
Args:
|
117
|
-
**kwargs: Keyword arguments for the properties to be set.
|
118
|
-
|
119
|
-
Possible properties:
|
120
|
-
- downsample
|
121
|
-
- color_map
|
122
|
-
- monitor
|
123
|
-
- opacity
|
124
|
-
- vrange
|
125
|
-
- fft
|
126
|
-
- log
|
127
|
-
- rot
|
128
|
-
- transpose
|
129
|
-
"""
|
130
|
-
method_map = {
|
131
|
-
"downsample": self.set_auto_downsample,
|
132
|
-
"color_map": self.set_color_map,
|
133
|
-
"monitor": self.set_monitor,
|
134
|
-
"opacity": self.set_opacity,
|
135
|
-
"vrange": self.set_vrange,
|
136
|
-
"fft": self.set_fft,
|
137
|
-
"log": self.set_log,
|
138
|
-
"rot": self.set_rotation,
|
139
|
-
"transpose": self.set_transpose,
|
140
|
-
}
|
141
|
-
for key, value in kwargs.items():
|
142
|
-
if key in method_map:
|
143
|
-
method_map[key](value)
|
144
|
-
else:
|
145
|
-
print(f"Warning: '{key}' is not a recognized property.")
|
146
|
-
|
147
|
-
def set_fft(self, enable: bool = False):
|
148
|
-
"""
|
149
|
-
Set the FFT of the image.
|
150
|
-
|
151
|
-
Args:
|
152
|
-
enable(bool): Whether to perform FFT on the monitor data.
|
153
|
-
"""
|
154
|
-
self.config.processing.fft = enable
|
155
|
-
|
156
|
-
def set_log(self, enable: bool = False):
|
157
|
-
"""
|
158
|
-
Set the log of the image.
|
159
|
-
|
160
|
-
Args:
|
161
|
-
enable(bool): Whether to perform log on the monitor data.
|
162
|
-
"""
|
163
|
-
self.config.processing.log = enable
|
164
|
-
if enable and self.color_bar and self.config.color_bar == "full":
|
165
|
-
self.color_bar.autoHistogramRange()
|
166
|
-
|
167
|
-
def set_rotation(self, deg_90: int = 0):
|
168
|
-
"""
|
169
|
-
Set the rotation of the image.
|
170
|
-
|
171
|
-
Args:
|
172
|
-
deg_90(int): The rotation angle of the monitor data before displaying.
|
173
|
-
"""
|
174
|
-
self.config.processing.rotation = deg_90
|
175
|
-
|
176
|
-
def set_transpose(self, enable: bool = False):
|
177
|
-
"""
|
178
|
-
Set the transpose of the image.
|
179
|
-
|
180
|
-
Args:
|
181
|
-
enable(bool): Whether to transpose the image.
|
182
|
-
"""
|
183
|
-
self.config.processing.transpose = enable
|
184
|
-
|
185
|
-
def set_opacity(self, opacity: float = 1.0):
|
186
|
-
"""
|
187
|
-
Set the opacity of the image.
|
188
|
-
|
189
|
-
Args:
|
190
|
-
opacity(float): The opacity of the image.
|
191
|
-
"""
|
192
|
-
self.setOpacity(opacity)
|
193
|
-
self.config.opacity = opacity
|
194
|
-
|
195
|
-
def set_autorange(self, autorange: bool = False):
|
196
|
-
"""
|
197
|
-
Set the autorange of the color bar.
|
198
|
-
|
199
|
-
Args:
|
200
|
-
autorange(bool): Whether to autorange the color bar.
|
201
|
-
"""
|
202
|
-
self.config.autorange = autorange
|
203
|
-
if self.color_bar is not None:
|
204
|
-
self.color_bar.autoHistogramRange()
|
205
|
-
|
206
|
-
def set_color_map(self, cmap: str = "magma"):
|
207
|
-
"""
|
208
|
-
Set the color map of the image.
|
209
|
-
|
210
|
-
Args:
|
211
|
-
cmap(str): The color map of the image.
|
212
|
-
"""
|
213
|
-
self.setColorMap(cmap)
|
214
|
-
if self.color_bar is not None:
|
215
|
-
if self.config.color_bar == "simple":
|
216
|
-
self.color_bar.setColorMap(cmap)
|
217
|
-
elif self.config.color_bar == "full":
|
218
|
-
self.color_bar.gradient.loadPreset(cmap)
|
219
|
-
self.config.color_map = cmap
|
220
|
-
|
221
|
-
def set_auto_downsample(self, auto: bool = True):
|
222
|
-
"""
|
223
|
-
Set the auto downsample of the image.
|
224
|
-
|
225
|
-
Args:
|
226
|
-
auto(bool): Whether to downsample the image.
|
227
|
-
"""
|
228
|
-
self.setAutoDownsample(auto)
|
229
|
-
self.config.downsample = auto
|
230
|
-
|
231
|
-
def set_monitor(self, monitor: str):
|
232
|
-
"""
|
233
|
-
Set the monitor of the image.
|
234
|
-
|
235
|
-
Args:
|
236
|
-
monitor(str): The name of the monitor.
|
237
|
-
"""
|
238
|
-
self.config.monitor = monitor
|
239
|
-
|
240
|
-
def set_vrange(self, vmin: float = None, vmax: float = None, vrange: tuple[int, int] = None):
|
241
|
-
"""
|
242
|
-
Set the range of the color bar.
|
243
|
-
|
244
|
-
Args:
|
245
|
-
vmin(float): Minimum value of the color bar.
|
246
|
-
vmax(float): Maximum value of the color bar.
|
247
|
-
"""
|
248
|
-
if vrange is not None:
|
249
|
-
vmin, vmax = vrange
|
250
|
-
self.setLevels([vmin, vmax])
|
251
|
-
self.config.vrange = (vmin, vmax)
|
252
|
-
self.config.autorange = False
|
253
|
-
if self.color_bar is not None:
|
254
|
-
if self.config.color_bar == "simple":
|
255
|
-
self.color_bar.setLevels(low=vmin, high=vmax)
|
256
|
-
elif self.config.color_bar == "full":
|
257
|
-
self.color_bar.setLevels(min=vmin, max=vmax)
|
258
|
-
self.color_bar.setHistogramRange(vmin - 0.1 * vmin, vmax + 0.1 * vmax)
|
259
|
-
|
260
|
-
def get_data(self) -> np.ndarray:
|
261
|
-
"""
|
262
|
-
Get the data of the image.
|
263
|
-
Returns:
|
264
|
-
np.ndarray: The data of the image.
|
265
|
-
"""
|
266
|
-
return self.image
|
267
|
-
|
268
|
-
def _add_color_bar(
|
269
|
-
self, color_bar_style: str = "simple", vrange: Optional[tuple[int, int]] = None
|
270
|
-
):
|
271
|
-
"""
|
272
|
-
Add color bar to the layout.
|
273
|
-
|
274
|
-
Args:
|
275
|
-
style(Literal["simple,full"]): The style of the color bar.
|
276
|
-
vrange(tuple[int,int]): The range of the color bar.
|
277
|
-
"""
|
278
|
-
if color_bar_style == "simple":
|
279
|
-
self.color_bar = pg.ColorBarItem(colorMap=self.config.color_map)
|
280
|
-
if vrange is not None:
|
281
|
-
self.color_bar.setLevels(low=vrange[0], high=vrange[1])
|
282
|
-
self.color_bar.setImageItem(self)
|
283
|
-
self.parent_image.addItem(self.color_bar) # , row=0, col=1)
|
284
|
-
self.config.color_bar = "simple"
|
285
|
-
elif color_bar_style == "full":
|
286
|
-
# Setting histogram
|
287
|
-
self.color_bar = pg.HistogramLUTItem()
|
288
|
-
self.color_bar.setImageItem(self)
|
289
|
-
self.color_bar.gradient.loadPreset(self.config.color_map)
|
290
|
-
if vrange is not None:
|
291
|
-
self.color_bar.setLevels(min=vrange[0], max=vrange[1])
|
292
|
-
self.color_bar.setHistogramRange(
|
293
|
-
vrange[0] - 0.1 * vrange[0], vrange[1] + 0.1 * vrange[1]
|
294
|
-
)
|
295
|
-
|
296
|
-
# Adding histogram to the layout
|
297
|
-
self.parent_image.addItem(self.color_bar) # , row=0, col=1)
|
298
|
-
|
299
|
-
# save settings
|
300
|
-
self.config.color_bar = "full"
|
301
|
-
else:
|
302
|
-
raise ValueError("style should be 'simple' or 'full'")
|
303
|
-
|
304
|
-
|
305
26
|
class BECImageShow(BECPlotBase):
|
306
27
|
USER_ACCESS = [
|
307
28
|
"rpc_id",
|
@@ -837,134 +558,3 @@ class BECImageShow(BECPlotBase):
|
|
837
558
|
image.cleanup()
|
838
559
|
|
839
560
|
super().cleanup()
|
840
|
-
|
841
|
-
|
842
|
-
class ImageProcessor:
|
843
|
-
"""
|
844
|
-
Class for processing the image data.
|
845
|
-
"""
|
846
|
-
|
847
|
-
def __init__(self, config: ProcessingConfig = None):
|
848
|
-
if config is None:
|
849
|
-
config = ProcessingConfig()
|
850
|
-
self.config = config
|
851
|
-
|
852
|
-
def set_config(self, config: ProcessingConfig):
|
853
|
-
"""
|
854
|
-
Set the configuration of the processor.
|
855
|
-
|
856
|
-
Args:
|
857
|
-
config(ProcessingConfig): The configuration of the processor.
|
858
|
-
"""
|
859
|
-
self.config = config
|
860
|
-
|
861
|
-
def FFT(self, data: np.ndarray) -> np.ndarray:
|
862
|
-
"""
|
863
|
-
Perform FFT on the data.
|
864
|
-
|
865
|
-
Args:
|
866
|
-
data(np.ndarray): The data to be processed.
|
867
|
-
|
868
|
-
Returns:
|
869
|
-
np.ndarray: The processed data.
|
870
|
-
"""
|
871
|
-
return np.abs(np.fft.fftshift(np.fft.fft2(data)))
|
872
|
-
|
873
|
-
def rotation(self, data: np.ndarray, rotate_90: int) -> np.ndarray:
|
874
|
-
"""
|
875
|
-
Rotate the data by 90 degrees n times.
|
876
|
-
|
877
|
-
Args:
|
878
|
-
data(np.ndarray): The data to be processed.
|
879
|
-
rotate_90(int): The number of 90 degree rotations.
|
880
|
-
|
881
|
-
Returns:
|
882
|
-
np.ndarray: The processed data.
|
883
|
-
"""
|
884
|
-
return np.rot90(data, k=rotate_90, axes=(0, 1))
|
885
|
-
|
886
|
-
def transpose(self, data: np.ndarray) -> np.ndarray:
|
887
|
-
"""
|
888
|
-
Transpose the data.
|
889
|
-
|
890
|
-
Args:
|
891
|
-
data(np.ndarray): The data to be processed.
|
892
|
-
|
893
|
-
Returns:
|
894
|
-
np.ndarray: The processed data.
|
895
|
-
"""
|
896
|
-
return np.transpose(data)
|
897
|
-
|
898
|
-
def log(self, data: np.ndarray) -> np.ndarray:
|
899
|
-
"""
|
900
|
-
Perform log on the data.
|
901
|
-
|
902
|
-
Args:
|
903
|
-
data(np.ndarray): The data to be processed.
|
904
|
-
|
905
|
-
Returns:
|
906
|
-
np.ndarray: The processed data.
|
907
|
-
"""
|
908
|
-
# TODO this is not final solution -> data should stay as int16
|
909
|
-
data = data.astype(np.float32)
|
910
|
-
offset = 1e-6
|
911
|
-
data_offset = data + offset
|
912
|
-
return np.log10(data_offset)
|
913
|
-
|
914
|
-
# def center_of_mass(self, data: np.ndarray) -> tuple: # TODO check functionality
|
915
|
-
# return np.unravel_index(np.argmax(data), data.shape)
|
916
|
-
|
917
|
-
def process_image(self, data: np.ndarray) -> np.ndarray:
|
918
|
-
"""
|
919
|
-
Process the data according to the configuration.
|
920
|
-
|
921
|
-
Args:
|
922
|
-
data(np.ndarray): The data to be processed.
|
923
|
-
|
924
|
-
Returns:
|
925
|
-
np.ndarray: The processed data.
|
926
|
-
"""
|
927
|
-
if self.config.fft:
|
928
|
-
data = self.FFT(data)
|
929
|
-
if self.config.rotation is not None:
|
930
|
-
data = self.rotation(data, self.config.rotation)
|
931
|
-
if self.config.transpose:
|
932
|
-
data = self.transpose(data)
|
933
|
-
if self.config.log:
|
934
|
-
data = self.log(data)
|
935
|
-
return data
|
936
|
-
|
937
|
-
|
938
|
-
class ProcessorWorker(QObject):
|
939
|
-
"""
|
940
|
-
Worker for processing the image data.
|
941
|
-
"""
|
942
|
-
|
943
|
-
processed = pyqtSignal(str, np.ndarray)
|
944
|
-
stopRequested = pyqtSignal()
|
945
|
-
finished = pyqtSignal()
|
946
|
-
|
947
|
-
def __init__(self, processor):
|
948
|
-
super().__init__()
|
949
|
-
self.processor = processor
|
950
|
-
self._isRunning = False
|
951
|
-
self.stopRequested.connect(self.stop)
|
952
|
-
|
953
|
-
@pyqtSlot(str, np.ndarray)
|
954
|
-
def process_image(self, device: str, image: np.ndarray):
|
955
|
-
"""
|
956
|
-
Process the image data.
|
957
|
-
|
958
|
-
Args:
|
959
|
-
device(str): The name of the device.
|
960
|
-
image(np.ndarray): The image data.
|
961
|
-
"""
|
962
|
-
self._isRunning = True
|
963
|
-
processed_image = self.processor.process_image(image)
|
964
|
-
self._isRunning = False
|
965
|
-
if not self._isRunning:
|
966
|
-
self.processed.emit(device, processed_image)
|
967
|
-
self.finished.emit()
|
968
|
-
|
969
|
-
def stop(self):
|
970
|
-
self._isRunning = False
|