bec-widgets 2.4.3__py3-none-any.whl → 2.5.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/workflows/pytest-matrix.yml +5 -4
- .github/workflows/pytest.yml +5 -4
- CHANGELOG.md +13 -0
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +335 -0
- bec_widgets/examples/jupyter_console/jupyter_console_window.py +8 -8
- bec_widgets/widgets/plots/image/image.py +90 -0
- bec_widgets/widgets/plots/roi/__init__.py +0 -0
- bec_widgets/widgets/plots/roi/image_roi.py +867 -0
- {bec_widgets-2.4.3.dist-info → bec_widgets-2.5.0.dist-info}/METADATA +1 -1
- {bec_widgets-2.4.3.dist-info → bec_widgets-2.5.0.dist-info}/RECORD +15 -13
- pyproject.toml +1 -1
- {bec_widgets-2.4.3.dist-info → bec_widgets-2.5.0.dist-info}/WHEEL +0 -0
- {bec_widgets-2.4.3.dist-info → bec_widgets-2.5.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-2.4.3.dist-info → bec_widgets-2.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -37,10 +37,11 @@ jobs:
|
|
37
37
|
echo -e "\033[35;1m Using branch $OPHYD_DEVICES_BRANCH of OPHYD_DEVICES \033[0;m";
|
38
38
|
git clone --branch $OPHYD_DEVICES_BRANCH https://github.com/bec-project/ophyd_devices.git
|
39
39
|
export OHPYD_DEVICES_PATH=$PWD/ophyd_devices
|
40
|
-
pip install
|
41
|
-
pip install -e ./
|
42
|
-
pip install -e ./bec/
|
43
|
-
pip install -e
|
40
|
+
pip install uv
|
41
|
+
uv pip install --system -e ./ophyd_devices
|
42
|
+
uv pip install --system -e ./bec/bec_lib[dev]
|
43
|
+
uv pip install --system -e ./bec/bec_ipython_client
|
44
|
+
uv pip install --system -e .[dev,pyside6]
|
44
45
|
|
45
46
|
- name: Run Pytest
|
46
47
|
run: |
|
.github/workflows/pytest.yml
CHANGED
@@ -48,10 +48,11 @@ jobs:
|
|
48
48
|
echo -e "\033[35;1m Using branch $OPHYD_DEVICES_BRANCH of OPHYD_DEVICES \033[0;m";
|
49
49
|
git clone --branch $OPHYD_DEVICES_BRANCH https://github.com/bec-project/ophyd_devices.git
|
50
50
|
export OHPYD_DEVICES_PATH=$PWD/ophyd_devices
|
51
|
-
pip install
|
52
|
-
pip install -e ./
|
53
|
-
pip install -e ./bec/
|
54
|
-
pip install -e
|
51
|
+
pip install uv
|
52
|
+
uv pip install --system -e ./ophyd_devices
|
53
|
+
uv pip install --system -e ./bec/bec_lib[dev]
|
54
|
+
uv pip install --system -e ./bec/bec_ipython_client
|
55
|
+
uv pip install --system -e .[dev,pyside6]
|
55
56
|
|
56
57
|
- name: Run Pytest with Coverage
|
57
58
|
id: coverage
|
CHANGELOG.md
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
3
|
|
4
|
+
## v2.5.0 (2025-05-20)
|
5
|
+
|
6
|
+
### Continuous Integration
|
7
|
+
|
8
|
+
- Try uv for test env setup
|
9
|
+
([`6ee0f50`](https://github.com/bec-project/bec_widgets/commit/6ee0f5004d6c840a31a9394f5d1610f635e9b83b))
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
- **image_rois**: Image rois with RPC can be added to Image widget
|
14
|
+
([`1d018e8`](https://github.com/bec-project/bec_widgets/commit/1d018e863ca0cdb3274002cf35d69a6961aaf07d))
|
15
|
+
|
16
|
+
|
4
17
|
## v2.4.3 (2025-05-19)
|
5
18
|
|
6
19
|
### Bug Fixes
|
PKG-INFO
CHANGED
bec_widgets/cli/client.py
CHANGED
@@ -504,6 +504,204 @@ class BECStatusBox(RPCBase):
|
|
504
504
|
"""
|
505
505
|
|
506
506
|
|
507
|
+
class BaseROI(RPCBase):
|
508
|
+
"""Base class for all Region of Interest (ROI) implementations."""
|
509
|
+
|
510
|
+
@property
|
511
|
+
@rpc_call
|
512
|
+
def label(self) -> "str":
|
513
|
+
"""
|
514
|
+
Gets the display name of this ROI.
|
515
|
+
|
516
|
+
Returns:
|
517
|
+
str: The current name of the ROI.
|
518
|
+
"""
|
519
|
+
|
520
|
+
@label.setter
|
521
|
+
@rpc_call
|
522
|
+
def label(self) -> "str":
|
523
|
+
"""
|
524
|
+
Gets the display name of this ROI.
|
525
|
+
|
526
|
+
Returns:
|
527
|
+
str: The current name of the ROI.
|
528
|
+
"""
|
529
|
+
|
530
|
+
@property
|
531
|
+
@rpc_call
|
532
|
+
def line_color(self) -> "str":
|
533
|
+
"""
|
534
|
+
Gets the current line color of the ROI.
|
535
|
+
|
536
|
+
Returns:
|
537
|
+
str: The current line color as a string (e.g., hex color code).
|
538
|
+
"""
|
539
|
+
|
540
|
+
@line_color.setter
|
541
|
+
@rpc_call
|
542
|
+
def line_color(self) -> "str":
|
543
|
+
"""
|
544
|
+
Gets the current line color of the ROI.
|
545
|
+
|
546
|
+
Returns:
|
547
|
+
str: The current line color as a string (e.g., hex color code).
|
548
|
+
"""
|
549
|
+
|
550
|
+
@property
|
551
|
+
@rpc_call
|
552
|
+
def line_width(self) -> "int":
|
553
|
+
"""
|
554
|
+
Gets the current line width of the ROI.
|
555
|
+
|
556
|
+
Returns:
|
557
|
+
int: The current line width in pixels.
|
558
|
+
"""
|
559
|
+
|
560
|
+
@line_width.setter
|
561
|
+
@rpc_call
|
562
|
+
def line_width(self) -> "int":
|
563
|
+
"""
|
564
|
+
Gets the current line width of the ROI.
|
565
|
+
|
566
|
+
Returns:
|
567
|
+
int: The current line width in pixels.
|
568
|
+
"""
|
569
|
+
|
570
|
+
@rpc_call
|
571
|
+
def get_coordinates(self):
|
572
|
+
"""
|
573
|
+
Gets the coordinates that define this ROI's position and shape.
|
574
|
+
|
575
|
+
This is an abstract method that must be implemented by subclasses.
|
576
|
+
Implementations should return either a dictionary with descriptive keys
|
577
|
+
or a tuple of coordinates, depending on the value of self.description.
|
578
|
+
|
579
|
+
Returns:
|
580
|
+
dict or tuple: The coordinates defining the ROI's position and shape.
|
581
|
+
|
582
|
+
Raises:
|
583
|
+
NotImplementedError: This method must be implemented by subclasses.
|
584
|
+
"""
|
585
|
+
|
586
|
+
@rpc_call
|
587
|
+
def get_data_from_image(
|
588
|
+
self, image_item: "pg.ImageItem | None" = None, returnMappedCoords: "bool" = False, **kwargs
|
589
|
+
):
|
590
|
+
"""
|
591
|
+
Wrapper around `pyqtgraph.ROI.getArrayRegion`.
|
592
|
+
|
593
|
+
Args:
|
594
|
+
image_item (pg.ImageItem or None): The ImageItem to sample. If None, auto-detects
|
595
|
+
the first `ImageItem` in the same GraphicsScene as this ROI.
|
596
|
+
returnMappedCoords (bool): If True, also returns the coordinate array generated by
|
597
|
+
*getArrayRegion*.
|
598
|
+
**kwargs: Additional keyword arguments passed to *getArrayRegion* or *affineSlice*,
|
599
|
+
such as `axes`, `order`, `shape`, etc.
|
600
|
+
|
601
|
+
Returns:
|
602
|
+
ndarray: Pixel data inside the ROI, or (data, coords) if *returnMappedCoords* is True.
|
603
|
+
"""
|
604
|
+
|
605
|
+
|
606
|
+
class CircularROI(RPCBase):
|
607
|
+
"""Circular Region of Interest with center/diameter tracking and auto-labeling."""
|
608
|
+
|
609
|
+
@property
|
610
|
+
@rpc_call
|
611
|
+
def label(self) -> "str":
|
612
|
+
"""
|
613
|
+
Gets the display name of this ROI.
|
614
|
+
|
615
|
+
Returns:
|
616
|
+
str: The current name of the ROI.
|
617
|
+
"""
|
618
|
+
|
619
|
+
@label.setter
|
620
|
+
@rpc_call
|
621
|
+
def label(self) -> "str":
|
622
|
+
"""
|
623
|
+
Gets the display name of this ROI.
|
624
|
+
|
625
|
+
Returns:
|
626
|
+
str: The current name of the ROI.
|
627
|
+
"""
|
628
|
+
|
629
|
+
@property
|
630
|
+
@rpc_call
|
631
|
+
def line_color(self) -> "str":
|
632
|
+
"""
|
633
|
+
Gets the current line color of the ROI.
|
634
|
+
|
635
|
+
Returns:
|
636
|
+
str: The current line color as a string (e.g., hex color code).
|
637
|
+
"""
|
638
|
+
|
639
|
+
@line_color.setter
|
640
|
+
@rpc_call
|
641
|
+
def line_color(self) -> "str":
|
642
|
+
"""
|
643
|
+
Gets the current line color of the ROI.
|
644
|
+
|
645
|
+
Returns:
|
646
|
+
str: The current line color as a string (e.g., hex color code).
|
647
|
+
"""
|
648
|
+
|
649
|
+
@property
|
650
|
+
@rpc_call
|
651
|
+
def line_width(self) -> "int":
|
652
|
+
"""
|
653
|
+
Gets the current line width of the ROI.
|
654
|
+
|
655
|
+
Returns:
|
656
|
+
int: The current line width in pixels.
|
657
|
+
"""
|
658
|
+
|
659
|
+
@line_width.setter
|
660
|
+
@rpc_call
|
661
|
+
def line_width(self) -> "int":
|
662
|
+
"""
|
663
|
+
Gets the current line width of the ROI.
|
664
|
+
|
665
|
+
Returns:
|
666
|
+
int: The current line width in pixels.
|
667
|
+
"""
|
668
|
+
|
669
|
+
@rpc_call
|
670
|
+
def get_coordinates(self, typed: "bool | None" = None) -> "dict | tuple":
|
671
|
+
"""
|
672
|
+
Calculates and returns the coordinates and size of an object, either as a
|
673
|
+
typed dictionary or as a tuple.
|
674
|
+
|
675
|
+
Args:
|
676
|
+
typed (bool | None): If True, returns coordinates as a dictionary. Defaults
|
677
|
+
to None, which utilizes the object's description value.
|
678
|
+
|
679
|
+
Returns:
|
680
|
+
dict: A dictionary with keys 'center_x', 'center_y', 'diameter', and 'radius'
|
681
|
+
if `typed` is True.
|
682
|
+
tuple: A tuple containing (center_x, center_y, diameter, radius) if `typed` is False.
|
683
|
+
"""
|
684
|
+
|
685
|
+
@rpc_call
|
686
|
+
def get_data_from_image(
|
687
|
+
self, image_item: "pg.ImageItem | None" = None, returnMappedCoords: "bool" = False, **kwargs
|
688
|
+
):
|
689
|
+
"""
|
690
|
+
Wrapper around `pyqtgraph.ROI.getArrayRegion`.
|
691
|
+
|
692
|
+
Args:
|
693
|
+
image_item (pg.ImageItem or None): The ImageItem to sample. If None, auto-detects
|
694
|
+
the first `ImageItem` in the same GraphicsScene as this ROI.
|
695
|
+
returnMappedCoords (bool): If True, also returns the coordinate array generated by
|
696
|
+
*getArrayRegion*.
|
697
|
+
**kwargs: Additional keyword arguments passed to *getArrayRegion* or *affineSlice*,
|
698
|
+
such as `axes`, `order`, `shape`, etc.
|
699
|
+
|
700
|
+
Returns:
|
701
|
+
ndarray: Pixel data inside the ROI, or (data, coords) if *returnMappedCoords* is True.
|
702
|
+
"""
|
703
|
+
|
704
|
+
|
507
705
|
class Curve(RPCBase):
|
508
706
|
@rpc_call
|
509
707
|
def remove(self):
|
@@ -1215,6 +1413,44 @@ class Image(RPCBase):
|
|
1215
1413
|
Access the main image item.
|
1216
1414
|
"""
|
1217
1415
|
|
1416
|
+
@rpc_call
|
1417
|
+
def add_roi(
|
1418
|
+
self,
|
1419
|
+
kind: "Literal['rect', 'circle']" = "rect",
|
1420
|
+
name: "str | None" = None,
|
1421
|
+
line_width: "int | None" = 10,
|
1422
|
+
pos: "tuple[float, float] | None" = (10, 10),
|
1423
|
+
size: "tuple[float, float] | None" = (50, 50),
|
1424
|
+
**pg_kwargs,
|
1425
|
+
) -> "RectangularROI | CircularROI":
|
1426
|
+
"""
|
1427
|
+
Add a ROI to the image.
|
1428
|
+
|
1429
|
+
Args:
|
1430
|
+
kind(str): The type of ROI to add. Options are "rect" or "circle".
|
1431
|
+
name(str): The name of the ROI.
|
1432
|
+
line_width(int): The line width of the ROI.
|
1433
|
+
pos(tuple): The position of the ROI.
|
1434
|
+
size(tuple): The size of the ROI.
|
1435
|
+
**pg_kwargs: Additional arguments for the ROI.
|
1436
|
+
|
1437
|
+
Returns:
|
1438
|
+
RectangularROI | CircularROI: The created ROI object.
|
1439
|
+
"""
|
1440
|
+
|
1441
|
+
@rpc_call
|
1442
|
+
def remove_roi(self, roi: "int | str"):
|
1443
|
+
"""
|
1444
|
+
Remove an ROI by index or label via the ROIController.
|
1445
|
+
"""
|
1446
|
+
|
1447
|
+
@property
|
1448
|
+
@rpc_call
|
1449
|
+
def rois(self) -> "list[BaseROI]":
|
1450
|
+
"""
|
1451
|
+
Get the list of ROIs.
|
1452
|
+
"""
|
1453
|
+
|
1218
1454
|
|
1219
1455
|
class ImageItem(RPCBase):
|
1220
1456
|
@property
|
@@ -2318,6 +2554,105 @@ class PositionerGroup(RPCBase):
|
|
2318
2554
|
"""
|
2319
2555
|
|
2320
2556
|
|
2557
|
+
class RectangularROI(RPCBase):
|
2558
|
+
"""Defines a rectangular Region of Interest (ROI) with additional functionality."""
|
2559
|
+
|
2560
|
+
@property
|
2561
|
+
@rpc_call
|
2562
|
+
def label(self) -> "str":
|
2563
|
+
"""
|
2564
|
+
Gets the display name of this ROI.
|
2565
|
+
|
2566
|
+
Returns:
|
2567
|
+
str: The current name of the ROI.
|
2568
|
+
"""
|
2569
|
+
|
2570
|
+
@label.setter
|
2571
|
+
@rpc_call
|
2572
|
+
def label(self) -> "str":
|
2573
|
+
"""
|
2574
|
+
Gets the display name of this ROI.
|
2575
|
+
|
2576
|
+
Returns:
|
2577
|
+
str: The current name of the ROI.
|
2578
|
+
"""
|
2579
|
+
|
2580
|
+
@property
|
2581
|
+
@rpc_call
|
2582
|
+
def line_color(self) -> "str":
|
2583
|
+
"""
|
2584
|
+
Gets the current line color of the ROI.
|
2585
|
+
|
2586
|
+
Returns:
|
2587
|
+
str: The current line color as a string (e.g., hex color code).
|
2588
|
+
"""
|
2589
|
+
|
2590
|
+
@line_color.setter
|
2591
|
+
@rpc_call
|
2592
|
+
def line_color(self) -> "str":
|
2593
|
+
"""
|
2594
|
+
Gets the current line color of the ROI.
|
2595
|
+
|
2596
|
+
Returns:
|
2597
|
+
str: The current line color as a string (e.g., hex color code).
|
2598
|
+
"""
|
2599
|
+
|
2600
|
+
@property
|
2601
|
+
@rpc_call
|
2602
|
+
def line_width(self) -> "int":
|
2603
|
+
"""
|
2604
|
+
Gets the current line width of the ROI.
|
2605
|
+
|
2606
|
+
Returns:
|
2607
|
+
int: The current line width in pixels.
|
2608
|
+
"""
|
2609
|
+
|
2610
|
+
@line_width.setter
|
2611
|
+
@rpc_call
|
2612
|
+
def line_width(self) -> "int":
|
2613
|
+
"""
|
2614
|
+
Gets the current line width of the ROI.
|
2615
|
+
|
2616
|
+
Returns:
|
2617
|
+
int: The current line width in pixels.
|
2618
|
+
"""
|
2619
|
+
|
2620
|
+
@rpc_call
|
2621
|
+
def get_coordinates(self, typed: "bool | None" = None) -> "dict | tuple":
|
2622
|
+
"""
|
2623
|
+
Returns the coordinates of a rectangle's corners. Supports returning them
|
2624
|
+
as either a dictionary with descriptive keys or a tuple of coordinates.
|
2625
|
+
|
2626
|
+
Args:
|
2627
|
+
typed (bool | None): If True, returns coordinates as a dictionary with
|
2628
|
+
descriptive keys. If False, returns them as a tuple. Defaults to
|
2629
|
+
the value of `self.description`.
|
2630
|
+
|
2631
|
+
Returns:
|
2632
|
+
dict | tuple: The rectangle's corner coordinates, where the format
|
2633
|
+
depends on the `typed` parameter.
|
2634
|
+
"""
|
2635
|
+
|
2636
|
+
@rpc_call
|
2637
|
+
def get_data_from_image(
|
2638
|
+
self, image_item: "pg.ImageItem | None" = None, returnMappedCoords: "bool" = False, **kwargs
|
2639
|
+
):
|
2640
|
+
"""
|
2641
|
+
Wrapper around `pyqtgraph.ROI.getArrayRegion`.
|
2642
|
+
|
2643
|
+
Args:
|
2644
|
+
image_item (pg.ImageItem or None): The ImageItem to sample. If None, auto-detects
|
2645
|
+
the first `ImageItem` in the same GraphicsScene as this ROI.
|
2646
|
+
returnMappedCoords (bool): If True, also returns the coordinate array generated by
|
2647
|
+
*getArrayRegion*.
|
2648
|
+
**kwargs: Additional keyword arguments passed to *getArrayRegion* or *affineSlice*,
|
2649
|
+
such as `axes`, `order`, `shape`, etc.
|
2650
|
+
|
2651
|
+
Returns:
|
2652
|
+
ndarray: Pixel data inside the ROI, or (data, coords) if *returnMappedCoords* is True.
|
2653
|
+
"""
|
2654
|
+
|
2655
|
+
|
2321
2656
|
class ResetButton(RPCBase):
|
2322
2657
|
"""A button that resets the scan queue."""
|
2323
2658
|
|
@@ -43,7 +43,7 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
|
|
43
43
|
"pg": pg,
|
44
44
|
"wh": wh,
|
45
45
|
"dock": self.dock,
|
46
|
-
|
46
|
+
"im": self.im,
|
47
47
|
# "mi": self.mi,
|
48
48
|
# "mm": self.mm,
|
49
49
|
# "lm": self.lm,
|
@@ -112,13 +112,13 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
|
|
112
112
|
# tab_widget.addTab(fifth_tab, "Waveform Next Gen")
|
113
113
|
# tab_widget.setCurrentIndex(4)
|
114
114
|
#
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
115
|
+
sixth_tab = QWidget()
|
116
|
+
sixth_tab_layout = QVBoxLayout(sixth_tab)
|
117
|
+
self.im = Image(popups=False)
|
118
|
+
self.mi = self.im.main_image
|
119
|
+
sixth_tab_layout.addWidget(self.im)
|
120
|
+
tab_widget.addTab(sixth_tab, "Image Next Gen")
|
121
|
+
tab_widget.setCurrentIndex(1)
|
122
122
|
#
|
123
123
|
# seventh_tab = QWidget()
|
124
124
|
# seventh_tab_layout = QVBoxLayout(seventh_tab)
|
@@ -20,6 +20,12 @@ from bec_widgets.widgets.plots.image.toolbar_bundles.image_selection import (
|
|
20
20
|
)
|
21
21
|
from bec_widgets.widgets.plots.image.toolbar_bundles.processing import ImageProcessingToolbarBundle
|
22
22
|
from bec_widgets.widgets.plots.plot_base import PlotBase
|
23
|
+
from bec_widgets.widgets.plots.roi.image_roi import (
|
24
|
+
BaseROI,
|
25
|
+
CircularROI,
|
26
|
+
RectangularROI,
|
27
|
+
ROIController,
|
28
|
+
)
|
23
29
|
|
24
30
|
logger = bec_logger.logger
|
25
31
|
|
@@ -111,6 +117,9 @@ class Image(PlotBase):
|
|
111
117
|
"transpose.setter",
|
112
118
|
"image",
|
113
119
|
"main_image",
|
120
|
+
"add_roi",
|
121
|
+
"remove_roi",
|
122
|
+
"rois",
|
114
123
|
]
|
115
124
|
sync_colorbar_with_autorange = Signal()
|
116
125
|
|
@@ -128,6 +137,7 @@ class Image(PlotBase):
|
|
128
137
|
self.gui_id = config.gui_id
|
129
138
|
self._color_bar = None
|
130
139
|
self._main_image = ImageItem()
|
140
|
+
self.roi_controller = ROIController(colormap="viridis")
|
131
141
|
super().__init__(
|
132
142
|
parent=parent, config=config, client=client, gui_id=gui_id, popups=popups, **kwargs
|
133
143
|
)
|
@@ -139,6 +149,9 @@ class Image(PlotBase):
|
|
139
149
|
# Default Color map to plasma
|
140
150
|
self.color_map = "plasma"
|
141
151
|
|
152
|
+
# Headless controller keeps the canonical list.
|
153
|
+
self._roi_manager_dialog = None
|
154
|
+
|
142
155
|
################################################################################
|
143
156
|
# Widget Specific GUI interactions
|
144
157
|
################################################################################
|
@@ -304,9 +317,81 @@ class Image(PlotBase):
|
|
304
317
|
if vrange: # should be at the end to disable the autorange if defined
|
305
318
|
self.v_range = vrange
|
306
319
|
|
320
|
+
################################################################################
|
321
|
+
# Static rois with roi manager
|
322
|
+
|
323
|
+
def add_roi(
|
324
|
+
self,
|
325
|
+
kind: Literal["rect", "circle"] = "rect",
|
326
|
+
name: str | None = None,
|
327
|
+
line_width: int | None = 10,
|
328
|
+
pos: tuple[float, float] | None = (10, 10),
|
329
|
+
size: tuple[float, float] | None = (50, 50),
|
330
|
+
**pg_kwargs,
|
331
|
+
) -> RectangularROI | CircularROI:
|
332
|
+
"""
|
333
|
+
Add a ROI to the image.
|
334
|
+
|
335
|
+
Args:
|
336
|
+
kind(str): The type of ROI to add. Options are "rect" or "circle".
|
337
|
+
name(str): The name of the ROI.
|
338
|
+
line_width(int): The line width of the ROI.
|
339
|
+
pos(tuple): The position of the ROI.
|
340
|
+
size(tuple): The size of the ROI.
|
341
|
+
**pg_kwargs: Additional arguments for the ROI.
|
342
|
+
|
343
|
+
Returns:
|
344
|
+
RectangularROI | CircularROI: The created ROI object.
|
345
|
+
"""
|
346
|
+
if name is None:
|
347
|
+
name = f"ROI_{len(self.roi_controller.rois) + 1}"
|
348
|
+
if kind == "rect":
|
349
|
+
roi = RectangularROI(
|
350
|
+
pos=pos,
|
351
|
+
size=size,
|
352
|
+
parent_image=self,
|
353
|
+
line_width=line_width,
|
354
|
+
label=name,
|
355
|
+
**pg_kwargs,
|
356
|
+
)
|
357
|
+
elif kind == "circle":
|
358
|
+
roi = CircularROI(
|
359
|
+
pos=pos,
|
360
|
+
size=size,
|
361
|
+
parent_image=self,
|
362
|
+
line_width=line_width,
|
363
|
+
label=name,
|
364
|
+
**pg_kwargs,
|
365
|
+
)
|
366
|
+
else:
|
367
|
+
raise ValueError("kind must be 'rect' or 'circle'")
|
368
|
+
|
369
|
+
# Add to plot and controller (controller assigns color)
|
370
|
+
self.plot_item.addItem(roi)
|
371
|
+
self.roi_controller.add_roi(roi)
|
372
|
+
return roi
|
373
|
+
|
374
|
+
def remove_roi(self, roi: int | str):
|
375
|
+
"""Remove an ROI by index or label via the ROIController."""
|
376
|
+
if isinstance(roi, int):
|
377
|
+
self.roi_controller.remove_roi_by_index(roi)
|
378
|
+
elif isinstance(roi, str):
|
379
|
+
self.roi_controller.remove_roi_by_name(roi)
|
380
|
+
else:
|
381
|
+
raise ValueError("roi must be an int index or str name")
|
382
|
+
|
307
383
|
################################################################################
|
308
384
|
# Widget Specific Properties
|
309
385
|
################################################################################
|
386
|
+
################################################################################
|
387
|
+
# Rois
|
388
|
+
|
389
|
+
@property
|
390
|
+
def rois(self) -> list[BaseROI]:
|
391
|
+
"""
|
392
|
+
Get the list of ROIs.
|
393
|
+
"""
|
394
|
+
return self.roi_controller.rois
|
310
395
|
|
311
396
|
################################################################################
|
312
397
|
# Colorbar toggle
|
@@ -925,6 +1010,11 @@ class Image(PlotBase):
|
|
925
1010
|
"""
|
926
1011
|
Disconnect the image update signals and clean up the image.
|
927
1012
|
"""
|
1013
|
+
# Remove all ROIs
|
1014
|
+
rois = self.rois
|
1015
|
+
for roi in rois:
|
1016
|
+
roi.remove()
|
1017
|
+
|
928
1018
|
# Main Image cleanup
|
929
1019
|
if self._main_image.config.monitor is not None:
|
930
1020
|
self.disconnect_monitor(self._main_image.config.monitor)
|
File without changes
|