drawsvg-ui 0.4.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.
- app_info.py +61 -0
- canvas_view.py +2506 -0
- constants.py +49 -0
- drawsvg_ui-0.4.0.dist-info/METADATA +86 -0
- drawsvg_ui-0.4.0.dist-info/RECORD +28 -0
- drawsvg_ui-0.4.0.dist-info/WHEEL +5 -0
- drawsvg_ui-0.4.0.dist-info/entry_points.txt +2 -0
- drawsvg_ui-0.4.0.dist-info/top_level.txt +11 -0
- export_drawsvg.py +1700 -0
- import_drawsvg.py +807 -0
- items/__init__.py +66 -0
- items/base.py +606 -0
- items/labels.py +247 -0
- items/shapes/__init__.py +20 -0
- items/shapes/curves.py +139 -0
- items/shapes/lines.py +439 -0
- items/shapes/polygons.py +359 -0
- items/shapes/rects.py +310 -0
- items/text.py +331 -0
- items/widgets/__init__.py +5 -0
- items/widgets/folder_tree.py +415 -0
- main.py +23 -0
- main_window.py +254 -0
- palette.py +556 -0
- properties_panel.py +1406 -0
- ui/__init__.py +1 -0
- ui/main_window.ui +157 -0
- ui/properties_panel.ui +996 -0
items/labels.py
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""Label mixins and helpers shared by shape items."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from PySide6 import QtCore, QtGui, QtWidgets
|
|
8
|
+
from constants import DEFAULT_TEXT_COLOR, DEFAULT_FONT_FAMILY
|
|
9
|
+
if TYPE_CHECKING: # pragma: no cover - only for type checkers
|
|
10
|
+
from .shapes.rects import RectItem
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class _ShapeLabelItem(QtWidgets.QGraphicsTextItem):
|
|
14
|
+
def __init__(self, parent: "RectItem") -> None:
|
|
15
|
+
super().__init__("", parent)
|
|
16
|
+
self.setFont(QtGui.QFont(DEFAULT_FONT_FAMILY))
|
|
17
|
+
self.setDefaultTextColor(DEFAULT_TEXT_COLOR)
|
|
18
|
+
self.setVisible(False)
|
|
19
|
+
self.setAcceptedMouseButtons(QtCore.Qt.MouseButton.NoButton)
|
|
20
|
+
self.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.NoTextInteraction)
|
|
21
|
+
self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsSelectable, False)
|
|
22
|
+
self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsMovable, False)
|
|
23
|
+
self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIgnoresTransformations, False)
|
|
24
|
+
self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsFocusable, True)
|
|
25
|
+
self.setZValue(1.0)
|
|
26
|
+
doc = self.document()
|
|
27
|
+
doc.setDocumentMargin(0.0)
|
|
28
|
+
text_option = doc.defaultTextOption()
|
|
29
|
+
text_option.setWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere)
|
|
30
|
+
doc.setDefaultTextOption(text_option)
|
|
31
|
+
|
|
32
|
+
def focusOutEvent(self, event: QtGui.QFocusEvent) -> None: # type: ignore[override]
|
|
33
|
+
super().focusOutEvent(event)
|
|
34
|
+
parent = self.parentItem()
|
|
35
|
+
if isinstance(parent, ShapeLabelMixin):
|
|
36
|
+
parent._finish_label_edit()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ShapeLabelMixin:
|
|
40
|
+
def _init_shape_label(self) -> None:
|
|
41
|
+
self._label_color_override: QtGui.QColor | None = None
|
|
42
|
+
self._label_base_color: QtGui.QColor = QtGui.QColor(DEFAULT_TEXT_COLOR)
|
|
43
|
+
self._label = _ShapeLabelItem(self)
|
|
44
|
+
self._label_h_align = "center"
|
|
45
|
+
self._label_v_align = "middle"
|
|
46
|
+
self._label_padding = 8.0
|
|
47
|
+
self._label.document().contentsChanged.connect(self._update_label_geometry)
|
|
48
|
+
self._apply_label_alignment()
|
|
49
|
+
self._update_label_color()
|
|
50
|
+
self._update_label_geometry()
|
|
51
|
+
|
|
52
|
+
def _label_base_rect(self) -> QtCore.QRectF:
|
|
53
|
+
raise NotImplementedError
|
|
54
|
+
|
|
55
|
+
def label_text(self) -> str:
|
|
56
|
+
return self._label.toPlainText()
|
|
57
|
+
|
|
58
|
+
def set_label_text(self, text: str) -> None:
|
|
59
|
+
self._label.setPlainText(text)
|
|
60
|
+
self._label.setVisible(bool(text.strip()))
|
|
61
|
+
self._update_label_geometry()
|
|
62
|
+
|
|
63
|
+
def has_label(self) -> bool:
|
|
64
|
+
return bool(self._label.toPlainText().strip())
|
|
65
|
+
|
|
66
|
+
def label_alignment(self) -> tuple[str, str]:
|
|
67
|
+
return self._label_h_align, self._label_v_align
|
|
68
|
+
|
|
69
|
+
def set_label_alignment(
|
|
70
|
+
self,
|
|
71
|
+
*,
|
|
72
|
+
horizontal: str | None = None,
|
|
73
|
+
vertical: str | None = None,
|
|
74
|
+
) -> None:
|
|
75
|
+
valid_h = {"left", "center", "right"}
|
|
76
|
+
valid_v = {"top", "middle", "bottom"}
|
|
77
|
+
changed = False
|
|
78
|
+
if horizontal in valid_h and horizontal != self._label_h_align:
|
|
79
|
+
self._label_h_align = horizontal
|
|
80
|
+
changed = True
|
|
81
|
+
if vertical in valid_v and vertical != self._label_v_align:
|
|
82
|
+
self._label_v_align = vertical
|
|
83
|
+
changed = True
|
|
84
|
+
if changed:
|
|
85
|
+
self._apply_label_alignment()
|
|
86
|
+
self._update_label_geometry()
|
|
87
|
+
|
|
88
|
+
def edit_label(self) -> None:
|
|
89
|
+
self._begin_label_edit()
|
|
90
|
+
|
|
91
|
+
def set_label_font_pixel_size(self, value: float) -> None:
|
|
92
|
+
font = QtGui.QFont(self._label.font())
|
|
93
|
+
font.setPixelSize(int(value))
|
|
94
|
+
self._label.setFont(font)
|
|
95
|
+
self._update_label_geometry()
|
|
96
|
+
|
|
97
|
+
def label_color(self) -> QtGui.QColor:
|
|
98
|
+
return QtGui.QColor(self._label.defaultTextColor())
|
|
99
|
+
|
|
100
|
+
def label_has_custom_color(self) -> bool:
|
|
101
|
+
override = getattr(self, "_label_color_override", None)
|
|
102
|
+
return isinstance(override, QtGui.QColor) and override.isValid()
|
|
103
|
+
|
|
104
|
+
def set_label_color(self, color: QtGui.QColor | str) -> None:
|
|
105
|
+
qcolor = QtGui.QColor(color)
|
|
106
|
+
if not qcolor.isValid():
|
|
107
|
+
return
|
|
108
|
+
self._label_color_override = QtGui.QColor(qcolor)
|
|
109
|
+
self._label.setDefaultTextColor(self._label_color_override)
|
|
110
|
+
|
|
111
|
+
def reset_label_color(
|
|
112
|
+
self,
|
|
113
|
+
*,
|
|
114
|
+
update: bool = True,
|
|
115
|
+
base_color: QtGui.QColor | str | None = None,
|
|
116
|
+
) -> None:
|
|
117
|
+
self._label_color_override = None
|
|
118
|
+
if base_color is None:
|
|
119
|
+
base = QtGui.QColor(DEFAULT_TEXT_COLOR)
|
|
120
|
+
else:
|
|
121
|
+
base = QtGui.QColor(base_color)
|
|
122
|
+
if not base.isValid():
|
|
123
|
+
base = QtGui.QColor(DEFAULT_TEXT_COLOR)
|
|
124
|
+
self._label_base_color = base
|
|
125
|
+
if update:
|
|
126
|
+
self._update_label_color()
|
|
127
|
+
|
|
128
|
+
def label_item(self) -> QtWidgets.QGraphicsTextItem:
|
|
129
|
+
return self._label
|
|
130
|
+
|
|
131
|
+
def _apply_label_alignment(self) -> None:
|
|
132
|
+
option = self._label.document().defaultTextOption()
|
|
133
|
+
if self._label_h_align == "left":
|
|
134
|
+
option.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
|
|
135
|
+
elif self._label_h_align == "right":
|
|
136
|
+
option.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
|
|
137
|
+
else:
|
|
138
|
+
option.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter)
|
|
139
|
+
self._label.document().setDefaultTextOption(option)
|
|
140
|
+
|
|
141
|
+
def _label_available_rect(self) -> QtCore.QRectF:
|
|
142
|
+
rect = QtCore.QRectF(self._label_base_rect())
|
|
143
|
+
pad = self._label_padding
|
|
144
|
+
rect.adjust(pad, pad, -pad, -pad)
|
|
145
|
+
if rect.width() <= 0.0:
|
|
146
|
+
rect.setWidth(1.0)
|
|
147
|
+
if rect.height() <= 0.0:
|
|
148
|
+
rect.setHeight(1.0)
|
|
149
|
+
return rect
|
|
150
|
+
|
|
151
|
+
def _update_label_geometry(self) -> None:
|
|
152
|
+
rect = self._label_available_rect()
|
|
153
|
+
self._label.setTextWidth(rect.width())
|
|
154
|
+
br = self._label.boundingRect()
|
|
155
|
+
left = br.left()
|
|
156
|
+
right = br.right()
|
|
157
|
+
top = br.top()
|
|
158
|
+
bottom = br.bottom()
|
|
159
|
+
width = br.width()
|
|
160
|
+
height = br.height()
|
|
161
|
+
|
|
162
|
+
if self._label_h_align == "left":
|
|
163
|
+
x = rect.left() - left
|
|
164
|
+
elif self._label_h_align == "right":
|
|
165
|
+
x = rect.right() - right
|
|
166
|
+
else:
|
|
167
|
+
target_cx = rect.center().x()
|
|
168
|
+
x = target_cx - (left + width / 2.0)
|
|
169
|
+
|
|
170
|
+
if self._label_v_align == "top":
|
|
171
|
+
y = rect.top() - top
|
|
172
|
+
elif self._label_v_align == "bottom":
|
|
173
|
+
y = rect.bottom() - bottom
|
|
174
|
+
else:
|
|
175
|
+
target_cy = rect.center().y()
|
|
176
|
+
y = target_cy - (top + height / 2.0)
|
|
177
|
+
|
|
178
|
+
base_rect = self._label_base_rect()
|
|
179
|
+
min_x = base_rect.left() - left
|
|
180
|
+
max_x = base_rect.right() - right
|
|
181
|
+
min_y = base_rect.top() - top
|
|
182
|
+
max_y = base_rect.bottom() - bottom
|
|
183
|
+
x = max(min_x, min(x, max_x))
|
|
184
|
+
y = max(min_y, min(y, max_y))
|
|
185
|
+
self._label.setPos(x, y)
|
|
186
|
+
self._label.setVisible(self.has_label())
|
|
187
|
+
|
|
188
|
+
def _update_label_color(self) -> None:
|
|
189
|
+
if hasattr(self, "_label") and self._label is not None:
|
|
190
|
+
override = getattr(self, "_label_color_override", None)
|
|
191
|
+
if isinstance(override, QtGui.QColor) and override.isValid():
|
|
192
|
+
target = QtGui.QColor(override)
|
|
193
|
+
else:
|
|
194
|
+
base = getattr(self, "_label_base_color", QtGui.QColor(DEFAULT_TEXT_COLOR))
|
|
195
|
+
target = QtGui.QColor(base)
|
|
196
|
+
self._label.setDefaultTextColor(target)
|
|
197
|
+
|
|
198
|
+
def _begin_label_edit(self) -> None:
|
|
199
|
+
self._label.setVisible(True)
|
|
200
|
+
self._apply_label_alignment()
|
|
201
|
+
self._label.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.TextEditorInteraction)
|
|
202
|
+
self._label.setAcceptedMouseButtons(QtCore.Qt.MouseButton.LeftButton)
|
|
203
|
+
self._label.setFocus(QtCore.Qt.FocusReason.MouseFocusReason)
|
|
204
|
+
cursor = self._label.textCursor()
|
|
205
|
+
cursor.select(QtGui.QTextCursor.SelectionType.Document)
|
|
206
|
+
self._label.setTextCursor(cursor)
|
|
207
|
+
|
|
208
|
+
def _clear_label_highlight(self) -> None:
|
|
209
|
+
if not hasattr(self, "_label") or self._label is None:
|
|
210
|
+
return
|
|
211
|
+
cursor = self._label.textCursor()
|
|
212
|
+
if cursor.hasSelection():
|
|
213
|
+
cursor.clearSelection()
|
|
214
|
+
self._label.setTextCursor(cursor)
|
|
215
|
+
self._label.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.NoTextInteraction)
|
|
216
|
+
self._label.setAcceptedMouseButtons(QtCore.Qt.MouseButton.NoButton)
|
|
217
|
+
self._label.clearFocus()
|
|
218
|
+
|
|
219
|
+
def _finish_label_edit(self) -> None:
|
|
220
|
+
if not hasattr(self, "_label") or self._label is None:
|
|
221
|
+
return
|
|
222
|
+
self._clear_label_highlight()
|
|
223
|
+
if not self.has_label():
|
|
224
|
+
self._label.setVisible(False)
|
|
225
|
+
self._update_label_geometry()
|
|
226
|
+
|
|
227
|
+
def copy_label_from(self, other: "ShapeLabelMixin") -> None:
|
|
228
|
+
if not isinstance(other, ShapeLabelMixin):
|
|
229
|
+
return
|
|
230
|
+
self._label_h_align, self._label_v_align = other.label_alignment()
|
|
231
|
+
other_label = other.label_item()
|
|
232
|
+
self._label.setFont(QtGui.QFont(other_label.font()))
|
|
233
|
+
if other.label_has_custom_color():
|
|
234
|
+
self.set_label_color(other.label_color())
|
|
235
|
+
else:
|
|
236
|
+
self.reset_label_color(update=True, base_color=other_label.defaultTextColor())
|
|
237
|
+
self._apply_label_alignment()
|
|
238
|
+
self.set_label_text(other.label_text())
|
|
239
|
+
|
|
240
|
+
def itemChange(self, change, value): # type: ignore[override]
|
|
241
|
+
if change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemSelectedHasChanged:
|
|
242
|
+
if not bool(value):
|
|
243
|
+
self._clear_label_highlight()
|
|
244
|
+
return super().itemChange(change, value)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
__all__ = ["ShapeLabelMixin", "_ShapeLabelItem"]
|
items/shapes/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Shape item re-exports for convenient access."""
|
|
2
|
+
|
|
3
|
+
from .curves import CurvyBracketItem, EllipseItem
|
|
4
|
+
from .lines import LineHandle, LineItem
|
|
5
|
+
from .polygons import BlockArrowHandle, BlockArrowItem, DiamondItem, TriangleItem
|
|
6
|
+
from .rects import RectItem, SplitDividerHandle, SplitRoundedRectItem
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"BlockArrowHandle",
|
|
10
|
+
"BlockArrowItem",
|
|
11
|
+
"CurvyBracketItem",
|
|
12
|
+
"DiamondItem",
|
|
13
|
+
"EllipseItem",
|
|
14
|
+
"LineHandle",
|
|
15
|
+
"LineItem",
|
|
16
|
+
"RectItem",
|
|
17
|
+
"SplitDividerHandle",
|
|
18
|
+
"SplitRoundedRectItem",
|
|
19
|
+
"TriangleItem",
|
|
20
|
+
]
|
items/shapes/curves.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Curved shapes such as ellipses and curly brackets."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
from PySide6 import QtCore, QtGui, QtWidgets
|
|
8
|
+
|
|
9
|
+
from constants import DEFAULT_FILL, PEN_NORMAL, PEN_SELECTED
|
|
10
|
+
from ..base import ResizableItem, _should_draw_selection, build_curvy_bracket_path
|
|
11
|
+
from ..labels import ShapeLabelMixin
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EllipseItem(ShapeLabelMixin, ResizableItem, QtWidgets.QGraphicsEllipseItem):
|
|
15
|
+
def __init__(self, x, y, w, h):
|
|
16
|
+
QtWidgets.QGraphicsEllipseItem.__init__(self, 0, 0, w, h)
|
|
17
|
+
ResizableItem.__init__(self)
|
|
18
|
+
self.setPos(x, y)
|
|
19
|
+
self.setTransformOriginPoint(w / 2.0, h / 2.0)
|
|
20
|
+
self.setFlags(
|
|
21
|
+
QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsMovable
|
|
22
|
+
| QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsSelectable
|
|
23
|
+
| QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges
|
|
24
|
+
| QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsFocusable
|
|
25
|
+
)
|
|
26
|
+
self._init_shape_label()
|
|
27
|
+
self.setPen(PEN_NORMAL)
|
|
28
|
+
self.setBrush(DEFAULT_FILL)
|
|
29
|
+
|
|
30
|
+
def _label_base_rect(self) -> QtCore.QRectF:
|
|
31
|
+
return QtCore.QRectF(self.rect())
|
|
32
|
+
|
|
33
|
+
def setRect(self, x: float, y: float, w: float, h: float) -> None: # type: ignore[override]
|
|
34
|
+
QtWidgets.QGraphicsEllipseItem.setRect(self, x, y, w, h)
|
|
35
|
+
self._update_label_geometry()
|
|
36
|
+
|
|
37
|
+
def setPen(self, pen): # type: ignore[override]
|
|
38
|
+
super().setPen(pen)
|
|
39
|
+
self._update_label_color()
|
|
40
|
+
|
|
41
|
+
def mouseDoubleClickEvent(
|
|
42
|
+
self, event: QtWidgets.QGraphicsSceneMouseEvent
|
|
43
|
+
) -> None: # type: ignore[override]
|
|
44
|
+
if event.button() == QtCore.Qt.MouseButton.LeftButton:
|
|
45
|
+
self._begin_label_edit()
|
|
46
|
+
event.accept()
|
|
47
|
+
return
|
|
48
|
+
super().mouseDoubleClickEvent(event)
|
|
49
|
+
|
|
50
|
+
def paint(self, painter, option, widget=None):
|
|
51
|
+
opt = QtWidgets.QStyleOptionGraphicsItem(option)
|
|
52
|
+
opt.state &= ~QtWidgets.QStyle.StateFlag.State_Selected
|
|
53
|
+
super().paint(painter, opt, widget)
|
|
54
|
+
if _should_draw_selection(self):
|
|
55
|
+
painter.save()
|
|
56
|
+
painter.setPen(PEN_SELECTED)
|
|
57
|
+
painter.setBrush(QtCore.Qt.BrushStyle.NoBrush)
|
|
58
|
+
painter.drawRect(self.boundingRect())
|
|
59
|
+
painter.restore()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class CurvyBracketItem(ResizableItem, QtWidgets.QGraphicsPathItem):
|
|
63
|
+
"""Resizable right-facing curly bracket."""
|
|
64
|
+
|
|
65
|
+
DEFAULT_HOOK_RATIO = 0.3
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
x: float,
|
|
70
|
+
y: float,
|
|
71
|
+
w: float,
|
|
72
|
+
h: float,
|
|
73
|
+
hook_ratio: float | None = None,
|
|
74
|
+
) -> None:
|
|
75
|
+
QtWidgets.QGraphicsPathItem.__init__(self)
|
|
76
|
+
ResizableItem.__init__(self)
|
|
77
|
+
self._w = max(8.0, float(w))
|
|
78
|
+
self._h = max(40.0, float(h))
|
|
79
|
+
if hook_ratio is None:
|
|
80
|
+
hook_ratio = self.DEFAULT_HOOK_RATIO
|
|
81
|
+
self._hook_ratio = self._clamp_ratio(float(hook_ratio))
|
|
82
|
+
self._update_path()
|
|
83
|
+
self.setPos(x, y)
|
|
84
|
+
self.setTransformOriginPoint(self._w / 2.0, self._h / 2.0)
|
|
85
|
+
self.setFlags(
|
|
86
|
+
QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsMovable
|
|
87
|
+
| QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsSelectable
|
|
88
|
+
| QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges
|
|
89
|
+
| QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsFocusable
|
|
90
|
+
)
|
|
91
|
+
self.setPen(PEN_NORMAL)
|
|
92
|
+
self.setBrush(QtCore.Qt.BrushStyle.NoBrush)
|
|
93
|
+
|
|
94
|
+
def width(self) -> float:
|
|
95
|
+
return self._w
|
|
96
|
+
|
|
97
|
+
def height(self) -> float:
|
|
98
|
+
return self._h
|
|
99
|
+
|
|
100
|
+
def hook_ratio(self) -> float:
|
|
101
|
+
return self._hook_ratio
|
|
102
|
+
|
|
103
|
+
def set_hook_ratio(self, ratio: float) -> None:
|
|
104
|
+
clamped = self._clamp_ratio(ratio)
|
|
105
|
+
if not math.isclose(clamped, self._hook_ratio, abs_tol=1e-4):
|
|
106
|
+
self._hook_ratio = clamped
|
|
107
|
+
self._update_path()
|
|
108
|
+
|
|
109
|
+
def set_size(self, w: float, h: float, adjust_origin: bool = True) -> None:
|
|
110
|
+
self._w = max(8.0, float(w))
|
|
111
|
+
self._h = max(40.0, float(h))
|
|
112
|
+
self._update_path()
|
|
113
|
+
if adjust_origin:
|
|
114
|
+
self.setTransformOriginPoint(self._w / 2.0, self._h / 2.0)
|
|
115
|
+
|
|
116
|
+
def _clamp_ratio(self, ratio: float | None = None) -> float:
|
|
117
|
+
value = self._hook_ratio if ratio is None else float(ratio)
|
|
118
|
+
return max(0.08, min(0.45, value))
|
|
119
|
+
|
|
120
|
+
def _update_path(self) -> None:
|
|
121
|
+
self.prepareGeometryChange()
|
|
122
|
+
hook = self._hook_ratio * self._h
|
|
123
|
+
path = build_curvy_bracket_path(self._w, self._h, hook)
|
|
124
|
+
self.setPath(path)
|
|
125
|
+
self.setTransformOriginPoint(self._w / 2.0, self._h / 2.0)
|
|
126
|
+
|
|
127
|
+
def paint(self, painter, option, widget=None):
|
|
128
|
+
opt = QtWidgets.QStyleOptionGraphicsItem(option)
|
|
129
|
+
opt.state &= ~QtWidgets.QStyle.StateFlag.State_Selected
|
|
130
|
+
super().paint(painter, opt, widget)
|
|
131
|
+
if _should_draw_selection(self):
|
|
132
|
+
painter.save()
|
|
133
|
+
painter.setPen(PEN_SELECTED)
|
|
134
|
+
painter.setBrush(QtCore.Qt.BrushStyle.NoBrush)
|
|
135
|
+
painter.drawRect(self.boundingRect())
|
|
136
|
+
painter.restore()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
__all__ = ["CurvyBracketItem", "EllipseItem"]
|