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.
- batoolset/GUI/__init__.py +0 -0
- batoolset/GUI/list_gui.py +513 -0
- batoolset/GUI/multi_image_display.py +989 -0
- batoolset/GUI/open.py +325 -0
- batoolset/GUI/overlay_hints.py +45 -0
- batoolset/GUI/paint2.py +1345 -0
- batoolset/GUI/scrollablepaint.py +1152 -0
- batoolset/IJ_ROIs/__init__.py +0 -0
- batoolset/IJ_ROIs/tools.py +40 -0
- batoolset/PATH/__init__.py +0 -0
- batoolset/PATH/tools.py +79 -0
- batoolset/SQLite_tools/__init__.py +0 -0
- batoolset/SQLite_tools/tools.py +840 -0
- batoolset/__init__.py +0 -0
- batoolset/binary/__init__.py +0 -0
- batoolset/binary/tools.py +123 -0
- batoolset/binarytools/__init__.py +0 -0
- batoolset/binarytools/cell_center_detector.py +290 -0
- batoolset/clipboard_tools/__init__.py +0 -0
- batoolset/clipboard_tools/tools.py +7 -0
- batoolset/colors/__init__.py +0 -0
- batoolset/colors/colorgen.py +193 -0
- batoolset/colors/tools.py +22 -0
- batoolset/command_line/__init__.py +0 -0
- batoolset/command_line/tools.py +18 -0
- batoolset/dialogs/__init__.py +0 -0
- batoolset/dialogs/opensave.py +228 -0
- batoolset/dicts/__init__.py +0 -0
- batoolset/dicts/tools.py +299 -0
- batoolset/dims/__init__.py +0 -0
- batoolset/dims/tools.py +83 -0
- batoolset/draw/__init__.py +0 -0
- batoolset/draw/shapes/Position.py +183 -0
- batoolset/draw/shapes/__init__.py +0 -0
- batoolset/draw/shapes/automated_alignment_drawing_refactored.py +346 -0
- batoolset/draw/shapes/circle2d.py +99 -0
- batoolset/draw/shapes/compute_optimal_size_all_merged.py +317 -0
- batoolset/draw/shapes/ellipse2d.py +336 -0
- batoolset/draw/shapes/freehand2d.py +235 -0
- batoolset/draw/shapes/group.py +1830 -0
- batoolset/draw/shapes/image2d.py +2469 -0
- batoolset/draw/shapes/line2d.py +575 -0
- batoolset/draw/shapes/point2d.py +257 -0
- batoolset/draw/shapes/polygon2d.py +357 -0
- batoolset/draw/shapes/polyline2d.py +235 -0
- batoolset/draw/shapes/rect2d.py +388 -0
- batoolset/draw/shapes/rectangle2d.py +601 -0
- batoolset/draw/shapes/scalebar.py +304 -0
- batoolset/draw/shapes/serializablefont.py +126 -0
- batoolset/draw/shapes/serializableguiparams.py +10 -0
- batoolset/draw/shapes/square2d.py +103 -0
- batoolset/draw/shapes/textorientation.py +45 -0
- batoolset/draw/shapes/tools.py +467 -0
- batoolset/draw/shapes/transform2D.py +350 -0
- batoolset/draw/shapes/txt2d.py +550 -0
- batoolset/draw/widgets/__init__.py +0 -0
- batoolset/draw/widgets/col.py +343 -0
- batoolset/draw/widgets/image_group.py +248 -0
- batoolset/draw/widgets/image_organizer.py +96 -0
- batoolset/draw/widgets/paint.py +184 -0
- batoolset/draw/widgets/vectorial.py +257 -0
- batoolset/figure/__init__.py +0 -0
- batoolset/figure/alignment.py +814 -0
- batoolset/figure/column.py +1117 -0
- batoolset/figure/fig_tools.py +285 -0
- batoolset/figure/minimal_debugger_scale_etc.py +139 -0
- batoolset/figure/panel.py +697 -0
- batoolset/figure/row.py +852 -0
- batoolset/figure/user_selection.py +287 -0
- batoolset/files/__init__.py +0 -0
- batoolset/files/tools.py +1321 -0
- batoolset/images/__init__.py +0 -0
- batoolset/images/tools.py +81 -0
- batoolset/img.py +5216 -0
- batoolset/linux/__init__.py +0 -0
- batoolset/linux/tools.py +72 -0
- batoolset/lists/__init__.py +0 -0
- batoolset/lists/tools.py +436 -0
- batoolset/luts/__init__.py +0 -0
- batoolset/luts/lut_minimal_test.py +1207 -0
- batoolset/matplolib/__init__.py +0 -0
- batoolset/matplolib/tools.py +44 -0
- batoolset/np_tools/__init__.py +0 -0
- batoolset/np_tools/tools.py +252 -0
- batoolset/np_tools/trash.py +15 -0
- batoolset/pandas_tools/__init__.py +0 -0
- batoolset/pandas_tools/tools.py +880 -0
- batoolset/partials/__init__.py +0 -0
- batoolset/partials/tools.py +83 -0
- batoolset/plot_tools/__init__.py +0 -0
- batoolset/plot_tools/tools.py +70 -0
- batoolset/pyqt/__init__.py +0 -0
- batoolset/pyqt/tools.py +691 -0
- batoolset/re_tools/__init__.py +0 -0
- batoolset/re_tools/tools.py +35 -0
- batoolset/serialization/__init__.py +0 -0
- batoolset/serialization/tools.py +1192 -0
- batoolset/sets/__init__.py +0 -0
- batoolset/sets/tools.py +109 -0
- batoolset/settings/__init__.py +0 -0
- batoolset/settings/global_settings.py +134 -0
- batoolset/strings/__init__.py +0 -0
- batoolset/strings/tools.py +225 -0
- batoolset/ta/__init__.py +0 -0
- batoolset/ta/database/__init__.py +0 -0
- batoolset/ta/database/advanced_sql_plotter.py +1170 -0
- batoolset/ta/database/my_qt_table.py +79 -0
- batoolset/ta/database/preview_and_edit_sqlite_db.py +405 -0
- batoolset/ta/database/sql.py +2141 -0
- batoolset/ta/database/sql_column_name_dialog.py +122 -0
- batoolset/ta/measurements/TAmeasures.py +2226 -0
- batoolset/ta/measurements/__init__.py +0 -0
- batoolset/ta/measurements/measurements3D/__init__.py +0 -0
- batoolset/ta/measurements/measurements3D/get_point_on_surface_if_centroid_is_bad.py +175 -0
- batoolset/ta/measurements/measurements3D/height_map_to_projection.py +82 -0
- batoolset/ta/measurements/measurements3D/measures3D.py +208 -0
- batoolset/ta/measurements/measurements3D/triangulate.py +331 -0
- batoolset/ta/measurements/nematic.py +562 -0
- batoolset/ta/measurements/nematic_measurements.py +107 -0
- batoolset/ta/segmentation/__init__.py +0 -0
- batoolset/ta/segmentation/neo_wshed.py +314 -0
- batoolset/ta/selections/__init__.py +0 -0
- batoolset/ta/selections/selection.py +329 -0
- batoolset/ta/tracking/__init__.py +0 -0
- batoolset/ta/tracking/last_tracking_based_on_matching_or_on_translation_from_mermaid_warp.py +653 -0
- batoolset/ta/tracking/local_to_track_correspondance.py +138 -0
- batoolset/ta/tracking/rapid_detection_of_vertices.py +517 -0
- batoolset/ta/tracking/registration.py +108 -0
- batoolset/ta/tracking/swap_detector_and_fixer.py +296 -0
- batoolset/ta/tracking/track_correction.py +238 -0
- batoolset/ta/tracking/tracking_error_detector_and_fixer.py +3099 -0
- batoolset/ta/tracking/tracking_yet_another_approach_pyramidal_registration_n_neo_swapping_correction.py +725 -0
- batoolset/ta/utils/TA_tools.py +43 -0
- batoolset/ta/utils/__init__.py +0 -0
- batoolset/ta/utils/rps_tools.py +1783 -0
- batoolset/tests/__init__.py +0 -0
- batoolset/tests/rps_tools.py +51 -0
- batoolset/tests/tools.py +10 -0
- batoolset/tools/__init__.py +0 -0
- batoolset/tools/early_stopper_class.py +9 -0
- batoolset/tools/guided_data_recoverer.py +110 -0
- batoolset/tools/logger.py +100 -0
- batoolset/tools/mytimer.py +31 -0
- batoolset/tools/prompt_color.py +160 -0
- batoolset/tools/qthandler.py +118 -0
- batoolset/uitools/__init__.py +0 -0
- batoolset/uitools/blinker.py +56 -0
- batoolset/unsafe/__init__.py +0 -0
- batoolset/unsafe/tools.py +115 -0
- batoolset/utils/__init__.py +0 -0
- batoolset/utils/commontools.py +158 -0
- batoolset/utils/loadlist.py +728 -0
- batoolset/version.py +10 -0
- batoolset/xml/__init__.py +0 -0
- batoolset/xml/tools.py +65 -0
- batoolset/zip_tools/__init__.py +0 -0
- batoolset/zip_tools/tools.py +76 -0
- batoolset-0.1.0.dist-info/LICENSE +24 -0
- batoolset-0.1.0.dist-info/METADATA +45 -0
- batoolset-0.1.0.dist-info/RECORD +162 -0
- batoolset-0.1.0.dist-info/WHEEL +5 -0
- batoolset-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
from batoolset.draw.shapes.rectangle2d import Rectangle2D
|
|
2
|
+
# from PyQt5.QtCore import QRectF, QSizeF
|
|
3
|
+
from qtpy.QtCore import QRectF, QSizeF
|
|
4
|
+
|
|
5
|
+
# def resize_rect_preserve_aspect_ratio(rect: Rectangle2D, new_height: float):
|
|
6
|
+
# current_width = rect.width()
|
|
7
|
+
# current_height = rect.height()
|
|
8
|
+
# aspect_ratio = current_width / current_height
|
|
9
|
+
#
|
|
10
|
+
# new_width = aspect_ratio * new_height
|
|
11
|
+
# # new_rect = Rectangle2D(QRectF(rect.x(), rect.y(), new_width, new_height)) # here I rather need to scale the rect
|
|
12
|
+
# # rect.set_to_scale(aspect_ratio)
|
|
13
|
+
# # dirty hack for test
|
|
14
|
+
# # rect = Rectangle2D(rect)
|
|
15
|
+
#
|
|
16
|
+
# # print(current_width, new_width)
|
|
17
|
+
#
|
|
18
|
+
# rect.set_to_scale(current_width/new_width)
|
|
19
|
+
#
|
|
20
|
+
# return rect
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# TODO --> fuse all of this in a single super smart stuff
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# can I use my own LLM there ???
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def resize_rect_preserve_aspect_ratio(rect: Rectangle2D, new_width: float, new_height:float):
|
|
34
|
+
# I guess the bug is there
|
|
35
|
+
current_width = rect.width()
|
|
36
|
+
current_height = rect.height()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# if current_height == 0:
|
|
40
|
+
# return rect
|
|
41
|
+
# print(current_height)
|
|
42
|
+
aspect_ratio = float(current_width) / float(current_height)
|
|
43
|
+
|
|
44
|
+
# very good --> so I could fix it
|
|
45
|
+
if new_height is not None:
|
|
46
|
+
# if False: # this is to favor height
|
|
47
|
+
# new_height = new_size.height()
|
|
48
|
+
# # new_width = aspect_ratio * new_height if aspect_ratio < new_size.width() / new_size.height() else new_size.width() * current_height / current_width
|
|
49
|
+
new_width = aspect_ratio * new_height
|
|
50
|
+
rect.set_to_scale(1./(new_width/current_width))
|
|
51
|
+
else:
|
|
52
|
+
# new_width = new_size.width()
|
|
53
|
+
new_height = new_width/aspect_ratio
|
|
54
|
+
rect.set_to_scale(1./(new_height / current_height)) # TODO --> clean this code --> make it better
|
|
55
|
+
|
|
56
|
+
# new_rect = QRectF(rect.x(), rect.y(), new_width, new_height)
|
|
57
|
+
# return new_rect
|
|
58
|
+
return rect
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def resize_rects(rects: list, target_width:float, target_height: float):
|
|
62
|
+
resized_rects = []
|
|
63
|
+
for rect in rects:
|
|
64
|
+
resized_rect = resize_rect_preserve_aspect_ratio(rect, target_width, target_height)
|
|
65
|
+
resized_rects.append(resized_rect)
|
|
66
|
+
|
|
67
|
+
return resized_rects
|
|
68
|
+
|
|
69
|
+
# I need get its size set its scale to 1 get just the text size yet crop the
|
|
70
|
+
def resize_incompressible_rects(rects: list, target_width:float, target_height: float):
|
|
71
|
+
# max_w = 0
|
|
72
|
+
# max_h = 0
|
|
73
|
+
# failed_images = []
|
|
74
|
+
# text_images = []
|
|
75
|
+
# individual_width = []
|
|
76
|
+
# individual_height = []
|
|
77
|
+
# for elm in self.paint.EZFIG_panel.selected_shape:
|
|
78
|
+
# try:
|
|
79
|
+
# print(elm, elm.annotations, elm.annotations[0], elm.annotations[0].width(all=True),
|
|
80
|
+
# elm.annotations[0].height(all=True))
|
|
81
|
+
# if len(elm.annotations) != 1:
|
|
82
|
+
# raise Exception('not good')
|
|
83
|
+
# individual_width.append(elm.annotations[0].width(all=True))
|
|
84
|
+
# individual_height.append(elm.annotations[0].height(all=True))
|
|
85
|
+
# max_w = max(elm.annotations[0].width(all=True), max_w)
|
|
86
|
+
# max_h = max(elm.annotations[0].height(all=True), max_h)
|
|
87
|
+
# text_images.append(elm)
|
|
88
|
+
# except:
|
|
89
|
+
# # pass # if fails this is not a text image
|
|
90
|
+
# failed_images.append(elm) # if image is failed then we have it
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
max_w = 0
|
|
95
|
+
max_h = 0
|
|
96
|
+
resized_rects = []
|
|
97
|
+
for rect in rects:
|
|
98
|
+
# resized_rect = resize_rect_preserve_aspect_ratio(rect, target_width, target_height)
|
|
99
|
+
# resized_rects.append(resized_rect)
|
|
100
|
+
txt_width = rect.annotations[0].width(all=True)
|
|
101
|
+
txt_height = rect.annotations[0].height(all=True)
|
|
102
|
+
max_w = max(txt_width, max_w)
|
|
103
|
+
max_h = max(txt_height, max_h)
|
|
104
|
+
# set the texts to their minimal size --> then resize the stuff in the very end so that all fits
|
|
105
|
+
rect.setWidth(txt_width)
|
|
106
|
+
rect.setHeight(txt_height)
|
|
107
|
+
|
|
108
|
+
return max_w, max_h, resized_rects
|
|
109
|
+
|
|
110
|
+
# def calculate_target_width(rects: list, max_total_height: float):
|
|
111
|
+
# total_aspect_ratio = sum(rect.height() / rect.width() for rect in rects if not rect.immutable) # immutable objects should be removed from the stuff -−> TODO
|
|
112
|
+
# target_width = max_total_height / total_aspect_ratio
|
|
113
|
+
# return target_width
|
|
114
|
+
|
|
115
|
+
# def calculate_target_height(rects: list, max_total_width: float):
|
|
116
|
+
# # in fact if there are incompressible ones here I need
|
|
117
|
+
# total_aspect_ratio = sum(rect.width() / rect.height() for rect in rects if not rect.immutable) # immutable objects should be removed from the stuff -−> TODO
|
|
118
|
+
# target_height = max_total_width / total_aspect_ratio
|
|
119
|
+
# return target_height
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# def resize_rects_within_total_width(rects: list, max_total_width: float, space_in_px: float):
|
|
123
|
+
# incompressible_width = (len(rects) - 1) * space_in_px
|
|
124
|
+
# available_width = max_total_width - incompressible_width
|
|
125
|
+
#
|
|
126
|
+
# target_height = calculate_target_height(rects, available_width)
|
|
127
|
+
# resized_rects = resize_rects(rects, None, target_height) # --> indeed that works
|
|
128
|
+
#
|
|
129
|
+
# return resized_rects
|
|
130
|
+
|
|
131
|
+
# def resize_rects_within_total_height(rects: list, max_total_height: float, space_in_px: float):
|
|
132
|
+
# incompressible_height = (len(rects) - 1) * space_in_px
|
|
133
|
+
# available_height = max_total_height - incompressible_height
|
|
134
|
+
#
|
|
135
|
+
# target_width = calculate_target_width(rects, available_height)
|
|
136
|
+
# resized_rects = resize_rects(rects, target_width,None) # --> indeed that works
|
|
137
|
+
#
|
|
138
|
+
# return resized_rects
|
|
139
|
+
|
|
140
|
+
def resize_rects_within_total_width(rects: list, max_total_width: float, sum_incompressible_space_along_x: float):
|
|
141
|
+
# incompressible_width = (len(rects) - 1) * space_in_px
|
|
142
|
+
available_width = max_total_width - sum_incompressible_space_along_x
|
|
143
|
+
initial_width=available_width
|
|
144
|
+
|
|
145
|
+
total_incompressible_text_width = sum(rect.width(all=True) for rect in rects if rect.isText)
|
|
146
|
+
|
|
147
|
+
compressible = [rect for rect in rects if not rect.isText]
|
|
148
|
+
incompressible = [rect for rect in rects if rect.isText]
|
|
149
|
+
|
|
150
|
+
available_width-=total_incompressible_text_width
|
|
151
|
+
|
|
152
|
+
# print('total_incompressible_text_width',total_incompressible_text_width)
|
|
153
|
+
# print(compressible)
|
|
154
|
+
# print(incompressible)
|
|
155
|
+
|
|
156
|
+
# target_height = calculate_target_height(rects, available_width)
|
|
157
|
+
|
|
158
|
+
# total_aspect_ratio = sum(rect.width() / rect.height() for rect in rects if not rect.immutable) # immutable objects should be removed from the stuff -−> TODO
|
|
159
|
+
# total_aspect_ratio = sum(rect.getAR()for rect in rects if not rect.immutable) # immutable objects should be removed from the stuff -−> TODO
|
|
160
|
+
# total_aspect_ratio = sum(rect.getAR() for rect in rects) # immutable objects should be removed from the stuff -−> TODO
|
|
161
|
+
|
|
162
|
+
# print('final compressible', compressible)
|
|
163
|
+
|
|
164
|
+
total_aspect_ratio = sum(rect.getAR() for rect in compressible) # immutable objects should be removed from the stuff -−> TODO
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# print('total_aspect_ratio modified',total_aspect_ratio)
|
|
168
|
+
# the idea is that I need to fit the non text stuff there
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# if total_aspect_ratio == 0:
|
|
173
|
+
# print('DEBUG DABU')
|
|
174
|
+
# for rect in rects:
|
|
175
|
+
# print('tata', rect.getAR())
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
if total_aspect_ratio!=0: # this is just to avoid errors # maybe fit all to current otherwise
|
|
179
|
+
target_height = available_width / total_aspect_ratio
|
|
180
|
+
else:
|
|
181
|
+
target_height = available_width
|
|
182
|
+
|
|
183
|
+
# resized_rects = resize_rects(rects, None, target_height) # --> indeed that works
|
|
184
|
+
resized_compressible_rects = resize_rects(compressible, None, target_height) # --> indeed that works
|
|
185
|
+
|
|
186
|
+
# if resized_compressible_rects:
|
|
187
|
+
# target_width, target_height =
|
|
188
|
+
max_w, max_h, resized_incompressible_rects = resize_incompressible_rects(incompressible, None, target_height) # --> indeed that works
|
|
189
|
+
if compressible:
|
|
190
|
+
height = compressible[0].height(all=True)
|
|
191
|
+
else:
|
|
192
|
+
height = max_h
|
|
193
|
+
if incompressible : # incompressible
|
|
194
|
+
sum_of_widths = sum(rect.width(all=True) for rect in incompressible)
|
|
195
|
+
# Compute the scaling factor
|
|
196
|
+
scaling_factor = initial_width / sum_of_widths
|
|
197
|
+
|
|
198
|
+
# print('total_aspect_ratio in changed',scaling_factor)
|
|
199
|
+
for elm in incompressible:
|
|
200
|
+
elm.setWidth(elm.width(all=True)*scaling_factor)
|
|
201
|
+
elm.set_to_scale(1.)
|
|
202
|
+
|
|
203
|
+
if incompressible:
|
|
204
|
+
for elm in incompressible:
|
|
205
|
+
elm.setHeight(height)
|
|
206
|
+
elm.set_to_scale(1.)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# last thing to do -> if there is no compressible I need adjust the total size to the deisred by keeping height constant and increasing height
|
|
210
|
+
# by the rescaling factor
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
return rects # return all rects that have been modified in original order
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def resize_rects_within_total_height(rects: list, max_total_height: float, sum_incompressible_space_along_y: float):
|
|
218
|
+
# incompressible_height = (len(rects) - 1) * space_in_px
|
|
219
|
+
available_height = max_total_height - sum_incompressible_space_along_y
|
|
220
|
+
initial_height=available_height
|
|
221
|
+
total_incompressible_text_height = sum(rect.getRect(all=True).height() for rect in rects if rect.isText)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# target_width = calculate_target_width(rects, available_height)
|
|
225
|
+
|
|
226
|
+
# total_aspect_ratio = sum(rect.height() / rect.width() for rect in rects if not rect.immutable) # immutable objects should be removed from the stuff -−> TODO
|
|
227
|
+
# total_aspect_ratio = sum(rect.getAR(invert=True) for rect in rects if not rect.immutable) # immutable objects should be removed from the stuff -−> TODO
|
|
228
|
+
|
|
229
|
+
compressible = [rect for rect in rects if not rect.isText]
|
|
230
|
+
incompressible = [rect for rect in rects if rect.isText]
|
|
231
|
+
|
|
232
|
+
available_height -= total_incompressible_text_height
|
|
233
|
+
|
|
234
|
+
# print('total_incompressible_text_height', total_incompressible_text_height)
|
|
235
|
+
# print(compressible)
|
|
236
|
+
# print(incompressible)
|
|
237
|
+
|
|
238
|
+
# target_height = calculate_target_height(rects, available_width)
|
|
239
|
+
|
|
240
|
+
# total_aspect_ratio = sum(rect.width() / rect.height() for rect in rects if not rect.immutable) # immutable objects should be removed from the stuff -−> TODO
|
|
241
|
+
# total_aspect_ratio = sum(rect.getAR()for rect in rects if not rect.immutable) # immutable objects should be removed from the stuff -−> TODO
|
|
242
|
+
# total_aspect_ratio = sum(rect.getAR() for rect in rects) # immutable objects should be removed from the stuff -−> TODO
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
# total_aspect_ratio = sum(rect.getAR(invert=True) for rect in rects) # immutable objects should be removed from the stuff -−> TODO
|
|
246
|
+
total_aspect_ratio = sum(rect.getAR(invert=True) for rect in compressible) # immutable objects should be removed from the stuff -−> TODO
|
|
247
|
+
|
|
248
|
+
# print('total_aspect_ratio modified', total_aspect_ratio)
|
|
249
|
+
|
|
250
|
+
if total_aspect_ratio != 0: # this is just to avoid errors # maybe fit all to current otherwise
|
|
251
|
+
target_width = available_height / total_aspect_ratio
|
|
252
|
+
else:
|
|
253
|
+
target_width = available_height
|
|
254
|
+
|
|
255
|
+
# target_width = available_height / total_aspect_ratio
|
|
256
|
+
|
|
257
|
+
# resized_rects = resize_rects(rects, target_width,None) # --> indeed that works
|
|
258
|
+
resized_compressible_rects = resize_rects(compressible, target_width, None) # --> indeed that works
|
|
259
|
+
max_w, max_h, resized_incompressible_rects = resize_incompressible_rects(incompressible, target_width,None) # --> indeed that works
|
|
260
|
+
if compressible:
|
|
261
|
+
width = compressible[0].width(all=True)
|
|
262
|
+
else:
|
|
263
|
+
width = max_w
|
|
264
|
+
if incompressible : # incompressible
|
|
265
|
+
sum_of_heights = sum(rect.height(all=True) for rect in incompressible)
|
|
266
|
+
# Compute the scaling factor
|
|
267
|
+
scaling_factor = initial_height / sum_of_heights
|
|
268
|
+
|
|
269
|
+
# print('total_aspect_ratio in chnaged',scaling_factor)
|
|
270
|
+
for elm in incompressible:
|
|
271
|
+
elm.setHeight(elm.height(all=True)*scaling_factor)
|
|
272
|
+
elm.set_to_scale(1.)
|
|
273
|
+
|
|
274
|
+
if incompressible:
|
|
275
|
+
for elm in incompressible:
|
|
276
|
+
elm.setWidth(width)
|
|
277
|
+
elm.set_to_scale(1.)
|
|
278
|
+
|
|
279
|
+
return rects
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def resize_rects_within_grid(rects: list, grid_size: tuple, max_total_width: float, max_total_height: float, incompressible_width: float, incompressible_height: float, fit_in_width:int):
|
|
283
|
+
# this is a bit too complicated with grids as they will have complex incompressibility in width and height that will vary depending on the amount of rows and cols !!!!
|
|
284
|
+
|
|
285
|
+
# this sounds like good and simple code!!!
|
|
286
|
+
rows, cols = grid_size
|
|
287
|
+
|
|
288
|
+
# rather than passing the incompressible size I just would need to compute it per object on the fly !!!
|
|
289
|
+
|
|
290
|
+
available_width = max_total_width - (cols - 1) * incompressible_width # NB maybe this should be done per row ????
|
|
291
|
+
available_height = max_total_height - (rows - 1) * incompressible_height
|
|
292
|
+
|
|
293
|
+
target_width = available_width / cols
|
|
294
|
+
target_height = available_height / rows
|
|
295
|
+
|
|
296
|
+
if fit_in_width:
|
|
297
|
+
target_height=None
|
|
298
|
+
else:
|
|
299
|
+
target_width=None
|
|
300
|
+
# here I need to choose between width and height
|
|
301
|
+
# ideally always prefer width except if extends beyond height
|
|
302
|
+
# when resized
|
|
303
|
+
# start with width
|
|
304
|
+
|
|
305
|
+
# in fact I should take either here --> the best fit in width and or in height -−> auto take it
|
|
306
|
+
# print('sizes',target_width, available_width, max_total_width, target_height, available_height, max_total_height) #92.5 370.0 400 193.33333333333334 580.0 600
|
|
307
|
+
|
|
308
|
+
resized_rects = []
|
|
309
|
+
for rect in rects:
|
|
310
|
+
resized_rect = resize_rect_preserve_aspect_ratio(rect, target_width, target_height)
|
|
311
|
+
resized_rects.append(resized_rect)
|
|
312
|
+
|
|
313
|
+
return resized_rects
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
# I could make it smarter by reading the incompressible height from the image and from the packing -−> this is why I need a group maybe???
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
from batoolset.settings.global_settings import set_UI # set the UI to qtpy
|
|
2
|
+
set_UI()
|
|
3
|
+
import os
|
|
4
|
+
from qtpy import QtWidgets
|
|
5
|
+
from qtpy.QtCore import QPoint, QPointF, Qt, QRectF
|
|
6
|
+
from qtpy.QtGui import QBrush, QPen, QColor, QTransform
|
|
7
|
+
|
|
8
|
+
from batoolset.tools.logger import TA_logger
|
|
9
|
+
|
|
10
|
+
logger = TA_logger()
|
|
11
|
+
|
|
12
|
+
class Ellipse2D(QtWidgets.QGraphicsEllipseItem):
|
|
13
|
+
|
|
14
|
+
isSet = False
|
|
15
|
+
|
|
16
|
+
def __init__(self, *args,x=0, y=0, width=0, height=0, color=0xFFFFFF, fill_color=None, opacity=1., stroke=0.65, line_style=None,theta=0,__version__=1.0, **kwargs):
|
|
17
|
+
if args:
|
|
18
|
+
super(Ellipse2D, self).__init__(*args)
|
|
19
|
+
else:
|
|
20
|
+
super(Ellipse2D, self).__init__(x,y,width,height)
|
|
21
|
+
|
|
22
|
+
# super(, self).__init__(*args)
|
|
23
|
+
# if not args:
|
|
24
|
+
# self.isSet = False
|
|
25
|
+
# else:
|
|
26
|
+
# self.isSet = True
|
|
27
|
+
|
|
28
|
+
# print('self.rect()',self.rect())
|
|
29
|
+
|
|
30
|
+
self.setRect(self.rect())
|
|
31
|
+
self.color = color
|
|
32
|
+
self.fill_color = fill_color
|
|
33
|
+
self.stroke = stroke
|
|
34
|
+
self.opacity = opacity
|
|
35
|
+
self.scale = 1
|
|
36
|
+
self.translation = QPointF()
|
|
37
|
+
self.line_style = line_style
|
|
38
|
+
# rotation
|
|
39
|
+
self.theta = theta
|
|
40
|
+
self.__version__ = __version__
|
|
41
|
+
|
|
42
|
+
def set_rotation(self, theta):
|
|
43
|
+
self.theta = theta
|
|
44
|
+
|
|
45
|
+
def set_opacity(self, opacity):
|
|
46
|
+
self.opacity = opacity
|
|
47
|
+
|
|
48
|
+
def set_line_style(self,style):
|
|
49
|
+
"""
|
|
50
|
+
allows lines to be dashed or dotted or have custom pattern
|
|
51
|
+
:param style: a list of numbers or any of the following Qt.SolidLine, Qt.DashLine, Qt.DashDotLine, Qt.DotLine, Qt.DashDotDotLine but not Qt.CustomDashLine, Qt.CustomDashLine is assumed by default if a list is passed in. None is also a valid value that resets the line --> assume plain line
|
|
52
|
+
:return:
|
|
53
|
+
"""
|
|
54
|
+
self.line_style = style
|
|
55
|
+
# if style is a list then assume custom pattern otherwise apply solidline
|
|
56
|
+
|
|
57
|
+
def draw(self, painter, draw=True, parent=None):
|
|
58
|
+
if self.color is None and self.fill_color is None:
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
if draw:
|
|
62
|
+
painter.save()
|
|
63
|
+
painter.setPen(Qt.NoPen)
|
|
64
|
+
painter.setBrush(Qt.NoBrush)
|
|
65
|
+
painter.setOpacity(self.opacity)
|
|
66
|
+
if self.color is not None:
|
|
67
|
+
pen = QPen(QColor(self.color))
|
|
68
|
+
if self.stroke is not None:
|
|
69
|
+
pen.setWidthF(self.stroke)
|
|
70
|
+
if self.line_style is not None:
|
|
71
|
+
if self.line_style in [Qt.SolidLine, Qt.DashLine, Qt.DashDotLine, Qt.DotLine, Qt.DashDotDotLine]:
|
|
72
|
+
pen.setStyle(self.line_style)
|
|
73
|
+
elif isinstance(self.line_style, list):
|
|
74
|
+
pen.setStyle(Qt.CustomDashLine)
|
|
75
|
+
pen.setDashPattern(self.line_style)
|
|
76
|
+
painter.setPen(pen)
|
|
77
|
+
else:
|
|
78
|
+
painter.setPen(Qt.NoPen)
|
|
79
|
+
if self.fill_color is not None:
|
|
80
|
+
painter.setBrush(QBrush(QColor(self.fill_color)))
|
|
81
|
+
rect_to_plot = self.rect().adjusted(0, 0, 0, 0) # just a copy of the rect --> TODO maybe do it in a cleaner way
|
|
82
|
+
|
|
83
|
+
if parent is not None and parent.scale is not None and parent.scale != 1:
|
|
84
|
+
rect_to_plot = self.__get_scaled_rect(rect_to_plot, 1. / parent.scale)
|
|
85
|
+
|
|
86
|
+
if parent is not None:
|
|
87
|
+
rect_to_plot = rect_to_plot.translated(parent.topLeft())
|
|
88
|
+
|
|
89
|
+
# if self.scale is not None and self.scale != 1:
|
|
90
|
+
# # TODO KEEP THE ORDER THIS MUST BE DONE THIS WAY OR IT WILL GENERATE PLENTY OF BUGS...
|
|
91
|
+
# new_width = rect_to_plot.width() * self.scale
|
|
92
|
+
# new_height = rect_to_plot.height() * self.scale
|
|
93
|
+
# # TODO BE EXTREMELY CAREFUL AS SETX AND SETY CAN CHANGE WIDTH AND HEIGHT --> ALWAYS TAKE SIZE BEFORE OTHERWISE THERE WILL BE A PB AND ALWAYS RESET THE SIZE WHEN SETX IS CALLED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
94
|
+
# # Sets the left edge of the rectangle to the given x coordinate. May change the width, but will never change the right edge of the rectangle. --> NO CLUE WHY SHOULD CHANGE WIDTH THOUGH BUT BE CAREFUL!!!
|
|
95
|
+
# rect_to_plot.setX(rect_to_plot.x() * self.scale)
|
|
96
|
+
# rect_to_plot.setY(rect_to_plot.y() * self.scale)
|
|
97
|
+
# rect_to_plot.setWidth(new_width)
|
|
98
|
+
# rect_to_plot.setHeight(new_height)
|
|
99
|
+
# if self.translation is not None:
|
|
100
|
+
# rect_to_plot.translate(self.translation)
|
|
101
|
+
|
|
102
|
+
# if self.color is not None:
|
|
103
|
+
# painter.drawRect(rect_to_plot)
|
|
104
|
+
# else:
|
|
105
|
+
# painter.fillRect(rect_to_plot, QColor(self.fill_color))
|
|
106
|
+
if self.theta is not None and self.theta != 0:
|
|
107
|
+
painter.translate(rect_to_plot.center())
|
|
108
|
+
painter.rotate(self.theta)
|
|
109
|
+
painter.translate(-rect_to_plot.center())
|
|
110
|
+
|
|
111
|
+
painter.drawEllipse(rect_to_plot)
|
|
112
|
+
painter.restore()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def __get_scaled_rect(self, rect_to_plot, scale):
|
|
116
|
+
rect = QRectF(rect_to_plot.x()*scale, rect_to_plot.y()*scale, rect_to_plot.width()*scale, rect_to_plot.height()*scale)
|
|
117
|
+
return rect
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# def fill(self, painter, draw=True):
|
|
121
|
+
# if self.fill_color is None:
|
|
122
|
+
# return
|
|
123
|
+
# if draw:
|
|
124
|
+
# painter.save()
|
|
125
|
+
# painter.setBrush(QBrush(QColor(self.fill_color)))
|
|
126
|
+
# painter.setOpacity(self.opacity)
|
|
127
|
+
# if draw:
|
|
128
|
+
# painter.drawEllipse(self.rect())
|
|
129
|
+
# painter.restore()
|
|
130
|
+
#
|
|
131
|
+
# # TODO pb will draw the shape twice.... ---> because painter drawpolygon is called twice
|
|
132
|
+
#
|
|
133
|
+
# def drawAndFill(self, painter):
|
|
134
|
+
# painter.save()
|
|
135
|
+
# self.draw(painter, draw=False)
|
|
136
|
+
# self.fill(painter, draw=False)
|
|
137
|
+
# painter.drawEllipse(self.rect())
|
|
138
|
+
# painter.restore()
|
|
139
|
+
|
|
140
|
+
def translate(self, *translation):
|
|
141
|
+
# if isinstance(translation[0], (QPointF, QPoint)):
|
|
142
|
+
|
|
143
|
+
# print('translation szouba', translation)
|
|
144
|
+
|
|
145
|
+
if isinstance(translation[0], (int, float)):
|
|
146
|
+
translation = QPointF(translation[0], translation[1])
|
|
147
|
+
|
|
148
|
+
if isinstance(translation, tuple):
|
|
149
|
+
# not sure this is normal that I have to do that but if I don't do it dragging the ellipse in the demo image crashes the soft
|
|
150
|
+
translation= QPointF(translation[0])
|
|
151
|
+
|
|
152
|
+
# print('zoubi', type(translation))
|
|
153
|
+
|
|
154
|
+
self.moveBy(translation.x(), translation.y())
|
|
155
|
+
rect = self.rect()
|
|
156
|
+
rect.translate(translation.x(), translation.y())
|
|
157
|
+
self.setRect(rect)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def add(self, *args):
|
|
161
|
+
p1 = args[0]
|
|
162
|
+
p2 = args[1]
|
|
163
|
+
rect = self.rect()
|
|
164
|
+
rect.setWidth(abs(p1.x()-p2.x()))
|
|
165
|
+
rect.setHeight(abs(p1.y()-p2.y()))
|
|
166
|
+
x = p2.x()
|
|
167
|
+
y = p2.y()
|
|
168
|
+
if p1.x() < p2.x():
|
|
169
|
+
x = p1.x()
|
|
170
|
+
if p1.y() < p2.y():
|
|
171
|
+
y = p1.y()
|
|
172
|
+
rect.setX(x)
|
|
173
|
+
rect.setY(y)
|
|
174
|
+
|
|
175
|
+
self.setRect(rect)
|
|
176
|
+
# self.isSet = True
|
|
177
|
+
|
|
178
|
+
def boundingRect(self):
|
|
179
|
+
# should I return the scaled version or the orig --> think about it...
|
|
180
|
+
rect_to_plot = self.rect().adjusted(0, 0, 0, 0)
|
|
181
|
+
try:
|
|
182
|
+
# print('tada')
|
|
183
|
+
if self.theta is not None and self.theta!=0:
|
|
184
|
+
# print('entering')
|
|
185
|
+
center = rect_to_plot.center()
|
|
186
|
+
# print('entering2')
|
|
187
|
+
t = QTransform().translate(center.x(), center.y()).rotate(self.theta).translate(-center.x(),
|
|
188
|
+
-center.y())
|
|
189
|
+
# print('entering3')
|
|
190
|
+
# self.setTransform(t)
|
|
191
|
+
# self.transform()
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# transformed = QRectF(self.rect())
|
|
195
|
+
# print('entering5', transformed)
|
|
196
|
+
# self.resetTransform()
|
|
197
|
+
# print('entering5', rect_to_plot)
|
|
198
|
+
# print('entering4')
|
|
199
|
+
transformed = t.mapRect(rect_to_plot)
|
|
200
|
+
|
|
201
|
+
# self.setTransform(t)
|
|
202
|
+
# self.transform()
|
|
203
|
+
#
|
|
204
|
+
# print(self.shape().boundingRect())
|
|
205
|
+
#
|
|
206
|
+
# # print(self.rect(), transformed)
|
|
207
|
+
#
|
|
208
|
+
# transformed = QRectF(self.shape().boundingRect())
|
|
209
|
+
# # self.resetTransform()
|
|
210
|
+
|
|
211
|
+
# not perfect but ok for now though --> bounds are not sharp at the edges upon rotation
|
|
212
|
+
# print('entering45', transformed)
|
|
213
|
+
return transformed
|
|
214
|
+
except:
|
|
215
|
+
pass
|
|
216
|
+
return rect_to_plot
|
|
217
|
+
|
|
218
|
+
# def get_P1(self):
|
|
219
|
+
def topLeft(self):
|
|
220
|
+
return self.boundingRect().topLeft()
|
|
221
|
+
|
|
222
|
+
def setTopLeft(self, point):
|
|
223
|
+
# TODO
|
|
224
|
+
# def set_P1(self, point):
|
|
225
|
+
rect = self.rect()
|
|
226
|
+
width = rect.width()
|
|
227
|
+
height = rect.height()
|
|
228
|
+
rect.setX(point.x())
|
|
229
|
+
rect.setY(point.y())
|
|
230
|
+
# required due to setX changing width and sety changing height
|
|
231
|
+
rect.setWidth(width)
|
|
232
|
+
rect.setHeight(height)
|
|
233
|
+
self.setRect(rect)
|
|
234
|
+
|
|
235
|
+
def set_to_scale(self, factor):
|
|
236
|
+
self.scale = factor
|
|
237
|
+
|
|
238
|
+
def set_to_translation(self, translation):
|
|
239
|
+
self.translation = translation
|
|
240
|
+
|
|
241
|
+
def __repr__(self):
|
|
242
|
+
class_name = type(self).__name__
|
|
243
|
+
memory_address = hex(id(self))
|
|
244
|
+
return f"{class_name}-{memory_address}"
|
|
245
|
+
|
|
246
|
+
def getRect(self, *args, **kwargs):
|
|
247
|
+
# if args or kwargs:
|
|
248
|
+
# # print('I am called')
|
|
249
|
+
# rect = QRectF(self.x(), self.y(), self.width() / self.scale, self.height() / self.scale)
|
|
250
|
+
# rect = rect.translated(rect.width()/2., rect.height()/2.)
|
|
251
|
+
# # rect.setX(rect.x()-rect.width()/2.)
|
|
252
|
+
# # rect.setY(rect.y()-rect.height()/2.)
|
|
253
|
+
# return rect
|
|
254
|
+
# else:
|
|
255
|
+
# # return super().getRect()
|
|
256
|
+
# return QRectF(*super().getRect())
|
|
257
|
+
return self.boundingRect()
|
|
258
|
+
|
|
259
|
+
def x(self, *args, **kwargs):
|
|
260
|
+
return self.boundingRect().x()
|
|
261
|
+
|
|
262
|
+
def y(self, *args, **kwargs):
|
|
263
|
+
return self.boundingRect().y()
|
|
264
|
+
|
|
265
|
+
def width(self, *args, **kwargs):
|
|
266
|
+
# if args or kwargs:
|
|
267
|
+
# return self.width() / self.scale
|
|
268
|
+
# else:
|
|
269
|
+
# return self.rect().width()
|
|
270
|
+
return self.boundingRect().width()
|
|
271
|
+
|
|
272
|
+
def height(self, *args, **kwargs):
|
|
273
|
+
# if args or kwargs:
|
|
274
|
+
# return self.height() / self.scale
|
|
275
|
+
# else:
|
|
276
|
+
# # return super().height()
|
|
277
|
+
# return self.rect().height()
|
|
278
|
+
return self.boundingRect().height()
|
|
279
|
+
|
|
280
|
+
def to_dict(self):
|
|
281
|
+
x = self.rect().x()
|
|
282
|
+
y = self.rect().y()
|
|
283
|
+
width = self.rect().width()
|
|
284
|
+
height = self.rect().height()
|
|
285
|
+
|
|
286
|
+
# Create a dictionary representation of the values of the super object
|
|
287
|
+
output_dict = {
|
|
288
|
+
'x': x,
|
|
289
|
+
'y': y,
|
|
290
|
+
'width': width,
|
|
291
|
+
'height': height,
|
|
292
|
+
}
|
|
293
|
+
# Update the dictionary with the __dict__ of Rectangle2D
|
|
294
|
+
output_dict.update(self.__dict__)
|
|
295
|
+
return output_dict
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
if __name__ == '__main__':
|
|
299
|
+
# ça marche --> voici deux examples de shapes
|
|
300
|
+
test = Ellipse2D(0, 0, 100, 100)
|
|
301
|
+
# print(test.x(), test.y(), test.width(), test.height())
|
|
302
|
+
print(test.contains(QPointF(50, 50)))
|
|
303
|
+
print(test.contains(QPointF(15, 15)))
|
|
304
|
+
print(test.contains(QPointF(-1, -1)))
|
|
305
|
+
print(test.contains(QPointF(0, 0)))
|
|
306
|
+
print(test.contains(QPointF(100, 100)))
|
|
307
|
+
print(test.contains(QPointF(100, 100.1)))
|
|
308
|
+
print(test.x())
|
|
309
|
+
print(test.y())
|
|
310
|
+
print(test.translate(QPoint(10, 10)))
|
|
311
|
+
print(test.x())
|
|
312
|
+
print(test.y())
|
|
313
|
+
|
|
314
|
+
# p1 = test.p1()
|
|
315
|
+
# print(p1.x(), p1.y())
|
|
316
|
+
# p2 = test.p2()
|
|
317
|
+
# print(p2.x(), p2.y())
|
|
318
|
+
# print(test.arrow)
|
|
319
|
+
# print(test.length()) # sqrt 2 --> 141
|
|
320
|
+
# # if it's an arrow I can add easily all the stuff I need
|
|
321
|
+
#
|
|
322
|
+
# test = Rect2D(0, 0, 1, 1)
|
|
323
|
+
# p1 = test.p1()
|
|
324
|
+
# print(p1.x(), p1.y())
|
|
325
|
+
# p2 = test.p2()
|
|
326
|
+
# print(p2.x(), p2.y())
|
|
327
|
+
# print(test.arrow)
|
|
328
|
+
# import math
|
|
329
|
+
# print(test.length() == math.sqrt(2)) # sqrt 2
|
|
330
|
+
#
|
|
331
|
+
# test2 = Rect2D()
|
|
332
|
+
# p1 = test2.p1()
|
|
333
|
+
# print(p1.x(), p1.y())
|
|
334
|
+
# p2 = test2.p2()
|
|
335
|
+
# print(p2.x(), p2.y())
|
|
336
|
+
# print(test2.arrow)
|