bec-widgets 2.12.3__py3-none-any.whl → 2.13.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/stale-issues.yml +15 -0
- CHANGELOG.md +29 -0
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +62 -0
- bec_widgets/widgets/plots/image/image_base.py +4 -1
- bec_widgets/widgets/plots/image/setting_widgets/image_roi_tree.py +66 -4
- bec_widgets/widgets/plots/roi/image_roi.py +99 -29
- {bec_widgets-2.12.3.dist-info → bec_widgets-2.13.0.dist-info}/METADATA +1 -1
- {bec_widgets-2.12.3.dist-info → bec_widgets-2.13.0.dist-info}/RECORD +13 -12
- pyproject.toml +1 -1
- {bec_widgets-2.12.3.dist-info → bec_widgets-2.13.0.dist-info}/WHEEL +0 -0
- {bec_widgets-2.12.3.dist-info → bec_widgets-2.13.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-2.12.3.dist-info → bec_widgets-2.13.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
name: 'Close stale issues and PRs'
|
2
|
+
on:
|
3
|
+
schedule:
|
4
|
+
- cron: '00 10 * * *'
|
5
|
+
|
6
|
+
jobs:
|
7
|
+
stale:
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
steps:
|
10
|
+
- uses: actions/stale@v9
|
11
|
+
with:
|
12
|
+
stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
|
13
|
+
stale-pr-message: 'This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
|
14
|
+
days-before-stale: 60
|
15
|
+
days-before-close: 7
|
CHANGELOG.md
CHANGED
@@ -1,6 +1,35 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
3
|
|
4
|
+
## v2.13.0 (2025-06-10)
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
- **roi**: Removed roi handle adding/removing inconsistencies
|
9
|
+
([`f904499`](https://github.com/bec-project/bec_widgets/commit/f9044996f6d62cdbb693149934b09625fb39fd55))
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
- **image_roi_tree**: Lock/unlock rois possible through the ROIPropertyTree
|
14
|
+
([`594185d`](https://github.com/bec-project/bec_widgets/commit/594185dde9c73991489f2154507f1c3d3822c5b4))
|
15
|
+
|
16
|
+
- **roi**: Rois can be lock to be not moved by mouse
|
17
|
+
([`46d7e3f`](https://github.com/bec-project/bec_widgets/commit/46d7e3f5170a5f8b444043bc49651921816f7003))
|
18
|
+
|
19
|
+
|
20
|
+
## v2.12.4 (2025-06-10)
|
21
|
+
|
22
|
+
### Bug Fixes
|
23
|
+
|
24
|
+
- **image_roi**: Coordinates are emitted correctly when handles are inverted; closes #672
|
25
|
+
([`9ef418b`](https://github.com/bec-project/bec_widgets/commit/9ef418bf5597d4be77adc3c0c88c1c1619c9aa2f))
|
26
|
+
|
27
|
+
### Continuous Integration
|
28
|
+
|
29
|
+
- Add stale issue job
|
30
|
+
([`b3ce680`](https://github.com/bec-project/bec_widgets/commit/b3ce68070d58cdd76559cbd7db04cdbcc6c1f075))
|
31
|
+
|
32
|
+
|
4
33
|
## v2.12.3 (2025-06-05)
|
5
34
|
|
6
35
|
### Bug Fixes
|
PKG-INFO
CHANGED
bec_widgets/cli/client.py
CHANGED
@@ -530,6 +530,26 @@ class BaseROI(RPCBase):
|
|
530
530
|
str: The current name of the ROI.
|
531
531
|
"""
|
532
532
|
|
533
|
+
@property
|
534
|
+
@rpc_call
|
535
|
+
def movable(self) -> "bool":
|
536
|
+
"""
|
537
|
+
Gets whether this ROI is movable.
|
538
|
+
|
539
|
+
Returns:
|
540
|
+
bool: True if the ROI can be moved, False otherwise.
|
541
|
+
"""
|
542
|
+
|
543
|
+
@movable.setter
|
544
|
+
@rpc_call
|
545
|
+
def movable(self) -> "bool":
|
546
|
+
"""
|
547
|
+
Gets whether this ROI is movable.
|
548
|
+
|
549
|
+
Returns:
|
550
|
+
bool: True if the ROI can be moved, False otherwise.
|
551
|
+
"""
|
552
|
+
|
533
553
|
@property
|
534
554
|
@rpc_call
|
535
555
|
def line_color(self) -> "str":
|
@@ -639,6 +659,26 @@ class CircularROI(RPCBase):
|
|
639
659
|
str: The current name of the ROI.
|
640
660
|
"""
|
641
661
|
|
662
|
+
@property
|
663
|
+
@rpc_call
|
664
|
+
def movable(self) -> "bool":
|
665
|
+
"""
|
666
|
+
Gets whether this ROI is movable.
|
667
|
+
|
668
|
+
Returns:
|
669
|
+
bool: True if the ROI can be moved, False otherwise.
|
670
|
+
"""
|
671
|
+
|
672
|
+
@movable.setter
|
673
|
+
@rpc_call
|
674
|
+
def movable(self) -> "bool":
|
675
|
+
"""
|
676
|
+
Gets whether this ROI is movable.
|
677
|
+
|
678
|
+
Returns:
|
679
|
+
bool: True if the ROI can be moved, False otherwise.
|
680
|
+
"""
|
681
|
+
|
642
682
|
@property
|
643
683
|
@rpc_call
|
644
684
|
def line_color(self) -> "str":
|
@@ -1494,6 +1534,7 @@ class Image(RPCBase):
|
|
1494
1534
|
line_width: "int | None" = 5,
|
1495
1535
|
pos: "tuple[float, float] | None" = (10, 10),
|
1496
1536
|
size: "tuple[float, float] | None" = (50, 50),
|
1537
|
+
movable: "bool" = True,
|
1497
1538
|
**pg_kwargs,
|
1498
1539
|
) -> "RectangularROI | CircularROI":
|
1499
1540
|
"""
|
@@ -1505,6 +1546,7 @@ class Image(RPCBase):
|
|
1505
1546
|
line_width(int): The line width of the ROI.
|
1506
1547
|
pos(tuple): The position of the ROI.
|
1507
1548
|
size(tuple): The size of the ROI.
|
1549
|
+
movable(bool): Whether the ROI is movable.
|
1508
1550
|
**pg_kwargs: Additional arguments for the ROI.
|
1509
1551
|
|
1510
1552
|
Returns:
|
@@ -2664,6 +2706,26 @@ class RectangularROI(RPCBase):
|
|
2664
2706
|
str: The current name of the ROI.
|
2665
2707
|
"""
|
2666
2708
|
|
2709
|
+
@property
|
2710
|
+
@rpc_call
|
2711
|
+
def movable(self) -> "bool":
|
2712
|
+
"""
|
2713
|
+
Gets whether this ROI is movable.
|
2714
|
+
|
2715
|
+
Returns:
|
2716
|
+
bool: True if the ROI can be moved, False otherwise.
|
2717
|
+
"""
|
2718
|
+
|
2719
|
+
@movable.setter
|
2720
|
+
@rpc_call
|
2721
|
+
def movable(self) -> "bool":
|
2722
|
+
"""
|
2723
|
+
Gets whether this ROI is movable.
|
2724
|
+
|
2725
|
+
Returns:
|
2726
|
+
bool: True if the ROI can be moved, False otherwise.
|
2727
|
+
"""
|
2728
|
+
|
2667
2729
|
@property
|
2668
2730
|
@rpc_call
|
2669
2731
|
def line_color(self) -> "str":
|
@@ -559,6 +559,7 @@ class ImageBase(PlotBase):
|
|
559
559
|
line_width: int | None = 5,
|
560
560
|
pos: tuple[float, float] | None = (10, 10),
|
561
561
|
size: tuple[float, float] | None = (50, 50),
|
562
|
+
movable: bool = True,
|
562
563
|
**pg_kwargs,
|
563
564
|
) -> RectangularROI | CircularROI:
|
564
565
|
"""
|
@@ -570,6 +571,7 @@ class ImageBase(PlotBase):
|
|
570
571
|
line_width(int): The line width of the ROI.
|
571
572
|
pos(tuple): The position of the ROI.
|
572
573
|
size(tuple): The size of the ROI.
|
574
|
+
movable(bool): Whether the ROI is movable.
|
573
575
|
**pg_kwargs: Additional arguments for the ROI.
|
574
576
|
|
575
577
|
Returns:
|
@@ -584,6 +586,7 @@ class ImageBase(PlotBase):
|
|
584
586
|
parent_image=self,
|
585
587
|
line_width=line_width,
|
586
588
|
label=name,
|
589
|
+
movable=movable,
|
587
590
|
**pg_kwargs,
|
588
591
|
)
|
589
592
|
elif kind == "circle":
|
@@ -593,6 +596,7 @@ class ImageBase(PlotBase):
|
|
593
596
|
parent_image=self,
|
594
597
|
line_width=line_width,
|
595
598
|
label=name,
|
599
|
+
movable=movable,
|
596
600
|
**pg_kwargs,
|
597
601
|
)
|
598
602
|
else:
|
@@ -601,7 +605,6 @@ class ImageBase(PlotBase):
|
|
601
605
|
# Add to plot and controller (controller assigns color)
|
602
606
|
self.plot_item.addItem(roi)
|
603
607
|
self.roi_controller.add_roi(roi)
|
604
|
-
roi.add_scale_handle()
|
605
608
|
return roi
|
606
609
|
|
607
610
|
def remove_roi(self, roi: int | str):
|
@@ -8,6 +8,7 @@ from qtpy.QtCore import QEvent, Qt
|
|
8
8
|
from qtpy.QtGui import QColor
|
9
9
|
from qtpy.QtWidgets import (
|
10
10
|
QColorDialog,
|
11
|
+
QHBoxLayout,
|
11
12
|
QHeaderView,
|
12
13
|
QSpinBox,
|
13
14
|
QToolButton,
|
@@ -35,6 +36,28 @@ if TYPE_CHECKING:
|
|
35
36
|
from bec_widgets.widgets.plots.image.image import Image
|
36
37
|
|
37
38
|
|
39
|
+
class ROILockButton(QToolButton):
|
40
|
+
"""Keeps its icon and checked state in sync with a single ROI."""
|
41
|
+
|
42
|
+
def __init__(self, roi: BaseROI, parent=None):
|
43
|
+
super().__init__(parent)
|
44
|
+
self.setCheckable(True)
|
45
|
+
self._roi = roi
|
46
|
+
self.clicked.connect(self._toggle)
|
47
|
+
roi.movableChanged.connect(lambda _: self._sync())
|
48
|
+
self._sync()
|
49
|
+
|
50
|
+
def _toggle(self):
|
51
|
+
# checked -> locked -> movable = False
|
52
|
+
self._roi.movable = not self.isChecked()
|
53
|
+
|
54
|
+
def _sync(self):
|
55
|
+
movable = self._roi.movable
|
56
|
+
self.setChecked(not movable)
|
57
|
+
icon = "lock_open_right" if movable else "lock"
|
58
|
+
self.setIcon(material_icon(icon, size=(20, 20), convert_to_pixmap=False))
|
59
|
+
|
60
|
+
|
38
61
|
class ROIPropertyTree(BECWidget, QWidget):
|
39
62
|
"""
|
40
63
|
Two-column tree: [ROI] [Properties]
|
@@ -124,6 +147,24 @@ class ROIPropertyTree(BECWidget, QWidget):
|
|
124
147
|
self.expand_toggle.action.toggled.connect(_exp_toggled)
|
125
148
|
|
126
149
|
self.expand_toggle.action.setChecked(False)
|
150
|
+
|
151
|
+
# Lock/Unlock all ROIs
|
152
|
+
self.lock_all_action = MaterialIconAction(
|
153
|
+
"lock_open_right", "Lock/Unlock all ROIs", checkable=True, parent=self
|
154
|
+
)
|
155
|
+
tb.add_action("Lock/Unlock all ROIs", self.lock_all_action, self)
|
156
|
+
|
157
|
+
def _lock_all(checked: bool):
|
158
|
+
# checked -> everything locked (movable = False)
|
159
|
+
for r in self.controller.rois:
|
160
|
+
r.movable = not checked
|
161
|
+
new_icon = material_icon(
|
162
|
+
"lock" if checked else "lock_open_right", size=(20, 20), convert_to_pixmap=False
|
163
|
+
)
|
164
|
+
self.lock_all_action.action.setIcon(new_icon)
|
165
|
+
|
166
|
+
self.lock_all_action.action.toggled.connect(_lock_all)
|
167
|
+
|
127
168
|
# colormap widget
|
128
169
|
self.cmap = BECColorMapWidget(cmap=self.controller.colormap)
|
129
170
|
tb.addWidget(QWidget()) # spacer
|
@@ -235,18 +276,30 @@ class ROIPropertyTree(BECWidget, QWidget):
|
|
235
276
|
self._temp_roi = None
|
236
277
|
self._set_roi_draw_mode(None)
|
237
278
|
# register via controller
|
238
|
-
final_roi.add_scale_handle()
|
239
279
|
self.controller.add_roi(final_roi)
|
240
280
|
return True
|
241
281
|
return super().eventFilter(obj, event)
|
242
282
|
|
243
283
|
# --------------------------------------------------------- controller slots
|
244
284
|
def _on_roi_added(self, roi: BaseROI):
|
285
|
+
# check the global setting from the toolbar
|
286
|
+
if self.lock_all_action.action.isChecked():
|
287
|
+
roi.movable = False
|
245
288
|
# parent row with blank action column, name in ROI column
|
246
289
|
parent = QTreeWidgetItem(self.tree, ["", "", ""])
|
247
290
|
parent.setText(self.COL_ROI, roi.label)
|
248
291
|
parent.setFlags(parent.flags() | Qt.ItemIsEditable)
|
249
|
-
# ---
|
292
|
+
# --- actions widget (lock/unlock + delete) ---
|
293
|
+
actions_widget = QWidget()
|
294
|
+
actions_layout = QHBoxLayout(actions_widget)
|
295
|
+
actions_layout.setContentsMargins(0, 0, 0, 0)
|
296
|
+
actions_layout.setSpacing(3)
|
297
|
+
|
298
|
+
# lock / unlock toggle
|
299
|
+
lock_btn = ROILockButton(roi, parent=self)
|
300
|
+
actions_layout.addWidget(lock_btn)
|
301
|
+
|
302
|
+
# delete button
|
250
303
|
del_btn = QToolButton()
|
251
304
|
delete_icon = material_icon(
|
252
305
|
"delete",
|
@@ -256,8 +309,11 @@ class ROIPropertyTree(BECWidget, QWidget):
|
|
256
309
|
color=self.DELETE_BUTTON_COLOR,
|
257
310
|
)
|
258
311
|
del_btn.setIcon(delete_icon)
|
259
|
-
self.tree.setItemWidget(parent, self.COL_ACTION, del_btn)
|
260
312
|
del_btn.clicked.connect(lambda _=None, r=roi: self._delete_roi(r))
|
313
|
+
actions_layout.addWidget(del_btn)
|
314
|
+
|
315
|
+
# install composite widget into the tree
|
316
|
+
self.tree.setItemWidget(parent, self.COL_ACTION, actions_widget)
|
261
317
|
# color button
|
262
318
|
color_btn = ColorButtonNative(parent=self, color=roi.line_color)
|
263
319
|
self.tree.setItemWidget(parent, self.COL_PROPS, color_btn)
|
@@ -309,6 +365,12 @@ class ROIPropertyTree(BECWidget, QWidget):
|
|
309
365
|
for c in range(3):
|
310
366
|
self.tree.resizeColumnToContents(c)
|
311
367
|
|
368
|
+
def _toggle_movable(self, roi: BaseROI):
|
369
|
+
"""
|
370
|
+
Toggle the `movable` property of the given ROI.
|
371
|
+
"""
|
372
|
+
roi.movable = not roi.movable
|
373
|
+
|
312
374
|
def _on_roi_removed(self, roi: BaseROI):
|
313
375
|
item = self.roi_items.pop(roi, None)
|
314
376
|
if item:
|
@@ -345,7 +407,7 @@ if __name__ == "__main__": # pragma: no cover
|
|
345
407
|
import sys
|
346
408
|
|
347
409
|
import numpy as np
|
348
|
-
from qtpy.QtWidgets import QApplication
|
410
|
+
from qtpy.QtWidgets import QApplication
|
349
411
|
|
350
412
|
from bec_widgets.widgets.plots.image.image import Image
|
351
413
|
|
@@ -104,9 +104,12 @@ class BaseROI(BECConnector):
|
|
104
104
|
|
105
105
|
nameChanged = Signal(str)
|
106
106
|
penChanged = Signal()
|
107
|
+
movableChanged = Signal(bool)
|
107
108
|
USER_ACCESS = [
|
108
109
|
"label",
|
109
110
|
"label.setter",
|
111
|
+
"movable",
|
112
|
+
"movable.setter",
|
110
113
|
"line_color",
|
111
114
|
"line_color.setter",
|
112
115
|
"line_width",
|
@@ -127,6 +130,7 @@ class BaseROI(BECConnector):
|
|
127
130
|
label: str | None = None,
|
128
131
|
line_color: str | None = None,
|
129
132
|
line_width: int = 5,
|
133
|
+
movable: bool = True,
|
130
134
|
# all remaining pg.*ROI kwargs (pos, size, pen, …)
|
131
135
|
**pg_kwargs,
|
132
136
|
):
|
@@ -155,6 +159,7 @@ class BaseROI(BECConnector):
|
|
155
159
|
gui_id=gui_id,
|
156
160
|
removable=True,
|
157
161
|
invertible=True,
|
162
|
+
movable=movable,
|
158
163
|
**pg_kwargs,
|
159
164
|
)
|
160
165
|
|
@@ -162,8 +167,14 @@ class BaseROI(BECConnector):
|
|
162
167
|
self._line_color = line_color or "#ffffff"
|
163
168
|
self._line_width = line_width
|
164
169
|
self._description = True
|
170
|
+
self._movable = movable
|
165
171
|
self.setPen(mkPen(self._line_color, width=self._line_width))
|
166
172
|
|
173
|
+
# Reset Handles to avoid inherited handles from pyqtgraph
|
174
|
+
self.remove_scale_handles() # remove any existing handles from pyqtgraph.RectROI
|
175
|
+
if movable:
|
176
|
+
self.add_scale_handle() # add custom scale handles
|
177
|
+
|
167
178
|
def set_parent(self, parent: Image):
|
168
179
|
"""
|
169
180
|
Sets the parent image for this ROI.
|
@@ -182,6 +193,40 @@ class BaseROI(BECConnector):
|
|
182
193
|
"""
|
183
194
|
return self.parent_image
|
184
195
|
|
196
|
+
@property
|
197
|
+
def movable(self) -> bool:
|
198
|
+
"""
|
199
|
+
Gets whether this ROI is movable.
|
200
|
+
|
201
|
+
Returns:
|
202
|
+
bool: True if the ROI can be moved, False otherwise.
|
203
|
+
"""
|
204
|
+
return self._movable
|
205
|
+
|
206
|
+
@movable.setter
|
207
|
+
def movable(self, value: bool):
|
208
|
+
"""
|
209
|
+
Sets whether this ROI is movable.
|
210
|
+
|
211
|
+
If the new value is different from the current value, this method updates
|
212
|
+
the internal state and emits the penChanged signal.
|
213
|
+
|
214
|
+
Args:
|
215
|
+
value (bool): True to make the ROI movable, False to make it fixed.
|
216
|
+
"""
|
217
|
+
if value != self._movable:
|
218
|
+
self._movable = value
|
219
|
+
# All relevant properties from pyqtgraph to block movement
|
220
|
+
self.translatable = value
|
221
|
+
self.rotatable = value
|
222
|
+
self.resizable = value
|
223
|
+
self.removable = value
|
224
|
+
if value:
|
225
|
+
self.add_scale_handle() # add custom scale handles
|
226
|
+
else:
|
227
|
+
self.remove_scale_handles() # remove custom scale handles
|
228
|
+
self.movableChanged.emit(value)
|
229
|
+
|
185
230
|
@property
|
186
231
|
def label(self) -> str:
|
187
232
|
"""
|
@@ -337,8 +382,18 @@ class BaseROI(BECConnector):
|
|
337
382
|
)
|
338
383
|
|
339
384
|
def add_scale_handle(self):
|
385
|
+
"""Add scale handles to the ROI."""
|
340
386
|
return
|
341
387
|
|
388
|
+
def remove_scale_handles(self):
|
389
|
+
"""Remove all scale handles from the ROI."""
|
390
|
+
handles = self.handles
|
391
|
+
for i in range(len(handles)):
|
392
|
+
try:
|
393
|
+
self.removeHandle(0)
|
394
|
+
except IndexError:
|
395
|
+
continue
|
396
|
+
|
342
397
|
def set_position(self, x: float, y: float):
|
343
398
|
"""
|
344
399
|
Sets the position of the ROI.
|
@@ -355,12 +410,7 @@ class BaseROI(BECConnector):
|
|
355
410
|
if controller and self in controller.rois:
|
356
411
|
controller.remove_roi(self)
|
357
412
|
return # controller will call back into this method once deregistered
|
358
|
-
|
359
|
-
for i in range(len(handles)):
|
360
|
-
try:
|
361
|
-
self.removeHandle(0)
|
362
|
-
except IndexError:
|
363
|
-
continue
|
413
|
+
self.remove_scale_handles()
|
364
414
|
self.rpc_register.remove_rpc(self)
|
365
415
|
self.parent_image.plot_item.removeItem(self)
|
366
416
|
viewBox = self.parent_plot_item.vb
|
@@ -399,6 +449,7 @@ class RectangularROI(BaseROI, pg.RectROI):
|
|
399
449
|
label: str | None = None,
|
400
450
|
line_color: str | None = None,
|
401
451
|
line_width: int = 5,
|
452
|
+
movable: bool = True,
|
402
453
|
resize_handles: bool = True,
|
403
454
|
**extra_pg,
|
404
455
|
):
|
@@ -429,6 +480,7 @@ class RectangularROI(BaseROI, pg.RectROI):
|
|
429
480
|
pos=pos,
|
430
481
|
size=size,
|
431
482
|
pen=pen,
|
483
|
+
movable=movable,
|
432
484
|
**extra_pg,
|
433
485
|
)
|
434
486
|
|
@@ -437,6 +489,23 @@ class RectangularROI(BaseROI, pg.RectROI):
|
|
437
489
|
self.hoverPen = fn.mkPen(color=(255, 0, 0), width=3, style=QtCore.Qt.DashLine)
|
438
490
|
self.handleHoverPen = fn.mkPen("lime", width=4)
|
439
491
|
|
492
|
+
def _normalized_edges(self) -> tuple[float, float, float, float]:
|
493
|
+
"""
|
494
|
+
Return rectangle edges as (left, bottom, right, top) with consistent
|
495
|
+
ordering even when the ROI has been inverted by its scale handles.
|
496
|
+
|
497
|
+
Returns:
|
498
|
+
tuple: A tuple containing the left, bottom, right, and top edges
|
499
|
+
of the ROI rectangle in normalized coordinates.
|
500
|
+
"""
|
501
|
+
x0, y0 = self.pos().x(), self.pos().y()
|
502
|
+
w, h = self.state["size"]
|
503
|
+
x_left = min(x0, x0 + w)
|
504
|
+
x_right = max(x0, x0 + w)
|
505
|
+
y_bottom = min(y0, y0 + h)
|
506
|
+
y_top = max(y0, y0 + h)
|
507
|
+
return x_left, y_bottom, x_right, y_top
|
508
|
+
|
440
509
|
def add_scale_handle(self):
|
441
510
|
"""
|
442
511
|
Add scale handles at every corner and edge of the ROI.
|
@@ -458,24 +527,17 @@ class RectangularROI(BaseROI, pg.RectROI):
|
|
458
527
|
self.addScaleHandle([0, 0.5], [1, 0.5]) # left edge
|
459
528
|
self.addScaleHandle([1, 0.5], [0, 0.5]) # right edge
|
460
529
|
|
461
|
-
self.handlePen = fn.mkPen("#ffff00", width=5) # bright yellow outline
|
462
|
-
self.handleHoverPen = fn.mkPen("#00ffff", width=4) # cyan, thicker when hovered
|
463
|
-
self.handleBrush = (200, 200, 0, 120) # semi-transparent fill
|
464
|
-
self.handleHoverBrush = (0, 255, 255, 160)
|
465
|
-
|
466
530
|
def _on_region_changed(self):
|
467
531
|
"""
|
468
|
-
Handles ROI region
|
532
|
+
Handles changes to the ROI's region.
|
469
533
|
|
470
534
|
This method is called whenever the ROI's position or size changes.
|
471
535
|
It calculates the new corner coordinates and emits the edgesChanged signal
|
472
536
|
with the updated coordinates.
|
473
537
|
"""
|
474
|
-
|
475
|
-
|
476
|
-
self.
|
477
|
-
viewBox = self.parent_plot_item.vb
|
478
|
-
viewBox.update()
|
538
|
+
x_left, y_bottom, x_right, y_top = self._normalized_edges()
|
539
|
+
self.edgesChanged.emit(x_left, y_bottom, x_right, y_top)
|
540
|
+
self.parent_plot_item.vb.update()
|
479
541
|
|
480
542
|
def mouseDragEvent(self, ev):
|
481
543
|
"""
|
@@ -489,9 +551,8 @@ class RectangularROI(BaseROI, pg.RectROI):
|
|
489
551
|
"""
|
490
552
|
super().mouseDragEvent(ev)
|
491
553
|
if ev.isFinish():
|
492
|
-
|
493
|
-
|
494
|
-
self.edgesReleased.emit(x0, y0, x0 + w, y0 + h)
|
554
|
+
x_left, y_bottom, x_right, y_top = self._normalized_edges()
|
555
|
+
self.edgesReleased.emit(x_left, y_bottom, x_right, y_top)
|
495
556
|
|
496
557
|
def get_coordinates(self, typed: bool | None = None) -> dict | tuple:
|
497
558
|
"""
|
@@ -510,17 +571,16 @@ class RectangularROI(BaseROI, pg.RectROI):
|
|
510
571
|
if typed is None:
|
511
572
|
typed = self.description
|
512
573
|
|
513
|
-
|
514
|
-
|
515
|
-
x1, y1 = x0 + w, y0 + h
|
574
|
+
x_left, y_bottom, x_right, y_top = self._normalized_edges()
|
575
|
+
|
516
576
|
if typed:
|
517
577
|
return {
|
518
|
-
"bottom_left": (
|
519
|
-
"bottom_right": (
|
520
|
-
"top_left": (
|
521
|
-
"top_right": (
|
578
|
+
"bottom_left": (x_left, y_bottom),
|
579
|
+
"bottom_right": (x_right, y_bottom),
|
580
|
+
"top_left": (x_left, y_top),
|
581
|
+
"top_right": (x_right, y_top),
|
522
582
|
}
|
523
|
-
return (
|
583
|
+
return (x_left, y_bottom), (x_right, y_bottom), (x_left, y_top), (x_right, y_top)
|
524
584
|
|
525
585
|
def _lookup_scene_image(self):
|
526
586
|
"""
|
@@ -568,6 +628,7 @@ class CircularROI(BaseROI, pg.CircleROI):
|
|
568
628
|
label: str | None = None,
|
569
629
|
line_color: str | None = None,
|
570
630
|
line_width: int = 5,
|
631
|
+
movable: bool = True,
|
571
632
|
**extra_pg,
|
572
633
|
):
|
573
634
|
"""
|
@@ -599,10 +660,19 @@ class CircularROI(BaseROI, pg.CircleROI):
|
|
599
660
|
pos=pos,
|
600
661
|
size=size,
|
601
662
|
pen=pen,
|
663
|
+
movable=movable,
|
602
664
|
**extra_pg,
|
603
665
|
)
|
604
666
|
self.sigRegionChanged.connect(self._on_region_changed)
|
605
667
|
self._adorner = LabelAdorner(self)
|
668
|
+
self.hoverPen = fn.mkPen(color=(255, 0, 0), width=3, style=QtCore.Qt.DashLine)
|
669
|
+
self.handleHoverPen = fn.mkPen("lime", width=4)
|
670
|
+
|
671
|
+
def add_scale_handle(self):
|
672
|
+
"""
|
673
|
+
Adds scale handles to the circular ROI.
|
674
|
+
"""
|
675
|
+
self._addHandles() # wrapper around pg.CircleROI._addHandles
|
606
676
|
|
607
677
|
def _on_region_changed(self):
|
608
678
|
"""
|
@@ -654,7 +724,7 @@ class CircularROI(BaseROI, pg.CircleROI):
|
|
654
724
|
if typed is None:
|
655
725
|
typed = self.description
|
656
726
|
|
657
|
-
d = self.state["size"][0]
|
727
|
+
d = abs(self.state["size"][0])
|
658
728
|
cx = self.pos().x() + d / 2
|
659
729
|
cy = self.pos().y() + d / 2
|
660
730
|
|
@@ -2,11 +2,11 @@
|
|
2
2
|
.gitlab-ci.yml,sha256=1nMYldzVk0tFkBWYTcUjumOrdSADASheWOAc0kOFDYs,9509
|
3
3
|
.pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
|
4
4
|
.readthedocs.yaml,sha256=ivqg3HTaOxNbEW3bzWh9MXAkrekuGoNdj0Mj3SdRYuw,639
|
5
|
-
CHANGELOG.md,sha256
|
5
|
+
CHANGELOG.md,sha256=lpk84iB31ZE4vtdCbH3gdT4uaXDPjBtS5wRWdWVfAWs,298987
|
6
6
|
LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
|
7
|
-
PKG-INFO,sha256=
|
7
|
+
PKG-INFO,sha256=V1mspizllUKCge8RUbKpm3syVXrjTQ2o-HNcjmuGycA,1252
|
8
8
|
README.md,sha256=oY5Jc1uXehRASuwUJ0umin2vfkFh7tHF-LLruHTaQx0,3560
|
9
|
-
pyproject.toml,sha256=
|
9
|
+
pyproject.toml,sha256=tpSXUHRyxcZAanH3gC6f-6olaIoOknm1-uDLA3nyt2I,2827
|
10
10
|
.git_hooks/pre-commit,sha256=n3RofIZHJl8zfJJIUomcMyYGFi_rwq4CC19z0snz3FI,286
|
11
11
|
.github/pull_request_template.md,sha256=F_cJXzooWMFgMGtLK-7KeGcQt0B4AYFse5oN0zQ9p6g,801
|
12
12
|
.github/ISSUE_TEMPLATE/bug_report.yml,sha256=WdRnt7HGxvsIBLzhkaOWNfg8IJQYa_oV9_F08Ym6znQ,1081
|
@@ -23,6 +23,7 @@ pyproject.toml,sha256=yNICdyTZSQATRSjrmwHJ2Jh832NbrsObMzGZmc7t2wU,2827
|
|
23
23
|
.github/workflows/pytest-matrix.yml,sha256=0gL5wNPJKJF1JapqstlYNYiJ44ko05uaTD7epa7smVw,1834
|
24
24
|
.github/workflows/pytest.yml,sha256=hYOB7XK_79MaiELaTH7zDT-WRw-pRDe4mHyB_WfcGDc,1747
|
25
25
|
.github/workflows/semantic_release.yml,sha256=pdkv1rVG3YgG7yu4nNWvbGZifH4bqO1eh3pM0KHRK04,3720
|
26
|
+
.github/workflows/stale-issues.yml,sha256=Q1n-RO1FjEtuWJFRXpqzWnl7Y6EUn0T2ZSRdZQadJA4,568
|
26
27
|
.github/workflows/sync-issues-pr.yml,sha256=Wn68mK8h0xhwIX1VZPEqHEJmku2BRPOrQ6zQsuCIBJk,1039
|
27
28
|
bec_widgets/__init__.py,sha256=mZhbU6zfFt8-A7q_do74ie89budSevwpKZ6FKtEBdmo,170
|
28
29
|
bec_widgets/applications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -35,7 +36,7 @@ bec_widgets/assets/app_icons/bec_widgets_icon.png,sha256=K8dgGwIjalDh9PRHUsSQBqg
|
|
35
36
|
bec_widgets/assets/app_icons/ui_loader_tile.png,sha256=qSK3XHqvnAVGV9Q0ulORcGFbXJ9LDq2uz8l9uTtMsNk,1812476
|
36
37
|
bec_widgets/assets/app_icons/widget_launch_tile.png,sha256=bWsICHFfSe9-ESUj3AwlE95dDOea-f6M-s9fBapsxB4,2252911
|
37
38
|
bec_widgets/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
38
|
-
bec_widgets/cli/client.py,sha256=
|
39
|
+
bec_widgets/cli/client.py,sha256=D0mZjM2VE_hqi6QYuzNqwZs9VTRlO5Ral1V4mLjkhX0,99324
|
39
40
|
bec_widgets/cli/client_utils.py,sha256=F2hyt--jL53bN8NoWifNUMqwwx5FbpS6I1apERdTRzM,18114
|
40
41
|
bec_widgets/cli/generate_cli.py,sha256=K_wMxo2XBUn92SnY3dSrlyUn8ax6Y20QBGCuP284DsQ,10986
|
41
42
|
bec_widgets/cli/server.py,sha256=h7QyBOOGjyrP_fxJIIOSEMc4E06cLG0JyaofjNV6oCA,5671
|
@@ -258,14 +259,14 @@ bec_widgets/widgets/plots/plot_base.py,sha256=GETUsx51BE_Tuop8bC-KiFVrkR82TJ5S0c
|
|
258
259
|
bec_widgets/widgets/plots/image/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
259
260
|
bec_widgets/widgets/plots/image/image.py,sha256=wrI9C2xNEoQmCWoKjlSdRHroWHffIg_DFT4fNDTgAyE,20299
|
260
261
|
bec_widgets/widgets/plots/image/image.pyproject,sha256=_sRCIu4MNgToaB4D7tUMWq3xKX6T2VoRS3UzGNIseHQ,23
|
261
|
-
bec_widgets/widgets/plots/image/image_base.py,sha256=
|
262
|
+
bec_widgets/widgets/plots/image/image_base.py,sha256=yvL3tfHKlkBPb5yVHKe7Ce-qx9MGTRU6KpGAe18dkWg,36224
|
262
263
|
bec_widgets/widgets/plots/image/image_item.py,sha256=rkL1o35Pgs1zhvv2wpSG1gt_bjP5kO4Z1oy6d2q-Yls,8577
|
263
264
|
bec_widgets/widgets/plots/image/image_plugin.py,sha256=R0Hzh2GgYlfZLPZwOMgqLKKIA5DxEnTcSrbI7zTe-tI,1204
|
264
265
|
bec_widgets/widgets/plots/image/image_processor.py,sha256=0qsQIyB__xxBwNIoXhFbB0wSiB8n7n_oX4cvfFsUzOs,4304
|
265
266
|
bec_widgets/widgets/plots/image/image_roi_plot.py,sha256=5CWy_eC-GS2ZLJTj2ItrVCoKhPN2g3fx6L4ktf5yVFQ,1191
|
266
267
|
bec_widgets/widgets/plots/image/register_image.py,sha256=0rvFyAMGRZcknc7nMVwsMGSfY8L2j9cdtQhbO5TMzeM,455
|
267
268
|
bec_widgets/widgets/plots/image/setting_widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
268
|
-
bec_widgets/widgets/plots/image/setting_widgets/image_roi_tree.py,sha256=
|
269
|
+
bec_widgets/widgets/plots/image/setting_widgets/image_roi_tree.py,sha256=uOzsj-2582LO7lOOw3c2En5UcaAo1br8QJJk4XP90Bc,16368
|
269
270
|
bec_widgets/widgets/plots/image/toolbar_bundles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
270
271
|
bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py,sha256=ezs2TWZCz-3npbIFEiYuHNiuSvJptJznTEZWD_CX3po,4705
|
271
272
|
bec_widgets/widgets/plots/image/toolbar_bundles/processing.py,sha256=99hgd1q86ZUhQYTTsFCk3Ml8oAEeZJy-WqdmsMO4bzA,3367
|
@@ -290,7 +291,7 @@ bec_widgets/widgets/plots/multi_waveform/settings/multi_waveform_controls.ui,sha
|
|
290
291
|
bec_widgets/widgets/plots/multi_waveform/toolbar_bundles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
291
292
|
bec_widgets/widgets/plots/multi_waveform/toolbar_bundles/monitor_selection.py,sha256=lGreAkRBd-A4X_wqYZiKyGDmb_3uzLunjSju9A2PjWw,2532
|
292
293
|
bec_widgets/widgets/plots/roi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
293
|
-
bec_widgets/widgets/plots/roi/image_roi.py,sha256=
|
294
|
+
bec_widgets/widgets/plots/roi/image_roi.py,sha256=ytD_i4gG6DsUwBJGjrUE-yQrjboiSDvlqMy0_BPVFoU,34819
|
294
295
|
bec_widgets/widgets/plots/scatter_waveform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
295
296
|
bec_widgets/widgets/plots/scatter_waveform/register_scatter_waveform.py,sha256=KttVjlAK3PfP9tyMfLnqEm6kphap8NZyqyaRry8oebY,514
|
296
297
|
bec_widgets/widgets/plots/scatter_waveform/scatter_curve.py,sha256=nCyZ_6EunS1m5XkLB-CwfBV9L4IX04D9SpHlHc8zG_I,6763
|
@@ -407,8 +408,8 @@ bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py,sha256=O
|
|
407
408
|
bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.pyproject,sha256=Lbi9zb6HNlIq14k6hlzR-oz6PIFShBuF7QxE6d87d64,34
|
408
409
|
bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button_plugin.py,sha256=CzChz2SSETYsR8-36meqWnsXCT-FIy_J_xeU5coWDY8,1350
|
409
410
|
bec_widgets/widgets/utility/visual/dark_mode_button/register_dark_mode_button.py,sha256=rMpZ1CaoucwobgPj1FuKTnt07W82bV1GaSYdoqcdMb8,521
|
410
|
-
bec_widgets-2.
|
411
|
-
bec_widgets-2.
|
412
|
-
bec_widgets-2.
|
413
|
-
bec_widgets-2.
|
414
|
-
bec_widgets-2.
|
411
|
+
bec_widgets-2.13.0.dist-info/METADATA,sha256=V1mspizllUKCge8RUbKpm3syVXrjTQ2o-HNcjmuGycA,1252
|
412
|
+
bec_widgets-2.13.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
413
|
+
bec_widgets-2.13.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
|
414
|
+
bec_widgets-2.13.0.dist-info/licenses/LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
|
415
|
+
bec_widgets-2.13.0.dist-info/RECORD,,
|
pyproject.toml
CHANGED
File without changes
|
File without changes
|
File without changes
|