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,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
@@ -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