bec-widgets 2.5.4__py3-none-any.whl → 2.7.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.
- .github/ISSUE_TEMPLATE/bug_report.yml +41 -0
- .gitlab/issue_templates/documentation_update_template.md → .github/ISSUE_TEMPLATE/documentation_update.md +10 -0
- .github/ISSUE_TEMPLATE/feature_request.md +3 -2
- .gitlab/merge_request_templates/default.md → .github/pull_request_template.md +8 -3
- .github/scripts/pr_issue_sync/pr_issue_sync.py +342 -0
- .github/scripts/pr_issue_sync/requirements.txt +2 -0
- .github/workflows/sync-issues-pr.yml +40 -0
- CHANGELOG.md +47 -0
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +31 -1
- bec_widgets/examples/jupyter_console/jupyter_console_window.py +1 -1
- bec_widgets/utils/crosshair.py +35 -11
- bec_widgets/widgets/plots/image/image.py +219 -19
- bec_widgets/widgets/plots/image/image_roi_plot.py +37 -0
- bec_widgets/widgets/plots/image/setting_widgets/__init__.py +0 -0
- bec_widgets/widgets/plots/image/setting_widgets/image_roi_tree.py +375 -0
- bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py +4 -3
- bec_widgets/widgets/plots/roi/image_roi.py +36 -14
- bec_widgets/widgets/plots/toolbar_bundles/mouse_interactions.py +1 -1
- bec_widgets/widgets/plots/waveform/waveform.py +2 -0
- {bec_widgets-2.5.4.dist-info → bec_widgets-2.7.0.dist-info}/METADATA +1 -1
- {bec_widgets-2.5.4.dist-info → bec_widgets-2.7.0.dist-info}/RECORD +26 -22
- pyproject.toml +1 -1
- .github/ISSUE_TEMPLATE/bug_report.md +0 -26
- .gitlab/issue_templates/bug_report_template.md +0 -17
- .gitlab/issue_templates/feature_request_template.md +0 -40
- {bec_widgets-2.5.4.dist-info → bec_widgets-2.7.0.dist-info}/WHEEL +0 -0
- {bec_widgets-2.5.4.dist-info → bec_widgets-2.7.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-2.5.4.dist-info → bec_widgets-2.7.0.dist-info}/licenses/LICENSE +0 -0
bec_widgets/utils/crosshair.py
CHANGED
@@ -85,7 +85,8 @@ class Crosshair(QObject):
|
|
85
85
|
self.items = []
|
86
86
|
self.marker_moved_1d = {}
|
87
87
|
self.marker_clicked_1d = {}
|
88
|
-
self.
|
88
|
+
self.marker_2d_row = None
|
89
|
+
self.marker_2d_col = None
|
89
90
|
self.update_markers()
|
90
91
|
self.check_log()
|
91
92
|
self.check_derivatives()
|
@@ -195,13 +196,23 @@ class Crosshair(QObject):
|
|
195
196
|
marker_clicked_list.append(marker_clicked)
|
196
197
|
self.marker_clicked_1d[name] = marker_clicked_list
|
197
198
|
elif isinstance(item, pg.ImageItem): # 2D plot
|
198
|
-
if self.
|
199
|
+
if self.marker_2d_row is not None and self.marker_2d_col is not None:
|
199
200
|
continue
|
200
|
-
|
201
|
-
|
201
|
+
# Create horizontal ROI for row highlighting
|
202
|
+
if item.image is None:
|
203
|
+
continue
|
204
|
+
self.marker_2d_row = pg.ROI(
|
205
|
+
[0, 0], size=[item.image.shape[0], 1], pen=pg.mkPen("r", width=2), movable=False
|
206
|
+
)
|
207
|
+
self.marker_2d_row.skip_auto_range = True
|
208
|
+
self.plot_item.addItem(self.marker_2d_row)
|
209
|
+
|
210
|
+
# Create vertical ROI for column highlighting
|
211
|
+
self.marker_2d_col = pg.ROI(
|
212
|
+
[0, 0], size=[1, item.image.shape[1]], pen=pg.mkPen("r", width=2), movable=False
|
202
213
|
)
|
203
|
-
self.
|
204
|
-
self.plot_item.addItem(self.
|
214
|
+
self.marker_2d_col.skip_auto_range = True
|
215
|
+
self.plot_item.addItem(self.marker_2d_col)
|
205
216
|
|
206
217
|
def snap_to_data(
|
207
218
|
self, x: float, y: float
|
@@ -243,6 +254,8 @@ class Crosshair(QObject):
|
|
243
254
|
elif isinstance(item, pg.ImageItem): # 2D plot
|
244
255
|
name = item.config.monitor or str(id(item))
|
245
256
|
image_2d = item.image
|
257
|
+
if image_2d is None:
|
258
|
+
continue
|
246
259
|
# Clip the x and y values to the image dimensions to avoid out of bounds errors
|
247
260
|
y_values[name] = int(np.clip(y, 0, image_2d.shape[1] - 1))
|
248
261
|
x_values[name] = int(np.clip(x, 0, image_2d.shape[0] - 1))
|
@@ -330,7 +343,10 @@ class Crosshair(QObject):
|
|
330
343
|
x, y = x_snap_values[name], y_snap_values[name]
|
331
344
|
if x is None or y is None:
|
332
345
|
continue
|
333
|
-
|
346
|
+
# Set position of horizontal ROI (row)
|
347
|
+
self.marker_2d_row.setPos([0, y])
|
348
|
+
# Set position of vertical ROI (column)
|
349
|
+
self.marker_2d_col.setPos([x, 0])
|
334
350
|
coordinate_to_emit = (name, x, y)
|
335
351
|
self.coordinatesChanged2D.emit(coordinate_to_emit)
|
336
352
|
else:
|
@@ -384,7 +400,10 @@ class Crosshair(QObject):
|
|
384
400
|
x, y = x_snap_values[name], y_snap_values[name]
|
385
401
|
if x is None or y is None:
|
386
402
|
continue
|
387
|
-
|
403
|
+
# Set position of horizontal ROI (row)
|
404
|
+
self.marker_2d_row.setPos([0, y])
|
405
|
+
# Set position of vertical ROI (column)
|
406
|
+
self.marker_2d_col.setPos([x, 0])
|
388
407
|
coordinate_to_emit = (name, x, y)
|
389
408
|
self.coordinatesClicked2D.emit(coordinate_to_emit)
|
390
409
|
else:
|
@@ -428,6 +447,8 @@ class Crosshair(QObject):
|
|
428
447
|
for item in self.items:
|
429
448
|
if isinstance(item, pg.ImageItem):
|
430
449
|
image = item.image
|
450
|
+
if image is None:
|
451
|
+
continue
|
431
452
|
ix = int(np.clip(x, 0, image.shape[0] - 1))
|
432
453
|
iy = int(np.clip(y, 0, image.shape[1] - 1))
|
433
454
|
intensity = image[ix, iy]
|
@@ -450,9 +471,12 @@ class Crosshair(QObject):
|
|
450
471
|
self.clear_markers()
|
451
472
|
|
452
473
|
def cleanup(self):
|
453
|
-
if self.
|
454
|
-
self.plot_item.removeItem(self.
|
455
|
-
self.
|
474
|
+
if self.marker_2d_row is not None:
|
475
|
+
self.plot_item.removeItem(self.marker_2d_row)
|
476
|
+
self.marker_2d_row = None
|
477
|
+
if self.marker_2d_col is not None:
|
478
|
+
self.plot_item.removeItem(self.marker_2d_col)
|
479
|
+
self.marker_2d_col = None
|
456
480
|
self.plot_item.removeItem(self.v_line)
|
457
481
|
self.plot_item.removeItem(self.h_line)
|
458
482
|
self.plot_item.removeItem(self.coord_label)
|
@@ -8,13 +8,16 @@ from bec_lib import bec_logger
|
|
8
8
|
from bec_lib.endpoints import MessageEndpoints
|
9
9
|
from pydantic import Field, ValidationError, field_validator
|
10
10
|
from qtpy.QtCore import QPointF, Signal
|
11
|
-
from qtpy.QtWidgets import QWidget
|
11
|
+
from qtpy.QtWidgets import QDialog, QVBoxLayout, QWidget
|
12
12
|
|
13
13
|
from bec_widgets.utils import ConnectionConfig
|
14
14
|
from bec_widgets.utils.colors import Colors
|
15
15
|
from bec_widgets.utils.error_popups import SafeProperty, SafeSlot
|
16
|
+
from bec_widgets.utils.side_panel import SidePanel
|
16
17
|
from bec_widgets.utils.toolbar import MaterialIconAction, SwitchableToolBarAction
|
17
18
|
from bec_widgets.widgets.plots.image.image_item import ImageItem
|
19
|
+
from bec_widgets.widgets.plots.image.image_roi_plot import ImageROIPlot
|
20
|
+
from bec_widgets.widgets.plots.image.setting_widgets.image_roi_tree import ROIPropertyTree
|
18
21
|
from bec_widgets.widgets.plots.image.toolbar_bundles.image_selection import (
|
19
22
|
MonitorSelectionToolbarBundle,
|
20
23
|
)
|
@@ -122,6 +125,7 @@ class Image(PlotBase):
|
|
122
125
|
"rois",
|
123
126
|
]
|
124
127
|
sync_colorbar_with_autorange = Signal()
|
128
|
+
image_updated = Signal()
|
125
129
|
|
126
130
|
def __init__(
|
127
131
|
self,
|
@@ -138,6 +142,8 @@ class Image(PlotBase):
|
|
138
142
|
self._color_bar = None
|
139
143
|
self._main_image = ImageItem()
|
140
144
|
self.roi_controller = ROIController(colormap="viridis")
|
145
|
+
self.x_roi = None
|
146
|
+
self.y_roi = None
|
141
147
|
super().__init__(
|
142
148
|
parent=parent, config=config, client=client, gui_id=gui_id, popups=popups, **kwargs
|
143
149
|
)
|
@@ -149,25 +155,60 @@ class Image(PlotBase):
|
|
149
155
|
# Default Color map to plasma
|
150
156
|
self.color_map = "plasma"
|
151
157
|
|
152
|
-
#
|
153
|
-
self.
|
158
|
+
# Initialize ROI plots and side panels
|
159
|
+
self._add_roi_plots()
|
160
|
+
|
161
|
+
self.roi_manager_dialog = None
|
162
|
+
|
163
|
+
# Refresh theme for ROI plots
|
164
|
+
self._update_theme()
|
154
165
|
|
155
166
|
################################################################################
|
156
167
|
# Widget Specific GUI interactions
|
157
168
|
################################################################################
|
169
|
+
def apply_theme(self, theme: str):
|
170
|
+
super().apply_theme(theme)
|
171
|
+
if self.x_roi is not None and self.y_roi is not None:
|
172
|
+
self.x_roi.apply_theme(theme)
|
173
|
+
self.y_roi.apply_theme(theme)
|
174
|
+
|
158
175
|
def _init_toolbar(self):
|
159
176
|
|
160
177
|
# add to the first position
|
161
178
|
self.selection_bundle = MonitorSelectionToolbarBundle(
|
162
179
|
bundle_id="selection", target_widget=self
|
163
180
|
)
|
164
|
-
self.toolbar.add_bundle(self.selection_bundle, self)
|
181
|
+
self.toolbar.add_bundle(bundle=self.selection_bundle, target_widget=self)
|
165
182
|
|
166
183
|
super()._init_toolbar()
|
167
184
|
|
168
185
|
# Image specific changes to PlotBase toolbar
|
169
186
|
self.toolbar.widgets["reset_legend"].action.setVisible(False)
|
170
187
|
|
188
|
+
# ROI Bundle replacement with switchable crosshair
|
189
|
+
self.toolbar.remove_bundle("roi")
|
190
|
+
crosshair = MaterialIconAction(
|
191
|
+
icon_name="point_scan", tooltip="Show Crosshair", checkable=True
|
192
|
+
)
|
193
|
+
crosshair_roi = MaterialIconAction(
|
194
|
+
icon_name="my_location",
|
195
|
+
tooltip="Show Crosshair with ROI plots",
|
196
|
+
checkable=True,
|
197
|
+
parent=self,
|
198
|
+
)
|
199
|
+
crosshair_roi.action.toggled.connect(self.toggle_roi_panels)
|
200
|
+
crosshair.action.toggled.connect(self.toggle_crosshair)
|
201
|
+
switch_crosshair = SwitchableToolBarAction(
|
202
|
+
actions={"crosshair_simple": crosshair, "crosshair_roi": crosshair_roi},
|
203
|
+
initial_action="crosshair_simple",
|
204
|
+
tooltip="Crosshair",
|
205
|
+
checkable=True,
|
206
|
+
parent=self,
|
207
|
+
)
|
208
|
+
self.toolbar.add_action(
|
209
|
+
action_id="switch_crosshair", action=switch_crosshair, target_widget=self
|
210
|
+
)
|
211
|
+
|
171
212
|
# Lock aspect ratio button
|
172
213
|
self.lock_aspect_ratio_action = MaterialIconAction(
|
173
214
|
icon_name="aspect_ratio", tooltip="Lock Aspect Ratio", checkable=True, parent=self
|
@@ -216,11 +257,8 @@ class Image(PlotBase):
|
|
216
257
|
parent=self,
|
217
258
|
)
|
218
259
|
|
219
|
-
self.toolbar.
|
220
|
-
|
221
|
-
action_id="autorange_image",
|
222
|
-
action=self.autorange_switch,
|
223
|
-
target_widget=self,
|
260
|
+
self.toolbar.add_action(
|
261
|
+
action_id="autorange_image", action=self.autorange_switch, target_widget=self
|
224
262
|
)
|
225
263
|
|
226
264
|
self.autorange_mean_action.action.toggled.connect(
|
@@ -252,11 +290,8 @@ class Image(PlotBase):
|
|
252
290
|
parent=self,
|
253
291
|
)
|
254
292
|
|
255
|
-
self.toolbar.
|
256
|
-
|
257
|
-
action_id="switch_colorbar",
|
258
|
-
action=self.colorbar_switch,
|
259
|
-
target_widget=self,
|
293
|
+
self.toolbar.add_action(
|
294
|
+
action_id="switch_colorbar", action=self.colorbar_switch, target_widget=self
|
260
295
|
)
|
261
296
|
|
262
297
|
self.simple_colorbar_action.action.toggled.connect(
|
@@ -266,6 +301,55 @@ class Image(PlotBase):
|
|
266
301
|
lambda checked: self.enable_colorbar(checked, style="full")
|
267
302
|
)
|
268
303
|
|
304
|
+
########################################
|
305
|
+
# ROI Gui Manager
|
306
|
+
def add_side_menus(self):
|
307
|
+
super().add_side_menus()
|
308
|
+
|
309
|
+
roi_mgr = ROIPropertyTree(parent=self, image_widget=self)
|
310
|
+
self.side_panel.add_menu(
|
311
|
+
action_id="roi_mgr",
|
312
|
+
icon_name="view_list",
|
313
|
+
tooltip="ROI Manager",
|
314
|
+
widget=roi_mgr,
|
315
|
+
title="ROI Manager",
|
316
|
+
)
|
317
|
+
|
318
|
+
def add_popups(self):
|
319
|
+
super().add_popups() # keep Axis Settings
|
320
|
+
|
321
|
+
roi_action = MaterialIconAction(
|
322
|
+
icon_name="view_list", tooltip="ROI Manager", checkable=True, parent=self
|
323
|
+
)
|
324
|
+
# self.popup_bundle.add_action("roi_mgr", roi_action)
|
325
|
+
self.toolbar.add_action_to_bundle(
|
326
|
+
bundle_id="popup_bundle", action_id="roi_mgr", action=roi_action, target_widget=self
|
327
|
+
)
|
328
|
+
self.toolbar.widgets["roi_mgr"].action.triggered.connect(self.show_roi_manager_popup)
|
329
|
+
|
330
|
+
def show_roi_manager_popup(self):
|
331
|
+
roi_action = self.toolbar.widgets["roi_mgr"].action
|
332
|
+
if self.roi_manager_dialog is None or not self.roi_manager_dialog.isVisible():
|
333
|
+
self.roi_mgr = ROIPropertyTree(parent=self, image_widget=self)
|
334
|
+
self.roi_manager_dialog = QDialog(modal=False)
|
335
|
+
self.roi_manager_dialog.layout = QVBoxLayout(self.roi_manager_dialog)
|
336
|
+
self.roi_manager_dialog.layout.addWidget(self.roi_mgr)
|
337
|
+
self.roi_manager_dialog.finished.connect(self._roi_mgr_closed)
|
338
|
+
self.roi_manager_dialog.show()
|
339
|
+
roi_action.setChecked(True)
|
340
|
+
else:
|
341
|
+
self.roi_manager_dialog.raise_()
|
342
|
+
self.roi_manager_dialog.activateWindow()
|
343
|
+
roi_action.setChecked(True)
|
344
|
+
|
345
|
+
def _roi_mgr_closed(self):
|
346
|
+
self.roi_mgr.close()
|
347
|
+
self.roi_mgr.deleteLater()
|
348
|
+
self.roi_manager_dialog.close()
|
349
|
+
self.roi_manager_dialog.deleteLater()
|
350
|
+
self.roi_manager_dialog = None
|
351
|
+
self.toolbar.widgets["roi_mgr"].action.setChecked(False)
|
352
|
+
|
269
353
|
def enable_colorbar(
|
270
354
|
self,
|
271
355
|
enabled: bool,
|
@@ -324,7 +408,7 @@ class Image(PlotBase):
|
|
324
408
|
self,
|
325
409
|
kind: Literal["rect", "circle"] = "rect",
|
326
410
|
name: str | None = None,
|
327
|
-
line_width: int | None =
|
411
|
+
line_width: int | None = 5,
|
328
412
|
pos: tuple[float, float] | None = (10, 10),
|
329
413
|
size: tuple[float, float] | None = (50, 50),
|
330
414
|
**pg_kwargs,
|
@@ -369,6 +453,7 @@ class Image(PlotBase):
|
|
369
453
|
# Add to plot and controller (controller assigns color)
|
370
454
|
self.plot_item.addItem(roi)
|
371
455
|
self.roi_controller.add_roi(roi)
|
456
|
+
roi.add_scale_handle()
|
372
457
|
return roi
|
373
458
|
|
374
459
|
def remove_roi(self, roi: int | str):
|
@@ -380,6 +465,101 @@ class Image(PlotBase):
|
|
380
465
|
else:
|
381
466
|
raise ValueError("roi must be an int index or str name")
|
382
467
|
|
468
|
+
def _add_roi_plots(self):
|
469
|
+
"""
|
470
|
+
Initialize the ROI plots and side panels.
|
471
|
+
"""
|
472
|
+
# Create ROI plot widgets
|
473
|
+
self.x_roi = ImageROIPlot(parent=self)
|
474
|
+
self.y_roi = ImageROIPlot(parent=self)
|
475
|
+
self.x_roi.apply_theme("dark")
|
476
|
+
self.y_roi.apply_theme("dark")
|
477
|
+
|
478
|
+
# Set titles for the plots
|
479
|
+
self.x_roi.plot_item.setTitle("X ROI")
|
480
|
+
self.y_roi.plot_item.setTitle("Y ROI")
|
481
|
+
|
482
|
+
# Create side panels
|
483
|
+
self.side_panel_x = SidePanel(
|
484
|
+
parent=self, orientation="bottom", panel_max_width=200, show_toolbar=False
|
485
|
+
)
|
486
|
+
self.side_panel_y = SidePanel(
|
487
|
+
parent=self, orientation="left", panel_max_width=200, show_toolbar=False
|
488
|
+
)
|
489
|
+
|
490
|
+
# Add ROI plots to side panels
|
491
|
+
self.x_panel_index = self.side_panel_x.add_menu(widget=self.x_roi)
|
492
|
+
self.y_panel_index = self.side_panel_y.add_menu(widget=self.y_roi)
|
493
|
+
|
494
|
+
# # Add side panels to the layout
|
495
|
+
self.layout_manager.add_widget_relative(
|
496
|
+
self.side_panel_x, self.round_plot_widget, position="bottom", shift_direction="down"
|
497
|
+
)
|
498
|
+
self.layout_manager.add_widget_relative(
|
499
|
+
self.side_panel_y, self.round_plot_widget, position="left", shift_direction="right"
|
500
|
+
)
|
501
|
+
|
502
|
+
def toggle_roi_panels(self, checked: bool):
|
503
|
+
"""
|
504
|
+
Show or hide the ROI panels based on the test action toggle state.
|
505
|
+
|
506
|
+
Args:
|
507
|
+
checked (bool): Whether the test action is checked.
|
508
|
+
"""
|
509
|
+
if checked:
|
510
|
+
# Show the ROI panels
|
511
|
+
self.hook_crosshair()
|
512
|
+
self.side_panel_x.show_panel(self.x_panel_index)
|
513
|
+
self.side_panel_y.show_panel(self.y_panel_index)
|
514
|
+
self.crosshair.coordinatesChanged2D.connect(self.update_image_slices)
|
515
|
+
self.image_updated.connect(self.update_image_slices)
|
516
|
+
else:
|
517
|
+
self.unhook_crosshair()
|
518
|
+
# Hide the ROI panels
|
519
|
+
self.side_panel_x.hide_panel()
|
520
|
+
self.side_panel_y.hide_panel()
|
521
|
+
self.image_updated.disconnect(self.update_image_slices)
|
522
|
+
|
523
|
+
@SafeSlot()
|
524
|
+
def update_image_slices(self, coordinates: tuple[int, int, int] = None):
|
525
|
+
"""
|
526
|
+
Update the image slices based on the crosshair position.
|
527
|
+
|
528
|
+
Args:
|
529
|
+
coordinates(tuple): The coordinates of the crosshair.
|
530
|
+
"""
|
531
|
+
if coordinates is None:
|
532
|
+
# Try to get coordinates from crosshair position (like in crosshair mouse_moved)
|
533
|
+
if (
|
534
|
+
hasattr(self, "crosshair")
|
535
|
+
and hasattr(self.crosshair, "v_line")
|
536
|
+
and hasattr(self.crosshair, "h_line")
|
537
|
+
):
|
538
|
+
x = int(round(self.crosshair.v_line.value()))
|
539
|
+
y = int(round(self.crosshair.h_line.value()))
|
540
|
+
else:
|
541
|
+
return
|
542
|
+
else:
|
543
|
+
x = coordinates[1]
|
544
|
+
y = coordinates[2]
|
545
|
+
image = self._main_image.image
|
546
|
+
if image is None:
|
547
|
+
return
|
548
|
+
max_row, max_col = image.shape[0] - 1, image.shape[1] - 1
|
549
|
+
row, col = x, y
|
550
|
+
if not (0 <= row <= max_row and 0 <= col <= max_col):
|
551
|
+
return
|
552
|
+
# Horizontal slice
|
553
|
+
h_slice = image[:, col]
|
554
|
+
x_axis = np.arange(h_slice.shape[0])
|
555
|
+
self.x_roi.plot_item.clear()
|
556
|
+
self.x_roi.plot_item.plot(x_axis, h_slice, pen=pg.mkPen(self.x_roi.curve_color, width=3))
|
557
|
+
# Vertical slice
|
558
|
+
v_slice = image[row, :]
|
559
|
+
y_axis = np.arange(v_slice.shape[0])
|
560
|
+
self.y_roi.plot_item.clear()
|
561
|
+
self.y_roi.plot_item.plot(v_slice, y_axis, pen=pg.mkPen(self.y_roi.curve_color, width=3))
|
562
|
+
|
383
563
|
################################################################################
|
384
564
|
# Widget Specific Properties
|
385
565
|
################################################################################
|
@@ -934,6 +1114,7 @@ class Image(PlotBase):
|
|
934
1114
|
self._main_image.set_data(image_buffer)
|
935
1115
|
if self._color_bar is not None:
|
936
1116
|
self._color_bar.blockSignals(False)
|
1117
|
+
self.image_updated.emit()
|
937
1118
|
|
938
1119
|
def adjust_image_buffer(self, image: ImageItem, new_data: np.ndarray) -> np.ndarray:
|
939
1120
|
"""
|
@@ -985,6 +1166,7 @@ class Image(PlotBase):
|
|
985
1166
|
self._main_image.set_data(data)
|
986
1167
|
if self._color_bar is not None:
|
987
1168
|
self._color_bar.blockSignals(False)
|
1169
|
+
self.image_updated.emit()
|
988
1170
|
|
989
1171
|
################################################################################
|
990
1172
|
# Clean up
|
@@ -1031,20 +1213,38 @@ class Image(PlotBase):
|
|
1031
1213
|
self._color_bar.deleteLater()
|
1032
1214
|
self._color_bar = None
|
1033
1215
|
|
1216
|
+
# Popup cleanup
|
1217
|
+
if self.roi_manager_dialog is not None:
|
1218
|
+
self.roi_manager_dialog.reject()
|
1219
|
+
self.roi_manager_dialog = None
|
1220
|
+
|
1034
1221
|
# Toolbar cleanup
|
1035
1222
|
self.toolbar.widgets["monitor"].widget.close()
|
1036
1223
|
self.toolbar.widgets["monitor"].widget.deleteLater()
|
1037
1224
|
|
1225
|
+
# ROI plots cleanup
|
1226
|
+
self.x_roi.cleanup_pyqtgraph()
|
1227
|
+
self.y_roi.cleanup_pyqtgraph()
|
1228
|
+
|
1038
1229
|
super().cleanup()
|
1039
1230
|
|
1040
1231
|
|
1041
1232
|
if __name__ == "__main__": # pragma: no cover
|
1042
1233
|
import sys
|
1043
1234
|
|
1044
|
-
from qtpy.QtWidgets import QApplication
|
1235
|
+
from qtpy.QtWidgets import QApplication, QHBoxLayout
|
1045
1236
|
|
1046
1237
|
app = QApplication(sys.argv)
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1238
|
+
win = QWidget()
|
1239
|
+
win.setWindowTitle("Image Demo")
|
1240
|
+
ml = QHBoxLayout(win)
|
1241
|
+
|
1242
|
+
image_popup = Image(popups=True)
|
1243
|
+
image_side_panel = Image(popups=False)
|
1244
|
+
|
1245
|
+
ml.addWidget(image_popup)
|
1246
|
+
ml.addWidget(image_side_panel)
|
1247
|
+
|
1248
|
+
win.resize(1500, 800)
|
1249
|
+
win.show()
|
1050
1250
|
sys.exit(app.exec_())
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import pyqtgraph as pg
|
2
|
+
|
3
|
+
from bec_widgets.utils.round_frame import RoundedFrame
|
4
|
+
from bec_widgets.widgets.plots.plot_base import BECViewBox
|
5
|
+
|
6
|
+
|
7
|
+
class ImageROIPlot(RoundedFrame):
|
8
|
+
"""
|
9
|
+
A widget for displaying an image with a region of interest (ROI) overlay.
|
10
|
+
"""
|
11
|
+
|
12
|
+
def __init__(self, parent=None):
|
13
|
+
super().__init__(parent=parent)
|
14
|
+
|
15
|
+
self.content_widget = pg.GraphicsLayoutWidget(self)
|
16
|
+
self.layout.addWidget(self.content_widget)
|
17
|
+
self.plot_item = pg.PlotItem(viewBox=BECViewBox(enableMenu=True))
|
18
|
+
self.content_widget.addItem(self.plot_item)
|
19
|
+
self.curve_color = "w"
|
20
|
+
|
21
|
+
self.apply_plot_widget_style()
|
22
|
+
|
23
|
+
def apply_theme(self, theme: str):
|
24
|
+
if theme == "dark":
|
25
|
+
self.curve_color = "w"
|
26
|
+
else:
|
27
|
+
self.curve_color = "k"
|
28
|
+
for curve in self.plot_item.curves:
|
29
|
+
curve.setPen(pg.mkPen(self.curve_color, width=3))
|
30
|
+
super().apply_theme(theme)
|
31
|
+
|
32
|
+
def cleanup_pyqtgraph(self):
|
33
|
+
"""Cleanup pyqtgraph items."""
|
34
|
+
self.plot_item.vb.menu.close()
|
35
|
+
self.plot_item.vb.menu.deleteLater()
|
36
|
+
self.plot_item.ctrlMenu.close()
|
37
|
+
self.plot_item.ctrlMenu.deleteLater()
|
File without changes
|