batoolset 0.1.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 (162) hide show
  1. batoolset/GUI/__init__.py +0 -0
  2. batoolset/GUI/list_gui.py +513 -0
  3. batoolset/GUI/multi_image_display.py +989 -0
  4. batoolset/GUI/open.py +325 -0
  5. batoolset/GUI/overlay_hints.py +45 -0
  6. batoolset/GUI/paint2.py +1345 -0
  7. batoolset/GUI/scrollablepaint.py +1152 -0
  8. batoolset/IJ_ROIs/__init__.py +0 -0
  9. batoolset/IJ_ROIs/tools.py +40 -0
  10. batoolset/PATH/__init__.py +0 -0
  11. batoolset/PATH/tools.py +79 -0
  12. batoolset/SQLite_tools/__init__.py +0 -0
  13. batoolset/SQLite_tools/tools.py +840 -0
  14. batoolset/__init__.py +0 -0
  15. batoolset/binary/__init__.py +0 -0
  16. batoolset/binary/tools.py +123 -0
  17. batoolset/binarytools/__init__.py +0 -0
  18. batoolset/binarytools/cell_center_detector.py +290 -0
  19. batoolset/clipboard_tools/__init__.py +0 -0
  20. batoolset/clipboard_tools/tools.py +7 -0
  21. batoolset/colors/__init__.py +0 -0
  22. batoolset/colors/colorgen.py +193 -0
  23. batoolset/colors/tools.py +22 -0
  24. batoolset/command_line/__init__.py +0 -0
  25. batoolset/command_line/tools.py +18 -0
  26. batoolset/dialogs/__init__.py +0 -0
  27. batoolset/dialogs/opensave.py +228 -0
  28. batoolset/dicts/__init__.py +0 -0
  29. batoolset/dicts/tools.py +299 -0
  30. batoolset/dims/__init__.py +0 -0
  31. batoolset/dims/tools.py +83 -0
  32. batoolset/draw/__init__.py +0 -0
  33. batoolset/draw/shapes/Position.py +183 -0
  34. batoolset/draw/shapes/__init__.py +0 -0
  35. batoolset/draw/shapes/automated_alignment_drawing_refactored.py +346 -0
  36. batoolset/draw/shapes/circle2d.py +99 -0
  37. batoolset/draw/shapes/compute_optimal_size_all_merged.py +317 -0
  38. batoolset/draw/shapes/ellipse2d.py +336 -0
  39. batoolset/draw/shapes/freehand2d.py +235 -0
  40. batoolset/draw/shapes/group.py +1830 -0
  41. batoolset/draw/shapes/image2d.py +2469 -0
  42. batoolset/draw/shapes/line2d.py +575 -0
  43. batoolset/draw/shapes/point2d.py +257 -0
  44. batoolset/draw/shapes/polygon2d.py +357 -0
  45. batoolset/draw/shapes/polyline2d.py +235 -0
  46. batoolset/draw/shapes/rect2d.py +388 -0
  47. batoolset/draw/shapes/rectangle2d.py +601 -0
  48. batoolset/draw/shapes/scalebar.py +304 -0
  49. batoolset/draw/shapes/serializablefont.py +126 -0
  50. batoolset/draw/shapes/serializableguiparams.py +10 -0
  51. batoolset/draw/shapes/square2d.py +103 -0
  52. batoolset/draw/shapes/textorientation.py +45 -0
  53. batoolset/draw/shapes/tools.py +467 -0
  54. batoolset/draw/shapes/transform2D.py +350 -0
  55. batoolset/draw/shapes/txt2d.py +550 -0
  56. batoolset/draw/widgets/__init__.py +0 -0
  57. batoolset/draw/widgets/col.py +343 -0
  58. batoolset/draw/widgets/image_group.py +248 -0
  59. batoolset/draw/widgets/image_organizer.py +96 -0
  60. batoolset/draw/widgets/paint.py +184 -0
  61. batoolset/draw/widgets/vectorial.py +257 -0
  62. batoolset/figure/__init__.py +0 -0
  63. batoolset/figure/alignment.py +814 -0
  64. batoolset/figure/column.py +1117 -0
  65. batoolset/figure/fig_tools.py +285 -0
  66. batoolset/figure/minimal_debugger_scale_etc.py +139 -0
  67. batoolset/figure/panel.py +697 -0
  68. batoolset/figure/row.py +852 -0
  69. batoolset/figure/user_selection.py +287 -0
  70. batoolset/files/__init__.py +0 -0
  71. batoolset/files/tools.py +1321 -0
  72. batoolset/images/__init__.py +0 -0
  73. batoolset/images/tools.py +81 -0
  74. batoolset/img.py +5216 -0
  75. batoolset/linux/__init__.py +0 -0
  76. batoolset/linux/tools.py +72 -0
  77. batoolset/lists/__init__.py +0 -0
  78. batoolset/lists/tools.py +436 -0
  79. batoolset/luts/__init__.py +0 -0
  80. batoolset/luts/lut_minimal_test.py +1207 -0
  81. batoolset/matplolib/__init__.py +0 -0
  82. batoolset/matplolib/tools.py +44 -0
  83. batoolset/np_tools/__init__.py +0 -0
  84. batoolset/np_tools/tools.py +252 -0
  85. batoolset/np_tools/trash.py +15 -0
  86. batoolset/pandas_tools/__init__.py +0 -0
  87. batoolset/pandas_tools/tools.py +880 -0
  88. batoolset/partials/__init__.py +0 -0
  89. batoolset/partials/tools.py +83 -0
  90. batoolset/plot_tools/__init__.py +0 -0
  91. batoolset/plot_tools/tools.py +70 -0
  92. batoolset/pyqt/__init__.py +0 -0
  93. batoolset/pyqt/tools.py +691 -0
  94. batoolset/re_tools/__init__.py +0 -0
  95. batoolset/re_tools/tools.py +35 -0
  96. batoolset/serialization/__init__.py +0 -0
  97. batoolset/serialization/tools.py +1192 -0
  98. batoolset/sets/__init__.py +0 -0
  99. batoolset/sets/tools.py +109 -0
  100. batoolset/settings/__init__.py +0 -0
  101. batoolset/settings/global_settings.py +134 -0
  102. batoolset/strings/__init__.py +0 -0
  103. batoolset/strings/tools.py +225 -0
  104. batoolset/ta/__init__.py +0 -0
  105. batoolset/ta/database/__init__.py +0 -0
  106. batoolset/ta/database/advanced_sql_plotter.py +1170 -0
  107. batoolset/ta/database/my_qt_table.py +79 -0
  108. batoolset/ta/database/preview_and_edit_sqlite_db.py +405 -0
  109. batoolset/ta/database/sql.py +2141 -0
  110. batoolset/ta/database/sql_column_name_dialog.py +122 -0
  111. batoolset/ta/measurements/TAmeasures.py +2226 -0
  112. batoolset/ta/measurements/__init__.py +0 -0
  113. batoolset/ta/measurements/measurements3D/__init__.py +0 -0
  114. batoolset/ta/measurements/measurements3D/get_point_on_surface_if_centroid_is_bad.py +175 -0
  115. batoolset/ta/measurements/measurements3D/height_map_to_projection.py +82 -0
  116. batoolset/ta/measurements/measurements3D/measures3D.py +208 -0
  117. batoolset/ta/measurements/measurements3D/triangulate.py +331 -0
  118. batoolset/ta/measurements/nematic.py +562 -0
  119. batoolset/ta/measurements/nematic_measurements.py +107 -0
  120. batoolset/ta/segmentation/__init__.py +0 -0
  121. batoolset/ta/segmentation/neo_wshed.py +314 -0
  122. batoolset/ta/selections/__init__.py +0 -0
  123. batoolset/ta/selections/selection.py +329 -0
  124. batoolset/ta/tracking/__init__.py +0 -0
  125. batoolset/ta/tracking/last_tracking_based_on_matching_or_on_translation_from_mermaid_warp.py +653 -0
  126. batoolset/ta/tracking/local_to_track_correspondance.py +138 -0
  127. batoolset/ta/tracking/rapid_detection_of_vertices.py +517 -0
  128. batoolset/ta/tracking/registration.py +108 -0
  129. batoolset/ta/tracking/swap_detector_and_fixer.py +296 -0
  130. batoolset/ta/tracking/track_correction.py +238 -0
  131. batoolset/ta/tracking/tracking_error_detector_and_fixer.py +3099 -0
  132. batoolset/ta/tracking/tracking_yet_another_approach_pyramidal_registration_n_neo_swapping_correction.py +725 -0
  133. batoolset/ta/utils/TA_tools.py +43 -0
  134. batoolset/ta/utils/__init__.py +0 -0
  135. batoolset/ta/utils/rps_tools.py +1783 -0
  136. batoolset/tests/__init__.py +0 -0
  137. batoolset/tests/rps_tools.py +51 -0
  138. batoolset/tests/tools.py +10 -0
  139. batoolset/tools/__init__.py +0 -0
  140. batoolset/tools/early_stopper_class.py +9 -0
  141. batoolset/tools/guided_data_recoverer.py +110 -0
  142. batoolset/tools/logger.py +100 -0
  143. batoolset/tools/mytimer.py +31 -0
  144. batoolset/tools/prompt_color.py +160 -0
  145. batoolset/tools/qthandler.py +118 -0
  146. batoolset/uitools/__init__.py +0 -0
  147. batoolset/uitools/blinker.py +56 -0
  148. batoolset/unsafe/__init__.py +0 -0
  149. batoolset/unsafe/tools.py +115 -0
  150. batoolset/utils/__init__.py +0 -0
  151. batoolset/utils/commontools.py +158 -0
  152. batoolset/utils/loadlist.py +728 -0
  153. batoolset/version.py +10 -0
  154. batoolset/xml/__init__.py +0 -0
  155. batoolset/xml/tools.py +65 -0
  156. batoolset/zip_tools/__init__.py +0 -0
  157. batoolset/zip_tools/tools.py +76 -0
  158. batoolset-0.1.0.dist-info/LICENSE +24 -0
  159. batoolset-0.1.0.dist-info/METADATA +45 -0
  160. batoolset-0.1.0.dist-info/RECORD +162 -0
  161. batoolset-0.1.0.dist-info/WHEEL +5 -0
  162. batoolset-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,691 @@
1
+ from batoolset.settings.global_settings import set_UI # set the UI to qtpy
2
+ set_UI()
3
+ from qtpy.QtGui import QPainter,QTransform,QImage,QPolygonF,QFont, QTextDocument,QColor,QPolygon
4
+ from qtpy.QtCore import QRectF, QPointF, QSize, QRect, QPoint
5
+ from qtpy.QtCore import Qt, QObject, QEvent
6
+ from qtpy.QtWidgets import QHBoxLayout, QSlider,QLabel,QComboBox,QSpinBox,QWidget,QScrollArea,QDoubleSpinBox
7
+ import math
8
+ import sys
9
+
10
+
11
+ # class WheelFilter(QObject):
12
+ # def eventFilter(self, obj, event):
13
+ # if event.type() == QEvent.Wheel:
14
+ # if isinstance(obj, (QComboBox, QSpinBox)):
15
+ # return True
16
+ # return super().eventFilter(obj, event)
17
+ #
18
+ #
19
+ # def disable_wheel_for_widget_and_children(widget):
20
+ # wheel_filter = WheelFilter(widget)
21
+ # widget.installEventFilter(wheel_filter)
22
+ #
23
+ # for child in widget.findChildren(QWidget):
24
+ # if isinstance(child, (QComboBox, QSpinBox)):
25
+ # child.installEventFilter(wheel_filter)
26
+ # elif not isinstance(child, QScrollArea):
27
+ # child.installEventFilter(wheel_filter)
28
+ # disable_wheel_for_widget_and_children(child)
29
+
30
+ # def disable_wheel_for_combo_and_spin(app):
31
+ # app.setStyleSheet("""
32
+ # QComboBox, QSpinBox, QDoubleSpinBox {
33
+ # qproperty-wheelEnabled: false;
34
+ # }
35
+ # """)
36
+
37
+ # class WheelBlocker(QObject):
38
+ # def eventFilter(self, obj, event):
39
+ # if event.type() == QEvent.Wheel:
40
+ # return isinstance(obj, (QComboBox, QSpinBox, QDoubleSpinBox))
41
+ # return False
42
+ #
43
+ # def disable_wheel_for_combo_and_spin(app):
44
+ # blocker = WheelBlocker(app)
45
+ # app.installEventFilter(blocker)
46
+
47
+ def getCtrlModifierAsString():
48
+ if sys.platform == "darwin":
49
+ return "Meta" # Command key on macOS
50
+ # else:
51
+ return "Ctrl" # Control key on other platforms
52
+
53
+
54
+ def getCtrlModifier():
55
+ if sys.platform == "darwin":
56
+ return Qt.MetaModifier
57
+ else:
58
+ return Qt.ControlModifier
59
+
60
+ class WheelBlocker(QObject):
61
+ def eventFilter(self, obj, event):
62
+ if event.type() == QEvent.Wheel:
63
+ return isinstance(obj, (QComboBox, QSpinBox, QDoubleSpinBox))
64
+ return False
65
+
66
+
67
+ def disable_wheel_for_combo_and_spin(widget):
68
+ blocker = WheelBlocker(widget)
69
+ widget.installEventFilter(blocker)
70
+
71
+ for child in widget.findChildren(QWidget):
72
+ child.installEventFilter(blocker)
73
+
74
+ # return true if antialiasing is on
75
+ def check_antialiasing(painter):
76
+ # print('testing antialias', painter.testRenderHint(QPainter.Antialiasing))
77
+ return painter.testRenderHint(QPainter.Antialiasing)
78
+
79
+ def get_items_of_combo(combo_box):
80
+ items = [combo_box.itemText(i) for i in range(combo_box.count())]
81
+ return items
82
+
83
+ def get_painter_transform_and_rotation(painter):
84
+ # Get the current transformation matrix
85
+ transform = painter.transform()
86
+
87
+ # Get the rotation angle from the transformation matrix
88
+ rotation_angle = math.atan2(transform.m12(), transform.m11()) * 180 / math.pi
89
+
90
+ return transform, rotation_angle
91
+
92
+ def copy_transform_without_rotation(current_transform):
93
+ # Extract the components from the current transform
94
+ m11 = current_transform.m11() # Horizontal scaling
95
+ m12 = current_transform.m12() # Vertical shearing
96
+ m13 = current_transform.m13() # Horizontal projection (should be 0 for affine transforms)
97
+ m21 = current_transform.m21() # Horizontal shearing
98
+ m22 = current_transform.m22() # Vertical scaling
99
+ m23 = current_transform.m23() # Vertical projection (should be 0 for affine transforms)
100
+ m31 = current_transform.m31() # Horizontal translation
101
+ m32 = current_transform.m32() # Vertical translation
102
+ m33 = current_transform.m33() # Additional scaling factor (usually 1 for affine transforms)
103
+
104
+ # Create a new transform without the rotation
105
+ new_transform = QTransform(m11, 0, m13,
106
+ 0, m22, m23,
107
+ m31, m32, m33)
108
+
109
+ return new_transform
110
+
111
+
112
+ def copy_scale_only(current_transform):
113
+ # Extract the scale components from the current transform
114
+ m11 = current_transform.m11() # Horizontal scaling
115
+ m22 = current_transform.m22() # Vertical scaling
116
+
117
+ # Create a new transform with only the scaling components
118
+ new_transform = QTransform(m11, 0, 0,
119
+ 0, m22, 0,
120
+ 0, 0, 1)
121
+
122
+ return new_transform
123
+
124
+
125
+ def copy_scale_and_translation_only(current_transform):
126
+ # Extract the scale components from the current transform
127
+ m11 = current_transform.m11() # Horizontal scaling
128
+ m22 = current_transform.m22() # Vertical scaling
129
+
130
+ # Extract the translation components from the current transform
131
+ m31 = current_transform.m31() # Horizontal translation
132
+ m32 = current_transform.m32() # Vertical translation
133
+
134
+ # Create a new transform with only the scaling and translation components
135
+ new_transform = QTransform(m11, 0, 0,
136
+ 0, m22, 0,
137
+ m31, m32, 1)
138
+
139
+ return new_transform
140
+
141
+ def rotate_image(image, angle, parent_painter=None, annotations=None,__DEBUG__=False):
142
+ # Get the dimensions of the original image
143
+ width, height = image.width(), image.height()
144
+ # print('inside', width, height, angle)
145
+
146
+ # Define the initial rectangle
147
+ init_rect = QRectF(0, 0, width, height)
148
+ center = init_rect.center()
149
+
150
+ # Create the transformation for rotating the image
151
+ transformation_for_image = QTransform()
152
+ transformation_for_image.translate(center.x(), center.y())
153
+ transformation_for_image.rotate(angle)
154
+ transformation_for_image.translate(-center.x(), -center.y())
155
+
156
+ # Map the initial rectangle to the rotated polygon
157
+ rotated_polygon = transformation_for_image.mapToPolygon(init_rect.toRect())
158
+ rotated_rect = QRectF(rotated_polygon.boundingRect())
159
+
160
+ # try:
161
+ # print('rotated_rect', rotated_rect.size(), rotated_rect, 'angle', angle, 'init', init_rect)
162
+ # except:
163
+ # print('rotated_rect', rotated_rect.size(), rotated_rect, 'angle', angle)
164
+
165
+ # Create a new QImage with the dimensions of the rotated rectangle
166
+ rotated_image = QImage(int(rotated_rect.width()), int(rotated_rect.height()), QImage.Format_ARGB32_Premultiplied)
167
+ rotated_image.fill(Qt.transparent)
168
+
169
+ # Create a QPainter to draw the rotated image
170
+ painter2 = QPainter(rotated_image)
171
+ if parent_painter:
172
+ painter2.setRenderHint(parent_painter.renderHints())
173
+ else:
174
+ # print('removing antialisaing')
175
+ # painter2.setRenderHint(QPainter.Antialiasing, False)
176
+ painter2.setRenderHint(QPainter.Antialiasing, True) # just for now
177
+
178
+ if __DEBUG__:
179
+ pass # TODO -−> set a pen and plot the rects
180
+
181
+ # Calculate the offset to center the rotated image
182
+ offset = QPointF((rotated_rect.width() - width) / 2, (rotated_rect.height() - height) / 2)
183
+
184
+
185
+ # print('offset inside dabula', offset)
186
+
187
+ # Translate the painter to the calculated offset
188
+ painter2.translate(offset)
189
+
190
+ # Apply the rotation transformation
191
+ painter2.translate(center.x(), center.y())
192
+ painter2.rotate(angle)
193
+ painter2.translate(-center.x(), -center.y())
194
+
195
+
196
+
197
+ # Draw the original image onto the rotated image
198
+ painter2.drawImage(init_rect, image, init_rect)
199
+
200
+ if annotations:
201
+ for elm in annotations:
202
+ if hasattr(elm, 'placement'):
203
+ if elm.placement is not None:
204
+ if not elm.placement.position_to_string():
205
+ # continue
206
+ # else:
207
+ continue
208
+
209
+
210
+ try:
211
+ painter2.save()
212
+ elm.draw(painter2)
213
+ painter2.restore()
214
+ except:
215
+ pass
216
+
217
+ # then draw all annotations
218
+
219
+
220
+
221
+ painter2.end()
222
+
223
+ # Save the rotated image for verification
224
+ # rotated_image.save('/home/aigouy/Bureau/test.png')
225
+
226
+ return rotated_image
227
+
228
+ def crop_image(image: QImage, crop_left: int, crop_right: int, crop_top: int, crop_bottom: int) -> QImage:
229
+ """
230
+ Crops the given QImage object by the specified amount of pixels on each side.
231
+
232
+ Args:
233
+ image: The QImage object to crop.
234
+ crop_left: The number of pixels to crop from the left side.
235
+ crop_top: The number of pixels to crop from the top side.
236
+ crop_right: The number of pixels to crop from the right side.
237
+ crop_bottom: The number of pixels to crop from the bottom side.
238
+
239
+ Returns:
240
+ A new QImage object with the cropped image.
241
+ """
242
+ if not crop_left:
243
+ crop_left=0
244
+ if not crop_right:
245
+ crop_right=0
246
+ if not crop_top:
247
+ crop_top=0
248
+ if not crop_bottom:
249
+ crop_bottom=0
250
+
251
+ # Calculate the position and dimensions of the cropped image
252
+ x = int(crop_left)
253
+ y = int(crop_top)
254
+ width = int(image.width() - crop_left - crop_right)
255
+ height = int(image.height() - crop_top - crop_bottom)
256
+
257
+ # Create a QRect object with the position and dimensions of the cropped image
258
+ rect = QRect(x, y, width, height)
259
+
260
+ # Create a new QImage object with the cropped image
261
+ cropped_image = image.copy(rect)
262
+
263
+ return cropped_image
264
+
265
+
266
+ def tst_translation(rect: QRectF, angle: float, crop_left: float, crop_right: float, crop_top: float, crop_bottom: float) -> QPolygonF:
267
+ rotated_shape = get_shape_after_rotation_and_crop(rect, angle, crop_left, crop_right, crop_top, crop_bottom)
268
+ return rect.center()-get_centroid(rotated_shape)
269
+
270
+
271
+ def get_shape_after_rotation_and_crop(rect, angle, crop_left, crop_right, crop_top, crop_bottom, return_AR = False):
272
+ # this retruns the qrectf corresponding to the initial image after rotation and crop --> what will be displayed --> that seems to work fine --> TODO
273
+
274
+
275
+
276
+ if not angle and not crop_left and not crop_right and not crop_top and not crop_bottom:
277
+ # print('NADA FOUND --> cancelling')
278
+ if return_AR:
279
+ return rect.width()/rect.height(), None
280
+
281
+ return None
282
+
283
+ if True:
284
+ # maybe this should be an option
285
+ # rect.setX(0)
286
+ # rect.setY(0)
287
+ rect = QRectF(0,0,rect.width(), rect.height())
288
+
289
+ # assume all is at 0 -->
290
+
291
+ # Calculate the center of the rectangle
292
+ center = rect.center()
293
+
294
+ # Create a transformation matrix to rotate the rectangle around its center
295
+ transform = QTransform()
296
+ if angle:
297
+ transform.translate(center.x(), center.y())
298
+ transform.rotate(angle)
299
+ transform.translate(-center.x(), -center.y())
300
+
301
+ # Apply the transformation to the rectangle
302
+ # bounding_rect = transform.mapRect(rect)
303
+ rotated_rect_as_polygon = transform.mapToPolygon(rect.toRect())
304
+
305
+ # Calculate the bounding rectangle of the rotated rectangle
306
+
307
+ bounds_rotated = rotated_rect_as_polygon.boundingRect()
308
+
309
+ # print(bounds_rotated)
310
+
311
+ bounds_rotated.setLeft(int(bounds_rotated.left()+crop_left))
312
+ bounds_rotated.setRight(int(bounds_rotated.right()-crop_right))
313
+ bounds_rotated.setTop(int(bounds_rotated.top()+crop_top))
314
+ bounds_rotated.setBottom(int(bounds_rotated.bottom()-crop_bottom))
315
+
316
+
317
+ # this is what should be used to get AR !!! ---> TODO
318
+ # print('AR in the bidule', bounds_rotated.width()/bounds_rotated.height())
319
+
320
+
321
+
322
+
323
+
324
+ # this gives me a rect that I can plot back on the orig that will show what is displayed -->
325
+
326
+ # no in fact I need to get the union of the yellow rotated poly/rect and the blue --> TODO
327
+
328
+ # Get the overlapping area between the polygon and the rectangle
329
+ transform = QTransform()
330
+ crop_rect_as_polygon = transform.mapToPolygon(bounds_rotated)
331
+
332
+ # print(crop_rect_as_polygon.boundingRect()) # c'est le bounding rect difference --> -23 --> see why that is ???
333
+
334
+ overlapping_polygon = rotated_rect_as_polygon.intersected(crop_rect_as_polygon)
335
+ # print(overlapping_polygon.boundingRect())
336
+
337
+ # overlapping_polygon_f = QPolygonF(overlapping_polygon)
338
+ transform = QTransform()
339
+ # --> all good --> I have it now see how I can plot it back onto the original --> see the translation and rotation I need to apply
340
+ # transform.translate(bounds_rotated.center().x(),bounds_rotated.center().y())
341
+ # transform.rotate(-angle)
342
+ # transform.translate(-bounds_rotated.center().x(), -bounds_rotated.center().y())
343
+
344
+ # we revert to initial rotation !!!
345
+ if angle:
346
+ transform.translate(center.x(),center.y()) #
347
+ transform.rotate(-angle)
348
+ transform.translate(-center.x(), -center.y())
349
+ # overlapping_polygon_rotated = overlapping_polygon_f.transformed(transform)
350
+ overlapping_polygon_rotated = transform.map(overlapping_polygon)
351
+
352
+ # print(overlapping_polygon_rotated.boundingRect())
353
+ # print()
354
+ if not return_AR:
355
+ return overlapping_polygon_rotated
356
+ else:
357
+ return bounds_rotated.width()/bounds_rotated.height(), overlapping_polygon_rotated
358
+
359
+ def get_original_shape_from_rect_and_angle(parent_rect, drawn_rect, angle):
360
+ # that works perfect for shapes below 90 degrees --> see how to fix that
361
+ # Calculate the bounding rectangle of the rotated rectangle
362
+ drawn_rect = drawn_rect.normalized()
363
+
364
+ center = parent_rect.center()
365
+ # the mapped rect will have the same centroid position as the polygon and as the rect by the way or as the
366
+ transform = QTransform()
367
+ transform.translate(center.x(), center.y())
368
+ transform.rotate(-angle)
369
+ transform.translate(-center.x(), -center.y())
370
+
371
+ # get the transformed shape
372
+ rotated_back_drawn = transform.mapToPolygon(drawn_rect.toRect())
373
+ rotated_back_parent = transform.mapToPolygon(parent_rect.toRect())
374
+
375
+ # if I have the position of the centroid I can place any rect on top
376
+ drawn_rect.translate(QPointF(rotated_back_drawn.boundingRect().center())-drawn_rect.center())
377
+
378
+ crop_left = -(rotated_back_parent.boundingRect().left()-drawn_rect.left())
379
+ crop_right = rotated_back_parent.boundingRect().right()-drawn_rect.right()
380
+ crop_top = -(rotated_back_parent.boundingRect().top() - drawn_rect.top())
381
+ crop_bottom = rotated_back_parent.boundingRect().bottom()-drawn_rect.bottom()
382
+
383
+ if True: # maybe put this as a parameter
384
+ crop_left = int(crop_left)
385
+ crop_right = int(crop_right)
386
+ crop_top = int(crop_top)
387
+ crop_bottom = int(crop_bottom)
388
+
389
+ return -angle, crop_left, crop_right, crop_top, crop_bottom
390
+
391
+ def get_html_text_with_font(text, font):
392
+ # Create a font
393
+ # font = QFont()
394
+ # font.setFamily(font_family)
395
+ # font.setPointSize(font_size)
396
+ # font.setWeight(QFont.Normal if font_style == 'Normal' else QFont.Bold)
397
+
398
+ # Create a QTextDocument object
399
+ text_document = QTextDocument()
400
+
401
+ # Apply the font to the text document
402
+ text_document.setDefaultFont(font)
403
+
404
+ # Set the text
405
+ text_document.setHtml('<p>' + text + '</p>')
406
+
407
+ # Get the HTML equivalent with the applied font
408
+ html_text = text_document.toHtml()
409
+
410
+ return html_text
411
+
412
+ def get_centroid(polygon: QPolygonF) -> QPointF:
413
+ # Ensure the polygon is closed
414
+ if polygon.isEmpty():
415
+ return QPointF()
416
+
417
+ if polygon.first() != polygon.last():
418
+ polygon.append(polygon.first())
419
+
420
+ n = len(polygon)
421
+ A = 0 # Partial area
422
+ Cx = 0 # x coordinate of centroid
423
+ Cy = 0 # y coordinate of centroid
424
+
425
+ for i in range(n - 1):
426
+ x0, y0 = polygon[i].x(), polygon[i].y()
427
+ x1, y1 = polygon[i + 1].x(), polygon[i + 1].y()
428
+ cross_product = x0 * y1 - x1 * y0
429
+ A += cross_product
430
+ Cx += (x0 + x1) * cross_product
431
+ Cy += (y0 + y1) * cross_product
432
+
433
+ A /= 2
434
+ Cx /= (6 * A)
435
+ Cy /= (6 * A)
436
+
437
+ return QPointF(Cx, Cy)
438
+ # def get_centroid2(polygon):
439
+ # """
440
+ # Returns the centroid (center of mass) of a QPolygonF object.
441
+ # """
442
+ # area = 0.0
443
+ # centroid = QPointF(0.0, 0.0)
444
+ #
445
+ # # Calculate the signed area and centroid using the Shoelace formula
446
+ # for i in range(len(polygon)):
447
+ # j = (i + 1) % len(polygon)
448
+ # a = polygon[i].x() * polygon[j].y() - polygon[j].x() * polygon[i].y()
449
+ # area += a
450
+ # centroid += QPointF((polygon[i].x() + polygon[j].x()) * a, (polygon[i].y() + polygon[j].y()) * a)
451
+ #
452
+ # area *= 0.5
453
+ # if area != 0.0:
454
+ # centroid /= (3.0 * area)
455
+ #
456
+ # return centroid
457
+
458
+ # def get_centroid(qpolygonf):
459
+ # x_sum = 0
460
+ # y_sum = 0
461
+ # num_points = qpolygonf.count()-1 # assume the shape is closed so the last point is the first and we should take it only once
462
+ #
463
+ # for i in range(num_points):
464
+ # point = qpolygonf.at(i)
465
+ # x_sum += point.x()
466
+ # y_sum += point.y()
467
+ #
468
+ # centroid_x = x_sum / num_points
469
+ # centroid_y = y_sum / num_points
470
+ #
471
+ # return QPointF(centroid_x, centroid_y)
472
+ def grab_n_find_color(qwidget, color=QColor(0, 0, 0)):
473
+ # Capture the contents of the widget as a QPixmap object
474
+ first_black_pixel = None
475
+ pixmap = qwidget.grab()
476
+ # Find the coordinates of the first black pixel
477
+ width = pixmap.width()
478
+ height = pixmap.height()
479
+ image = pixmap.toImage()
480
+ for x in range(width):
481
+ for y in range(height):
482
+ cur_col = image.pixelColor(x, y)
483
+ if cur_col == color:
484
+ first_black_pixel = (x, y)
485
+ print(first_black_pixel) # --> 15,89 --> ok -−> do a fix then but ok for now
486
+ break
487
+ else:
488
+ continue
489
+ break
490
+ del pixmap
491
+ del image
492
+ return first_black_pixel
493
+
494
+ def average_polygons(*polygons):
495
+ """
496
+ Calculate the average of the QPolygonF objects.
497
+
498
+ Args:
499
+ *polygons: A variable number of QPolygonF objects.
500
+
501
+ Returns:
502
+ QPolygonF: The average of the QPolygonF objects.
503
+ """
504
+ if not polygons:
505
+ return QPolygon()
506
+
507
+ # Check that all polygons have the same number of points
508
+ num_points = len(polygons[0])
509
+ for polygon in polygons[1:]:
510
+ if len(polygon) != num_points:
511
+ raise ValueError("All polygons must have the same number of points.")
512
+
513
+
514
+ out = QPolygon()
515
+ # Calculate the average of the x and y coordinates of each point
516
+ # average_points = []
517
+ for i in range(num_points):
518
+ x_sum = 0
519
+ y_sum = 0
520
+ for polygon in polygons:
521
+ x_sum += polygon[i].x()
522
+ y_sum += polygon[i].y()
523
+ x_avg = x_sum / len(polygons)
524
+ y_avg = y_sum / len(polygons)
525
+ # average_points.append(QPointF(x_avg, y_avg))
526
+ out.append(QPoint(int(x_avg), int(y_avg)))
527
+ return out
528
+
529
+
530
+ def create_dim_slider(dimension=None, max_dim=1, return_slider=True):
531
+ dim_slider_with_label1 = QHBoxLayout()
532
+ label_slider1 = QLabel()
533
+ if dimension is not None:
534
+ label_slider1.setText(dimension)
535
+ dim_slider = QSlider(Qt.Horizontal)
536
+ dim_slider.setMinimum(0)
537
+ dim_slider.setMaximum(max_dim-1)
538
+ dim_slider_with_label1.addWidget(label_slider1)
539
+ dim_slider_with_label1.addWidget(dim_slider)
540
+ dim_slider.setObjectName(dimension)
541
+ if not return_slider:
542
+ return dim_slider_with_label1
543
+ else:
544
+ return dim_slider_with_label1, dim_slider
545
+
546
+ # def empty_widget_layout(widget):
547
+ # if widget is None:
548
+ # return
549
+ # layout = widget.layout()
550
+ # if not layout:
551
+ # return
552
+ # while layout.count():
553
+ # item = layout.takeAt(0)
554
+ # widget = item.widget()
555
+ # # Delete the widget associated with the item
556
+ # if widget is not None:
557
+ # widget.setParent(None)
558
+ # widget.deleteLater()
559
+ # # else:
560
+ # # print('weirdo stuff',item)
561
+ # # layout = item.layout()
562
+ # # Delete the item
563
+ # # del item
564
+ # # return layout
565
+
566
+ def clear_layout(layout):
567
+ while layout.count():
568
+ item = layout.takeAt(0)
569
+ if item.widget() is not None:
570
+ item.widget().deleteLater()
571
+ else:
572
+ clear_layout(item.layout())
573
+
574
+
575
+ def distance(point1, point2):
576
+ """
577
+ Compute the Euclidean distance between two points.
578
+
579
+ Parameters:
580
+ point1 (QPointF): First point
581
+ point2 (QPointF): Second point
582
+
583
+ Returns:
584
+ float: Euclidean distance between the two points
585
+ """
586
+ dx = point1.x() - point2.x()
587
+ dy = point1.y() - point2.y()
588
+ return math.sqrt(dx * dx + dy * dy)
589
+
590
+ def select_in_combobox(combobox, text=None,value=None, default_index_on_not_found=None):
591
+ if text is None and value is None:
592
+ print('Error text or value must be specified!!!')
593
+
594
+ if value is not None:
595
+ index = combobox.findVale(value)
596
+ elif text is not None:
597
+ index = combobox.findText(text)
598
+ if index >= 0:
599
+ combobox.setCurrentIndex(index)
600
+ else:
601
+ if default_index_on_not_found is not None:
602
+ combobox.setCurrentIndex(default_index_on_not_found)
603
+
604
+ def find_action_by_text(menu, text='ignore'):
605
+ for action in menu.actions():
606
+ if action.text() == text:
607
+ return action
608
+ return None
609
+
610
+
611
+ if __name__ == '__main__':
612
+ import sys
613
+
614
+ if False:
615
+ from PyQt6.QtCore import QPointF, QRectF
616
+
617
+ # Define the two sets of rectangles
618
+ rects1 = [
619
+ QRectF(0.0, 0.0, 145.0, 303.0),
620
+ QRectF(0.0, 0.0, 205.20137371117733, 428.80011196197745),
621
+ QRectF(0.0, 0.0, 205.20137371117733, 428.80011196197745)
622
+ ]
623
+ rects2 = [
624
+ QRectF(0.0, 0.0, 145.0, 303.0),
625
+ QRectF(0.0, 0.0, 106.1386415747469, 230.67464768911657),
626
+ QRectF(0.0, 0.0, 342.47401681451663, 260.3934673300457)
627
+ ]
628
+
629
+ point = rects1[-1].center()-rects2[-1].center()
630
+ print(point)
631
+ print(rects2[-1].translated(point.x(), point.y()))
632
+
633
+ # Define the desired overall translation
634
+ desired_translation = QPointF(50, -50)
635
+
636
+ # Calculate the center of each rectangle
637
+ centers1 = [rect.center() for rect in rects1]
638
+ centers2 = [rect.center() for rect in rects2]
639
+
640
+ print('centers1',centers1)
641
+ print('centers2',centers2)
642
+
643
+ # Calculate the translations required to move the centers of the rectangles in the first set to the centers of the rectangles in the second set
644
+ translations = [centers2[i] - centers1[i] for i in range(len(rects1))]
645
+
646
+ # Calculate the average of the translations
647
+ average_translation = QPointF(sum(t.x() for t in translations) / len(translations),
648
+ sum(t.y() for t in translations) / len(translations))
649
+
650
+ # If the average translation is not equal to the desired translation, adjust the individual translations
651
+ if average_translation != desired_translation:
652
+ # Calculate the difference between the average translation and the desired translation
653
+ diff = desired_translation - average_translation
654
+
655
+ # Adjust the individual translations
656
+ adjusted_translations = [t + diff for t in translations]
657
+
658
+ # Use the adjusted translations to move the rectangles in the first set to the second set
659
+ moved_rects = [rect.translated(t) for rect, t in zip(rects1, adjusted_translations)]
660
+ else:
661
+ # Use the original translations to move the rectangles in the first set to the second set
662
+ moved_rects = [rect.translated(t) for rect, t in zip(rects1, translations)]
663
+
664
+ print(translations)
665
+ print(average_translation)
666
+
667
+ sys.exit(0)
668
+
669
+ if True:
670
+ # get_shape_after_rotation_and_crop(QRectF(0,0,145,303), 42, 30,40,60,80)
671
+ print(get_shape_after_rotation_and_crop(QRectF(0,0,145,303), 42, 30,40,60,80))
672
+ print(tst_translation(QRectF(0,0,145,303), 42, 30,40,60,80))
673
+ # print(get_shape_after_rotation_and_crop(QRectF(0, 0, 145, 303), 42, 60, 30, 0, 0))
674
+ # print(tst_translation(QRectF(0,0,145,303), 42, 60,30,0,0))
675
+
676
+ print(get_centroid(get_shape_after_rotation_and_crop(QRectF(0,0,145,303), 42, 30,40,60,80)))
677
+
678
+
679
+
680
+ sys.exit(0)
681
+
682
+ if True:
683
+ # rotation of images 1 and 2 are correct so the error is clearly elsewhere if not centered -−> see why that is
684
+ image = QImage('/E/Sample_images/counter/01.png')
685
+ # image = QImage('/E/Sample_images/counter/02.png')
686
+ # image = QImage('/E/Sample_images/sample_images_PA/test.png')
687
+ rotated = rotate_image(image, 42)
688
+ rotated.save('/home/aigouy/Bureau/test.png')
689
+
690
+
691
+ sys.exit(0)
File without changes