gui-utilities 1.3.42__tar.gz → 1.4.2__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.

Potentially problematic release.


This version of gui-utilities might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gui_utilities
3
- Version: 1.3.42
3
+ Version: 1.4.2
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
3
+ from PyQt6.QtGui import QIcon, QTextDocument
4
4
  import re
5
5
  import requests
6
6
  import importlib.resources
@@ -9,6 +9,16 @@ 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 latex_to_html(expression):
13
+ html_expression = expression
14
+ html_expression = re.sub(r"\\frac\{(.*?)\}\{(.*?)\}", r"<span style='font-size:90%;'>\1⁄\2</span>", html_expression)
15
+ html_expression = re.sub(r"_\{([^}]+)\}", r"<sub>\1</sub>", html_expression)
16
+ html_expression = re.sub(r"\^\{([^}]+)\}", r"<sup>\1</sup>", html_expression)
17
+ html_expression = re.sub(r"_([a-zA-Z0-9]+)", r"<sub>\1</sub>", html_expression)
18
+ html_expression = re.sub(r"\^([a-zA-Z0-9]+)", r"<sup>\1</sup>", html_expression)
19
+ html_expression = html_expression.replace(" ", "&nbsp;")
20
+ return html_expression
21
+
12
22
  def create_window(title, background_color = "#1e1e1e", parent = None):
13
23
  window = QWidget(parent)
14
24
  window.setObjectName("window")
@@ -201,16 +211,6 @@ def create_label(
201
211
  word_wrap = False,
202
212
  parent = None
203
213
  ):
204
- def latex_to_html(expression):
205
- html_expression = expression
206
- html_expression = re.sub(r"\\frac\{(.*?)\}\{(.*?)\}", r"<span style='font-size:90%;'>\1⁄\2</span>", html_expression)
207
- html_expression = re.sub(r"_\{([^}]+)\}", r"<sub>\1</sub>", html_expression)
208
- html_expression = re.sub(r"\^\{([^}]+)\}", r"<sup>\1</sup>", html_expression)
209
- html_expression = re.sub(r"_([a-zA-Z0-9]+)", r"<sub>\1</sub>", html_expression)
210
- html_expression = re.sub(r"\^([a-zA-Z0-9]+)", r"<sup>\1</sup>", html_expression)
211
- html_expression = html_expression.replace(" ", "&nbsp;")
212
- return html_expression
213
-
214
214
  if math_expression:
215
215
  label = QLabel(latex_to_html(text), parent)
216
216
  label.setTextFormat(Qt.TextFormat.RichText)
@@ -367,6 +367,7 @@ def create_text_box(
367
367
  focused_show_text_icon_url = str(icons_dir / "focused_show_text_icon.png"),
368
368
  focused_hide_text_icon_url = str(icons_dir / "focused_hide_text_icon.png"),
369
369
  hide_text = False,
370
+ math_expression = False,
370
371
  parent = None
371
372
  ):
372
373
  text_box = QLineEdit(parent)
@@ -403,7 +404,6 @@ def create_text_box(
403
404
  }}
404
405
  """
405
406
  text_box.setStyleSheet(style_sheet)
406
- text_box.setPlaceholderText(placeholder_text)
407
407
  if hide_text:
408
408
  show_text_icon = QIcon(show_text_icon_url)
409
409
  hide_text_icon = QIcon(hide_text_icon_url)
@@ -425,7 +425,7 @@ def create_text_box(
425
425
  margin-right: 10px;
426
426
  }
427
427
  """)
428
-
428
+
429
429
  def update_icon():
430
430
  is_password = text_box.echoMode() == QLineEdit.EchoMode.Password
431
431
  if text_box.hasFocus(): icon = focused_hide_text_icon if is_password else focused_show_text_icon
@@ -440,7 +440,6 @@ def create_text_box(
440
440
  toggle_text_visibility_button.clicked.connect(toggle_visibility)
441
441
 
442
442
  class _IconFocusWatcher(QObject):
443
-
444
443
  def __init__(self, watched, on_focus_change):
445
444
  super().__init__(watched)
446
445
  self._watched = watched
@@ -454,7 +453,45 @@ def create_text_box(
454
453
  icon_focus_watcher = _IconFocusWatcher(text_box, update_icon)
455
454
  text_box.installEventFilter(icon_focus_watcher)
456
455
  setattr(text_box, "_icon_focus_watcher", icon_focus_watcher)
457
- 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()
458
495
  return text_box
459
496
 
460
497
  def create_combo_box(
@@ -485,21 +522,107 @@ def create_combo_box(
485
522
  dropdown_selection_background_color = "#0078d7",
486
523
  dropdown_border_width = 1,
487
524
  dropdown_border_color = "#5c5c5c",
525
+ math_expression = False,
488
526
  parent = None
489
527
  ):
490
528
  combo_box = QComboBox(parent)
491
- combo_box.setPlaceholderText(f"{placeholder_text}")
492
- combo_box.addItems(items)
493
- combo_box.setCurrentIndex(-1)
494
-
529
+ combo_box.setAccessibleName("combo_box")
495
530
  padding_left_value = padding_left if padding_left is not None else padding
496
531
  padding_top_value = padding_top if padding_top is not None else padding
497
532
  padding_right_value = padding_right if padding_right is not None else padding
498
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
499
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(latex_to_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
+
500
623
  def get_stylesheet(font_color):
501
624
  return f"""
502
- QComboBox {{
625
+ QComboBox[accessibleName="combo_box"] {{
503
626
  font-family: {font_family};
504
627
  font-size: {font_size}px;
505
628
  color: {font_color};
@@ -511,12 +634,12 @@ def create_combo_box(
511
634
  border: {border_width}px solid {border_color};
512
635
  border-radius: {border_radius}px;
513
636
  }}
514
- QComboBox:hover {{
637
+ QComboBox[accessibleName="combo_box"]:hover {{
515
638
  background-color: {hover_background_color};
516
639
  border: {hover_border_width}px solid {hover_border_color};
517
640
  }}
518
- QComboBox:on {{
519
- color: {on_font_color};
641
+ QComboBox[accessibleName="combo_box"]:on {{
642
+ color: {"transparent" if math_expression else on_font_color};
520
643
  background-color: {on_background_color};
521
644
  border: {on_border_width}px solid {on_border_color};
522
645
  }}
@@ -532,8 +655,12 @@ def create_combo_box(
532
655
  """
533
656
 
534
657
  def change_color(index):
535
- if index == -1: combo_box.setStyleSheet(get_stylesheet(placeholder_font_color))
536
- 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))
537
664
 
538
665
  combo_box.currentIndexChanged.connect(change_color)
539
666
  change_color(-1)
@@ -1113,6 +1240,12 @@ def validate_integer(integer, suffix = "El", field = "campo"):
1113
1240
  if pattern.match(unformatted_integer): return None
1114
1241
  return f"No ha ingresado {"un" if suffix == "El" else "una"} {field} {"válido" if suffix == "El" else "válida"}."
1115
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 f"No ha ingresado {"un" if suffix == "El" else "una"} {field} {"válido" if suffix == "El" else "válida"}."
1247
+ return None
1248
+
1116
1249
  def validate_id(id_str):
1117
1250
  if not id_str or not id_str.strip(): return "El D.N.I. no puede dejarse vacio."
1118
1251
  pattern = re.compile(r"^(?:\d{8}|(?:\d{1,2}\.\d{3}\.\d{3}))$")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gui_utilities
3
- Version: 1.3.42
3
+ Version: 1.4.2
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.42"
3
+ version = "1.4.2"
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