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.
Files changed (74) hide show
  1. batoolset/GUI/__init__.py +0 -0
  2. batoolset/GUI/customdialog.py +136 -0
  3. batoolset/GUI/exception_handler.py +74 -0
  4. batoolset/GUI/list_gui.py +514 -0
  5. batoolset/GUI/multi_image_display.py +1038 -0
  6. batoolset/GUI/open.py +325 -0
  7. batoolset/GUI/overlay_hints.py +45 -0
  8. batoolset/GUI/paint2.py +1369 -0
  9. batoolset/GUI/scrollablepaint.py +1165 -0
  10. batoolset/dialogs/__init__.py +0 -0
  11. batoolset/dialogs/opensave.py +228 -0
  12. batoolset/drawings/__init__.py +0 -0
  13. batoolset/drawings/math_text.py +294 -0
  14. batoolset/drawings/pdf_svg_renderer.py +376 -0
  15. batoolset/drawings/shapes/Position.py +183 -0
  16. batoolset/drawings/shapes/__init__.py +0 -0
  17. batoolset/drawings/shapes/automated_alignment_drawing_refactored.py +346 -0
  18. batoolset/drawings/shapes/circle2d.py +99 -0
  19. batoolset/drawings/shapes/compute_optimal_size_all_merged.py +328 -0
  20. batoolset/drawings/shapes/ellipse2d.py +336 -0
  21. batoolset/drawings/shapes/freehand2d.py +235 -0
  22. batoolset/drawings/shapes/group.py +1985 -0
  23. batoolset/drawings/shapes/image2d.py +3157 -0
  24. batoolset/drawings/shapes/line2d.py +579 -0
  25. batoolset/drawings/shapes/point2d.py +257 -0
  26. batoolset/drawings/shapes/polygon2d.py +357 -0
  27. batoolset/drawings/shapes/polyline2d.py +235 -0
  28. batoolset/drawings/shapes/rect2d.py +388 -0
  29. batoolset/drawings/shapes/rectangle2d.py +601 -0
  30. batoolset/drawings/shapes/scalebar.py +310 -0
  31. batoolset/drawings/shapes/serializablefont.py +133 -0
  32. batoolset/drawings/shapes/serializableguiparams.py +14 -0
  33. batoolset/drawings/shapes/square2d.py +102 -0
  34. batoolset/drawings/shapes/test_publication_style.py +220 -0
  35. batoolset/drawings/shapes/textorientation.py +45 -0
  36. batoolset/drawings/shapes/tools.py +467 -0
  37. batoolset/drawings/shapes/transform2D.py +350 -0
  38. batoolset/drawings/shapes/txt2d.py +615 -0
  39. batoolset/drawings/svg_clip_renderer.py +247 -0
  40. batoolset/drawings/svg_embed.py +165 -0
  41. batoolset/drawings/svg_text_renderer.py +183 -0
  42. batoolset/drawings/widgets/__init__.py +0 -0
  43. batoolset/drawings/widgets/col.py +343 -0
  44. batoolset/drawings/widgets/image_group.py +248 -0
  45. batoolset/drawings/widgets/image_organizer.py +96 -0
  46. batoolset/drawings/widgets/paint.py +184 -0
  47. batoolset/drawings/widgets/vectorial.py +256 -0
  48. batoolset/figure/__init__.py +0 -0
  49. batoolset/figure/alignment.py +822 -0
  50. batoolset/figure/column.py +1117 -0
  51. batoolset/figure/fig_tools.py +285 -0
  52. batoolset/figure/minimal_debugger_scale_etc.py +139 -0
  53. batoolset/figure/panel.py +697 -0
  54. batoolset/figure/row.py +852 -0
  55. batoolset/figure/user_selection.py +287 -0
  56. batoolset/icons/__init__.py +0 -0
  57. batoolset/icons/tools.py +248 -0
  58. batoolset/luts/__init__.py +0 -0
  59. batoolset/luts/lut_minimal_test.py +1308 -0
  60. batoolset/luts/lutcreatorfromformula.py +209 -0
  61. batoolset/luts/luteditor.py +243 -0
  62. batoolset/pyqts/__init__.py +0 -0
  63. batoolset/pyqts/pleasewaitdialog.py +14 -0
  64. batoolset/pyqts/tools.py +760 -0
  65. batoolset/serializations/__init__.py +0 -0
  66. batoolset/serializations/tools.py +1338 -0
  67. batoolset/theme.py +647 -0
  68. batoolset/uis/__init__.py +0 -0
  69. batoolset/uis/blinker.py +56 -0
  70. batoolset_gui-0.2.0.dist-info/METADATA +54 -0
  71. batoolset_gui-0.2.0.dist-info/RECORD +74 -0
  72. batoolset_gui-0.2.0.dist-info/WHEEL +5 -0
  73. batoolset_gui-0.2.0.dist-info/licenses/LICENSE +24 -0
  74. 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