batoolset-gui 0.2.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.
- batoolset/GUI/__init__.py +0 -0
- batoolset/GUI/customdialog.py +136 -0
- batoolset/GUI/exception_handler.py +74 -0
- batoolset/GUI/list_gui.py +514 -0
- batoolset/GUI/multi_image_display.py +1038 -0
- batoolset/GUI/open.py +325 -0
- batoolset/GUI/overlay_hints.py +45 -0
- batoolset/GUI/paint2.py +1369 -0
- batoolset/GUI/scrollablepaint.py +1165 -0
- batoolset/dialogs/__init__.py +0 -0
- batoolset/dialogs/opensave.py +228 -0
- batoolset/drawings/__init__.py +0 -0
- batoolset/drawings/math_text.py +294 -0
- batoolset/drawings/pdf_svg_renderer.py +376 -0
- batoolset/drawings/shapes/Position.py +183 -0
- batoolset/drawings/shapes/__init__.py +0 -0
- batoolset/drawings/shapes/automated_alignment_drawing_refactored.py +346 -0
- batoolset/drawings/shapes/circle2d.py +99 -0
- batoolset/drawings/shapes/compute_optimal_size_all_merged.py +328 -0
- batoolset/drawings/shapes/ellipse2d.py +336 -0
- batoolset/drawings/shapes/freehand2d.py +235 -0
- batoolset/drawings/shapes/group.py +1985 -0
- batoolset/drawings/shapes/image2d.py +3157 -0
- batoolset/drawings/shapes/line2d.py +579 -0
- batoolset/drawings/shapes/point2d.py +257 -0
- batoolset/drawings/shapes/polygon2d.py +357 -0
- batoolset/drawings/shapes/polyline2d.py +235 -0
- batoolset/drawings/shapes/rect2d.py +388 -0
- batoolset/drawings/shapes/rectangle2d.py +601 -0
- batoolset/drawings/shapes/scalebar.py +310 -0
- batoolset/drawings/shapes/serializablefont.py +133 -0
- batoolset/drawings/shapes/serializableguiparams.py +14 -0
- batoolset/drawings/shapes/square2d.py +102 -0
- batoolset/drawings/shapes/test_publication_style.py +220 -0
- batoolset/drawings/shapes/textorientation.py +45 -0
- batoolset/drawings/shapes/tools.py +467 -0
- batoolset/drawings/shapes/transform2D.py +350 -0
- batoolset/drawings/shapes/txt2d.py +615 -0
- batoolset/drawings/svg_clip_renderer.py +247 -0
- batoolset/drawings/svg_embed.py +165 -0
- batoolset/drawings/svg_text_renderer.py +183 -0
- batoolset/drawings/widgets/__init__.py +0 -0
- batoolset/drawings/widgets/col.py +343 -0
- batoolset/drawings/widgets/image_group.py +248 -0
- batoolset/drawings/widgets/image_organizer.py +96 -0
- batoolset/drawings/widgets/paint.py +184 -0
- batoolset/drawings/widgets/vectorial.py +256 -0
- batoolset/figure/__init__.py +0 -0
- batoolset/figure/alignment.py +822 -0
- batoolset/figure/column.py +1117 -0
- batoolset/figure/fig_tools.py +285 -0
- batoolset/figure/minimal_debugger_scale_etc.py +139 -0
- batoolset/figure/panel.py +697 -0
- batoolset/figure/row.py +852 -0
- batoolset/figure/user_selection.py +287 -0
- batoolset/icons/__init__.py +0 -0
- batoolset/icons/tools.py +248 -0
- batoolset/luts/__init__.py +0 -0
- batoolset/luts/lut_minimal_test.py +1308 -0
- batoolset/luts/lutcreatorfromformula.py +209 -0
- batoolset/luts/luteditor.py +243 -0
- batoolset/pyqts/__init__.py +0 -0
- batoolset/pyqts/pleasewaitdialog.py +14 -0
- batoolset/pyqts/tools.py +760 -0
- batoolset/serializations/__init__.py +0 -0
- batoolset/serializations/tools.py +1338 -0
- batoolset/theme.py +647 -0
- batoolset/uis/__init__.py +0 -0
- batoolset/uis/blinker.py +56 -0
- batoolset_gui-0.2.0.dist-info/METADATA +54 -0
- batoolset_gui-0.2.0.dist-info/RECORD +74 -0
- batoolset_gui-0.2.0.dist-info/WHEEL +5 -0
- batoolset_gui-0.2.0.dist-info/licenses/LICENSE +24 -0
- batoolset_gui-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import os
|
|
3
|
+
from batoolset.settings.global_settings import set_UI # set the UI to qtpy
|
|
4
|
+
set_UI()
|
|
5
|
+
from qtpy.QtCore import QPointF, QLineF, QRectF, QPoint, Qt
|
|
6
|
+
from qtpy.QtGui import QBrush, QPen, QColor, QTransform,QPainterPath
|
|
7
|
+
from math import sqrt
|
|
8
|
+
|
|
9
|
+
from batoolset.tools.logger import TA_logger
|
|
10
|
+
|
|
11
|
+
logger = TA_logger()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def arrowhead_polygon(tail, tip, head_length, head_half_width):
|
|
15
|
+
"""Triangle of an arrowhead whose point is EXACTLY *tip* (QPointF), aimed
|
|
16
|
+
along tail->tip. The head is sized in drawing units (so it keeps a constant
|
|
17
|
+
size on the page) and grows backwards from the tip, so the tip stays on the
|
|
18
|
+
pointed feature whatever the panel scale. Returns (QPainterPath, base) where
|
|
19
|
+
base is the centre of the head's back edge; None if tail == tip."""
|
|
20
|
+
dx = tip.x() - tail.x()
|
|
21
|
+
dy = tip.y() - tail.y()
|
|
22
|
+
length = math.hypot(dx, dy)
|
|
23
|
+
if length <= 0:
|
|
24
|
+
return None
|
|
25
|
+
ux = dx / length
|
|
26
|
+
uy = dy / length
|
|
27
|
+
base = QPointF(tip.x() - ux * head_length, tip.y() - uy * head_length)
|
|
28
|
+
# normal to the shaft
|
|
29
|
+
nx = -uy * head_half_width
|
|
30
|
+
ny = ux * head_half_width
|
|
31
|
+
path = QPainterPath()
|
|
32
|
+
path.moveTo(tip)
|
|
33
|
+
path.lineTo(QPointF(base.x() + nx, base.y() + ny))
|
|
34
|
+
path.lineTo(QPointF(base.x() - nx, base.y() - ny))
|
|
35
|
+
path.closeSubpath()
|
|
36
|
+
return path, base
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def arrow_shaft(p1, p2, head_length, head_at_end, head_at_start):
|
|
40
|
+
"""The part of the line to stroke for an arrow: each end that carries a head
|
|
41
|
+
is pulled back into that head (80 % of its length, where the head is still
|
|
42
|
+
much wider than the line), so neither the line's end cap nor its width can
|
|
43
|
+
poke past the tip. None if the heads swallow the whole line."""
|
|
44
|
+
dx = p2.x() - p1.x()
|
|
45
|
+
dy = p2.y() - p1.y()
|
|
46
|
+
length = math.hypot(dx, dy)
|
|
47
|
+
if length <= 0:
|
|
48
|
+
return None
|
|
49
|
+
ux = dx / length
|
|
50
|
+
uy = dy / length
|
|
51
|
+
inset = 0.8 * head_length
|
|
52
|
+
start = inset if head_at_start else 0.
|
|
53
|
+
end = length - (inset if head_at_end else 0.)
|
|
54
|
+
if end <= start:
|
|
55
|
+
return None
|
|
56
|
+
return QLineF(QPointF(p1.x() + ux * start, p1.y() + uy * start),
|
|
57
|
+
QPointF(p1.x() + ux * end, p1.y() + uy * end))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Line2D(QLineF):
|
|
61
|
+
|
|
62
|
+
def __init__(self, *args, x1=0, y1=0, x2=0, y2=0, color=0xFFFFFF, opacity=1., stroke=0.65, arrow=False, line_style=None, theta=0,arrow_at_both_ends=False, arrowhead_width_scaler=4, arrowhead_height_scaler=12, __version__=1.0,**kwargs):
|
|
63
|
+
if args:
|
|
64
|
+
super(Line2D, self).__init__(*args)
|
|
65
|
+
else:
|
|
66
|
+
# x1pos: float, y1pos: float, x2pos: float, y2pos: float
|
|
67
|
+
super(Line2D, self).__init__(x1,y1,x2,y2)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# if not args:
|
|
71
|
+
# self.isSet = False
|
|
72
|
+
# else:
|
|
73
|
+
# self.isSet = True
|
|
74
|
+
self.color = color
|
|
75
|
+
self.stroke = stroke
|
|
76
|
+
self.opacity = opacity
|
|
77
|
+
self.scale = 1
|
|
78
|
+
# self.translation = QPointF()
|
|
79
|
+
self.line_style = line_style
|
|
80
|
+
# rotation
|
|
81
|
+
self.theta = theta
|
|
82
|
+
self.arrow = arrow
|
|
83
|
+
self.arrow_at_both_ends = arrow_at_both_ends
|
|
84
|
+
self.arrowhead_width_scaler =arrowhead_width_scaler
|
|
85
|
+
self.arrowhead_height_scaler = arrowhead_height_scaler # in a way the booleans above could be replaced simply by setting that to non 0
|
|
86
|
+
self.fill_color = None # by definition a line cannot be filled but that is useful to implement it to have an object that is compatible with the other shapes
|
|
87
|
+
self.__version__ = __version__
|
|
88
|
+
|
|
89
|
+
def set_rotation(self, theta):
|
|
90
|
+
self.theta = theta
|
|
91
|
+
|
|
92
|
+
def set_opacity(self, opacity):
|
|
93
|
+
self.opacity = opacity
|
|
94
|
+
|
|
95
|
+
def set_line_style(self,style):
|
|
96
|
+
"""
|
|
97
|
+
allows lines to be dashed or dotted or have custom pattern
|
|
98
|
+
|
|
99
|
+
:param style: a list of numbers or any of the following Qt.SolidLine, Qt.DashLine, Qt.DashDotLine, Qt.DotLine, Qt.DashDotDotLine but not Qt.CustomDashLine, Qt.CustomDashLine is assumed by default if a list is passed in. None is also a valid value that resets the line --> assume plain line
|
|
100
|
+
:return:
|
|
101
|
+
"""
|
|
102
|
+
self.line_style = style
|
|
103
|
+
# if style is a list then assume custom pattern otherwise apply solidline
|
|
104
|
+
|
|
105
|
+
# shall I remove all of these scale stuff in there --> best is to redraw them anyway ???
|
|
106
|
+
|
|
107
|
+
def draw(self, painter, draw=True, parent=None):
|
|
108
|
+
# TODO --> MAYBE ADD A PARENT AND ALWAYS DRAW RELATIVE TO IT
|
|
109
|
+
if self.color is None:
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
if draw:
|
|
113
|
+
painter.save()
|
|
114
|
+
# print('inside', self.scale)
|
|
115
|
+
|
|
116
|
+
pen = QPen(QColor(self.color))
|
|
117
|
+
if self.stroke is not None:
|
|
118
|
+
pen.setWidthF(self.stroke)
|
|
119
|
+
if self.line_style is not None:
|
|
120
|
+
if self.line_style in [Qt.SolidLine, Qt.DashLine, Qt.DashDotLine, Qt.DotLine, Qt.DashDotDotLine]:
|
|
121
|
+
pen.setStyle(self.line_style)
|
|
122
|
+
elif isinstance(self.line_style, list):
|
|
123
|
+
pen.setStyle(Qt.CustomDashLine)
|
|
124
|
+
pen.setDashPattern(self.line_style)
|
|
125
|
+
painter.setPen(pen)
|
|
126
|
+
painter.setOpacity(self.opacity)
|
|
127
|
+
# clone the line
|
|
128
|
+
line_to_plot = self.translated(0, 0)
|
|
129
|
+
|
|
130
|
+
if self.scale is not None and self.scale != 1: # probably I shall remove that as I will never use that outside!!!
|
|
131
|
+
line_to_plot =self.__get_scaled_line(line_to_plot, self.scale)
|
|
132
|
+
|
|
133
|
+
if parent is not None and parent.scale is not None and parent.scale != 1:
|
|
134
|
+
line_to_plot =self.__get_scaled_line(line_to_plot, 1./parent.scale)
|
|
135
|
+
|
|
136
|
+
if parent is not None:
|
|
137
|
+
line_to_plot = line_to_plot.translated(parent.topLeft())
|
|
138
|
+
|
|
139
|
+
# if self.translation is not None:
|
|
140
|
+
# line_to_plot.translate(self.translation)
|
|
141
|
+
# print(line_to_plot)
|
|
142
|
+
if self.theta is not None and self.theta != 0:
|
|
143
|
+
painter.translate(line_to_plot.center())
|
|
144
|
+
painter.rotate(self.theta)
|
|
145
|
+
painter.translate(-line_to_plot.center())
|
|
146
|
+
|
|
147
|
+
# print('line_to_plot',line_to_plot)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# Reset the rotation of the painter object
|
|
151
|
+
# painter.rotate(line_angle)
|
|
152
|
+
|
|
153
|
+
if not self.arrow:
|
|
154
|
+
painter.drawLine(line_to_plot)
|
|
155
|
+
|
|
156
|
+
# if self.arrow:
|
|
157
|
+
# # Calculate the arrowhead size based on the line width
|
|
158
|
+
# arrowhead_size = self.stroke * 6.
|
|
159
|
+
#
|
|
160
|
+
# pen.setWidthF(arrowhead_size)
|
|
161
|
+
# painter.setPen(pen)
|
|
162
|
+
#
|
|
163
|
+
# # Calculate the angle of the line
|
|
164
|
+
# line_angle = math.degrees(math.atan2(line_to_plot.dy(), line_to_plot.dx()))
|
|
165
|
+
#
|
|
166
|
+
# # Rotate the painter object to match the line orientation
|
|
167
|
+
# painter.translate(line_to_plot.p2())
|
|
168
|
+
# painter.rotate(line_angle)
|
|
169
|
+
# painter.translate(-line_to_plot.p2())
|
|
170
|
+
#
|
|
171
|
+
# # Extend the line slightly beyond the end point
|
|
172
|
+
# extended_line = QLineF(line_to_plot.p1(), line_to_plot.p2() + QPointF(arrowhead_size / 2, 0))
|
|
173
|
+
#
|
|
174
|
+
# # Create a QPainterPath for the arrowhead shape
|
|
175
|
+
# arrowhead_path = QPainterPath()
|
|
176
|
+
# arrowhead_path.moveTo(extended_line.p2())
|
|
177
|
+
# arrowhead_path.lineTo(extended_line.p2() - QPointF(arrowhead_size, arrowhead_size / 2))
|
|
178
|
+
# arrowhead_path.lineTo(extended_line.p2() - QPointF(arrowhead_size, -arrowhead_size / 2))
|
|
179
|
+
# arrowhead_path.closeSubpath()
|
|
180
|
+
#
|
|
181
|
+
# # Fill the arrowhead shape with the current pen
|
|
182
|
+
# painter.fillPath(arrowhead_path, pen.brush())
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# if self.arrow:
|
|
186
|
+
# # Calculate the arrowhead size based on the line width
|
|
187
|
+
# arrowhead_height = self.stroke * 12
|
|
188
|
+
# arrowhead_width = self.stroke * 4
|
|
189
|
+
#
|
|
190
|
+
# # pen.setWidthF(arrowhead_width)
|
|
191
|
+
# # painter.setPen(pen)
|
|
192
|
+
#
|
|
193
|
+
# # Calculate the angle of the line
|
|
194
|
+
# line_angle = math.degrees(math.atan2(line_to_plot.dy(), line_to_plot.dx()))
|
|
195
|
+
#
|
|
196
|
+
# # Rotate the painter object to match the line orientation
|
|
197
|
+
# painter.translate(line_to_plot.p2())
|
|
198
|
+
# painter.rotate(line_angle)
|
|
199
|
+
# painter.translate(-line_to_plot.p2())
|
|
200
|
+
#
|
|
201
|
+
# # Extend the line slightly beyond the end point
|
|
202
|
+
# extended_line = QLineF(line_to_plot.p1(), line_to_plot.p2() + QPointF(arrowhead_width, 0))
|
|
203
|
+
#
|
|
204
|
+
# # Create a QPainterPath for the arrowhead shape
|
|
205
|
+
# arrowhead_path = QPainterPath()
|
|
206
|
+
# arrowhead_path.moveTo(extended_line.p2())
|
|
207
|
+
# arrowhead_path.lineTo(extended_line.p2() - QPointF(arrowhead_height, arrowhead_width))
|
|
208
|
+
# arrowhead_path.lineTo(extended_line.p2() - QPointF(arrowhead_height, -arrowhead_width))
|
|
209
|
+
# arrowhead_path.closeSubpath()
|
|
210
|
+
#
|
|
211
|
+
# # Fill the arrowhead shape with the current pen
|
|
212
|
+
# painter.fillPath(arrowhead_path, pen.brush())
|
|
213
|
+
#
|
|
214
|
+
# # If arrow_at_both_ends is True, draw a second arrow at the start of the line
|
|
215
|
+
# # if self.arrow_at_both_ends:
|
|
216
|
+
# if True:
|
|
217
|
+
# arrowhead_path = QPainterPath()
|
|
218
|
+
# arrowhead_base = extended_line.p2() - QPointF(extended_line.length() + arrowhead_width, 0 )
|
|
219
|
+
# arrowhead_path.moveTo(arrowhead_base)
|
|
220
|
+
# arrowhead_path.lineTo(arrowhead_base + QPointF(arrowhead_height, arrowhead_width))
|
|
221
|
+
# arrowhead_path.lineTo(arrowhead_base + QPointF(arrowhead_height, -arrowhead_width))
|
|
222
|
+
# arrowhead_path.closeSubpath()
|
|
223
|
+
#
|
|
224
|
+
# # Fill the second arrowhead shape with the current pen
|
|
225
|
+
# painter.fillPath(arrowhead_path, pen.brush())
|
|
226
|
+
|
|
227
|
+
# if self.arrow:
|
|
228
|
+
# # Calculate the arrowhead size based on the line width
|
|
229
|
+
# arrowhead_height = self.stroke * 12
|
|
230
|
+
# arrowhead_width = self.stroke * 4
|
|
231
|
+
#
|
|
232
|
+
# # Calculate the angle of the line
|
|
233
|
+
# line_angle = math.degrees(math.atan2(line_to_plot.dy(), line_to_plot.dx()))
|
|
234
|
+
#
|
|
235
|
+
# # Save the current state of the painter
|
|
236
|
+
# painter.save()
|
|
237
|
+
#
|
|
238
|
+
# # Rotate the painter object to match the line orientation for the end arrowhead
|
|
239
|
+
# painter.translate(line_to_plot.p2())
|
|
240
|
+
# painter.rotate(line_angle)
|
|
241
|
+
# painter.translate(-line_to_plot.p2())
|
|
242
|
+
#
|
|
243
|
+
# # Extend the line slightly beyond the end point
|
|
244
|
+
# extended_line = QLineF(line_to_plot.p1(), line_to_plot.p2() + QPointF(arrowhead_width, 0))
|
|
245
|
+
#
|
|
246
|
+
# # Create a QPainterPath for the arrowhead shape
|
|
247
|
+
# arrowhead_path = QPainterPath()
|
|
248
|
+
# arrowhead_path.moveTo(extended_line.p2())
|
|
249
|
+
# arrowhead_path.lineTo(extended_line.p2() - QPointF(arrowhead_height, arrowhead_width))
|
|
250
|
+
# arrowhead_path.lineTo(extended_line.p2() - QPointF(arrowhead_height, -arrowhead_width))
|
|
251
|
+
# arrowhead_path.closeSubpath()
|
|
252
|
+
#
|
|
253
|
+
# # Fill the arrowhead shape with the current pen
|
|
254
|
+
# painter.fillPath(arrowhead_path, pen.brush())
|
|
255
|
+
#
|
|
256
|
+
# # If arrow_at_both_ends is True, draw a second arrow at the start of the line
|
|
257
|
+
# if True:
|
|
258
|
+
# # Restore the painter to its original state
|
|
259
|
+
# painter.restore()
|
|
260
|
+
#
|
|
261
|
+
# # Save the current state of the painter
|
|
262
|
+
# painter.save()
|
|
263
|
+
#
|
|
264
|
+
# # Rotate the painter object to match the line orientation for the start arrowhead
|
|
265
|
+
# painter.translate(line_to_plot.p1())
|
|
266
|
+
# painter.rotate(line_angle + 180)
|
|
267
|
+
# painter.translate(-line_to_plot.p1())
|
|
268
|
+
#
|
|
269
|
+
# # Extend the line slightly beyond the start point
|
|
270
|
+
# extended_line_start = QLineF(line_to_plot.p1() - QPointF(arrowhead_width, 0), line_to_plot.p1())
|
|
271
|
+
#
|
|
272
|
+
# # Create a QPainterPath for the second arrowhead shape
|
|
273
|
+
# arrowhead_path = QPainterPath()
|
|
274
|
+
# arrowhead_path.moveTo(extended_line_start.p1())
|
|
275
|
+
# arrowhead_path.lineTo(extended_line_start.p1() + QPointF(arrowhead_height, arrowhead_width))
|
|
276
|
+
# arrowhead_path.lineTo(extended_line_start.p1() + QPointF(arrowhead_height, -arrowhead_width))
|
|
277
|
+
# arrowhead_path.closeSubpath()
|
|
278
|
+
#
|
|
279
|
+
# # Fill the second arrowhead shape with the current pen
|
|
280
|
+
# painter.fillPath(arrowhead_path, pen.brush())
|
|
281
|
+
#
|
|
282
|
+
# # Restore the painter to its original state
|
|
283
|
+
# painter.restore()
|
|
284
|
+
|
|
285
|
+
if self.arrow:
|
|
286
|
+
# Head size is in drawing units (constant on the page, tied to
|
|
287
|
+
# the stroke), but its POINT is the line's end point itself: the
|
|
288
|
+
# head grows backwards from there. (It used to be pushed
|
|
289
|
+
# stroke*width_scaler beyond the end point, so the tip overshot
|
|
290
|
+
# the pointed feature by an amount that, in image pixels, changed
|
|
291
|
+
# with the panel's scale.)
|
|
292
|
+
stroke = self.stroke if self.stroke is not None else 0.65
|
|
293
|
+
head_length = stroke * self.arrowhead_height_scaler
|
|
294
|
+
head_half_width = stroke * self.arrowhead_width_scaler
|
|
295
|
+
shaft = arrow_shaft(line_to_plot.p1(), line_to_plot.p2(), head_length,
|
|
296
|
+
True, bool(self.arrow_at_both_ends))
|
|
297
|
+
if shaft is not None:
|
|
298
|
+
shaft_pen = QPen(pen)
|
|
299
|
+
shaft_pen.setCapStyle(Qt.PenCapStyle.FlatCap)
|
|
300
|
+
painter.setPen(shaft_pen)
|
|
301
|
+
painter.drawLine(shaft)
|
|
302
|
+
painter.setPen(pen)
|
|
303
|
+
heads = [(line_to_plot.p1(), line_to_plot.p2())]
|
|
304
|
+
if self.arrow_at_both_ends:
|
|
305
|
+
heads.append((line_to_plot.p2(), line_to_plot.p1()))
|
|
306
|
+
for tail, tip in heads:
|
|
307
|
+
geom = arrowhead_polygon(tail, tip, head_length, head_half_width)
|
|
308
|
+
if geom is not None:
|
|
309
|
+
painter.fillPath(geom[0], pen.brush())
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
painter.restore()
|
|
314
|
+
#
|
|
315
|
+
# def fill(self, painter, draw=True):
|
|
316
|
+
# if draw:
|
|
317
|
+
# painter.save()
|
|
318
|
+
# if self.fill_color is None:
|
|
319
|
+
# return
|
|
320
|
+
# painter.setBrush(QBrush(QColor(self.fill_color)))
|
|
321
|
+
# painter.setOpacity(self.opacity)
|
|
322
|
+
# if draw:
|
|
323
|
+
# painter.drawLine(self)
|
|
324
|
+
# painter.restore()
|
|
325
|
+
#
|
|
326
|
+
# def drawAndFill(self, painter):
|
|
327
|
+
# painter.save()
|
|
328
|
+
# self.draw(painter, draw=False)
|
|
329
|
+
# self.fill(painter, draw=False)
|
|
330
|
+
# painter.drawLine(self)
|
|
331
|
+
# painter.restore()
|
|
332
|
+
|
|
333
|
+
def __get_scaled_line(self, line_to_plot,scale):
|
|
334
|
+
p1 = line_to_plot.p1()
|
|
335
|
+
p2 = line_to_plot.p2()
|
|
336
|
+
line_to_plot.setP1(QPointF(p1.x() * scale, p1.y() * scale))
|
|
337
|
+
line_to_plot.setP2(QPointF(p2.x() * scale, p2.y() * scale))
|
|
338
|
+
return line_to_plot
|
|
339
|
+
|
|
340
|
+
def contains(self, *args):
|
|
341
|
+
# x = 0
|
|
342
|
+
# y = 0
|
|
343
|
+
if isinstance(args[0], QPoint) or isinstance(args[0], QPointF):
|
|
344
|
+
x = args[0].x()
|
|
345
|
+
y = args[0].y()
|
|
346
|
+
else:
|
|
347
|
+
x = args[0]
|
|
348
|
+
y = args[1]
|
|
349
|
+
return self.distToSegment(QPointF(x, y), self.p1(), self.p2()) < 10 and self.boundingContains(*args)
|
|
350
|
+
|
|
351
|
+
def lineFromPoints(self, x1, y1, x2, y2):
|
|
352
|
+
a = y2 - y1
|
|
353
|
+
b = x1 - x2
|
|
354
|
+
c = a * x1 + b * y1
|
|
355
|
+
return (a, b, c)
|
|
356
|
+
|
|
357
|
+
def len(self, v, w):
|
|
358
|
+
return (v.x() - w.x()) ** 2 + (v.y() - w.y()) ** 2
|
|
359
|
+
|
|
360
|
+
def distToSegment(self, p, v, w):
|
|
361
|
+
l2 = self.len(v, w)
|
|
362
|
+
if l2 == 0:
|
|
363
|
+
return self.len(p, v)
|
|
364
|
+
t = ((p.x() - v.x()) * (w.x() - v.x()) + (p.y() - v.y()) * (w.y() - v.y())) / l2
|
|
365
|
+
t = max(0, min(1, t))
|
|
366
|
+
return sqrt(self.len(p, QPointF(v.x() + t * (w.x() - v.x()), v.y() + t * (w.y() - v.y()))))
|
|
367
|
+
|
|
368
|
+
def boundingContains(self, *args):
|
|
369
|
+
return self.boundingRect().contains(*args)
|
|
370
|
+
|
|
371
|
+
# def boundingRect(self, scaled=True):
|
|
372
|
+
# scale = 1
|
|
373
|
+
# if not scaled and self.scale is not None:
|
|
374
|
+
# scale = self.scale
|
|
375
|
+
# return QRectF(min(self.p1().x(), self.p2().x()) * scale, min(self.p1().y(), self.p2().y()) * scale,
|
|
376
|
+
# abs(self.p2().x() - self.p1().x()) * scale, abs(self.p2().y() - self.p1().y()) * scale)
|
|
377
|
+
# TODO handle scale etc
|
|
378
|
+
def boundingRect(self):
|
|
379
|
+
rect = QRectF(min(self.p1().x(), self.p2().x()), min(self.p1().y(), self.p2().y()),
|
|
380
|
+
abs(self.p2().x() - self.p1().x()), abs(self.p2().y() - self.p1().y()))
|
|
381
|
+
|
|
382
|
+
try:
|
|
383
|
+
# print('tada')
|
|
384
|
+
if self.theta is not None and self.theta != 0:
|
|
385
|
+
# print('entering')
|
|
386
|
+
center = rect.center()
|
|
387
|
+
# print('entering2')
|
|
388
|
+
t = QTransform().translate(center.x(), center.y()).rotate(self.theta).translate(-center.x(),
|
|
389
|
+
-center.y())
|
|
390
|
+
# print('entering3')
|
|
391
|
+
# transformed = self.setTransform(t)
|
|
392
|
+
# print('entering4')
|
|
393
|
+
# print(transformed)
|
|
394
|
+
# print(QRectF(min(transformed.p1().x(), transformed.p2().x()), min(transformed.p1().y(), transformed.p2().y()),
|
|
395
|
+
# abs(transformed.p2().x() - transformed.p1().x()), abs(transformed.p2().y() - transformed.p1().y())))
|
|
396
|
+
# return QRectF(min(transformed.p1().x(), transformed.p2().x()), min(transformed.p1().y(), transformed.p2().y()),
|
|
397
|
+
# abs(transformed.p2().x() - transformed.p1().x()), abs(transformed.p2().y() - transformed.p1().y()))
|
|
398
|
+
|
|
399
|
+
# copy.setT
|
|
400
|
+
# print('entering')
|
|
401
|
+
|
|
402
|
+
# t = QTransform().translate( center.x(), center.y()).rotate(self.theta).translate(-center.x(), -center.y())
|
|
403
|
+
# # print('entersd')
|
|
404
|
+
transformed = t.map( self) #// mapRect() returns the bounding rect of the rotated rect
|
|
405
|
+
|
|
406
|
+
# print('rotated',rotatedRect )
|
|
407
|
+
# return rotatedRect
|
|
408
|
+
return QRectF(min(transformed.p1().x(), transformed.p2().x()),
|
|
409
|
+
min(transformed.p1().y(), transformed.p2().y()),
|
|
410
|
+
abs(transformed.p2().x() - transformed.p1().x()), abs(transformed.p2().y() - transformed.p1().y()))
|
|
411
|
+
except Exception:
|
|
412
|
+
pass
|
|
413
|
+
return rect
|
|
414
|
+
|
|
415
|
+
def getRect(self, *args, **kwargs):
|
|
416
|
+
return self.boundingRect()
|
|
417
|
+
|
|
418
|
+
def width(self, *args, **kwargs):
|
|
419
|
+
return self.boundingRect().width()
|
|
420
|
+
|
|
421
|
+
def height(self, *args, **kwargs):
|
|
422
|
+
return self.boundingRect().height()
|
|
423
|
+
|
|
424
|
+
def add(self, *args):
|
|
425
|
+
point = args[1]
|
|
426
|
+
self.setP2(point)
|
|
427
|
+
# self.isSet = True
|
|
428
|
+
|
|
429
|
+
def set_to_scale(self, factor):
|
|
430
|
+
self.scale = factor
|
|
431
|
+
|
|
432
|
+
# def set_to_translation(self, translation):
|
|
433
|
+
# self.translation = translation
|
|
434
|
+
|
|
435
|
+
# I may really use set and getP1 for points because that is what they are made for !!!
|
|
436
|
+
# def get_P1(self):
|
|
437
|
+
def topLeft(self):
|
|
438
|
+
return self.boundingRect().topLeft()
|
|
439
|
+
|
|
440
|
+
# faut pas utiliser ça sinon pbs --> car en fait ce que je veux c'est postionned le point et pas le setter
|
|
441
|
+
|
|
442
|
+
def setTopLeft(self, *args):
|
|
443
|
+
if args:
|
|
444
|
+
current_pos = self.boundingRect().topLeft()
|
|
445
|
+
if len(args) == 1:
|
|
446
|
+
point=args[0]
|
|
447
|
+
# assume a QpointF
|
|
448
|
+
# super().moveTopLeft(args[0])
|
|
449
|
+
self.translate(point.x() - current_pos.x(), point.y() - current_pos.y())
|
|
450
|
+
# self.translate(point.x(), point.y())
|
|
451
|
+
elif len(args) == 2:
|
|
452
|
+
# super().moveTopLeft(QPointF(args[0], args[1]))
|
|
453
|
+
self.translate(args[0] - current_pos.x(), args[1] - current_pos.y())
|
|
454
|
+
# self.translate(args[0], args[1])
|
|
455
|
+
else:
|
|
456
|
+
logger.error('invalid args for top left')
|
|
457
|
+
|
|
458
|
+
def get_centroid(self):
|
|
459
|
+
return self.x1()+self.x2()/2., self.y1()+self.y2()/2.
|
|
460
|
+
|
|
461
|
+
# def set_to_center(self, centroid):
|
|
462
|
+
# cur_centroid = self.get_centroid()
|
|
463
|
+
# if isinstance(centroid, (tuple, list)):
|
|
464
|
+
# self.setX(cur_centroid(0)-centroid(0))
|
|
465
|
+
# self.setY(cur_centroid(1)-centroid(1))
|
|
466
|
+
# else:
|
|
467
|
+
# self.setX(cur_centroid(0)-centroid.x())
|
|
468
|
+
# self.setY(cur_centroid(1)-centroid.y())
|
|
469
|
+
|
|
470
|
+
def set_to_center(self, centroid):
|
|
471
|
+
if isinstance(centroid, (tuple, list)):
|
|
472
|
+
self.setX(centroid[0]-self.width()/2.)
|
|
473
|
+
self.setY(centroid[1]-self.height()/2.)
|
|
474
|
+
else:
|
|
475
|
+
self.setX(centroid.x()-self.width()/2.)
|
|
476
|
+
self.setY(centroid.y()-self.height()/2.)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
# def set_P1(self, point):
|
|
480
|
+
# def setTopLeft(self, point):
|
|
481
|
+
# current_pos = self.boundingRect().topLeft()
|
|
482
|
+
# self.translate(point.x() - current_pos.x(), point.y() - current_pos.y())
|
|
483
|
+
# # self.translate(self.translation)
|
|
484
|
+
# # if not args:
|
|
485
|
+
# # logger.error("no coordinate set...")
|
|
486
|
+
# # return
|
|
487
|
+
# # if len(args) == 1:
|
|
488
|
+
# # self.setP1(args[0])
|
|
489
|
+
# # else:
|
|
490
|
+
# # self.setP1(QPointF(args[0], args[1]))
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
# def set_P2(self,*args):
|
|
494
|
+
# if not args:
|
|
495
|
+
# logger.error("no coordinate set...")
|
|
496
|
+
# return
|
|
497
|
+
# if len(args) == 1:
|
|
498
|
+
# self.setP2(args[0])
|
|
499
|
+
# else:
|
|
500
|
+
# self.setP2(QPointF(args[0], args[1]))
|
|
501
|
+
|
|
502
|
+
def erode(self, nb_erosion=1):
|
|
503
|
+
self.__computeNewMorphology(sizeChange=-nb_erosion)
|
|
504
|
+
|
|
505
|
+
def dilate(self, nb_dilation=1):
|
|
506
|
+
self.__computeNewMorphology(sizeChange=nb_dilation)
|
|
507
|
+
|
|
508
|
+
def __computeNewMorphology(self, sizeChange=1):
|
|
509
|
+
# grow (or shrink) the line by sizeChange px at each end, around its centre;
|
|
510
|
+
# it never gets shorter than 1 px
|
|
511
|
+
length = self.length()
|
|
512
|
+
if length == 0:
|
|
513
|
+
return
|
|
514
|
+
final_length = max(1., length + 2. * sizeChange)
|
|
515
|
+
factor = final_length / length
|
|
516
|
+
c = (self.p1() + self.p2()) / 2.
|
|
517
|
+
self.setP1(c + (self.p1() - c) * factor)
|
|
518
|
+
self.setP2(c + (self.p2() - c) * factor)
|
|
519
|
+
|
|
520
|
+
def __repr__(self):
|
|
521
|
+
class_name = type(self).__name__
|
|
522
|
+
memory_address = hex(id(self))
|
|
523
|
+
if self.arrow:
|
|
524
|
+
class_name='Arrow2D'
|
|
525
|
+
return f"{class_name}-{memory_address}"
|
|
526
|
+
|
|
527
|
+
def to_dict(self):
|
|
528
|
+
x1 = self.x1()
|
|
529
|
+
x2 = self.x2()
|
|
530
|
+
y1 = self.y1()
|
|
531
|
+
y2 = self.y2()
|
|
532
|
+
|
|
533
|
+
# Create a dictionary representation of the values of the super object
|
|
534
|
+
output_dict = {
|
|
535
|
+
'x1': x1,
|
|
536
|
+
'y1': y1,
|
|
537
|
+
'x2': x2,
|
|
538
|
+
'y2': y2,
|
|
539
|
+
}
|
|
540
|
+
# Update the dictionary with the __dict__ of Rectangle2D
|
|
541
|
+
output_dict.update(self.__dict__)
|
|
542
|
+
return output_dict
|
|
543
|
+
|
|
544
|
+
if __name__ == '__main__':
|
|
545
|
+
# ça marche --> voici deux examples de shapes
|
|
546
|
+
test = Line2D(0, 0, 100, 100, arrow=True)
|
|
547
|
+
|
|
548
|
+
print(test.lineFromPoints(0, 0, 100, 100))
|
|
549
|
+
print(test.contains(0, 0)) # true
|
|
550
|
+
print(test.contains(10, 10)) # true
|
|
551
|
+
print(test.contains(-10, -10)) # false # on line with that equation but outside range
|
|
552
|
+
print(test.contains(0, 18)) # false
|
|
553
|
+
|
|
554
|
+
p1 = test.p1()
|
|
555
|
+
print(p1.x(), p1.y())
|
|
556
|
+
p2 = test.p2()
|
|
557
|
+
print(p2.x(), p2.y())
|
|
558
|
+
print(test.arrow)
|
|
559
|
+
print(test.length()) # sqrt 2 --> 141
|
|
560
|
+
# if it's an arrow I can add easily all the stuff I need
|
|
561
|
+
|
|
562
|
+
test = Line2D(0, 0, 1, 1)
|
|
563
|
+
p1 = test.p1()
|
|
564
|
+
print(p1.x(), p1.y())
|
|
565
|
+
p2 = test.p2()
|
|
566
|
+
print(p2.x(), p2.y())
|
|
567
|
+
print(test.arrow)
|
|
568
|
+
import math
|
|
569
|
+
|
|
570
|
+
print(test.length() == sqrt(2)) # sqrt 2
|
|
571
|
+
|
|
572
|
+
test2 = Line2D()
|
|
573
|
+
p1 = test2.p1()
|
|
574
|
+
print(p1.x(), p1.y())
|
|
575
|
+
p2 = test2.p2()
|
|
576
|
+
print(p2.x(), p2.y())
|
|
577
|
+
print(test2.arrow)
|
|
578
|
+
|
|
579
|
+
# TODO add preview as an image to gain time --> TODO
|