batoolset-gui 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- batoolset/GUI/__init__.py +0 -0
- batoolset/GUI/customdialog.py +136 -0
- batoolset/GUI/exception_handler.py +74 -0
- batoolset/GUI/list_gui.py +514 -0
- batoolset/GUI/multi_image_display.py +1038 -0
- batoolset/GUI/open.py +325 -0
- batoolset/GUI/overlay_hints.py +45 -0
- batoolset/GUI/paint2.py +1369 -0
- batoolset/GUI/scrollablepaint.py +1165 -0
- batoolset/dialogs/__init__.py +0 -0
- batoolset/dialogs/opensave.py +228 -0
- batoolset/drawings/__init__.py +0 -0
- batoolset/drawings/math_text.py +294 -0
- batoolset/drawings/pdf_svg_renderer.py +376 -0
- batoolset/drawings/shapes/Position.py +183 -0
- batoolset/drawings/shapes/__init__.py +0 -0
- batoolset/drawings/shapes/automated_alignment_drawing_refactored.py +346 -0
- batoolset/drawings/shapes/circle2d.py +99 -0
- batoolset/drawings/shapes/compute_optimal_size_all_merged.py +328 -0
- batoolset/drawings/shapes/ellipse2d.py +336 -0
- batoolset/drawings/shapes/freehand2d.py +235 -0
- batoolset/drawings/shapes/group.py +1985 -0
- batoolset/drawings/shapes/image2d.py +3157 -0
- batoolset/drawings/shapes/line2d.py +579 -0
- batoolset/drawings/shapes/point2d.py +257 -0
- batoolset/drawings/shapes/polygon2d.py +357 -0
- batoolset/drawings/shapes/polyline2d.py +235 -0
- batoolset/drawings/shapes/rect2d.py +388 -0
- batoolset/drawings/shapes/rectangle2d.py +601 -0
- batoolset/drawings/shapes/scalebar.py +310 -0
- batoolset/drawings/shapes/serializablefont.py +133 -0
- batoolset/drawings/shapes/serializableguiparams.py +14 -0
- batoolset/drawings/shapes/square2d.py +102 -0
- batoolset/drawings/shapes/test_publication_style.py +220 -0
- batoolset/drawings/shapes/textorientation.py +45 -0
- batoolset/drawings/shapes/tools.py +467 -0
- batoolset/drawings/shapes/transform2D.py +350 -0
- batoolset/drawings/shapes/txt2d.py +615 -0
- batoolset/drawings/svg_clip_renderer.py +247 -0
- batoolset/drawings/svg_embed.py +165 -0
- batoolset/drawings/svg_text_renderer.py +183 -0
- batoolset/drawings/widgets/__init__.py +0 -0
- batoolset/drawings/widgets/col.py +343 -0
- batoolset/drawings/widgets/image_group.py +248 -0
- batoolset/drawings/widgets/image_organizer.py +96 -0
- batoolset/drawings/widgets/paint.py +184 -0
- batoolset/drawings/widgets/vectorial.py +256 -0
- batoolset/figure/__init__.py +0 -0
- batoolset/figure/alignment.py +822 -0
- batoolset/figure/column.py +1117 -0
- batoolset/figure/fig_tools.py +285 -0
- batoolset/figure/minimal_debugger_scale_etc.py +139 -0
- batoolset/figure/panel.py +697 -0
- batoolset/figure/row.py +852 -0
- batoolset/figure/user_selection.py +287 -0
- batoolset/icons/__init__.py +0 -0
- batoolset/icons/tools.py +248 -0
- batoolset/luts/__init__.py +0 -0
- batoolset/luts/lut_minimal_test.py +1308 -0
- batoolset/luts/lutcreatorfromformula.py +209 -0
- batoolset/luts/luteditor.py +243 -0
- batoolset/pyqts/__init__.py +0 -0
- batoolset/pyqts/pleasewaitdialog.py +14 -0
- batoolset/pyqts/tools.py +760 -0
- batoolset/serializations/__init__.py +0 -0
- batoolset/serializations/tools.py +1338 -0
- batoolset/theme.py +647 -0
- batoolset/uis/__init__.py +0 -0
- batoolset/uis/blinker.py +56 -0
- batoolset_gui-0.2.0.dist-info/METADATA +54 -0
- batoolset_gui-0.2.0.dist-info/RECORD +74 -0
- batoolset_gui-0.2.0.dist-info/WHEEL +5 -0
- batoolset_gui-0.2.0.dist-info/licenses/LICENSE +24 -0
- batoolset_gui-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# below is the java code of my selection --> I should inspire myself from it
|
|
2
|
+
# see the best way todo that
|
|
3
|
+
# or maybe allow to handle only elements of a given scope --> in a way that simplifies a lot the things !!! --> think about it
|
|
4
|
+
# if scope is images --> only offer images options, if scope is panel --> offer panel options
|
|
5
|
+
# if scope is Figure --> only offer figure options
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# package GUIs;
|
|
9
|
+
#
|
|
10
|
+
# import MyShapes.Figure;
|
|
11
|
+
# import MyShapes.Montage;
|
|
12
|
+
# import MyShapes.MyImage2D;
|
|
13
|
+
# import MyShapes.PARoi;
|
|
14
|
+
# import MyShapes.Row;
|
|
15
|
+
# import MyShapes.MyTextRow;
|
|
16
|
+
# import java.awt.geom.Point2D;
|
|
17
|
+
# import java.util.ArrayList;
|
|
18
|
+
#
|
|
19
|
+
# /**
|
|
20
|
+
# * This class deals with selections
|
|
21
|
+
# *
|
|
22
|
+
# * @author benoit aigouy
|
|
23
|
+
# */
|
|
24
|
+
# public class OptimalSelection {
|
|
25
|
+
#
|
|
26
|
+
# public static Object getSelectionAt(ArrayList<Object> ROIs, boolean goDeeper, Point2D.Double mouseClick, Object objectsAlreadySelected) {
|
|
27
|
+
# Object sel = null;
|
|
28
|
+
# if (ROIs == null || ROIs.isEmpty()) {
|
|
29
|
+
# return null;
|
|
30
|
+
# }
|
|
31
|
+
# int size = ROIs.size();
|
|
32
|
+
# /* we loop in reverse order */
|
|
33
|
+
# loop1:
|
|
34
|
+
# for (int i = size - 1; i >= 0; i--) {
|
|
35
|
+
# Object object = ROIs.get(i);
|
|
36
|
+
# if (object == null) {
|
|
37
|
+
# continue;
|
|
38
|
+
# }
|
|
39
|
+
# if (objectsAlreadySelected instanceof MyImage2D) {
|
|
40
|
+
# /* we are at the lowest level --> we go up again */
|
|
41
|
+
# if ((object instanceof Figure) || (object instanceof Row) || (object instanceof Montage)) {
|
|
42
|
+
# if (object instanceof PARoi) {
|
|
43
|
+
# if (((PARoi) object).contains(mouseClick)) {
|
|
44
|
+
# return object;
|
|
45
|
+
# }
|
|
46
|
+
# }
|
|
47
|
+
# }
|
|
48
|
+
# }
|
|
49
|
+
# /* go deeper --> if selected object is a figure --> return the row under the mouse */
|
|
50
|
+
# if (objectsAlreadySelected instanceof Figure) {
|
|
51
|
+
# Row montageContainingClick = ((Figure) objectsAlreadySelected).getRowAtPosition(mouseClick);
|
|
52
|
+
# if (montageContainingClick instanceof Row) {
|
|
53
|
+
# return montageContainingClick;
|
|
54
|
+
# }
|
|
55
|
+
# }
|
|
56
|
+
# /* go deeper --> if selected object is a row --> return the montage under the mouse */
|
|
57
|
+
# if (objectsAlreadySelected instanceof Row) {
|
|
58
|
+
# Montage montageContainingClick = ((Row) objectsAlreadySelected).getMontageAtPosition(mouseClick);
|
|
59
|
+
# if (montageContainingClick instanceof Montage) {
|
|
60
|
+
# return montageContainingClick;
|
|
61
|
+
# }
|
|
62
|
+
# }
|
|
63
|
+
# /* go deeper --> if selected object is a Montage --> return the image2D under the mouse */
|
|
64
|
+
# if (objectsAlreadySelected instanceof Montage) {
|
|
65
|
+
# MyImage2D montageContainingClick = ((Montage) objectsAlreadySelected).getMyImage2DAtPosition(mouseClick);
|
|
66
|
+
# if (montageContainingClick instanceof MyImage2D) {
|
|
67
|
+
# return montageContainingClick;
|
|
68
|
+
# }
|
|
69
|
+
# }
|
|
70
|
+
# if (object instanceof PARoi) {
|
|
71
|
+
# if (((PARoi) object).contains(mouseClick)) {
|
|
72
|
+
# return object;
|
|
73
|
+
# }
|
|
74
|
+
# }
|
|
75
|
+
# }
|
|
76
|
+
# return sel;
|
|
77
|
+
# }
|
|
78
|
+
#
|
|
79
|
+
# /**
|
|
80
|
+
# * adds to the current selection the newly selected object
|
|
81
|
+
# *
|
|
82
|
+
# * @param ROIs
|
|
83
|
+
# * @param breakOnFirstEncounter
|
|
84
|
+
# * @param onlyAllowSameInstanceAsSelection
|
|
85
|
+
# * @param mouseClick
|
|
86
|
+
# * @param objectsAlreadySelected
|
|
87
|
+
# * @return
|
|
88
|
+
# */
|
|
89
|
+
# public static Object addToSelection(ArrayList<Object> ROIs, boolean breakOnFirstEncounter, boolean onlyAllowSameInstanceAsSelection, Point2D.Double mouseClick, Object objectsAlreadySelected) {
|
|
90
|
+
# Object newSel = getSelection(ROIs, breakOnFirstEncounter, onlyAllowSameInstanceAsSelection, mouseClick, objectsAlreadySelected);
|
|
91
|
+
# // System.out.println("potentially newly selected object " + newSel + " " + objectsAlreadySelected);
|
|
92
|
+
# /* if there is no cur sel return new sel as cur sel */
|
|
93
|
+
# if (objectsAlreadySelected == null) {
|
|
94
|
+
# return newSel;
|
|
95
|
+
# }
|
|
96
|
+
# if (newSel == null) {
|
|
97
|
+
# return objectsAlreadySelected;
|
|
98
|
+
# }
|
|
99
|
+
# /* if sel is already a list of selected objects add the new sel to the list and return it */
|
|
100
|
+
# if (objectsAlreadySelected instanceof ArrayList) {
|
|
101
|
+
# ArrayList<Object> sel = ((ArrayList<Object>) objectsAlreadySelected);
|
|
102
|
+
# if (!sel.contains(newSel)) {
|
|
103
|
+
# sel.add(newSel);
|
|
104
|
+
# }
|
|
105
|
+
# return sel;
|
|
106
|
+
# } else {
|
|
107
|
+
# /* there is already a selected object but we create a list and add all objects to it */
|
|
108
|
+
# ArrayList<Object> sel = new ArrayList<Object>();
|
|
109
|
+
# sel.add(objectsAlreadySelected);
|
|
110
|
+
# if (newSel != objectsAlreadySelected) {
|
|
111
|
+
# sel.add(newSel);
|
|
112
|
+
# }
|
|
113
|
+
# return sel;
|
|
114
|
+
# }
|
|
115
|
+
# }
|
|
116
|
+
#
|
|
117
|
+
# public static Object getSelection(ArrayList<Object> ROIs, boolean breakOnFirstEncounter, boolean onlyAllowSameInstanceAsSelection, Point2D.Double mouseClick, Object objectsAlreadySelected) {
|
|
118
|
+
# if (ROIs == null || ROIs.isEmpty()) {
|
|
119
|
+
# return null;
|
|
120
|
+
# }
|
|
121
|
+
# int size = ROIs.size();
|
|
122
|
+
# ArrayList<Object> foundObjects = new ArrayList<Object>();
|
|
123
|
+
# /* we loop in reverse order */
|
|
124
|
+
# loop1:
|
|
125
|
+
# for (int i = size - 1; i >= 0; i--) {
|
|
126
|
+
# Object object = ROIs.get(i);
|
|
127
|
+
# /* we prevent the object from being selected twice */
|
|
128
|
+
# if (object == objectsAlreadySelected) {
|
|
129
|
+
# continue;
|
|
130
|
+
# }
|
|
131
|
+
# if (objectsAlreadySelected != null && (objectsAlreadySelected instanceof ArrayList)) {
|
|
132
|
+
# /* prevent from selecting the same object again */
|
|
133
|
+
# ArrayList<Object> curObjects = ((ArrayList<Object>) objectsAlreadySelected);
|
|
134
|
+
# for (Object foundObject : curObjects) {
|
|
135
|
+
# if (object == foundObject) {
|
|
136
|
+
# continue loop1;
|
|
137
|
+
# }
|
|
138
|
+
# }
|
|
139
|
+
# }
|
|
140
|
+
# if (object instanceof PARoi) {
|
|
141
|
+
# if (((PARoi) object).contains(mouseClick)) {
|
|
142
|
+
# if (objectsAlreadySelected == null) {
|
|
143
|
+
# return object;
|
|
144
|
+
# }
|
|
145
|
+
# if (onlyAllowSameInstanceAsSelection) {
|
|
146
|
+
# /* if they are of the same instance take it directly otherwise look inside */
|
|
147
|
+
# /* here we just get objects of the same type */
|
|
148
|
+
# if ((object instanceof Figure) && ((objectsAlreadySelected instanceof Figure) || ((objectsAlreadySelected instanceof ArrayList) && ((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof Figure))) {
|
|
149
|
+
# if (breakOnFirstEncounter) {
|
|
150
|
+
# return object;
|
|
151
|
+
# } else {
|
|
152
|
+
# foundObjects.add(object);
|
|
153
|
+
# }
|
|
154
|
+
# }
|
|
155
|
+
# /* prevent MyTextRow to be selected with Rows even if they are Rows */
|
|
156
|
+
# if (((object instanceof MyTextRow)) && (((objectsAlreadySelected instanceof MyTextRow)) || ((objectsAlreadySelected instanceof ArrayList) && (((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof MyTextRow)))) {
|
|
157
|
+
# if (breakOnFirstEncounter) {
|
|
158
|
+
# return object;
|
|
159
|
+
# } else {
|
|
160
|
+
# foundObjects.add(object);
|
|
161
|
+
# }
|
|
162
|
+
# } else if (((object instanceof Row) && !(object instanceof MyTextRow)) && (((objectsAlreadySelected instanceof Row) && !(objectsAlreadySelected instanceof MyTextRow)) || ((objectsAlreadySelected instanceof ArrayList) && (((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof Row) && !(((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof MyTextRow)))) {
|
|
163
|
+
# if (breakOnFirstEncounter) {
|
|
164
|
+
# return object;
|
|
165
|
+
# } else {
|
|
166
|
+
# foundObjects.add(object);
|
|
167
|
+
# }
|
|
168
|
+
# }
|
|
169
|
+
# if ((object instanceof Montage) && ((objectsAlreadySelected instanceof Montage) || ((objectsAlreadySelected instanceof ArrayList) && ((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof Montage))) {
|
|
170
|
+
# if (breakOnFirstEncounter) {
|
|
171
|
+
# return object;
|
|
172
|
+
# } else {
|
|
173
|
+
# foundObjects.add(object);
|
|
174
|
+
# }
|
|
175
|
+
# }
|
|
176
|
+
# if ((object instanceof MyImage2D) && ((objectsAlreadySelected instanceof MyImage2D) || ((objectsAlreadySelected instanceof ArrayList) && ((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof MyImage2D))) {
|
|
177
|
+
# if (breakOnFirstEncounter) {
|
|
178
|
+
# return object;
|
|
179
|
+
# } else {
|
|
180
|
+
# foundObjects.add(object);
|
|
181
|
+
# }
|
|
182
|
+
# }
|
|
183
|
+
# if ((object instanceof Figure) && ((objectsAlreadySelected instanceof MyTextRow) || ((objectsAlreadySelected instanceof ArrayList) && ((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof MyTextRow))) {
|
|
184
|
+
# Object innerSelectedObject = ((Figure) object).getRowAtPosition(mouseClick);
|
|
185
|
+
# if (innerSelectedObject instanceof MyTextRow) {
|
|
186
|
+
# if (breakOnFirstEncounter) {
|
|
187
|
+
# return innerSelectedObject;
|
|
188
|
+
# } else {
|
|
189
|
+
# foundObjects.add(innerSelectedObject);
|
|
190
|
+
# }
|
|
191
|
+
# }
|
|
192
|
+
# } else if ((object instanceof Figure) && (((objectsAlreadySelected instanceof Row) && !(objectsAlreadySelected instanceof MyTextRow)) || ((objectsAlreadySelected instanceof ArrayList) && ((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof Row && !(((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof MyTextRow)))) {
|
|
193
|
+
# Object innerSelectedObject = ((Figure) object).getRowAtPosition(mouseClick);
|
|
194
|
+
# if ((innerSelectedObject instanceof Row) && !(innerSelectedObject instanceof MyTextRow) ) {
|
|
195
|
+
# if (breakOnFirstEncounter) {
|
|
196
|
+
# return innerSelectedObject;
|
|
197
|
+
# } else {
|
|
198
|
+
# foundObjects.add(innerSelectedObject);
|
|
199
|
+
# }
|
|
200
|
+
# }
|
|
201
|
+
# }
|
|
202
|
+
# /* if instance of objects differ then take the compatible one when grouping */
|
|
203
|
+
# if ((object instanceof Figure) && ((objectsAlreadySelected instanceof Montage) || ((objectsAlreadySelected instanceof ArrayList) && ((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof Montage))) {
|
|
204
|
+
# Object innerSelectedObject = ((Figure) object).getRowAtPosition(mouseClick);
|
|
205
|
+
# if (innerSelectedObject instanceof Row) {
|
|
206
|
+
# innerSelectedObject = ((Row) innerSelectedObject).getMontageAtPosition(mouseClick);
|
|
207
|
+
# }
|
|
208
|
+
# if (innerSelectedObject instanceof Montage) {
|
|
209
|
+
# if (breakOnFirstEncounter) {
|
|
210
|
+
# return innerSelectedObject;
|
|
211
|
+
# } else {
|
|
212
|
+
# foundObjects.add(innerSelectedObject);
|
|
213
|
+
# }
|
|
214
|
+
# }
|
|
215
|
+
# }
|
|
216
|
+
# /* if instance of objects differ then take the compatible one when grouping */
|
|
217
|
+
# if ((object instanceof Figure) && ((objectsAlreadySelected instanceof MyImage2D) || ((objectsAlreadySelected instanceof ArrayList) && ((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof MyImage2D))) {
|
|
218
|
+
# Object innerSelectedObject = ((Figure) object).getRowAtPosition(mouseClick);
|
|
219
|
+
# if (innerSelectedObject instanceof Row) {
|
|
220
|
+
# innerSelectedObject = ((Row) innerSelectedObject).getMontageAtPosition(mouseClick);
|
|
221
|
+
# }
|
|
222
|
+
# if (innerSelectedObject instanceof Montage) {
|
|
223
|
+
# innerSelectedObject = ((Montage) innerSelectedObject).getMyImage2DAtPosition(mouseClick);
|
|
224
|
+
# }
|
|
225
|
+
# if (innerSelectedObject instanceof MyImage2D) {
|
|
226
|
+
# if (breakOnFirstEncounter) {
|
|
227
|
+
# return innerSelectedObject;
|
|
228
|
+
# } else {
|
|
229
|
+
# foundObjects.add(innerSelectedObject);
|
|
230
|
+
# }
|
|
231
|
+
# }
|
|
232
|
+
# }
|
|
233
|
+
# /* if instance of objects differ then take the compatible one when grouping */
|
|
234
|
+
# if ((object instanceof Row) && ((objectsAlreadySelected instanceof Montage) || ((objectsAlreadySelected instanceof ArrayList) && ((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof Montage))) {
|
|
235
|
+
# //get the montage at position
|
|
236
|
+
# Object innerSelectedObject = ((Row) object).getMontageAtPosition(mouseClick);
|
|
237
|
+
# if (innerSelectedObject instanceof Montage) {
|
|
238
|
+
# if (breakOnFirstEncounter) {
|
|
239
|
+
# return innerSelectedObject;
|
|
240
|
+
# } else {
|
|
241
|
+
# foundObjects.add(innerSelectedObject);
|
|
242
|
+
# }
|
|
243
|
+
# }
|
|
244
|
+
# }
|
|
245
|
+
# /* if instance of objects differ then take the compatible one when grouping */
|
|
246
|
+
# if ((object instanceof Row) && ((objectsAlreadySelected instanceof MyImage2D) || ((objectsAlreadySelected instanceof ArrayList) && ((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof MyImage2D))) {
|
|
247
|
+
# Object innerSelectedObject = ((Row) object).getMontageAtPosition(mouseClick);
|
|
248
|
+
# if (innerSelectedObject instanceof Montage) {
|
|
249
|
+
# innerSelectedObject = ((Montage) innerSelectedObject).getMyImage2DAtPosition(mouseClick);
|
|
250
|
+
# }
|
|
251
|
+
# if (innerSelectedObject instanceof MyImage2D) {
|
|
252
|
+
# if (breakOnFirstEncounter) {
|
|
253
|
+
# return innerSelectedObject;
|
|
254
|
+
# } else {
|
|
255
|
+
# foundObjects.add(innerSelectedObject);
|
|
256
|
+
# }
|
|
257
|
+
# }
|
|
258
|
+
# }
|
|
259
|
+
# /* if instance of objects differ then take the compatible one when grouping */
|
|
260
|
+
# if ((object instanceof Montage) && ((objectsAlreadySelected instanceof MyImage2D) || ((objectsAlreadySelected instanceof ArrayList) && ((ArrayList<Object>) objectsAlreadySelected).get(0) instanceof MyImage2D))) {
|
|
261
|
+
# //get the montage at position
|
|
262
|
+
# Object innerSelectedObject = ((Montage) object).getMyImage2DAtPosition(mouseClick);
|
|
263
|
+
# if (innerSelectedObject instanceof MyImage2D) {
|
|
264
|
+
# if (breakOnFirstEncounter) {
|
|
265
|
+
# return innerSelectedObject;
|
|
266
|
+
# } else {
|
|
267
|
+
# foundObjects.add(innerSelectedObject);
|
|
268
|
+
# }
|
|
269
|
+
# }
|
|
270
|
+
# }
|
|
271
|
+
# } else {
|
|
272
|
+
# return object;
|
|
273
|
+
# }
|
|
274
|
+
# }
|
|
275
|
+
# }
|
|
276
|
+
# }
|
|
277
|
+
# if (!foundObjects.isEmpty()) {
|
|
278
|
+
# return foundObjects;
|
|
279
|
+
# }
|
|
280
|
+
# return null;
|
|
281
|
+
# }
|
|
282
|
+
#
|
|
283
|
+
# public static void main(String[] args) {
|
|
284
|
+
# System.exit(0);
|
|
285
|
+
# }
|
|
286
|
+
#
|
|
287
|
+
# }
|
|
File without changes
|
batoolset/icons/tools.py
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# TODO maybe worth speeding this up especially calls to icons that are not used and or the full list of icons that can take time --> need think
|
|
2
|
+
|
|
3
|
+
from batoolset.settings.global_settings import set_UI # set the UI to qtpy
|
|
4
|
+
|
|
5
|
+
set_UI()
|
|
6
|
+
import qtawesome as qta
|
|
7
|
+
from batoolset.strings.tools import levenshtein_distance
|
|
8
|
+
from qtpy.QtGui import QColor
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
STYLE_SUFFIXES = [
|
|
13
|
+
"outline", "outlined", "fill", "filled",
|
|
14
|
+
"bold", "thin", "light", "regular",
|
|
15
|
+
"circle", "square",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
# Cache for available icons
|
|
19
|
+
_available_icons_cache = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def normalize_icon_name(name):
|
|
23
|
+
"""Normalize icon name to its semantic core."""
|
|
24
|
+
base = name.split('.')[-1].lower()
|
|
25
|
+
|
|
26
|
+
# remove common legacy markers
|
|
27
|
+
base = re.sub(r"-o$", "", base)
|
|
28
|
+
base = re.sub(r"-alt$", "", base)
|
|
29
|
+
|
|
30
|
+
# remove style suffixes
|
|
31
|
+
for suffix in STYLE_SUFFIXES:
|
|
32
|
+
base = re.sub(rf"-{suffix}$", "", base)
|
|
33
|
+
|
|
34
|
+
return base
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_closest_icon_name(target_name, available_icons):
|
|
38
|
+
"""Find the closest matching icon using semantic matching and Levenshtein distance."""
|
|
39
|
+
target_norm = normalize_icon_name(target_name)
|
|
40
|
+
target_base = target_name.split('.')[-1] if '.' in target_name else target_name
|
|
41
|
+
target_base_norm = normalize_icon_name(target_base)
|
|
42
|
+
|
|
43
|
+
best_score = float('inf')
|
|
44
|
+
best_icon = None
|
|
45
|
+
|
|
46
|
+
# Split target into words for matching
|
|
47
|
+
target_words = set(target_norm.split('-')) if target_norm else set()
|
|
48
|
+
|
|
49
|
+
for icon in available_icons:
|
|
50
|
+
icon_norm = normalize_icon_name(icon)
|
|
51
|
+
icon_base = icon.split('.')[-1] if '.' in icon else icon
|
|
52
|
+
icon_base_norm = normalize_icon_name(icon_base)
|
|
53
|
+
|
|
54
|
+
# Strong semantic preference - normalized bases match exactly
|
|
55
|
+
if icon_base_norm == target_base_norm:
|
|
56
|
+
return icon # Perfect match, return immediately
|
|
57
|
+
elif icon_norm == target_norm:
|
|
58
|
+
score = 0.5
|
|
59
|
+
elif icon_base == target_base:
|
|
60
|
+
score = 1
|
|
61
|
+
elif icon_norm.startswith(target_norm):
|
|
62
|
+
score = 2
|
|
63
|
+
elif target_norm in icon_norm:
|
|
64
|
+
score = 3
|
|
65
|
+
elif target_words and all(word in icon_norm for word in target_words if word):
|
|
66
|
+
score = 4
|
|
67
|
+
else:
|
|
68
|
+
# Only compute Levenshtein for icons with some similarity
|
|
69
|
+
# Skip if length difference is too large
|
|
70
|
+
len_diff = abs(len(target_base_norm) - len(icon_base_norm))
|
|
71
|
+
if len_diff > max(len(target_base_norm), len(icon_base_norm)) * 0.6:
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
# Skip if no common characters at all
|
|
75
|
+
if target_base_norm and icon_base_norm:
|
|
76
|
+
common = set(target_base_norm) & set(icon_base_norm)
|
|
77
|
+
if len(common) < min(3, len(target_base_norm) * 0.3):
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
dist = levenshtein_distance(target_base_norm, icon_base_norm)
|
|
81
|
+
|
|
82
|
+
# Reject if distance is too large
|
|
83
|
+
max_dist = max(len(target_base_norm), len(icon_base_norm)) * 0.5
|
|
84
|
+
if dist > max_dist:
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
score = 10 + dist
|
|
88
|
+
|
|
89
|
+
if score < best_score:
|
|
90
|
+
best_score = score
|
|
91
|
+
best_icon = icon
|
|
92
|
+
|
|
93
|
+
# If no good match found (score too high), return None to trigger help icon
|
|
94
|
+
if best_score > 15: # Threshold for "no good match"
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
return best_icon if best_icon else None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_available_icons():
|
|
101
|
+
global _available_icons_cache
|
|
102
|
+
|
|
103
|
+
if _available_icons_cache is not None:
|
|
104
|
+
return _available_icons_cache
|
|
105
|
+
|
|
106
|
+
# Force initialization by creating a dummy icon
|
|
107
|
+
try:
|
|
108
|
+
qta.icon('fa5s.home')
|
|
109
|
+
except Exception:
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
# Get the charmap which contains prefix -> {icon_name: code} mappings
|
|
113
|
+
charmap = qta._instance().charmap
|
|
114
|
+
|
|
115
|
+
icon_names = []
|
|
116
|
+
|
|
117
|
+
# Iterate through each font prefix (fa5s, mdi, etc.)
|
|
118
|
+
for prefix, icons_dict in charmap.items():
|
|
119
|
+
if isinstance(icons_dict, dict):
|
|
120
|
+
# Add all icon names with their prefix
|
|
121
|
+
for icon_name in icons_dict.keys():
|
|
122
|
+
icon_names.append(f"{prefix}.{icon_name}")
|
|
123
|
+
|
|
124
|
+
_available_icons_cache = sorted(icon_names)
|
|
125
|
+
return _available_icons_cache
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def get_icon(icon_name, color=None, color_disabled=None, **qta_kwargs):
|
|
129
|
+
"""
|
|
130
|
+
Get an icon with fallback support for different icon sets.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
icon_name (str): The name of the icon (e.g., 'fa.newspaper-o').
|
|
134
|
+
color (QColor or str or None): Icon color.
|
|
135
|
+
color_disabled (QColor or str or None): Disabled icon color.
|
|
136
|
+
**qta_kwargs: Any additional qtawesome options (options, scale_factor, offset, etc.)
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
QIcon
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
def to_color_str(c):
|
|
143
|
+
if isinstance(c, QColor):
|
|
144
|
+
return c.name()
|
|
145
|
+
return c
|
|
146
|
+
|
|
147
|
+
color_str = to_color_str(color)
|
|
148
|
+
color_disabled_str = to_color_str(color_disabled)
|
|
149
|
+
|
|
150
|
+
def make_icon(name):
|
|
151
|
+
kwargs = dict(qta_kwargs)
|
|
152
|
+
|
|
153
|
+
if color_str is not None:
|
|
154
|
+
kwargs.setdefault("color", color_str)
|
|
155
|
+
if color_disabled_str is not None:
|
|
156
|
+
kwargs.setdefault("color_disabled", color_disabled_str)
|
|
157
|
+
|
|
158
|
+
return qta.icon(name, **kwargs)
|
|
159
|
+
|
|
160
|
+
# Try exact icon
|
|
161
|
+
try:
|
|
162
|
+
return make_icon(icon_name)
|
|
163
|
+
except Exception:
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
# Try common icon family upgrades
|
|
167
|
+
if icon_name.startswith('fa.'):
|
|
168
|
+
base_name = icon_name[3:]
|
|
169
|
+
normalized_base = base_name.replace('-o', '').replace('-alt', '')
|
|
170
|
+
|
|
171
|
+
for prefix in ['fa5s.', 'fa5.', 'fa5b.', 'fa6s.', 'fa6.', 'fa6b.']:
|
|
172
|
+
try:
|
|
173
|
+
return make_icon(prefix + normalized_base)
|
|
174
|
+
except Exception:
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
# Fuzzy fallback
|
|
178
|
+
available_icons = get_available_icons()
|
|
179
|
+
closest_icon = get_closest_icon_name(icon_name, available_icons)
|
|
180
|
+
|
|
181
|
+
if closest_icon is None:
|
|
182
|
+
return make_icon("mdi.help")
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
return make_icon(closest_icon)
|
|
186
|
+
except Exception:
|
|
187
|
+
return make_icon("mdi.help")
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
if __name__ == "__main__":
|
|
192
|
+
from qtpy.QtWidgets import QApplication
|
|
193
|
+
from qtpy.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel
|
|
194
|
+
import sys
|
|
195
|
+
|
|
196
|
+
# Create a QApplication instance
|
|
197
|
+
app = QApplication(sys.argv)
|
|
198
|
+
|
|
199
|
+
if False:
|
|
200
|
+
# Get all available icon names
|
|
201
|
+
# Get the QtAwesome instance and access its icon map
|
|
202
|
+
# Force initialization by creating a dummy icon first
|
|
203
|
+
try:
|
|
204
|
+
qta.icon('fa5s.home')
|
|
205
|
+
except:
|
|
206
|
+
pass
|
|
207
|
+
|
|
208
|
+
# Now try to get the icon names
|
|
209
|
+
icon_names = list(qta._instance().charmap.keys())
|
|
210
|
+
print(icon_names)
|
|
211
|
+
print(f"Total icons: {len(icon_names)}")
|
|
212
|
+
sys.exit(0)
|
|
213
|
+
|
|
214
|
+
# Icons to test (mix of valid, legacy, and invalid)
|
|
215
|
+
test_icons = [
|
|
216
|
+
"fa.newspaper-o",
|
|
217
|
+
"fa.save",
|
|
218
|
+
"fa.folder",
|
|
219
|
+
"fa.file-o",
|
|
220
|
+
"fa.plus",
|
|
221
|
+
"fa.times",
|
|
222
|
+
"fa.trash-o",
|
|
223
|
+
"fa.pencil",
|
|
224
|
+
"fa.cog",
|
|
225
|
+
"fa.search",
|
|
226
|
+
"fa.home",
|
|
227
|
+
"fa.this-does-not-exist", # intentional failure
|
|
228
|
+
"ri.layout-row-line", # intentional failure
|
|
229
|
+
'fa.folder-open-o',
|
|
230
|
+
'fa.check'
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
window = QWidget()
|
|
234
|
+
window.setWindowTitle("qtawesome icon loading test")
|
|
235
|
+
|
|
236
|
+
layout = QVBoxLayout(window)
|
|
237
|
+
|
|
238
|
+
for icon_name in test_icons:
|
|
239
|
+
icon = get_icon(icon_name)
|
|
240
|
+
|
|
241
|
+
label = QLabel(f"{icon_name}")
|
|
242
|
+
label.setPixmap(icon.pixmap(24, 24))
|
|
243
|
+
layout.addWidget(label)
|
|
244
|
+
|
|
245
|
+
print(f"Requested icon: {icon_name}")
|
|
246
|
+
|
|
247
|
+
window.show()
|
|
248
|
+
sys.exit(app.exec_())
|
|
File without changes
|