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,1985 @@
1
+ # this seems to work better and better !!!
2
+ from batoolset.settings.global_settings import set_UI # set the UI to qtpy
3
+ set_UI()
4
+ import matplotlib.pyplot as plt
5
+ import numpy as np
6
+ # from batoolset.drawings.shapes.vectorgraphics2d import VectorGraphics2D
7
+ from batoolset.img import Img
8
+ from batoolset.lists.tools import move_left, move_right, is_iterable
9
+ import random
10
+ import sys
11
+ import traceback
12
+ # this will replace cols or rows and will be able to do both but more flexibly
13
+ from timeit import default_timer as timer
14
+ from batoolset.serializations.tools import create_objects_from_dict
15
+ from batoolset.drawings.shapes.image2d import Image2D
16
+ from batoolset.drawings.shapes.rectangle2d import Rectangle2D
17
+ from batoolset.drawings.shapes.txt2d import TAText2D
18
+ from qtpy.QtCore import QRectF, QSizeF,QPointF
19
+ from qtpy.QtWidgets import QApplication
20
+ from qtpy.QtGui import QPainter, QColor, QBrush, QPen, QTransform
21
+ from batoolset.figure.alignment import pack2, align2, align_positions
22
+ from batoolset.tests.tools import are_ratios_almost_equal
23
+ from batoolset.drawings.shapes.compute_optimal_size_all_merged import resize_rects_within_total_width, \
24
+ resize_rects_within_total_height, resize_rect_preserve_aspect_ratio
25
+
26
+ ORIENTATIONS = ["X","Y"] #,"grid"
27
+
28
+ def get_all_contained_images(obj):
29
+ """
30
+ Returns a list of all iterable elements in the given object.
31
+ """
32
+ # print(is_iterable(obj), obj)
33
+ if is_iterable(obj):
34
+ # obj is an iterable, so iterate over its elements
35
+ elements = []
36
+ for element in obj:
37
+ elements.extend(get_all_contained_images(element))
38
+ # if not elements:
39
+ # return None
40
+ return elements
41
+ else:
42
+ # obj is not an iterable, so return an empty list
43
+ return [obj]
44
+ # if not inner_elements:
45
+ # return None
46
+
47
+
48
+ # def get_imprecision2(grp, desired_size):
49
+ # if grp.orientation == 'X':
50
+ # take the first immutable stuff
51
+ def get_iterable_elements(obj):
52
+ """
53
+ Returns a list of all iterable elements in the given object.
54
+ """
55
+
56
+ # print(is_iterable(obj), obj)
57
+ if is_iterable(obj):
58
+ # obj is an iterable, so iterate over its elements
59
+ elements = [obj]
60
+ for element in obj:
61
+ elements.extend(get_iterable_elements(element))
62
+ # if not elements:
63
+ # return None
64
+ return elements
65
+ else:
66
+ # obj is not an iterable, so return an empty list
67
+ return []
68
+
69
+
70
+ def get_parent_of_obj(obj, shapes_to_draw):
71
+ if not shapes_to_draw:
72
+ return
73
+ if not isinstance(shapes_to_draw, list):
74
+ shapes_to_draw=[shapes_to_draw]
75
+
76
+ for shape in shapes_to_draw:
77
+ iterables = get_iterable_elements(shape)
78
+ if iterables:
79
+ for itr in iterables:
80
+ if obj in itr:
81
+ return itr
82
+
83
+
84
+ # def get_imprecision(grp, desired_size=None):
85
+ # # first = grp[0]
86
+ # # last = grp[-1]
87
+ #
88
+ # # print('first.immutable',first.immutable, last.immutable)
89
+ # #
90
+ # # if first.immutable:
91
+ # # for elm in grp:
92
+ # # if not elm.immutable:
93
+ # # first=elm
94
+ # # break
95
+ # # if last.immutable:
96
+ # # for elm in grp[::-1]:
97
+ # # if not elm.immutable:
98
+ # # last=elm
99
+ # # break
100
+ # #
101
+ # # print('first',first, grp.content[1]) -−> indeed I cannot use that
102
+ #
103
+ # # imprecisionX = abs(first.getRect().height() - last.getRect().height())
104
+ # # imprecisionY = abs(first.getRect().width() - last.getRect().width())
105
+ # # imprecision = min(abs(imprecisionX), abs(imprecisionY))
106
+ # if desired_size:
107
+ # imprecisionX = abs(grp.getRect().height() - desired_size)
108
+ # imprecisionY = abs(grp.getRect().width() -desired_size)
109
+ # else:
110
+ # imprecisionX = abs(grp.getRect().height() - int(round(grp.getRect().height())))
111
+ # imprecisionY = abs(grp.getRect().width() - int(round(grp.getRect().width())))
112
+ #
113
+ # imprecision = min(abs(imprecisionX), abs(imprecisionY))
114
+ #
115
+ # print('imprecision in pixels X', imprecisionX)
116
+ # print('imprecision in pixels Y', imprecisionY)
117
+ # print('imprecision', imprecision)
118
+
119
+ # def set_to_size_old(grp, size, nb_of_repeats=26):
120
+ # # even at 100 I get a small imprecision -−> maybe forget about that shit and take 26 always # dirty but ok for now -> I have bigger things to fix --> I tried 100 and it is perfect but 10 already seems to be enough --> maybe that is ok then --> even 3 is ok
121
+ # # for some reason the stuff needs to be run several times to converge --> maybe some day I'll get the reason but it is ok and does not take time to do so!!!
122
+ # for i in range(nb_of_repeats):
123
+ # grp.set_to_size_old(size)
124
+ # grp.size = size
125
+
126
+ _MIN_CROSS = 1. # a row lower than this (too many labels/gaps for its width) is kept at it: no negative scales
127
+
128
+
129
+ def _label_extent(label, axis):
130
+ """Width (axis 'X') or height of a label's text: what it needs."""
131
+ annotations = getattr(label, 'annotations', None)
132
+ shape = annotations[0] if annotations else label
133
+ return shape.width(all=True) if axis == 'X' else shape.height(all=True)
134
+
135
+
136
+ def _affine(elm, axis):
137
+ """(a, b) such that elm's extent along *axis* = a * (its extent across
138
+ *axis*) + b, as Group.set_to_size lays it out."""
139
+ if isinstance(elm, Group):
140
+ if elm.isText:
141
+ rect = elm.getRect(all=True)
142
+ return 0., (rect.width() if axis == 'X' else rect.height())
143
+ return elm._affine(axis)
144
+ if getattr(elm, 'isText', False):
145
+ return 0., _label_extent(elm, axis)
146
+ ar = elm.getAR()
147
+ return (ar, 0.) if axis == 'X' else (1. / ar, 0.)
148
+
149
+
150
+ def _exact(grp):
151
+ """The group can be laid out in one pass (see Group.set_to_size)."""
152
+ try:
153
+ return grp._affine(grp.orientation)[0] > 0
154
+ except ZeroDivisionError:
155
+ return False
156
+
157
+
158
+ def set_to_size(grp, size, nb_of_repeats=26):
159
+ if _exact(grp):
160
+ grp.set_to_size(size)
161
+ return
162
+ # even at 100 I get a small imprecision -−> maybe forget about that shit and take 26 always # dirty but ok for now -> I have bigger things to fix --> I tried 100 and it is perfect but 10 already seems to be enough --> maybe that is ok then --> even 3 is ok
163
+ # for some reason the stuff needs to be run several times to converge --> maybe some day I'll get the reason but it is ok and does not take time to do so!!!
164
+ for i in range(nb_of_repeats):
165
+ grp.set_to_size(size)
166
+ # grp.size = size
167
+
168
+
169
+ def set_to_width(grp, desired_width, nb_of_repeats=26):
170
+ # in fact that was not so hard to do
171
+ # Calculate the current aspect ratio of the object
172
+
173
+ if grp.orientation != 'X' and _exact(grp):
174
+ grp._layout_cross(desired_width) # a column: every panel gets that width
175
+ # the width asked for, not the one reached: squeezed past its 1 px
176
+ # minimum, a panel makes the group wider, and the next relayout
177
+ # (undo, reopen) started from that and drifted (monkey seed 122)
178
+ grp.size = desired_width
179
+ elif grp.orientation != 'X':
180
+ for i in range(nb_of_repeats):
181
+
182
+ current_width = grp.getRect(all=True).width()
183
+ current_height = grp.getRect(all=True).height()
184
+
185
+ # print('falbala',current_width, current_height, desired_width)
186
+ if current_width <= 0 or current_height <= 0: # (an empty group is 0 x 0)
187
+ # TODO --> KEEP DEV NOTE THIS PREVENTS INFINITE LOOPS WITH NEGATIVE VALUES
188
+ #
189
+ # current_width = grp.getRect().width()
190
+ # current_height = grp.getRect().height()
191
+ # print('falbala fixed', current_width, current_height, desired_width)
192
+ current_width = 512
193
+ current_height = 512
194
+
195
+ aspect_ratio = current_height / current_width if current_width != 0 else 0
196
+
197
+ factor_from_current_width_to_desired_width = desired_width/current_width
198
+ desired_height = current_height * factor_from_current_width_to_desired_width
199
+
200
+ # corresponding_desired_height = current_width * aspect_ratio
201
+
202
+ # corresponding_desired_height = current_width*aspect_ratio
203
+ # factor_from_current_width_to_predicted_height = corresponding_desired_height/
204
+
205
+ grp.set_to_size(desired_height)
206
+ grp.size = grp.width() if grp.content else desired_width
207
+
208
+ # print('aspect_ratio CURRENT CHECK ', aspect_ratio, grp.getRect(), desired_width, desired_height)
209
+ else:
210
+ set_to_size(grp, desired_width, nb_of_repeats=nb_of_repeats)
211
+ # (an empty group keeps the width asked for too: 0 wide, it made the
212
+ # panels added to it later 1 px small)
213
+ grp.size = desired_width if (_exact(grp) or not grp.content) else grp.width()
214
+
215
+ def set_to_height(grp, desired_height, nb_of_repeats=26):
216
+ if grp.orientation != 'Y' and _exact(grp):
217
+ grp._layout_cross(desired_height) # a row: every panel gets that height
218
+ grp.size = grp.height()
219
+ elif grp.orientation != 'Y':
220
+ for i in range(nb_of_repeats):
221
+ current_width = grp.getRect(all=True).width()
222
+ current_height = grp.getRect(all=True).height()
223
+
224
+ # print('falbala',current_width, current_height, desired_width)
225
+ if current_width <= 0 or current_height <= 0: # (an empty group is 0 x 0)
226
+ # TODO --> KEEP DEV NOTE THIS PREVENTS INFINITE LOOPS WITH NEGATIVE VALUES
227
+ #
228
+ # current_width = grp.getRect().width()
229
+ # current_height = grp.getRect().height()
230
+ # print('falbala fixed', current_width, current_height, desired_width)
231
+ current_width = 512
232
+ current_height = 512
233
+
234
+ aspect_ratio = current_width / current_height if current_height != 0 else 0
235
+
236
+ factor_from_current_height_to_desired_height = desired_height / current_height
237
+ desired_width = current_width * factor_from_current_height_to_desired_height
238
+
239
+ grp.set_to_size(desired_width)
240
+ grp.size = grp.height() if grp.content else desired_height
241
+
242
+ # print('aspect_ratio CURRENT CHECK ', aspect_ratio, grp.getRect(), desired_width, desired_height)
243
+ else:
244
+ set_to_size(grp, desired_height, nb_of_repeats=nb_of_repeats)
245
+ grp.size = grp.height() if grp.content else desired_height
246
+
247
+
248
+ # Calculate the new height based on the desired width and the aspect ratio
249
+ # new_height = desired_width * aspect_ratio
250
+ def set_to_width_im2d(rect, desired_width):
251
+ current_width = rect.width()
252
+ current_height = rect.height()
253
+ aspect_ratio = current_height / current_width if current_width != 0 else 0
254
+
255
+ factor_from_current_width_to_desired_width = desired_width / current_width
256
+ desired_height = current_height * factor_from_current_width_to_desired_width
257
+
258
+ # corresponding_desired_height = current_width * aspect_ratio
259
+
260
+ rect.set_to_scale(current_width / desired_width)
261
+ # corresponding_desired_height = current_width*aspect_ratio
262
+ # factor_from_current_width_to_predicted_height = corresponding_desired_height/
263
+ # grp.set_to_size(desired_height)
264
+ # return new_rect
265
+
266
+ def set_to_height_im2d(rect, desired_height):
267
+ # current_width = rect.width()
268
+ # current_height = rect.height()
269
+ # aspect_ratio = current_width / current_height if current_height != 0 else 0
270
+ #
271
+ # desired_width = current_width * (desired_height / current_height)
272
+ # rect.set_to_scale(1./(desired_width/desired_height))
273
+
274
+ current_width = rect.width()
275
+ current_height =rect.height()
276
+ aspect_ratio = current_width / current_height if current_height != 0 else 0
277
+
278
+ factor_from_current_height_to_desired_height = desired_height / current_height
279
+ desired_width = current_width * factor_from_current_height_to_desired_height
280
+
281
+ # grp.set_to_size(desired_width)
282
+ rect.set_to_scale(current_height/desired_height)
283
+
284
+ # new_rect = QRectF(rect.x(), rect.y(), desired_width, desired_height)
285
+
286
+ # return new_rect
287
+
288
+ # def set_to_width(grp, size, nb_of_repeats=26):
289
+ # for i in range(nb_of_repeats):
290
+ # grp.set_to_size(size)
291
+
292
+ def areIndicesOverlapping(indices_of_all_shapes):
293
+
294
+ # Iterate over the indices_of_all_shapes list
295
+ for i in range(len(indices_of_all_shapes) - 1):
296
+ # Get the current and next elements
297
+ curr = indices_of_all_shapes[i]
298
+ next = indices_of_all_shapes[i + 1]
299
+
300
+ # print('curr, next', curr, next)
301
+
302
+ # Check if the elements overlap
303
+ if curr[1] >= next[0]:
304
+ # print('Overlap found between elements', i, 'and', i + 1)
305
+ return True
306
+ return False
307
+
308
+
309
+ class Group(Rectangle2D):
310
+ _count=0 # this is a class variable
311
+
312
+ # NB maybe is text could contain the text position instead of true such as above or below --> and it would adjust to stuff below it --> TODO
313
+ def __init__(self, *args, space=3, x=None,y=None, width=None, height=None, fill_color=None, content=None, orientation=ORIENTATIONS[0], size=512, isText=False, add_empty_images_instead_of_None=True,**kwargs): # TODO DO NOT CALL IT ORIENTATION AS IT CAN BE MIXED WITH ANGLE -−> NEED SMARTER NAME
314
+ # if kwargs:
315
+ # print('ignored parameters', kwargs)
316
+
317
+ if x is not None and y is not None and width is not None and height is not None:
318
+ super(Rectangle2D, self).__init__(x,y,width,height)
319
+ else:
320
+ super(Rectangle2D, self).__init__()
321
+
322
+ # if self.width() == 0 and self.height==0:
323
+ # self.setWidth(size)
324
+ # self.setHeight(size)
325
+
326
+
327
+ # in fact I need to reload content
328
+
329
+
330
+ self.content = []
331
+ self.space = space
332
+ if isinstance(orientation, str):
333
+ # legacy .pyf files serialize orientation with embedded quotes ('"X"')
334
+ orientation = orientation.strip().strip('\'"') or ORIENTATIONS[0]
335
+ if isinstance(isText, str) and isText.strip('\'"').lower() in ('above', 'below'):
336
+ # a label row is always a row (as associate_text_row builds it):
337
+ # saved vertical, its layout divided by zero and sent every panel
338
+ # below it to y = 1e30, and the figure could not be exported
339
+ orientation = 'X'
340
+ self.orientation=orientation
341
+ if args:
342
+ for arg in args:
343
+ if isinstance(arg, str):
344
+ arg = Image2D(arg) # TODO --> need make this smarter especially with custom code files
345
+ if arg is None and add_empty_images_instead_of_None:
346
+ if self.content:
347
+ last = self.content[-1]
348
+ if isinstance(last, Image2D):
349
+ w,h = last.get_raw_size()
350
+ tmp = Image2D(width=w, height=h, crop_left=last.crop_left, crop_right=last.crop_right,crop_top=last.crop_top,crop_bottom=last.crop_bottom, theta=last.theta) # a true clone of the last
351
+ self.content.append(tmp)
352
+ else:
353
+ if arg is not None:
354
+ self.content.append(arg)
355
+
356
+ if isinstance(content, dict) or (isinstance(content, list) and content
357
+ and all(isinstance(c, dict) for c in content)):
358
+ # Reload content from serialized object (a list of one-key dicts
359
+ # keeps the order of mixed groups and images)
360
+ content = create_objects_from_dict(content)
361
+ self.content.extend(content)
362
+
363
+ # print(self.content)
364
+ # if size is not None:
365
+ self.size = size # maybe useful and I need to store that ??? -−> the good thing is that it will not change and so it is better
366
+ # set_to_size(self,size) # shall I use that ??? --> maybe ...
367
+ try:
368
+ self.set_to_size(self.size) # very good but maybe I also need to update it
369
+ set_to_width(self, self.size) # for now force size in x only to simplify things
370
+ except (ArithmeticError, ValueError):
371
+ # panels squeezed to nothing (too many gaps for the width, very
372
+ # deep nesting): the layout divides by zero. The group used to be
373
+ # dropped by the loader — most of a figure gone on open, and lost
374
+ # on the next save. Keep it, with its content as saved.
375
+ traceback.print_exc()
376
+ Group._count += 1
377
+ self.ID = Group._count
378
+ self.fill_color = fill_color
379
+ self.immutable = False
380
+ self.isText = isText # this is just for pure text groups (will have a different behaviour on double click)
381
+ # self.scale = 1
382
+
383
+ # List-like behavior
384
+ def __getitem__(self, key):
385
+ # print(key)
386
+ return self.content[key]
387
+
388
+ def __len__(self):
389
+ return len(self.content)
390
+
391
+ def __iter__(self):
392
+ return iter(self.content)
393
+
394
+ def index(self, item):
395
+ return self.content.index(item)
396
+ # def __delitem__(self, index):
397
+ # del self[index]
398
+ #
399
+ def remove(self, *value):
400
+
401
+ try:
402
+
403
+ # print('stuff to be removed', value)
404
+
405
+ # del self.content(value)
406
+ # idx = self.content.index(value)
407
+ # del self[idx]
408
+ # self.content.remove(value)
409
+ # self.content = [x for x in self.content if x not in value]
410
+ for element in value:
411
+ try:
412
+ self.content.remove(element)
413
+ except Exception:
414
+ pass
415
+ for inner in self.content:
416
+ try:
417
+ inner.remove(*value)
418
+ if inner.isEmpty():
419
+ self.content.remove(inner)
420
+ except Exception:
421
+ pass
422
+
423
+ if not self.isEmpty():
424
+ try:
425
+ # print('resizing', self, self.content)
426
+ set_to_size(self, self.width() if self.orientation == 'X' else self.height())
427
+ except Exception:
428
+ # print('just for info for now, comment that lines in the future')
429
+ traceback.print_exc()
430
+ print('just for info for now, comment that lines in the future')
431
+ pass
432
+ # i need go through all the stuff inside --> not that easy
433
+ # shall I resize it ??? TODO --> PROBABLY YES BECAUSE SIMPLER
434
+
435
+ except Exception:
436
+ traceback.print_exc()
437
+ pass
438
+
439
+ def update(self):
440
+ try:
441
+ if not self.isEmpty():
442
+ # print('updating', self, self.content)
443
+ # set_to_size(self, self.width() if self.orientation == 'X' else self.height())
444
+ set_to_size(self, self.size)
445
+ except Exception:
446
+ traceback.print_exc()
447
+ print('just for info for now, comment that lines in the future')
448
+ pass
449
+
450
+ def isEmpty(self):
451
+ return not self.content
452
+
453
+ def pack(self):
454
+ pack2(self.space, self.orientation,False, *self.content)
455
+
456
+ def align(self):
457
+ if self.orientation == 'X':
458
+ align2(align_positions[0], *self.content) # align top
459
+ else:
460
+ align2(align_positions[2], *self.content) # align Left
461
+
462
+ def set_to_scale(self, scale):
463
+ # the set to scale should change the size of the object --> either in width or in height depending on the object
464
+ # TODO
465
+ # maybe need repack if it is a group
466
+
467
+
468
+ # print('I have been called', scale)
469
+
470
+ # the scale of this one has to be magic and launch many more stuff
471
+
472
+ if self.orientation == 'X':
473
+ self.set_to_size(self.width() / scale)
474
+ else:
475
+ self.set_to_size(self.height()/scale)
476
+ # self.scale = 1
477
+ # pass
478
+
479
+ # def set_to_size_neo(self, desired_size):
480
+ # all_ok=False
481
+ #
482
+ # for elm in self:
483
+ # if elm.isText:
484
+ # all_ok=True
485
+ # break
486
+ #
487
+ # # print('all_ok', all_ok)
488
+ #
489
+ # if not all_ok:
490
+ # return self.set_to_size_old(desired_size)
491
+ #
492
+ # if self.orientation == 'X':
493
+ # max_w = 0
494
+ # max_h = 0
495
+ # failed_images = []
496
+ # text_images = []
497
+ # individual_width = []
498
+ # individual_height = []
499
+ # for elm in self:
500
+ # # try:
501
+ # if elm.isText:
502
+ # individual_width.append(elm.annotations[0].width(all=True))
503
+ # individual_height.append(elm.annotations[0].height(all=True))
504
+ # max_w = max(elm.annotations[0].width(all=True), max_w)
505
+ # max_h = max(elm.annotations[0].height(all=True), max_h)
506
+ # text_images.append(elm)
507
+ # else:
508
+ # # pass # if fails this is not a text image
509
+ # failed_images.append(elm) # if image is failed then we have it
510
+ #
511
+ # # I need to fit to the desired width
512
+ # desired_width = desired_size
513
+ # target_width_without_txt = desired_width - sum(individual_width)
514
+ #
515
+ # # fit into a group with the desired stuff
516
+ #
517
+ # print('max_w, max_h', max_w, max_h)
518
+ # print('individual_width', individual_width)
519
+ # print('individual_height', individual_height)
520
+ # print('desired_width', desired_width)
521
+ # print('target_width_without_txt',
522
+ # target_width_without_txt) # it is probably a bit more complex because I need also the spacer and the nb of elems of the spacer inside and outside
523
+ # print('elems in', len(failed_images))
524
+ # print('elems out', len(self) - len(failed_images))
525
+ # # try to do the fit --> TODO
526
+ #
527
+ # # TODO --> pack to the size
528
+ # # incompressible_space_of_group = (len(self.paint.EZFIG_panel.selected_shape)-1)*3
529
+ # incompressible_space_of_group = (len(text_images)) * self.space
530
+ # target_width_without_txt -= incompressible_space_of_group
531
+ #
532
+ # # I need fit the stuff
533
+ #
534
+ # # create a sub group for the failed images and set its size
535
+ # tmp = Group(*failed_images, space=self.space, orientation='X', size=target_width_without_txt)
536
+ # set_to_width(tmp, target_width_without_txt)
537
+ #
538
+ # # then get the height of the group
539
+ #
540
+ # max_h = tmp.height()
541
+ # # put the other images back into their stuff and just launch the packing of the group
542
+ # # and resize the images to that size
543
+ #
544
+ # for iii, elm in enumerate(text_images):
545
+ # # elm.scale=1.
546
+ # elm.setHeight(max_h)
547
+ # elm.setWidth(individual_width[iii])
548
+ # elm.set_to_scale(1)
549
+ #
550
+ # self.pack()
551
+ # self.align()
552
+ # self.update_bounds()
553
+ # # self.update()
554
+ # else:
555
+ #
556
+ #
557
+ # print('entering orientation Y', self)
558
+ # # this works but the embeded does not --> why is that
559
+ #
560
+ # # i need to fit all the shapes without the stuff to the desired width then just readd the other stuff
561
+ #
562
+ # # I need get the height
563
+ #
564
+ # # ça marche --> ça donne un moyen facile de faire tout ce que je voulais
565
+ # # now we fix the code so that we fit the height of an object according to that of the others --> TODO
566
+ # max_w = 0
567
+ # max_h = 0
568
+ # failed_images = []
569
+ # text_images = []
570
+ # individual_width = []
571
+ # individual_height = []
572
+ # for elm in self:
573
+ # if elm.isText:
574
+ # individual_width.append(elm.annotations[0].width(all=True))
575
+ # individual_height.append(elm.annotations[0].height(all=True))
576
+ # max_w = max(elm.annotations[0].width(all=True), max_w)
577
+ # max_h = max(elm.annotations[0].height(all=True), max_h)
578
+ # text_images.append(elm)
579
+ # else:
580
+ # # pass # if fails this is not a text image
581
+ # failed_images.append(elm) # if image is failed then we have it
582
+ #
583
+ # # I need to fit to the desired width
584
+ # desired_width = desired_size # 256 #
585
+ # target_width_without_txt = desired_width # since it is a col it does not need all of that
586
+ #
587
+ # # fit into a group with the desired stuff
588
+ #
589
+ # print('max_w, max_h', max_w, max_h)
590
+ # print('individual_width', individual_width)
591
+ # print('individual_height', individual_height)
592
+ # print('desired_width', desired_width)
593
+ # print('target_width_without_txt',
594
+ # target_width_without_txt) # it is probably a bit more complex because I need also the spacer and the nb of elems of the spacer inside and outside
595
+ # print('elems in', len(failed_images))
596
+ # print('elems out', len(self) - len(failed_images))
597
+ # # try to do the fit --> TODO
598
+ #
599
+ # # TODO --> pack to the size
600
+ # # incompressible_space_of_group = (len(self.paint.EZFIG_panel.selected_shape)-1)*3
601
+ # incompressible_space_of_group = (len(text_images)) * 3
602
+ # # target_width_without_txt -= incompressible_space_of_group
603
+ #
604
+ # # I need fit the stuff
605
+ #
606
+ # # create a sub group for the failed images and set its size
607
+ # tmp = Group(*failed_images, space=3, orientation='X')
608
+ # set_to_width(tmp, target_width_without_txt)
609
+ #
610
+ # # then get the height of the group
611
+ #
612
+ # max_w = tmp.width()
613
+ # # put the other images back into their stuff and just launch the packing of the group
614
+ # # and resize the images to that size
615
+ #
616
+ # for iii, elm in enumerate(text_images):
617
+ # # elm.scale=1.
618
+ # elm.setHeight(individual_height[iii])
619
+ # elm.setWidth(max_w)
620
+ # elm.set_to_scale(1)
621
+ #
622
+ #
623
+ #
624
+ # # del tmp
625
+ # # just pack the shape and nothing else and set x to 0
626
+ # # align top and pack
627
+ # # self.paint.EZFIG_panel.selected_shape.pack()
628
+ # # self.paint.EZFIG_panel.selected_shape.align()
629
+ # # self.paint.EZFIG_panel.selected_shape.update_bounds()
630
+ #
631
+ # set_to_width(self, target_width_without_txt)
632
+ #
633
+ # print('inner update', self.update_bounds())
634
+
635
+ def _affine(self, axis):
636
+ """(a, b) such that, laid out by set_to_size, this group's extent
637
+ along *axis* = a * (its extent across *axis*) + b. Labels and gaps
638
+ are the b part: they do not scale with the panels."""
639
+ if axis == self.orientation:
640
+ a_sum = 0.
641
+ b_sum = self.space * (len(self.content) - 1)
642
+ for elm in self.content:
643
+ a, b = _affine(elm, axis)
644
+ a_sum += a
645
+ b_sum += b
646
+ return a_sum, b_sum
647
+ a, b = self._affine(self.orientation)
648
+ if a > 0:
649
+ return 1. / a, -b / a
650
+ # labels only: as tall (wide) as the largest of them
651
+ return 0., max((_affine(elm, axis)[1] for elm in self.content), default=0.)
652
+
653
+ def _minimum_cross(self):
654
+ """Smallest row height (column width) keeping every panel of every
655
+ nested group at or above _MIN_CROSS.
656
+
657
+ Clamped deeper down instead, a nested row was bigger than its parent
658
+ solved for, and laying the figure out again at its own width gave
659
+ other sizes (redo and reopen drifted: monkey seed 201, found by
660
+ ChatGPT)."""
661
+ minimum = _MIN_CROSS
662
+ for elm in self.content:
663
+ if not isinstance(elm, Group) or elm.isText:
664
+ continue
665
+ needed = elm._minimum_cross()
666
+ if elm.orientation != self.orientation:
667
+ # our cross extent is its extent along its own axis
668
+ a, b = elm._affine(elm.orientation)
669
+ needed = a * needed + b
670
+ minimum = max(minimum, needed)
671
+ return minimum
672
+
673
+ def _layout_cross(self, cross):
674
+ """Lay the content out with *cross* as the row height (column width)."""
675
+ cross = max(cross, self._minimum_cross())
676
+ for elm in self.content:
677
+ if isinstance(elm, Group):
678
+ if elm.isText:
679
+ # a label row inside a group: as resize_rects_within_* did
680
+ if self.orientation == 'X':
681
+ elm.setHeight(cross)
682
+ else:
683
+ elm.setWidth(cross)
684
+ elm.set_to_scale(1.)
685
+ elif elm.orientation == self.orientation:
686
+ elm._layout_cross(cross)
687
+ else:
688
+ elm.set_to_size(cross)
689
+ elif getattr(elm, 'isText', False):
690
+ width, height = _label_extent(elm, 'X'), _label_extent(elm, 'Y')
691
+ if self.orientation == 'X':
692
+ elm.setWidth(width)
693
+ elm.setHeight(cross)
694
+ else:
695
+ elm.setHeight(height)
696
+ elm.setWidth(cross)
697
+ elm.set_to_scale(1.)
698
+ elif self.orientation == 'X':
699
+ resize_rect_preserve_aspect_ratio(elm, None, cross)
700
+ else:
701
+ resize_rect_preserve_aspect_ratio(elm, cross, None)
702
+ self.pack()
703
+ self.align()
704
+ self.update_bounds()
705
+
706
+ def set_to_size(self, desired_size):
707
+ # solved exactly: the old fixed-point iteration (26 passes, groups
708
+ # taken as scaling like images) did not converge with labels stacked
709
+ # in a column — undo/redo and reopening gave other sizes, or
710
+ # negative scales (panels not drawn)
711
+ try:
712
+ a, b = self._affine(self.orientation)
713
+ except ZeroDivisionError:
714
+ a = 0
715
+ if a > 0:
716
+ self._layout_cross((desired_size - b) / a)
717
+ return
718
+ sum_along_axis = self.get_incompressible_size(axis=self.orientation)
719
+ if self.orientation == 'X':
720
+ resize_rects_within_total_width(self.content, desired_size, sum_along_axis)
721
+ # resize_rects_within_total_width(self.content, desired_size, self.space)
722
+ else:
723
+ # resize_rects_within_total_height(self.content, desired_size, self.space)
724
+ resize_rects_within_total_height(self.content, desired_size, sum_along_axis)
725
+
726
+
727
+ # if True:
728
+ # print('beforesdsqdq0')
729
+ # self.check_coords()
730
+
731
+ self.pack()
732
+ # if True:
733
+ # print('beforesdsqdq1')
734
+ # self.check_coords()
735
+
736
+ self.align()
737
+ # if True:
738
+ # print('beforesdsqdq2')
739
+ # self.check_coords()
740
+ self.update_bounds()
741
+
742
+ # def set_to_size_old(self, desired_size):
743
+ # # for i in range(100): # weird but somehow I need a convergence and iteration stuff (is that due to a bug, can I iterate in a smarter manner) ???
744
+ # # print('entering the real shit')
745
+ # sum_along_axis = self.get_incompressible_size(axis=self.orientation)
746
+ # if self.orientation == 'X':
747
+ # resize_rects_within_total_width(self.content, desired_size, sum_along_axis)
748
+ # # resize_rects_within_total_width(self.content, desired_size, self.space)
749
+ # else:
750
+ # # resize_rects_within_total_height(self.content, desired_size, self.space)
751
+ # resize_rects_within_total_height(self.content, desired_size, sum_along_axis)
752
+ #
753
+ # # if True:
754
+ # # print('beforesdsqdq0')
755
+ # # self.check_coords()
756
+ #
757
+ # self.pack()
758
+ # # if True:
759
+ # # print('beforesdsqdq1')
760
+ # # self.check_coords()
761
+ #
762
+ # self.align()
763
+ # # if True:
764
+ # # print('beforesdsqdq2')
765
+ # # self.check_coords()
766
+ # self.update_bounds()
767
+
768
+ def check_coords(self):
769
+ for iii,img in enumerate(self):
770
+ print('iii', iii, '-->', img.getRect())
771
+
772
+ def checkAR(self):
773
+ for iii, rect in enumerate(self.content):
774
+ print(are_ratios_almost_equal(rect.width() / rect.height(), rect.width(all=True) / rect.height(all=True)), 'old AR', rect.width() / rect.height(), 'new AR', rect.width(all=True) / rect.height(all=True))
775
+
776
+ # NB THE RECT AND WIDTH OF GROUP SHOULD NOT BE SCALABLE !!! --> see how I can do this in a cleaner way !!!!
777
+ def getRect(self,*args, **kwargs):
778
+ # if args or kwargs:
779
+ return QRectF(self.x(), self.y(), self.width(), self.height())
780
+ # else:
781
+ # # return super().getRect()
782
+ # return QRectF(*super().getRect())
783
+ def width(self,*args, **kwargs):
784
+ # return self.width()
785
+ # else:
786
+ return super().width()
787
+
788
+ def height(self,*args, **kwargs):
789
+ # if args or kwargs:
790
+ # return self.height()
791
+ # else:
792
+ return super().height()
793
+
794
+ def update_bounds(self):
795
+ if not self.content:
796
+ # an emptied group (its panels dragged out) got infinite bounds,
797
+ # its row size -inf and NaN coordinates: painting raised and Qt
798
+ # crashed natively (monkey seed 143)
799
+ self.setRect(self.x(), self.y(), 0, 0)
800
+ return
801
+ # Initialize bounding_rect with maximum and minimum values
802
+ bounding_rect = [float('inf'), float('inf'), float('-inf'), float('-inf')]
803
+
804
+ for rect in self.content:
805
+ bounding_rect[0] = min(bounding_rect[0], rect.x())
806
+ bounding_rect[1] = min(bounding_rect[1], rect.y())
807
+ bounding_rect[2] = max(bounding_rect[2], rect.x() + rect.width(all=True))
808
+ bounding_rect[3] = max(bounding_rect[3], rect.y() + rect.height(all=True))
809
+
810
+ self.setRect(bounding_rect[0], bounding_rect[1], bounding_rect[2] - bounding_rect[0],
811
+ bounding_rect[3] - bounding_rect[1])
812
+ # print('bounding rect', self.getRect(scale=True))
813
+
814
+
815
+
816
+
817
+ def getCombinedBoundsAtIndices(self, image_indices):
818
+ if not image_indices:
819
+ return None
820
+ # if isinstance(image_indices, int):
821
+ # in
822
+ # for each there will be a begin and an end
823
+ begin, end = image_indices
824
+ # if begin == end:
825
+ # print('same begin and end')
826
+
827
+ # print('begin, end', begin, end)
828
+
829
+
830
+
831
+ # how can I keep assocition despite serialization --> by storing the element into another --> but that would complicate a bit the things though -−> see how I can do that
832
+ # or just say if it should be associated to above or below ????
833
+ if begin>end: # begin should always be before end
834
+ begin, end = end, begin
835
+
836
+ if begin<0:
837
+ begin = 0
838
+ if end>=len(self.content):
839
+ end = len(self.content)-1
840
+ if end<0:
841
+ end=0
842
+
843
+ if begin>=0 and begin<len(self.content):
844
+ begin_rect = self.content[begin].getRect(all=True)
845
+ # print('begin_rect',begin_rect)
846
+ if begin==end:
847
+ return begin_rect
848
+ if end>=0 and end < len(self.content):
849
+ end_rect = self.content[end].getRect(all=True)
850
+ # print('end_rect',end_rect)
851
+
852
+ try:
853
+ return begin_rect.united(end_rect) # this returns the combined rect of begin and end coordinates -−> that is exactly what I want
854
+ except Exception:
855
+ return None
856
+
857
+
858
+
859
+ # untested
860
+ def translate(self, *args):
861
+ if len(args) == 1:
862
+ point = args[0]
863
+ QRectF.translate(self, point.x(), point.y())
864
+ for img in self:
865
+ img.translate(point.x(), point.y())
866
+ else:
867
+ QRectF.translate(self, args[0], args[1])
868
+ for img in self:
869
+ img.translate(QPointF(args[0], args[1]))
870
+
871
+ # def swap(self, element1, element2): # so in fact it is not a swap it is a replace
872
+ # find element1 and replace it with element2
873
+ # swap can be seen as a chain of replace with inverting one by the other
874
+
875
+ # def replace(self, element1, element2):
876
+ # pass
877
+
878
+ # def swap(self, element1, element2):
879
+ # loop
880
+ # if not isinstance(__args[0], QPointF):
881
+ # for elm in self.content:
882
+ # if elm == __args[0]:
883
+ # return True
884
+ # if elm.contains(__args[0]):
885
+ # return True
886
+ # else:
887
+ # if super().contains(
888
+ # *__args): # if anything in there contains the point return the containing element else return False
889
+ # for elm in self.content:
890
+ # if elm.contains(*__args):
891
+ # return elm
892
+ # return True
893
+ # return False
894
+
895
+ def move_left(self, elements, distance=1):
896
+ if elements is None:
897
+ return
898
+ if distance <=0:
899
+ return
900
+ if not isinstance(elements, list):
901
+ elements = [elements]
902
+
903
+ list_of_iterables = get_iterable_elements(self)
904
+ for elm in list_of_iterables:
905
+ indices = [i for i, x in enumerate(elm.content) if x in elements]
906
+ elm.content = move_left(elm.content, indices, distance) # get indices
907
+
908
+
909
+ # TODO --> do it recursive inside -−> TODO
910
+
911
+
912
+ def move_right(self, elements, distance=1):
913
+ if elements is None:
914
+ return
915
+ if distance <=0:
916
+ return
917
+ if not isinstance(elements, list):
918
+ elements = [elements]
919
+ # indices = [i for i, x in enumerate(self.content) if x in elements]
920
+ # self.content = move_right(self.content, indices, distance) # get indices
921
+ list_of_iterables = get_iterable_elements(self)
922
+ for elm in list_of_iterables:
923
+ indices = [i for i, x in enumerate(elm.content) if x in elements]
924
+ elm.content = move_right(elm.content, indices, distance) # get indices
925
+
926
+ def draw_inner_layout_selection(self, painter):
927
+ painter.save()
928
+ try:
929
+ inner_elements = get_all_contained_images(self)
930
+ # painter.setPen(QPen(QColor(0, 255, 255)))
931
+
932
+ for elm in inner_elements:
933
+ # rect = elm.getRect(scale=True)
934
+ # painter.drawRect(rect)
935
+ elm.draw_inner_layout_selection(painter)
936
+ except Exception:
937
+ print('inner error')
938
+ traceback.print_exc()
939
+ painter.restore()
940
+
941
+
942
+ def draw(self, painter, draw=True):
943
+
944
+ if draw:
945
+ painter.save()
946
+ rect_to_plot = self.getRect()
947
+ if self.fill_color is not None:
948
+ # if the user wants to really draw a bg with a given color between the images -−> then I draw this first before drawing anything else
949
+ painter.setBrush(QBrush(QColor(self.fill_color)))
950
+ painter.drawRect(rect_to_plot)
951
+
952
+ for img in self:
953
+ img.draw(painter, draw=draw)
954
+
955
+ painter.restore()
956
+
957
+ # debug
958
+ if False:
959
+ pen = QPen(QColor(0xFF00FF))
960
+ pen.setWidthF(3)
961
+ painter.setPen(pen)
962
+
963
+ # print('rect_to_plot',rect_to_plot)
964
+
965
+ painter.drawRect(rect_to_plot)
966
+
967
+ # as much as it is possible do get rid of fill to better organize the stuff
968
+ # def fill(self, painter, draw=True):
969
+ # if self.fill_color is None:
970
+ # return
971
+ # if draw:
972
+ # painter.save()
973
+ # for img in self:
974
+ # img.fill(painter, draw=draw)
975
+ # painter.restore()
976
+ #
977
+ # def drawAndFill(self, painter):
978
+ # painter.save()
979
+ # self.draw(painter, draw=True)
980
+ # painter.restore()
981
+
982
+ def get_incompressible_size(self, axis=None):
983
+ # return the meaningful incompressible state of the image
984
+ # pass
985
+ # if self.orientation == 'X':
986
+ # print('group_count', self._count)
987
+ if axis is None:
988
+ # print('sum_incompressible_along_axis2', self.space*(len(self.content)-1))
989
+ return self.space*(len(self.content)-1) # THAT IS NOT ENOUGH I ALSO NEED ALL THE PROGENY INCOMPRESSIBLE SPACE IN THIS ORIENTATION --> TODO
990
+ else:
991
+ sum_incompressible_along_axis = 0
992
+
993
+ # If I don't put the below line then the size works --> I am overestimating the stuff !!!
994
+ # for elm in self.content:
995
+ # sum_incompressible_along_axis+=elm.get_incompressible_size(axis=axis)
996
+ # for elm in self.content:
997
+ # sum_incompressible_along_axis+=elm.get_incompressible_size(axis=axis) # in fact I should not atke into account the others
998
+ if self.orientation == axis:
999
+ sum_incompressible_along_axis+=self.space*(len(self.content)-1)
1000
+ # print('sum_incompressible_along_axis', axis, sum_incompressible_along_axis, len(self.content), self.space, self.orientation, self._count)
1001
+ return sum_incompressible_along_axis
1002
+
1003
+ def setTopLeft(self, *args):
1004
+ if args:
1005
+ cur_top_left = self.topLeft()
1006
+ # print('cur_top_left', cur_top_left, args)
1007
+ if len(args)==1:
1008
+ # # assume a QpointF
1009
+ for img in self.content:
1010
+ # img.moveTopLeft(args[0])
1011
+ img.translate(args[0].x()-cur_top_left.x(), args[0].y()-cur_top_left.y())
1012
+ elif len(args) == 2:
1013
+ for img in self.content:
1014
+ img.translate(args[0] - cur_top_left.x(), args[1] - cur_top_left.y())
1015
+ self.update_bounds()
1016
+ # elif len(args)==2:
1017
+ # for img in self.content:
1018
+ # img.moveTopLeft(QPointF(args[0], args[1]))
1019
+ # else:
1020
+ # # logger.error('invalid args for top left')
1021
+ # print('invalid args for top left')
1022
+
1023
+ def replace(self, old, new): # NB THIS IS NOT RECURSIVE SO IT SHOULD BE USED ONLY WITH THE IMMEDIATE PARENT
1024
+ if old in self.content:
1025
+ self.content[self.content.index(old)]=new
1026
+
1027
+ def __contains__(self, item):
1028
+ return item in self.content
1029
+
1030
+ def __setitem__(self, index, item):
1031
+ self.content[index] = item
1032
+
1033
+ def get_nested_groups(self):
1034
+ groups = [self]
1035
+ if self.content:
1036
+ for group in self.content:
1037
+ if isinstance(group, Group):
1038
+ groups.extend(group.get_nested_groups())
1039
+ return groups
1040
+
1041
+ def contains(self, *__args):
1042
+ # hacked contains that checks whether a point or an image is contained in the stuff
1043
+ # see for images
1044
+
1045
+ # if not __args:
1046
+ # return False
1047
+ # if len(__args)==2:
1048
+ # # check if an innner object contains a point and return it
1049
+ # for elm in self.content:
1050
+ # if elm.contains()
1051
+ # else:
1052
+ # if isinstance(__args[0],Rectangle2D):
1053
+ # if self == __args[0]:
1054
+ # return True
1055
+ # else:
1056
+ # return super().getRect(scale=True).contains(*__args)
1057
+
1058
+ if not isinstance(__args[0], QPointF):
1059
+ for elm in self.content:
1060
+ if elm == __args[0]:
1061
+ # print('elm found by equal', elm, __args[0])
1062
+ return True
1063
+ try:
1064
+ if __args[0] in elm: # I had to override contains in images to avoid issues
1065
+ # print('elm found by other way', __args[0], elm, __args[0] in elm)
1066
+ return True
1067
+ except Exception:
1068
+ pass # if not iterable then anyway cannot contain the stuff
1069
+ else:
1070
+ if super().contains(*__args): # if anything in there contains the point return the containing element else return False
1071
+ for elm in self.content:
1072
+ if elm.contains(*__args):
1073
+ return elm
1074
+ return True
1075
+ return False
1076
+
1077
+ def extend(self, lst):
1078
+ try:
1079
+ self.content.extend(lst)
1080
+ except Exception:
1081
+ # self.content.add(lst) # in case it is not a list then just add is
1082
+ self.add(lst)
1083
+
1084
+ def add(self, item):
1085
+ self.content.append(item)
1086
+
1087
+ # be careful with that because this is what is compared when using == !!! so if poorly implemented this can have dramatic effects
1088
+ def __str__(self):
1089
+ # return f"group {self.ID}"
1090
+ return self.__repr__()
1091
+
1092
+ def __repr__(self): # required to appear in the printed list
1093
+ # return self.__str__()
1094
+ class_name = type(self).__name__
1095
+ memory_address = hex(id(self))
1096
+ return f"{class_name}-{self.ID}-{memory_address}"
1097
+
1098
+
1099
+
1100
+ # probably the AR of the group should take into account the incompressible stuff --> this is why it is unfixed untill
1101
+ # there is always a solution for 0 space --> how can I from the 0 space find all the other solutions???
1102
+ if __name__ == '__main__':
1103
+ img0 = Image2D('/E/Sample_images/counter/00.png')
1104
+ img1 = Image2D('/E/Sample_images/counter/01.png')
1105
+ img2 = Image2D('/E/Sample_images/counter/02.png')
1106
+ img3 = Image2D('/E/Sample_images/counter/03.png')
1107
+ img4 = Image2D('/E/Sample_images/counter/04.png')
1108
+ img5 = Image2D('/E/Sample_images/counter/05.png')
1109
+ img6 = Image2D('/E/Sample_images/counter/06.png')
1110
+ img7 = Image2D('/E/Sample_images/counter/07.png')
1111
+ img8 = Image2D('/E/Sample_images/counter/08.png')
1112
+ img9 = Image2D('/E/Sample_images/counter/09.png')
1113
+ img10 = Image2D('/E/Sample_images/counter/10.png')
1114
+ img10b = Image2D('/E/Sample_images/counter/10.png')
1115
+ if True:
1116
+ grp1 = Group(Group(img0, img1, img2, img3), img4, Group(img5, Group(img6, img7, img8)), img9)
1117
+ print(get_all_contained_images(grp1))
1118
+ # print(get_iterable_elements(grp1))
1119
+ sys.exit(0)
1120
+
1121
+ print('parent_of_obj',get_parent_of_obj(img9, grp1))
1122
+ print('parent_of_obj',get_parent_of_obj(img4, grp1))
1123
+ print('parent_of_obj',get_parent_of_obj(img0, grp1)) # there is a bug
1124
+ print('parent_of_obj',get_parent_of_obj(img1, grp1))
1125
+ print('parent_of_obj',get_parent_of_obj(img5, grp1))
1126
+ print('parent_of_obj',get_parent_of_obj(img6, grp1))
1127
+ print('parent_of_obj',get_parent_of_obj(img7, grp1))
1128
+ print('parent_of_obj',get_parent_of_obj(img8, grp1))
1129
+
1130
+ sys.exit(0)
1131
+
1132
+
1133
+
1134
+ grp1 = Group(Group(img0,img1, img2, img3), img4, Group(img5, Group(img6, img7, img8)), img9)
1135
+ # print('all_iterables',get_iterable_elements(grp1))
1136
+ print(grp1.content)
1137
+ print(grp1.content[0].content)
1138
+ print(grp1.content[2].content)
1139
+ print(grp1.content[2].content[1].content)
1140
+ grp1.move_left([img1, img8, img9]) # really need to pass a list
1141
+ print('out')
1142
+ print(grp1.content)
1143
+ print(grp1.content[0].content)
1144
+ print(grp1.content[-1].content)
1145
+ print(grp1.content[-1].content[1].content)
1146
+ # print(grp1.content[2].content[1].content)
1147
+ # print(grp1.content[2].content)
1148
+ # print('bob')
1149
+ grp1 = Group(img0,img1, img2, img3, img4,img5, img6, img7, img8, img9)
1150
+ # print('all_iterables2', get_iterable_elements(grp1))
1151
+
1152
+
1153
+
1154
+
1155
+
1156
+ # sys.exit(0)
1157
+ print('old')
1158
+ grp1 = Group(img0, img1, img2, img3, img4, img5, img6, img7, img8, img9)
1159
+ print(grp1.content)
1160
+ grp1.move_left(img1) # really need to pass a list
1161
+ print(grp1.content)
1162
+ print('bob')
1163
+
1164
+
1165
+ grp1 = Group(img0, img1, img2, img3, img4, img5, img6, img7, img8, img9)
1166
+ print(grp1.content)
1167
+ grp1.move_left([img0, img1, img6])
1168
+ print(grp1.content)
1169
+
1170
+ grp1 = Group(img0, img1, img2, img3, img4, img5, img6, img7, img8, img9)
1171
+ print(grp1.content)
1172
+ grp1.move_right([img0, img1, img6])
1173
+ print(grp1.content)
1174
+
1175
+ print('with errors and missing')
1176
+ grp1 = Group(img0, img2, img3, img4, img5, img6, img7, img8, img9)
1177
+ print(grp1.content)
1178
+ grp1.move_left([img2, img1, img6])
1179
+ print(grp1.content)
1180
+
1181
+ grp1 = Group(img0, img2, img3, img4, img5, img6, img7, img8, img9)
1182
+ print(grp1.content)
1183
+ grp1.move_right([img0, img1, img6])
1184
+ print(grp1.content)
1185
+ grp1.move_right([img0, img1, img6])
1186
+ print(grp1.content)
1187
+ for i in range(10):
1188
+ grp1.move_right([img0, img1, img6])
1189
+ print(grp1.content)
1190
+ sys.exit(0)
1191
+
1192
+ if True:
1193
+
1194
+
1195
+
1196
+ grp1 = Group(img0, img1)
1197
+ print(isinstance(grp1, list))
1198
+ print(isinstance(grp1, Group)) # very good
1199
+ grp2 = Group(img2, img3)
1200
+ grp1.extend(grp2)
1201
+ print(grp1.content)
1202
+ grp1.extend(img10b)
1203
+ print(grp1.content)
1204
+
1205
+
1206
+
1207
+
1208
+ grp3 = Group(img4, img5)
1209
+ grp4 = Group(img6, img7)
1210
+ grp3.add(grp4)
1211
+ print(grp3.content)
1212
+
1213
+ grp5 = Group(img8, img9, img10)
1214
+ grp5.remove(img10)
1215
+ print(grp5.content)
1216
+
1217
+ grp5.add(Group(img10))
1218
+ print(grp5.content)
1219
+ grp5.remove(img10) # very good --> the parent group is empty
1220
+ print(grp5.content)
1221
+
1222
+
1223
+ # for extend --> maybe autodelete stuff
1224
+
1225
+
1226
+
1227
+
1228
+
1229
+
1230
+
1231
+
1232
+
1233
+
1234
+
1235
+
1236
+
1237
+
1238
+ sys.exit(0)
1239
+
1240
+ app = QApplication(sys.argv) # IMPORTANT KEEP !!!!!!!!!!! # required to use strings
1241
+ html = '''
1242
+ <!DOCTYPE html>
1243
+ <html>
1244
+ <head>
1245
+ <title>Font Face</title>
1246
+ </head>
1247
+ <body>
1248
+ <font color="red">
1249
+ <font face="Symbol" size="5">Symbol</font><br />
1250
+ <font face="Times New Roman" size="5">Times New Roman</font><br />
1251
+ <font face="Verdana" size="5">Verdana</font><br />
1252
+ <font face="Comic Sans MS" size="5">Comic Sans MS</font><br />
1253
+ <font face="WildWest" size="5">WildWest</font><br />
1254
+ <font face="Bedrock" size="5">Bedrock</font><br />
1255
+ </font>
1256
+ </body>
1257
+ </html>
1258
+ '''
1259
+
1260
+ html = '''
1261
+ <!DOCTYPE html>
1262
+ <html>
1263
+ <head>
1264
+ <title>Font Face</title>
1265
+ </head>
1266
+ <body>
1267
+ <font color="red">
1268
+ <font face="Symbol" size="5">Symbol</font><br />
1269
+ <font face="Times New Roman" size="5">Times New Roman</font><br />
1270
+ <font face="Bedrock" size="5">Bedrock</font><br />
1271
+ </font>
1272
+ </body>
1273
+ </html>
1274
+ '''
1275
+
1276
+ # this works super well -−> I can think of finalizing it very soon !!!
1277
+
1278
+ # VERY GOOD -−> TRY TO FIX ALL THE STUFF!!!
1279
+
1280
+ if True:
1281
+ # img1 = Image2D(Img(np.zeros((512, 512)), dimensions='hw'))
1282
+ # img2 = Image2D(Img(np.zeros((512, 512)), dimensions='hw'))
1283
+ # img3 = Image2D(Img(np.zeros((512, 512)), dimensions='hw'))
1284
+ # img4 = Image2D(Img(np.zeros((512, 512)), dimensions='hw'))
1285
+
1286
+ # img1 = Image2D(Img(np.zeros((256, 512)), dimensions='hw'))
1287
+ # img2 = Image2D(Img(np.zeros((512, 512)), dimensions='hw'))
1288
+ # img3 = Image2D(Img(np.zeros((256, 512)), dimensions='hw'))
1289
+ # img4 = Image2D(Img(np.zeros((512, 512)), dimensions='hw'))
1290
+ img1 = Image2D('/E/Sample_images/counter/00.png')
1291
+ img2 = Image2D('/E/Sample_images/counter/01.png')
1292
+ img3 = Image2D('/E/Sample_images/counter/02.png')
1293
+ img4 = Image2D('/E/Sample_images/counter/03.png')
1294
+ img5 = Image2D('/E/Sample_images/counter/05.png')
1295
+ img6 = Image2D('/E/Sample_images/counter/06.png')
1296
+ img7 = Image2D('/E/Sample_images/counter/07.png')
1297
+ img8 = Image2D('/E/Sample_images/counter/08.png')
1298
+ img9 = Image2D('/E/Sample_images/counter/09.png')
1299
+
1300
+ grp1 = Group(img1, img2)
1301
+ grp5 = Group(img5, img6, Group(img7, img8, orientation='Y'))
1302
+ grp2 = Group(img3, img4,grp5, orientation='Y')
1303
+
1304
+
1305
+
1306
+ grp3 = Group(grp1, grp2)
1307
+
1308
+
1309
+ set_to_size(grp3,512)
1310
+
1311
+ point = random.choice([QPointF(64,64),QPointF(430,100)])
1312
+ print(point)
1313
+ elements_under_selection = []
1314
+ elm = grp3
1315
+ while elm:
1316
+ if isinstance(elm, bool):
1317
+ break
1318
+ elements_under_selection.append(elm)
1319
+ elm = elm.contains(point)
1320
+ print(elements_under_selection)
1321
+
1322
+
1323
+ # this is the code to get all the elements below the stuff
1324
+
1325
+ # for elm in elements_under_selection:
1326
+ # print(type(elm))
1327
+ #
1328
+ # elm = grp3.contains(QPointF(64,64))
1329
+ # if elm:
1330
+ # print('contained',elm)
1331
+ # print(elm == img1)
1332
+ # print(elm == img2)
1333
+ # print(elm==grp1)
1334
+ # print(elm == grp2)
1335
+ # print(elm == grp3)
1336
+ # else:
1337
+ # print('not contained')
1338
+ #
1339
+ # elm = grp3.contains(QPointF(430,32))
1340
+ # if elm:
1341
+ # print('contained', elm)
1342
+ # print(elm == img1)
1343
+ # print(elm == img2)
1344
+ # print(elm == grp1)
1345
+ # print(elm == grp2)
1346
+ # print(elm == grp3)
1347
+ # else:
1348
+ # print('not contained')
1349
+
1350
+ # print(group._count)
1351
+ # print(grp3, grp2)
1352
+
1353
+ print('grp3.contains(img1)', grp3.contains(img1))
1354
+ print('grp3.contains(img1)', grp3.contains(img4))
1355
+ print('grp3.contains(img1)', grp3.contains(img8))
1356
+ print('#'*20)
1357
+ print('grp3.contains(img1)', grp1.contains(img1))
1358
+ print('grp3.contains(img1)', grp1.contains(img4))
1359
+ print('grp3.contains(img1)', grp2.contains(img1))
1360
+ print('grp3.contains(img1)', grp2.contains(img4))
1361
+ print('grp3.contains(img1)', grp2.contains(img8))
1362
+
1363
+ preview(grp3)
1364
+
1365
+ sys.exit(0)
1366
+
1367
+ if False:
1368
+ '''
1369
+ solution of a grp that has two images in a row and 2 images in a col
1370
+ PyQt6.QtCore.QRectF(0.0, 0.0, 511.99999999999994, 202.99999999999997)
1371
+
1372
+ ###sub###
1373
+ PyQt6.QtCore.QRectF(0.0, 0.0, 408.99999999999994, 202.99999999999997)
1374
+ -−> PyQt6.QtCore.QRectF(0.0, 0.0, 202.99999999999997, 202.99999999999997)
1375
+ -−> PyQt6.QtCore.QRectF(205.99999999999997, 0.0, 202.99999999999997, 202.99999999999997)
1376
+ PyQt6.QtCore.QRectF(411.99999999999994, 0.0, 100.0, 202.99999999999997)
1377
+ -−> PyQt6.QtCore.QRectF(411.99999999999994, 0.0, 99.99999999999999, 99.99999999999999)
1378
+ -−> PyQt6.QtCore.QRectF(411.99999999999994, 102.99999999999999, 99.99999999999999, 99.99999999999999)
1379
+
1380
+ so each small rect in col should be 100*100 and each big rect should be 203*203
1381
+
1382
+ -> now figure the math for that
1383
+
1384
+ if I put all same size (no space) can I compute the rect and then adjust space so that it still fits ??? --> not so easy
1385
+
1386
+ total_uncompressible_width = 512-(3-1)*3 = 506
1387
+ target_height = 512/512+512/512+256/512 = 2.5 --> 202.4 --> I almost have it real AR for last is in fact 256/(512-3)
1388
+
1389
+ the real AR factor should be 2.492611 to get 202.9999999 of height for both col group and for --> how can I get there ????
1390
+ # there isn't probably any solution and I need iter but with a good initial guess
1391
+ # --> take AR by default and adjust
1392
+
1393
+ solution for two groups of 256x512 and 512x512 -−> 138.54
1394
+ PyQt6.QtCore.QRectF(0.0, 0.0, 512.0, 138.54545454545453)
1395
+ ###sub###
1396
+ PyQt6.QtCore.QRectF(0.0, 0.0, 418.6363636363636, 138.54545454545453)
1397
+ -−> PyQt6.QtCore.QRectF(0.0, 0.0, 277.09090909090907, 138.54545454545453)
1398
+ -−> PyQt6.QtCore.QRectF(280.09090909090907, 0.0, 138.54545454545453, 138.54545454545453)
1399
+ PyQt6.QtCore.QRectF(421.6363636363636, 0.0, 90.36363636363637, 138.54545454545453)
1400
+ -−> PyQt6.QtCore.QRectF(421.6363636363636, 0.0, 90.36363636363636, 45.18181818181818)
1401
+ -−> PyQt6.QtCore.QRectF(421.6363636363636, 48.18181818181818, 90.36363636363636, 90.36363636363636)
1402
+
1403
+ total_uncompressible_width = 512-(3-1)*3 = 506
1404
+ target_height = 512/256+512/512+ 90.363636/138.54545454545454 --> 3.652231 -−> perfect --> that is this one --> I need do it like that --> not that hard in fact
1405
+
1406
+ # je dois trouver 3.6 --> ça ne marche pas du tout -> réflechir en fait
1407
+
1408
+ # how can I handle the incompressible texts--> see that later
1409
+
1410
+ '''
1411
+ img1 = Image2D(Img(np.zeros((512,512)), dimensions='hw'))
1412
+ img2 = Image2D(Img(np.zeros((512,512)), dimensions='hw'))
1413
+ img3 = Image2D(Img(np.zeros((512,512)), dimensions='hw'))
1414
+ img4 = Image2D(Img(np.zeros((512,512)), dimensions='hw'))
1415
+
1416
+ img1 = Image2D(Img(np.zeros((256, 512)), dimensions='hw'))
1417
+ img2 = Image2D(Img(np.zeros((512, 512)), dimensions='hw'))
1418
+ img3 = Image2D(Img(np.zeros((256, 512)), dimensions='hw'))
1419
+ img4 = Image2D(Img(np.zeros((512, 512)), dimensions='hw'))
1420
+
1421
+ grp1 = group(img1,img2)
1422
+ grp2 = group(img3, img4, orientation='Y')
1423
+
1424
+ grp3 = group(grp1, grp2)
1425
+ # grp3.set_to_size(512)
1426
+ set_to_size(grp3,512)
1427
+
1428
+ print(grp3.getRect())
1429
+
1430
+ print(grp3.get_incompressible_size(axis=grp3.orientation)) # --> 6 --> ok
1431
+
1432
+ get_imprecision(grp3,512) # --> imprecision of 3 pixels
1433
+
1434
+
1435
+ print('###sub###')
1436
+
1437
+ for elm in grp3:
1438
+ print(elm.getRect(scale=True))
1439
+ for el in elm:
1440
+ print('-−> ', el.getRect(scale=True))
1441
+
1442
+ preview(grp3)
1443
+
1444
+
1445
+ sys.exit(0)
1446
+
1447
+ if True:
1448
+ # that works
1449
+
1450
+ img1 = Image2D(Img(np.zeros((512,512)), dimensions='hw'))
1451
+ img2 = Image2D(Img(np.zeros((512,512)), dimensions='hw'))
1452
+ img3 = Image2D(Img(np.zeros((512,512)), dimensions='hw'))
1453
+ img4 = Image2D(Img(np.zeros((512,512)), dimensions='hw'))
1454
+
1455
+ img1 = Image2D(Img(np.zeros((256, 512)), dimensions='hw'))
1456
+ img2 = Image2D(Img(np.zeros((512, 512)), dimensions='hw'))
1457
+ img3 = Image2D(Img(np.zeros((256, 512)), dimensions='hw'))
1458
+ img4 = Image2D(Img(np.zeros((512, 512)), dimensions='hw'))
1459
+
1460
+ t = np.arange(0.0, 2.0, 0.01)
1461
+ s = 1 + np.sin(2 * np.pi * t)
1462
+ fig, ax = plt.subplots()
1463
+ ax.plot(t, s)
1464
+
1465
+ ax.set(xlabel='time (s)', ylabel='voltage (mV)', title=None)
1466
+ ax.grid()
1467
+
1468
+ # plt.show()
1469
+ # fig.savefig("test.png")
1470
+ # plt.show()
1471
+ # ça marche --> voici deux examples de shapes
1472
+ fig = VectorGraphics2D(fig, x=0, y=0, width=20., height=10.) # could also be used to create empty image with
1473
+
1474
+ svg = VectorGraphics2D('/E/Sample_images/EZF_SF_scientifig_EZFig/sample_images_svg/cartman.svg', x=12, y=0,
1475
+ width=1, height=1, fill_color=0xFFFFFF) # pb is I can change size if I don't put
1476
+
1477
+ # grp1 = group(img1,img2)
1478
+ # grp2 = group(img3, img4, orientation='Y')
1479
+ #
1480
+ # grp3 = group(grp1, grp2)
1481
+
1482
+
1483
+ # grp3 = group(img3, fig, space=42, orientation=random.choice(['X','Y']), fill_color=0xFF00FF)
1484
+ # grp3 = group(img3,fig, img1,svg, space=3, orientation=random.choice(['X','Y']), fill_color=0xFF00FF)
1485
+ grp3 = group(img3,fig, img1, space=3, orientation=random.choice(['X','Y']), fill_color=0xFF00FF)
1486
+
1487
+ # grp3.set_to_size(512)
1488
+ set_to_size(grp3,1024)
1489
+
1490
+ print(grp3.getRect())
1491
+
1492
+ print(grp3.get_incompressible_size(axis=grp3.orientation)) # --> 6 --> ok
1493
+
1494
+ get_imprecision(grp3,512) # --> imprecision of 3 pixels
1495
+
1496
+ print('###sub###')
1497
+
1498
+ for elm in grp3:
1499
+ print(elm.getRect(all=True))
1500
+ try:
1501
+ for el in elm:
1502
+ print('-−> ', el.getRect(all=True))
1503
+ except Exception:
1504
+ pass
1505
+
1506
+
1507
+
1508
+ preview(grp3)
1509
+
1510
+ sys.exit(0)
1511
+
1512
+ if False:
1513
+
1514
+ # this now works I shouldn't take the text as immutable -−> I may need to resize the image every time text is added -−> need to think how I can do that
1515
+ # this is now broken because of immutable -> see how I can fix that
1516
+
1517
+ img1 = Image2D('/E/Sample_images/counter/01.png')
1518
+ img2 = Image2D('/E/Sample_images/counter/01.png')
1519
+ img3 = Image2D('/E/Sample_images/counter/02.png')
1520
+ text = TAText2D(html)
1521
+ # text.set_rotation(-45)
1522
+ text.color = 0xFF0000
1523
+
1524
+ # bug in set_to_scale of the stuff
1525
+
1526
+ grp = group(text,img3,img1, fill_color=0xFFFFFF, orientation=random.choice(['X','Y'])) # --> correct
1527
+ # grp = group(text, img3, img1, fill_color=0xFFFFFF, orientation='Y') # --> this is incorrect in height --> see how I can handle that
1528
+
1529
+ # I need a smarter code --> maybe only compute size of mutable and scale them to size
1530
+
1531
+ # for some reason I cannot always see the text --> what is this bug ???
1532
+ # debug it with just that
1533
+
1534
+ # I need get the exact size of the text --> because now it seems much bigger than it really is
1535
+
1536
+ # I also need to be able to change the pen color of text --> I need split text by colors
1537
+
1538
+ # shall I draw only the stuff within the rect ??? --> maybe
1539
+
1540
+ # set_to_size(grp,512)
1541
+ grp.set_to_size(512) # --> good this is ok in X but then why not in Y???
1542
+
1543
+ print(grp.getRect()) # not at all right size --> 307 --> not ok
1544
+
1545
+ get_imprecision(grp)
1546
+
1547
+ preview(grp)
1548
+
1549
+ sys.exit(0)
1550
+
1551
+
1552
+ if False:
1553
+
1554
+ # seems ok but see how to handle text ??? --> very hard because unscalable -−> or assume scalable and truncate the text
1555
+ # best is to treat it as an image with a fixed width and height
1556
+
1557
+ # I need to treat the text independently of the rest of the figure and to crop it to the height of the others when done
1558
+
1559
+
1560
+ #sufficient to reproduce the size bug
1561
+
1562
+ # mathematically it is unsolvable if uncompressible size is > to desired final size -−> I need a warning for the user !!! -−> TODO
1563
+
1564
+
1565
+ SPACE=10
1566
+ # SPACE=0
1567
+
1568
+ img1 = Image2D('/E/Sample_images/counter/00.png')
1569
+ img2 = Image2D('/E/Sample_images/counter/01.png')
1570
+ img3 = Image2D('/E/Sample_images/counter/02.png')
1571
+ img4 = Image2D('/E/Sample_images/counter/03.png')
1572
+ img5 = Image2D('/E/Sample_images/counter/05.png')
1573
+ img6 = Image2D('/E/Sample_images/counter/06.png')
1574
+ img7 = Image2D('/E/Sample_images/counter/07.png')
1575
+ img8 = Image2D('/E/Sample_images/counter/08.png')
1576
+ img9 = Image2D('/E/Sample_images/counter/09.png')
1577
+
1578
+ # I NEED MAKE SURE THAT THE SPACE IS NOT CRAEY OTHERWISE THAT CANNOT work
1579
+ grp1= group(img1,img2, img3, img4, img5, space=6)
1580
+ grp2= group(img6,img7, img8, img9, orientation='Y', space=3)
1581
+ grp3 = group(grp1, grp2, space=9, orientation=random.choice(['X','Y'])) # there is a big bug there
1582
+
1583
+ # also sufficient to reproduce the bug...
1584
+ # grp1 = group(img1,img2, space=200) # this is the space missing --> why this bug??? " --> YES THIS IS ALWAYS THE SPACE MISSING --> WHY IS THAT!!!
1585
+ # grp2 = group(img6, orientation='Y', space=SPACE*20)
1586
+ # grp3 = group(grp1, grp2, space=SPACE*2)
1587
+
1588
+ # indeed smthg needs be done recursively to match the size properly --> am I not getting the right size in some cases ????
1589
+ start = timer()
1590
+
1591
+ # doing it 100 time fixes the size difference but there is still smthg missing --> so there is some convergence issue somewhere
1592
+ # for i in range(26): # 5 to 10 times seems to be enough --> I can maybe live with that!!! # time is negligibly longer --> so I can afford to have that (it become a little bit longer if I go around 1000 there the error is null
1593
+ # grp3.set_to_size(512) # NB THE SET TO SIZE FIXED the BUG --> WHY DO I NEED TO DO IT TWICE
1594
+ set_to_size(grp3, 512)
1595
+
1596
+ print('total time', timer()-start)
1597
+
1598
+ # grp3.set_to_size(1024) # NB THE SET TO SIZE FIXED the BUG --> WHY DO I NEED TO DO IT TWICE
1599
+ # grp3.set_to_size(1024) # NB THE SET TO SIZE FIXED the BUG --> WHY DO I NEED TO DO IT TWICE
1600
+
1601
+ print(grp3.orientation)
1602
+ print(grp3.space)
1603
+ print(grp3.get_incompressible_size())
1604
+ print(grp3.get_incompressible_size(axis=grp3.orientation))
1605
+
1606
+ print(grp3.getRect()) # not ok --> probably need do this properly #(0.0, 0.0, 502.0, 218.7883919228763) vs (0.0, 0.0, 501.9999999999999, 216.95561080400537) when repeated once --> why not deterministic ???
1607
+
1608
+ # why not all same height --> they should have the same
1609
+
1610
+ # print(len(grp3))
1611
+
1612
+ # if I take the unpacked size and append to it the incompressible size I should get back to the right shape
1613
+
1614
+ print('####sub####')
1615
+ for elm in grp3:
1616
+ print(elm.getRect())
1617
+
1618
+ print(grp1.space)
1619
+ print(grp1.orientation)
1620
+
1621
+
1622
+ # imprecisionX = grp3[0].getRect().height()-grp3[1].getRect().height()
1623
+ # imprecisionY = grp3[0].getRect().width()-grp3[1].getRect().width()
1624
+ # imprecision = min(abs(imprecisionX), abs(imprecisionY))
1625
+ # print('imprecision in pixels X', imprecisionX)
1626
+ # print('imprecision in pixels Y', imprecisionY)
1627
+ # print('imprecision', imprecision)
1628
+ get_imprecision(grp3)
1629
+
1630
+ #-4.663263553084107e-09 for 16 repeats
1631
+ # imprecision in pixels 0.0 for 100 or 50 or 30 or 26 repeats
1632
+ # imprecision in pixels 8.526512829121202e-14 for 25 repeats
1633
+ # -1.4921397450962104e-11 for 20 repeats
1634
+ # --> 26 is the first where I really get 0 --> ok
1635
+
1636
+
1637
+ preview(grp3)
1638
+ sys.exit(0)
1639
+
1640
+ # Example usage:
1641
+ # custom_list = col(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
1642
+ # print(custom_list[0])
1643
+
1644
+ orientation='X' # all same height --> ok
1645
+ # orientation='Y' # pb --> not all same width but all same height --> NOT GOOD --> packing is ok and align also --> it is the scaling that sucks
1646
+ # almost ok but height is incorrect -−> not the target height --> need a fix
1647
+ # VERY GOOD --> NOW BOTH ARE FIXED -> TRY TO FINISH THAT VERY SOON -−> SHOULD BE DOABLE
1648
+
1649
+ # ça marche ce truc behaves as a rect and as a list --> very useful in a way for iter
1650
+ if False:
1651
+ grp = group(Rectangle2D(QRectF(0, 0, 100, 50)),
1652
+ Rectangle2D(QRectF(20, 20, 200, 100)),
1653
+ Rectangle2D(QRectF(30, 30, 300, 150)),
1654
+ Rectangle2D(QRectF(60, 30, 200, 120)),
1655
+ Rectangle2D(QRectF(30, 30, 300, 100)), orientation=orientation)
1656
+ else:
1657
+
1658
+ # --> all is ok and the size is an iteration issue -−> maybe then I should not worry about that
1659
+
1660
+ INCOMPRESSIBLE_SPACE_TEST =10 # --> setting it to 0 fixes the misalignment --> that means that this is the incompressible part of the stuff that matters -> I need make functions that return the amount of incompressible space so that the AR CAN BE PROPERLY COMPUTED
1661
+
1662
+ # grp = group(Image2D('/E/Sample_images/counter/00.png'), Image2D('/E/Sample_images/counter/01.png'),Image2D('/E/Sample_images/counter/02.png'),Image2D('/E/Sample_images/counter/03.png'))
1663
+ img1 = Image2D('/E/Sample_images/counter/00.png')
1664
+ img2 = Image2D('/E/Sample_images/counter/01.png')
1665
+ img3 = Image2D('/E/Sample_images/counter/02.png')
1666
+ img4 = Image2D('/E/Sample_images/counter/03.png')
1667
+
1668
+
1669
+ # it seems I had never reimplemented the rotation --> is that possible !!!
1670
+ img1.set_rotation(45)
1671
+ img3.set_rotation(-33)
1672
+ # img1 = Rectangle2D(QRectF(20, 20, 200, 100))
1673
+ # img1.set_rotation(45) # --> rotation roughly works for rectangle even though the bounds are not correct and the crop is not correct either!!!
1674
+ # but rotation
1675
+
1676
+ # if rotated by 90degrees I would need to change the dimensions of the image and see also how to handle the other rotations ??? --> NEED THINK -−> IF NOT SAME SIZE -−> NEED BE SMART !!! --> TODO
1677
+
1678
+ img4.set_rotation(90) # TODO --> needs a fix --> need swap the dimensions of the image -−> see how to code because maybe not that easy TODO
1679
+
1680
+ # I need to think but not a bad idea!!!
1681
+
1682
+
1683
+
1684
+
1685
+ grp = group(img1, img2, img3, img4, space=INCOMPRESSIBLE_SPACE_TEST/3)
1686
+
1687
+ print('rect',grp.getRect())
1688
+
1689
+ grp.setTopLeft(512, 256) # this seems to work
1690
+
1691
+ print('rect2', grp.getRect())
1692
+
1693
+
1694
+ img5 = Image2D('/E/Sample_images/counter/05.png')
1695
+ img6 = Image2D('/E/Sample_images/counter/06.png')
1696
+ img7 = Image2D('/E/Sample_images/counter/07.png')
1697
+ img7.set_rotation(180)
1698
+
1699
+
1700
+ grp2 = group(img5, img6, img7, space=INCOMPRESSIBLE_SPACE_TEST*3)
1701
+
1702
+ grp2.setTopLeft(512, 516)
1703
+
1704
+ print('rect3',grp2.getRect())
1705
+
1706
+ img8 = Image2D('/E/Sample_images/counter/08.png')
1707
+ img9 = Image2D('/E/Sample_images/counter/09.png')
1708
+
1709
+ # grp4 = group(img8,img9,orientation='Y')
1710
+ grp4 = group(img8,img9,grp,orientation='Y', space=INCOMPRESSIBLE_SPACE_TEST)
1711
+
1712
+
1713
+
1714
+ # ça marche aussi mais pr je ne sais quelle raison il y a un petit decalage ???
1715
+ html_neo = '''
1716
+ <!DOCTYPE html>
1717
+ <html>
1718
+ <head>
1719
+ <title>Font Face</title>
1720
+ <style>
1721
+ .red {
1722
+ color: red;
1723
+ }
1724
+ .symbol {
1725
+ font-family: Symbol;
1726
+ font-size: x-large;
1727
+ }
1728
+ .times-new-roman {
1729
+ font-family: "Times New Roman";
1730
+ font-size: x-large;
1731
+ }
1732
+ .verdana {
1733
+ font-family: Verdana;
1734
+ font-size: x-large;
1735
+ }
1736
+ .comic-sans-ms {
1737
+ font-family: "Comic Sans MS";
1738
+ font-size: x-large;
1739
+ }
1740
+ .wildwest {
1741
+ font-family: WildWest;
1742
+ font-size: x-large;
1743
+ }
1744
+ .bedrock {
1745
+ font-family: Bedrock;
1746
+ font-size: x-large;
1747
+ }
1748
+ </style>
1749
+ </head>
1750
+ <body>
1751
+ <div class="red">
1752
+ <div class="symbol">Symbol</div>
1753
+ <div class="times-new-roman">Times New Roman</div>
1754
+ <div class="verdana">Verdana</div>
1755
+ <div class="comic-sans-ms">Comic Sans MS</div>
1756
+ <div class="wildwest">WildWest</div>
1757
+ <div class="bedrock">Bedrock</div>
1758
+ </div>
1759
+ </body>
1760
+ </html>
1761
+ '''
1762
+
1763
+ # html=html_neo # i prefer the old way
1764
+
1765
+ # use the deprecated text size because
1766
+ # html = "<font color=blue size=24>this is a test<sup>2</sup><br></font><font color=green size=12>continued<sub>1</sub><br></font><font color=white size=12>test greek <font face='Symbol' size=32>a</font> another &alpha;<font face='Arial' color='Orange'>I am a sentence!</font>"
1767
+ text = TAText2D(html)
1768
+ text.set_rotation(-45)
1769
+ text.color = 0xFF0000 # color does not work !!! --> again needs be fixed !!!
1770
+ # text.set_rotation(90)
1771
+
1772
+ t = np.arange(0.0, 2.0, 0.01)
1773
+ s = 1 + np.sin(2 * np.pi * t)
1774
+ fig, ax = plt.subplots()
1775
+ ax.plot(t, s)
1776
+
1777
+ ax.set(xlabel='time (s)', ylabel='voltage (mV)', title=None)
1778
+ ax.grid()
1779
+
1780
+ # plt.show()
1781
+ # fig.savefig("test.png")
1782
+ # plt.show()
1783
+ # ça marche --> voici deux examples de shapes
1784
+ fig = VectorGraphics2D(fig, x=0, y=0, width=512, height=512) # could also be used to create empty image with
1785
+
1786
+ # there is a bug !!!!
1787
+ # grp5 = group(grp2, grp4,grp, orientation='Y') # ok but pb is they have no same height -−> see how I can fix that --> see how I can set height --> DO I ALWAYS NEED A TARGET HEIGHT OR CAN I TAKE THE SMALLEST OF THE TWO ???
1788
+ # grp5 = group(grp2,grp4, orientation=random.choice(['X','Y']), space=INCOMPRESSIBLE_SPACE_TEST, fill_color=0x00FFFF) # ok but pb is they have no same height -−> see how I can fix that --> see how I can set height --> DO I ALWAYS NEED A TARGET HEIGHT OR CAN I TAKE THE SMALLEST OF THE TWO ???
1789
+ # grp5 = group(grp2,group(text), grp4, orientation=random.choice(['X','Y']), space=INCOMPRESSIBLE_SPACE_TEST, fill_color=0x00FFFF) # ok but pb is they have no same height -−> see how I can fix that --> see how I can set height --> DO I ALWAYS NEED A TARGET HEIGHT OR CAN I TAKE THE SMALLEST OF THE TWO ???
1790
+ grp5 = group(grp2,fig, grp4, orientation=random.choice(['X','Y']), space=INCOMPRESSIBLE_SPACE_TEST, fill_color=0x00FFFF) # ok but pb is they have no same height -−> see how I can fix that --> see how I can set height --> DO I ALWAYS NEED A TARGET HEIGHT OR CAN I TAKE THE SMALLEST OF THE TWO ???
1791
+
1792
+ # I at least managed to display a text but will I manage to get its size and alike
1793
+ # text should always be incompressible in fact
1794
+
1795
+ # very good --> it works even with very very complex shapes !!!!
1796
+
1797
+ # see how I can convert this into an xml portable code
1798
+ # todo
1799
+
1800
+ # tt marche sublimement
1801
+
1802
+ # TODO --> CHECK HOW TO DO THAT AND WHY IT DOESN'T WORK !!!
1803
+
1804
+ # TODO add bg if desired for the images
1805
+
1806
+ # grp = grp5
1807
+
1808
+ # grp.set_to_size(1024) # do I have a bug here ???
1809
+ grp5.setTopLeft(0,0)
1810
+
1811
+ # shall I add <?xml version="1.0" ?>
1812
+
1813
+ if False:
1814
+ try:
1815
+ xml = object_to_xml(grp5)
1816
+ print(xml) # pickle serialization of the object would allow me to implement some sort of a save...
1817
+
1818
+ tst = xml_string_to_object(xml)
1819
+
1820
+ print(tst)
1821
+ except Exception:
1822
+ traceback.print_exc()
1823
+
1824
+ # small error is likely due to packing --> TODO --> FIX IT
1825
+
1826
+ # here 26 is not enough --> I need a bit more
1827
+ # for i in range(26): # even at 100 I get a small imprecision -−> maybe forget about that shit and take 26 always # dirty but ok for now -> I have bigger things to fix --> I tried 100 and it is perfect but 10 already seems to be enough --> maybe that is ok then --> even 3 is ok
1828
+ # grp5.set_to_size(1024) # there is a super tiny misalignment --> is that real or a rounding artifact that would vanish with svg images
1829
+ set_to_size(grp5, 1024) # size is incorrect
1830
+
1831
+ print('text size', text.width(), text.height(), text.get_incompressible_size())
1832
+
1833
+ # --> the size is not respected --> there is a bug !!!
1834
+
1835
+ # imprecisionX = grp5[0].getRect().height() - grp5[1].getRect().height()
1836
+ # imprecisionY = grp5[0].getRect().width() - grp5[1].getRect().width()
1837
+ # imprecision = min(abs(imprecisionX), abs(imprecisionY))
1838
+ # print('imprecision in pixels X', imprecisionX)
1839
+ # print('imprecision in pixels Y', imprecisionY)
1840
+ # print('imprecision ', imprecision)
1841
+ get_imprecision(grp5)
1842
+
1843
+
1844
+ print(grp5.orientation)
1845
+ print(grp5.get_incompressible_size())
1846
+ print(grp5.get_incompressible_size(axis=grp5.orientation))
1847
+
1848
+ print(grp5.getRect()) # --> marche pas car 668.9099254763324 (0.0, 0.0, 237.55105999039787, 668.9099254763324) --> small bug somewhere --> size is ok if no space --> the pb comes from the space --> need a fix that is very smart
1849
+
1850
+ print('####INSIDE####')
1851
+ for elm in grp5:
1852
+ print(elm.getRect())
1853
+
1854
+ preview(grp5) # pb this causes the soft to exit --> I need make it modal or alike --> think of that
1855
+
1856
+
1857
+
1858
+
1859
+ # I may need to dissect the components
1860
+ # if this is an incompressible shit then setting all spaces to 0 would do the job !!
1861
+
1862
+
1863
+
1864
+ # very good it handles spaces too --> very good
1865
+
1866
+
1867
+
1868
+
1869
+
1870
+
1871
+
1872
+ # see how I can handle figures ???? --> I would need a smart stuff
1873
+ # see how I can generate the file !!! --> TODO
1874
+
1875
+
1876
+
1877
+
1878
+
1879
+
1880
+ if False:
1881
+ # do all of it
1882
+
1883
+
1884
+
1885
+ # # grp = group(img1)
1886
+ #
1887
+ grp3 = group(grp, grp2, orientation='Y')
1888
+
1889
+ # what happens if I add a group to another group -−> can I resize it and how will it determine the optimal size
1890
+
1891
+
1892
+ # can a row contain a group --> see
1893
+ # maybe I need the y ratio or the x and the incompressible height or width --> TODO
1894
+
1895
+
1896
+
1897
+
1898
+
1899
+
1900
+
1901
+ # grp3 = group(grp, grp2, orientation='X')
1902
+
1903
+
1904
+ # I am not very far apparently in Y --> am I doing a mistake ???? --> is that due to errors in incompressible stuff
1905
+
1906
+ # it roughly seems to work
1907
+
1908
+ # but is all of this working ???
1909
+
1910
+
1911
+
1912
+
1913
+ print('rect4', grp.getRect(), 'rect5',grp2.getRect())
1914
+
1915
+ grp = grp3
1916
+
1917
+
1918
+
1919
+
1920
+ # grp2.setTopLeft(512,256)
1921
+ # grp2=grp
1922
+
1923
+ # grp = grp2
1924
+
1925
+ # each group is ok but they cannot be combined
1926
+ # --> see what causes the bug
1927
+
1928
+ # shall I force a size on group creation -> force same height to be max height or min height upon creation --> min height is smartest
1929
+
1930
+
1931
+ # print(grp[0]) # first item
1932
+ # print(grp[-1]) # last item
1933
+ # print(grp.orientation)
1934
+ # print(len(grp))
1935
+ #
1936
+ # print(grp.width())
1937
+ # print(grp.getRect())
1938
+
1939
+ if False:
1940
+
1941
+ start = timer()
1942
+
1943
+ grp.set_to_size(512)
1944
+ grp.checkAR()
1945
+
1946
+ end = timer() - start
1947
+ print('bounds =', grp.getRect())
1948
+ print("elapsed time",end)
1949
+
1950
+ # this is super fast and works super well --> I have my killer app...
1951
+
1952
+ grp.orientation='Y'
1953
+ start = timer()
1954
+
1955
+ # grp.set_to_size(512)
1956
+ # grp.checkAR()
1957
+
1958
+ end = timer() - start
1959
+ print('bounds =', grp.getRect())
1960
+ print("elapsed time2", end)
1961
+ # grp.check()
1962
+
1963
+ # print(bounding_rect)
1964
+
1965
+ # very good --> I can access all the stuff in one go -> very cool
1966
+
1967
+ # very good --> all seems good
1968
+
1969
+ # group can be groups of images or of stuff
1970
+
1971
+ # see all I can do...
1972
+ # all is ok now !!!
1973
+ # all is good !!!
1974
+
1975
+ # very good --> now try to do the same with images and plot them --> should be easy !!!
1976
+ # then add functions to the group
1977
+
1978
+ # maybe offer panels as collections of rows or cols --> probably the simplest and allow empty images or spacers for incomplete panels
1979
+
1980
+ # try to draw
1981
+
1982
+ # TODO --> handle translations ... and alike
1983
+ # try to plot groups of groups
1984
+
1985
+ preview(grp)