gui-utilities 1.3.40__tar.gz → 1.4.3__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gui_utilities
3
- Version: 1.3.40
3
+ Version: 1.4.3
4
4
  Summary: Librería de utilidades gráficas en PyQt6
5
5
  Author-email: Guido Iván Gross <grossguidoivan@gmail.com>
6
6
  Requires-Python: >=3.8
@@ -1,6 +1,6 @@
1
1
  from PyQt6.QtCore import Qt, QObject, QEvent, QSize
2
- from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QLineEdit, QComboBox, QDialog, QToolButton, QWidgetAction, QCheckBox, QTableWidget, QHeaderView
3
- from PyQt6.QtGui import QIcon
2
+ from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QLineEdit, QComboBox, QDialog, QToolButton, QWidgetAction, QCheckBox, QTableWidget, QHeaderView, QApplication, QStyle, QStyledItemDelegate, QScrollArea
3
+ from PyQt6.QtGui import QIcon, QTextDocument
4
4
  import re
5
5
  import requests
6
6
  import importlib.resources
@@ -9,8 +9,8 @@ main_dir = importlib.resources.files("gui_utilities")
9
9
  icons_dir = main_dir / "icons"
10
10
  tlds_path = main_dir / "tlds"
11
11
 
12
- def create_window(title, background_color = "#1e1e1e"):
13
- window = QWidget()
12
+ def create_window(title, background_color = "#1e1e1e", parent = None):
13
+ window = QWidget(parent)
14
14
  window.setObjectName("window")
15
15
  window.setWindowTitle(title)
16
16
  window.setStyleSheet(f"#window {{background-color: {background_color};}}")
@@ -33,7 +33,9 @@ def create_menu(
33
33
  title_padding_bottom = None,
34
34
  title_border_width = 0,
35
35
  title_border_color = "#ffffff",
36
- title_border_radius = 0
36
+ title_border_radius = 0,
37
+ math_expression = False,
38
+ title_alignment = Qt.AlignmentFlag.AlignCenter
37
39
  ):
38
40
  main_layout = QVBoxLayout()
39
41
  main_layout.setContentsMargins(25, 25, 25, 25)
@@ -50,7 +52,9 @@ def create_menu(
50
52
  padding_bottom = title_padding_bottom,
51
53
  border_width = title_border_width,
52
54
  border_color = title_border_color,
53
- border_radius = title_border_radius
55
+ border_radius = title_border_radius,
56
+ math_expression = math_expression,
57
+ alignment = title_alignment
54
58
  ))
55
59
  body_layout = QHBoxLayout()
56
60
  main_layout.addLayout(body_layout)
@@ -79,8 +83,18 @@ def create_menu(
79
83
  menu_layout.addWidget(button)
80
84
  ui_instance.content_widget = QWidget()
81
85
  ui_instance.content_widget.setLayout(QVBoxLayout())
82
- body_layout.addWidget(ui_instance.content_widget)
83
86
  ui_instance.content_widget.setStyleSheet("background-color: #333333;")
87
+ scroll_area = QScrollArea()
88
+ scroll_area.setWidgetResizable(True)
89
+ scroll_area.setWidget(ui_instance.content_widget)
90
+ style_sheet = """
91
+ QScrollArea {
92
+ border: none;
93
+ background-color: #333333;
94
+ }
95
+ """
96
+ scroll_area.setStyleSheet(style_sheet)
97
+ body_layout.addWidget(scroll_area)
84
98
  body_layout.setStretch(0, 1)
85
99
  body_layout.setStretch(1, 4)
86
100
  return main_layout
@@ -102,7 +116,9 @@ def create_header(
102
116
  title_padding_bottom = None,
103
117
  title_border_width = 0,
104
118
  title_border_color = "#ffffff",
105
- title_border_radius = 0
119
+ title_border_radius = 0,
120
+ math_expression = False,
121
+ title_alignment = Qt.AlignmentFlag.AlignCenter
106
122
  ):
107
123
  main_layout = QVBoxLayout()
108
124
  main_layout.setContentsMargins(margin_left, margin_top, margin_right, margin_bottom)
@@ -119,7 +135,9 @@ def create_header(
119
135
  padding_bottom = title_padding_bottom,
120
136
  border_width = title_border_width,
121
137
  border_color = title_border_color,
122
- border_radius = title_border_radius
138
+ border_radius = title_border_radius,
139
+ math_expression = math_expression,
140
+ alignment = title_alignment
123
141
  )
124
142
  main_layout.addLayout(title_layout)
125
143
  main_layout.addStretch()
@@ -138,11 +156,12 @@ def create_title(
138
156
  padding_bottom = None,
139
157
  border_width = 0,
140
158
  border_color = "#ffffff",
141
- border_radius = 0
159
+ border_radius = 0,
160
+ math_expression = False,
161
+ alignment = Qt.AlignmentFlag.AlignCenter
142
162
  ):
143
163
  title_layout = QHBoxLayout()
144
164
  title_layout.setContentsMargins(0, 0, 0, 25)
145
- title_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
146
165
  title_label = create_label(
147
166
  text = text,
148
167
  font_family = font_family,
@@ -157,7 +176,9 @@ def create_title(
157
176
  padding_bottom = padding_bottom,
158
177
  border_width = border_width,
159
178
  border_color = border_color,
160
- border_radius = border_radius
179
+ border_radius = border_radius,
180
+ math_expression = math_expression,
181
+ alignment = alignment
161
182
  )
162
183
  title_layout.addWidget(title_label)
163
184
  return title_layout
@@ -184,9 +205,19 @@ def create_label(
184
205
  disabled_background_color = "transparent",
185
206
  disabled_border_width = 0,
186
207
  disabled_border_color = "#4a4a4a",
208
+ math_expression = False,
209
+ transparent_for_mouse = False,
210
+ alignment = None,
211
+ word_wrap = False,
187
212
  parent = None
188
213
  ):
189
- label = QLabel(text, parent)
214
+ if math_expression:
215
+ label = QLabel(format_html(text), parent)
216
+ label.setTextFormat(Qt.TextFormat.RichText)
217
+ else: label = QLabel(text, parent)
218
+ if transparent_for_mouse: label.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, True)
219
+ if alignment: label.setAlignment(alignment)
220
+ if word_wrap: label.setWordWrap(True)
190
221
  padding_left_value = padding_left if padding_left is not None else padding
191
222
  padding_top_value = padding_top if padding_top is not None else padding
192
223
  padding_right_value = padding_right if padding_right is not None else padding
@@ -243,10 +274,12 @@ def create_button(
243
274
  disabled_background_color = "#2d2d2d",
244
275
  disabled_border_width = 2,
245
276
  disabled_border_color = "#4a4a4a",
246
- maximum_width = 360
277
+ maximum_width = 360,
278
+ math_expression = False,
279
+ parent = None
247
280
  ):
248
- button = QPushButton()
249
- button.setMaximumWidth(maximum_width)
281
+ button = QPushButton(parent)
282
+ if maximum_width is not None: button.setMaximumWidth(maximum_width)
250
283
  main_layout = QVBoxLayout()
251
284
  button.setLayout(main_layout)
252
285
  main_layout.setContentsMargins(0, 0, 0, 0)
@@ -260,9 +293,11 @@ def create_button(
260
293
  padding_top = padding_top,
261
294
  padding_right = padding_right,
262
295
  padding_bottom = padding_bottom,
263
- disabled_border_width = disabled_border_width
296
+ disabled_border_width = disabled_border_width,
297
+ transparent_for_mouse = True,
298
+ alignment = Qt.AlignmentFlag.AlignCenter,
299
+ math_expression = math_expression
264
300
  )
265
- label.setAlignment(Qt.AlignmentFlag.AlignCenter)
266
301
  label.setWordWrap(True)
267
302
  main_layout.addWidget(label)
268
303
  button.setFixedHeight(main_layout.sizeHint().height() + 2 * border_width)
@@ -332,9 +367,10 @@ def create_text_box(
332
367
  focused_show_text_icon_url = str(icons_dir / "focused_show_text_icon.png"),
333
368
  focused_hide_text_icon_url = str(icons_dir / "focused_hide_text_icon.png"),
334
369
  hide_text = False,
335
- math_expression = False
370
+ math_expression = False,
371
+ parent = None
336
372
  ):
337
- text_box = QLineEdit()
373
+ text_box = QLineEdit(parent)
338
374
  padding_left_value = padding_left if padding_left is not None else padding
339
375
  padding_top_value = padding_top if padding_top is not None else padding
340
376
  padding_right_value = padding_right if padding_right is not None else padding
@@ -368,53 +404,6 @@ def create_text_box(
368
404
  }}
369
405
  """
370
406
  text_box.setStyleSheet(style_sheet)
371
-
372
- def latex_to_html(expression):
373
- html_expression = expression
374
- html_expression = re.sub(r"\\frac\{(.*?)\}\{(.*?)\}", r"<span style='font-size:90%;'>\1⁄\2</span>", html_expression)
375
- html_expression = re.sub(r"([a-zA-Z])_(\d+)", r"\1<sub>\2</sub>", html_expression)
376
- html_expression = re.sub(r"([a-zA-Z0-9])\^(\d+)", r"\1<sup>\2</sup>", html_expression)
377
- html_expression = html_expression.replace(" ", "&nbsp;")
378
- return html_expression
379
-
380
- if not math_expression: text_box.setPlaceholderText(placeholder_text)
381
- else:
382
- html_placeholder = latex_to_html(placeholder_text)
383
- placeholder_label = create_label(
384
- text = html_placeholder,
385
- font_color = placeholder_font_color,
386
- font_family = font_family,
387
- font_size = font_size,
388
- background_color = "transparent",
389
- disabled_background_color = "transparent",
390
- parent = text_box
391
- )
392
- placeholder_label.setTextFormat(Qt.TextFormat.RichText)
393
- placeholder_label.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, True)
394
- placeholder_label.move(0, -2)
395
- placeholder_label.show()
396
-
397
- class _PlaceholderFocusWatcher(QObject):
398
-
399
- def __init__(self, watched, placeholder_label):
400
- super().__init__(watched)
401
- self._watched = watched
402
- self._placeholder = placeholder_label
403
-
404
- def eventFilter(self, watched, event):
405
- if watched is self._watched and event.type() in (QEvent.Type.FocusIn, QEvent.Type.FocusOut):
406
- self._placeholder.setVisible((self._watched.text() == "") and (not self._watched.hasFocus()))
407
- return super().eventFilter(watched, event)
408
-
409
- def _update_placeholder_visibility():
410
- if placeholder_label is not None:
411
- placeholder_label.setVisible(text_box.text() == "" and not text_box.hasFocus())
412
-
413
- text_box.textChanged.connect(_update_placeholder_visibility)
414
- focus_watcher_placeholder = _PlaceholderFocusWatcher(text_box, placeholder_label)
415
- text_box.installEventFilter(focus_watcher_placeholder)
416
- setattr(text_box, "_placeholder_focus_watcher", focus_watcher_placeholder)
417
- _update_placeholder_visibility()
418
407
  if hide_text:
419
408
  show_text_icon = QIcon(show_text_icon_url)
420
409
  hide_text_icon = QIcon(hide_text_icon_url)
@@ -436,7 +425,7 @@ def create_text_box(
436
425
  margin-right: 10px;
437
426
  }
438
427
  """)
439
-
428
+
440
429
  def update_icon():
441
430
  is_password = text_box.echoMode() == QLineEdit.EchoMode.Password
442
431
  if text_box.hasFocus(): icon = focused_hide_text_icon if is_password else focused_show_text_icon
@@ -451,7 +440,6 @@ def create_text_box(
451
440
  toggle_text_visibility_button.clicked.connect(toggle_visibility)
452
441
 
453
442
  class _IconFocusWatcher(QObject):
454
-
455
443
  def __init__(self, watched, on_focus_change):
456
444
  super().__init__(watched)
457
445
  self._watched = watched
@@ -465,7 +453,45 @@ def create_text_box(
465
453
  icon_focus_watcher = _IconFocusWatcher(text_box, update_icon)
466
454
  text_box.installEventFilter(icon_focus_watcher)
467
455
  setattr(text_box, "_icon_focus_watcher", icon_focus_watcher)
468
- update_icon()
456
+
457
+ placeholder_label = create_label(
458
+ text = placeholder_text,
459
+ font_family = font_family,
460
+ font_size = font_size,
461
+ font_color = placeholder_font_color,
462
+ font_weight = font_weight,
463
+ padding = padding,
464
+ padding_left = padding_left,
465
+ padding_top = padding_top,
466
+ padding_right = padding_right,
467
+ padding_bottom = padding_bottom,
468
+ math_expression = math_expression,
469
+ transparent_for_mouse = True,
470
+ parent = text_box
471
+ )
472
+ placeholder_label.move(0, 2)
473
+
474
+ def update_placeholder_visibility():
475
+ has_text = bool(text_box.text().strip())
476
+ has_focus = text_box.hasFocus()
477
+ placeholder_label.setVisible(not has_text and not has_focus)
478
+ if hide_text: update_icon()
479
+
480
+ class _PlaceholderFocusWatcher(QObject):
481
+ def __init__(self, watched, on_focus_change):
482
+ super().__init__(watched)
483
+ self._watched = watched
484
+ self._on_focus_change = on_focus_change
485
+
486
+ def eventFilter(self, watched, event):
487
+ if watched is self._watched and event.type() in (QEvent.Type.FocusIn, QEvent.Type.FocusOut):
488
+ if callable(self._on_focus_change): self._on_focus_change()
489
+ return super().eventFilter(watched, event)
490
+
491
+ placeholder_focus_watcher = _PlaceholderFocusWatcher(text_box, update_placeholder_visibility)
492
+ text_box.installEventFilter(placeholder_focus_watcher)
493
+ setattr(text_box, "_placeholder_focus_watcher", placeholder_focus_watcher)
494
+ update_placeholder_visibility()
469
495
  return text_box
470
496
 
471
497
  def create_combo_box(
@@ -495,21 +521,108 @@ def create_combo_box(
495
521
  dropdown_background_color = "#1e1e1e",
496
522
  dropdown_selection_background_color = "#0078d7",
497
523
  dropdown_border_width = 1,
498
- dropdown_border_color = "#5c5c5c"
524
+ dropdown_border_color = "#5c5c5c",
525
+ math_expression = False,
526
+ parent = None
499
527
  ):
500
- combo_box = QComboBox()
501
- combo_box.setPlaceholderText(f"{placeholder_text}")
502
- combo_box.addItems(items)
503
- combo_box.setCurrentIndex(-1)
504
-
528
+ combo_box = QComboBox(parent)
529
+ combo_box.setAccessibleName("combo_box")
505
530
  padding_left_value = padding_left if padding_left is not None else padding
506
531
  padding_top_value = padding_top if padding_top is not None else padding
507
532
  padding_right_value = padding_right if padding_right is not None else padding
508
533
  padding_bottom_value = padding_bottom if padding_bottom is not None else padding
534
+ if math_expression:
535
+ placeholder_label = create_label(
536
+ text = placeholder_text,
537
+ font_family = font_family,
538
+ font_size = font_size,
539
+ font_color = placeholder_font_color,
540
+ padding = padding,
541
+ padding_left = padding_left,
542
+ padding_top = padding_top,
543
+ padding_right = padding_right,
544
+ padding_bottom = padding_bottom,
545
+ math_expression = True,
546
+ transparent_for_mouse = True,
547
+ parent = combo_box
548
+ )
549
+ placeholder_label.move(0, 2)
550
+
551
+ def update_placeholder_visibility():
552
+ has_selection = combo_box.currentIndex() != -1
553
+ placeholder_label.setVisible(not has_selection)
554
+
555
+ combo_box.currentIndexChanged.connect(update_placeholder_visibility)
556
+ update_placeholder_visibility()
557
+
558
+ class RichTextDelegate(QStyledItemDelegate):
559
+ def __init__(
560
+ self,
561
+ parent = None,
562
+ font_color = "#ffffff",
563
+ selection_font_color = "#ffffff"
564
+ ):
565
+ super().__init__(parent)
566
+ self.font_color = font_color
567
+ self.selection_font_color = selection_font_color
509
568
 
569
+ def paint(self, painter, option, index):
570
+ if option.state & QStyle.StateFlag.State_Selected: text_color = self.selection_font_color
571
+ else: text_color = self.font_color
572
+ document = QTextDocument()
573
+ html = f"<span style = \"color:{text_color};\">{index.data(Qt.ItemDataRole.DisplayRole)}</span>"
574
+ document.setHtml(html)
575
+ option.text = ""
576
+ QApplication.style().drawControl(QStyle.ControlElement.CE_ItemViewItem, option, painter)
577
+ painter.save()
578
+ painter.translate(option.rect.left(), option.rect.top() + (option.rect.height() - document.size().height()) / 2)
579
+ document.drawContents(painter)
580
+ painter.restore()
581
+
582
+ delegate = RichTextDelegate(
583
+ parent = combo_box,
584
+ font_color = dropdown_font_color,
585
+ selection_font_color = font_color
586
+ )
587
+ combo_box.setItemDelegate(delegate)
588
+ combo_box.clear()
589
+ for item in items: combo_box.addItem(format_html(item))
590
+ combo_box.view().setTextElideMode(Qt.TextElideMode.ElideNone)
591
+ else:
592
+ combo_box.setPlaceholderText(placeholder_text)
593
+ combo_box.addItems(items)
594
+ combo_box.setCurrentIndex(-1)
595
+ if math_expression:
596
+ selected_item_label = create_label(
597
+ text = "",
598
+ font_family = font_family,
599
+ font_size = font_size,
600
+ font_color = font_color,
601
+ padding = padding,
602
+ padding_left = padding_left,
603
+ padding_top = padding_top,
604
+ padding_right = padding_right,
605
+ padding_bottom = padding_bottom,
606
+ math_expression = True,
607
+ transparent_for_mouse = True,
608
+ alignment = Qt.AlignmentFlag.AlignCenter,
609
+ parent = combo_box
610
+ )
611
+ selected_item_label.setVisible(False)
612
+
613
+ def update_selected_item_display(index):
614
+ if index != -1:
615
+ html_text = combo_box.itemText(index)
616
+ selected_item_label.setText(html_text)
617
+ selected_item_label.setVisible(True)
618
+ else:
619
+ selected_item_label.setVisible(False)
620
+
621
+ combo_box.currentIndexChanged.connect(update_selected_item_display)
622
+
510
623
  def get_stylesheet(font_color):
511
624
  return f"""
512
- QComboBox {{
625
+ QComboBox[accessibleName="combo_box"] {{
513
626
  font-family: {font_family};
514
627
  font-size: {font_size}px;
515
628
  color: {font_color};
@@ -521,12 +634,12 @@ def create_combo_box(
521
634
  border: {border_width}px solid {border_color};
522
635
  border-radius: {border_radius}px;
523
636
  }}
524
- QComboBox:hover {{
637
+ QComboBox[accessibleName="combo_box"]:hover {{
525
638
  background-color: {hover_background_color};
526
639
  border: {hover_border_width}px solid {hover_border_color};
527
640
  }}
528
- QComboBox:on {{
529
- color: {on_font_color};
641
+ QComboBox[accessibleName="combo_box"]:on {{
642
+ color: {"transparent" if math_expression else on_font_color};
530
643
  background-color: {on_background_color};
531
644
  border: {on_border_width}px solid {on_border_color};
532
645
  }}
@@ -542,8 +655,12 @@ def create_combo_box(
542
655
  """
543
656
 
544
657
  def change_color(index):
545
- if index == -1: combo_box.setStyleSheet(get_stylesheet(placeholder_font_color))
546
- else: combo_box.setStyleSheet(get_stylesheet(font_color))
658
+ if index == -1:
659
+ text_color = "transparent" if math_expression else placeholder_font_color
660
+ combo_box.setStyleSheet(get_stylesheet(text_color))
661
+ else:
662
+ text_color = "transparent" if math_expression else font_color
663
+ combo_box.setStyleSheet(get_stylesheet(text_color))
547
664
 
548
665
  combo_box.currentIndexChanged.connect(change_color)
549
666
  change_color(-1)
@@ -576,9 +693,10 @@ def create_checkbox(
576
693
  disabled_font_color = "#888888",
577
694
  disabled_background_color = "#2d2d2d",
578
695
  disabled_border_width = 1,
579
- disabled_border_color = "#4a4a4a"
696
+ disabled_border_color = "#4a4a4a",
697
+ parent = None
580
698
  ):
581
- check_box = QCheckBox(text)
699
+ check_box = QCheckBox(text, parent)
582
700
  padding_left_value = padding_left if padding_left is not None else padding
583
701
  padding_top_value = padding_top if padding_top is not None else padding
584
702
  padding_right_value = padding_right if padding_right is not None else padding
@@ -669,9 +787,10 @@ def create_information_message_box(
669
787
  button_disabled_font_color = "#888888",
670
788
  button_disabled_background_color = "#2d2d2d",
671
789
  button_disabled_border_width = 2,
672
- button_disabled_border_color = "#4a4a4a"
790
+ button_disabled_border_color = "#4a4a4a",
791
+ parent = None
673
792
  ):
674
- message_box = QDialog()
793
+ message_box = QDialog(parent)
675
794
  message_box.setObjectName("message_box")
676
795
  message_box.setWindowFlags(Qt.WindowType.FramelessWindowHint)
677
796
  message_box.setFixedWidth(360)
@@ -700,10 +819,10 @@ def create_information_message_box(
700
819
  padding_bottom = label_padding_bottom,
701
820
  border_width = label_border_width,
702
821
  border_color = label_border_color,
703
- border_radius = label_border_radius
822
+ border_radius = label_border_radius,
823
+ alignment = Qt.AlignmentFlag.AlignCenter
704
824
  )
705
825
  main_layout.addWidget(text_label)
706
- text_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
707
826
  text_label.setWordWrap(True)
708
827
  accept_button = create_button(
709
828
  text = button_text,
@@ -778,9 +897,10 @@ def create_confirmation_message_box(
778
897
  button_disabled_font_color = "#888888",
779
898
  button_disabled_background_color = "#2d2d2d",
780
899
  button_disabled_border_width = 2,
781
- button_disabled_border_color = "#4a4a4a"
900
+ button_disabled_border_color = "#4a4a4a",
901
+ parent = None
782
902
  ):
783
- confirm_message_box = QDialog()
903
+ confirm_message_box = QDialog(parent)
784
904
  confirm_message_box.setObjectName("confirm_message_box")
785
905
  confirm_message_box.setWindowFlags(Qt.WindowType.FramelessWindowHint)
786
906
  confirm_message_box.setFixedWidth(360)
@@ -809,10 +929,10 @@ def create_confirmation_message_box(
809
929
  padding_bottom = label_padding_bottom,
810
930
  border_width = label_border_width,
811
931
  border_color = label_border_color,
812
- border_radius = label_border_radius
932
+ border_radius = label_border_radius,
933
+ alignment = Qt.AlignmentFlag.AlignCenter
813
934
  )
814
935
  main_layout.addWidget(text_label)
815
- text_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
816
936
  text_label.setWordWrap(True)
817
937
  buttons_layout = QHBoxLayout()
818
938
  main_layout.addLayout(buttons_layout)
@@ -907,9 +1027,10 @@ def create_list_table(
907
1027
  hover_scrollbar_handle_border_color = "#777777",
908
1028
  pressed_scrollbar_handle_background_color = "#4a4a4a",
909
1029
  pressed_scrollbar_handle_border_width = 3,
910
- pressed_scrollbar_handle_border_color = "#0078d7"
1030
+ pressed_scrollbar_handle_border_color = "#0078d7",
1031
+ parent = None
911
1032
  ):
912
- list_table = QTableWidget()
1033
+ list_table = QTableWidget(parent)
913
1034
  list_table.verticalHeader().setVisible(False)
914
1035
  list_table.setColumnCount(len(column_headers))
915
1036
  list_table.setHorizontalHeaderLabels(column_headers)
@@ -1119,6 +1240,12 @@ def validate_integer(integer, suffix = "El", field = "campo"):
1119
1240
  if pattern.match(unformatted_integer): return None
1120
1241
  return f"No ha ingresado {"un" if suffix == "El" else "una"} {field} {"válido" if suffix == "El" else "válida"}."
1121
1242
 
1243
+ def validate_float(decimal, suffix = "El", field = "campo"):
1244
+ if not decimal or not decimal.strip(): return f"{suffix} {field} no puede dejarse {"vacío" if suffix == "El" else "vacía"}."
1245
+ pattern = re.compile(r"^-?\d{1,3}(?:\.\d{3})*(?:,\d+)?$|^-?\d+(?:,\d+)?$")
1246
+ if not pattern.match(decimal): return None
1247
+ return f"No ha ingresado {"un" if suffix == "El" else "una"} {field} {"válido" if suffix == "El" else "válida"}."
1248
+
1122
1249
  def validate_id(id_str):
1123
1250
  if not id_str or not id_str.strip(): return "El D.N.I. no puede dejarse vacio."
1124
1251
  pattern = re.compile(r"^(?:\d{8}|(?:\d{1,2}\.\d{3}\.\d{3}))$")
@@ -1196,4 +1323,14 @@ def format_date(date):
1196
1323
  day = f"{date.day:02d}"
1197
1324
  month = f"{date.month:02d}"
1198
1325
  year = f"{date.year:,}".replace(",", ".")
1199
- return f"{day}/{month}/{year}"
1326
+ return f"{day}/{month}/{year}"
1327
+
1328
+ def format_html(expression):
1329
+ html_expression = expression
1330
+ html_expression = re.sub(r"\\frac\{(.*?)\}\{(.*?)\}", r"<span style='font-size:90%;'>\1⁄\2</span>", html_expression)
1331
+ html_expression = re.sub(r"_\{([^}]+)\}", r"<sub>\1</sub>", html_expression)
1332
+ html_expression = re.sub(r"\^\{([^}]+)\}", r"<sup>\1</sup>", html_expression)
1333
+ html_expression = re.sub(r"_([a-zA-Z0-9]+)", r"<sub>\1</sub>", html_expression)
1334
+ html_expression = re.sub(r"\^([a-zA-Z0-9]+)", r"<sup>\1</sup>", html_expression)
1335
+ html_expression = html_expression.replace(" ", "&nbsp;")
1336
+ return html_expression
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gui_utilities
3
- Version: 1.3.40
3
+ Version: 1.4.3
4
4
  Summary: Librería de utilidades gráficas en PyQt6
5
5
  Author-email: Guido Iván Gross <grossguidoivan@gmail.com>
6
6
  Requires-Python: >=3.8
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gui_utilities"
3
- version = "1.3.40"
3
+ version = "1.4.3"
4
4
  description = "Librería de utilidades gráficas en PyQt6"
5
5
  authors = [{name = "Guido Iván Gross", email = "grossguidoivan@gmail.com"}]
6
6
  requires-python = ">=3.8"
File without changes
File without changes
File without changes