jbqt 0.1.1__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.

Potentially problematic release.


This version of jbqt might be problematic. Click here for more details.

jbqt/__init__.py ADDED
File without changes
@@ -0,0 +1,6 @@
1
+ """Const exports"""
2
+
3
+ from jbqt.common.consts import COLORS, STYLES, SIZES, GlobalRefs, ICONS
4
+ from jbqt.common.qt_utils import register_app
5
+
6
+ __all__ = ["COLORS", "STYLES", "SIZES", "GlobalRefs", "ICONS", "register_app"]
jbqt/common/consts.py ADDED
@@ -0,0 +1,167 @@
1
+ import os
2
+ import re
3
+
4
+ import jb_utils
5
+
6
+ from PyQt6.QtWidgets import QApplication, QMainWindow, QScrollArea, QWidget
7
+ from PyQt6.QtCore import Qt, QSize
8
+ from PyQt6.QtGui import QIcon, QPixmap, QPainter, QColor
9
+
10
+ TAG_RE = re.compile(r"^\((.+)\)(\d+\.\d+)$")
11
+ LIST_ITEM_ROLE = Qt.ItemDataRole.UserRole - 1
12
+
13
+ STYLES = jb_utils.STYLES
14
+ COLORS = jb_utils.COLORS
15
+
16
+
17
+ class GlobalRefs:
18
+ app: QApplication = None
19
+ main_window: QMainWindow = None
20
+ scroll_area: QScrollArea = None
21
+ server_active: bool = False
22
+ debug_set: bool = False
23
+ seed_widget: QWidget = None
24
+
25
+
26
+ class QtPaths:
27
+ icon_dir: str = None
28
+
29
+
30
+ THEME_ICONS: dict[str, QIcon] = {
31
+ "save": QIcon.fromTheme("media-floppy-symbolic"),
32
+ "new_file": QIcon.fromTheme("document-new-symbolic"),
33
+ "open": QIcon.fromTheme("folder-open-symbolic"),
34
+ "copy": QIcon.fromTheme("edit-paste-symbolic"),
35
+ "inspect": QIcon.fromTheme("docviewer-app-symbolic"),
36
+ "clone": QIcon.fromTheme("edit-copy-symbolic"),
37
+ "plus": QIcon.fromTheme("list-add-symbolic"),
38
+ "minus": QIcon.fromTheme("list-remove-symbolic"),
39
+ "times": QIcon.fromTheme("cancel-operation-symbolic"),
40
+ "refresh": QIcon.fromTheme("view-refresh-symbolic"),
41
+ "reload": QIcon.fromTheme("update-symbolic"),
42
+ "circle_check": QIcon.fromTheme("selection-checked"),
43
+ "circle_x": QIcon.fromTheme("application-exit"),
44
+ "code": QIcon.fromTheme("input-tablet-symbolic"),
45
+ "font-selection-editor": QIcon.fromTheme("font-select-symbolic"),
46
+ "trash": QIcon.fromTheme("trash-symbolic"),
47
+ "edit_data": QIcon.fromTheme("document-edit-symbolic"),
48
+ "preview": QIcon.fromTheme("view-layout-symbolic"),
49
+ "sync": QIcon.fromTheme("mail-send-receive-symbolic"),
50
+ }
51
+ STD_ICONS: dict[str, QIcon] = {
52
+ "save": QIcon.fromTheme(QIcon.ThemeIcon.DocumentSave),
53
+ "new_file": QIcon.fromTheme(QIcon.ThemeIcon.DocumentNew),
54
+ "open": QIcon.fromTheme(QIcon.ThemeIcon.FolderOpen),
55
+ "copy": QIcon.fromTheme(QIcon.ThemeIcon.EditPaste),
56
+ "inspect": QIcon.fromTheme(QIcon.ThemeIcon.DocumentPageSetup),
57
+ "clone": QIcon.fromTheme(QIcon.ThemeIcon.EditCopy),
58
+ "plus": QIcon.fromTheme(QIcon.ThemeIcon.ListAdd),
59
+ "minus": QIcon.fromTheme(QIcon.ThemeIcon.DialogError),
60
+ "times": QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit),
61
+ "refresh": QIcon.fromTheme(QIcon.ThemeIcon.SystemReboot),
62
+ "reload": QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaylistRepeat),
63
+ "circle_check": QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaybackStart),
64
+ "circle_x": QIcon.fromTheme(QIcon.ThemeIcon.ProcessStop),
65
+ "code": QIcon.fromTheme(QIcon.ThemeIcon.Computer),
66
+ "font-selection-editor": QIcon.fromTheme(QIcon.ThemeIcon.EditFind),
67
+ "trash": QIcon.fromTheme(QIcon.ThemeIcon.EditDelete),
68
+ "edit_data": QIcon.fromTheme(QIcon.ThemeIcon.InputTablet),
69
+ "preview": QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn),
70
+ "sync": QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaylistShuffle),
71
+ }
72
+
73
+
74
+ def set_icon_dir(path: str) -> None:
75
+ QtPaths.icon_dir = path
76
+
77
+
78
+ class SIZES:
79
+ ICON_XS = QSize(16, 16)
80
+ ICON_SM = QSize(20, 20)
81
+ ICON_MD = QSize(22, 22)
82
+ ICON_LG = QSize(24, 24)
83
+ ICON_XL = QSize(32, 32)
84
+ ICON_XXL = QSize(48, 48)
85
+
86
+
87
+ def icon_dir(file_name: str) -> str:
88
+ if QtPaths.icon_dir:
89
+ return os.path.join(QtPaths.icon_dir, file_name)
90
+ return file_name
91
+
92
+
93
+ def recolor_icon(
94
+ image_path: str, color: str | QColor, scale: QSize = None
95
+ ) -> QIcon:
96
+ if not color:
97
+ color = QColor("black")
98
+ if isinstance(color, str):
99
+ color = QColor(color)
100
+
101
+ if not scale:
102
+ scale = SIZES.ICON_MD
103
+ pixmap = QPixmap(image_path)
104
+ icon_key = os.path.splitext(os.path.basename(image_path))[0]
105
+
106
+ if pixmap.isNull():
107
+ fallback = THEME_ICONS.get(icon_key, STD_ICONS.get(icon_key))
108
+ if fallback:
109
+ pixmap = fallback.pixmap()
110
+
111
+ recolored_pixmap = QPixmap(pixmap.size())
112
+
113
+ recolored_pixmap.fill(
114
+ QColor("transparent")
115
+ ) # Ensure the background is transparent
116
+ painter = QPainter(recolored_pixmap)
117
+ painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Source)
118
+
119
+ # Draw the original pixmap
120
+ painter.drawPixmap(0, 0, pixmap)
121
+
122
+ # Apply the desired color
123
+ painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_SourceIn)
124
+ painter.fillRect(pixmap.rect(), color)
125
+
126
+ painter.end()
127
+ return QIcon(recolored_pixmap.scaled(scale, Qt.AspectRatioMode.KeepAspectRatio))
128
+
129
+
130
+ def get_icon(file_name: str) -> QIcon:
131
+ def getter(color: str = "", size: QSize | int = None):
132
+ if isinstance(size, int):
133
+ size = QSize(size, size)
134
+
135
+ # return QIcon(QPixmap(_icon_dir(file_name)))
136
+ return recolor_icon(icon_dir(file_name), color, size)
137
+
138
+ return getter
139
+
140
+
141
+ class ICONS:
142
+ SAVE = get_icon("save.svg")
143
+ NEW = get_icon("new_file.svg")
144
+ OPEN = get_icon("open.png")
145
+ COPY = get_icon("copy.png")
146
+ INSPECT = get_icon("inspect.svg")
147
+ CLONE = get_icon("clone.svg")
148
+ PLUS = get_icon("plus.png")
149
+ MINUS = get_icon("minus.png")
150
+ TIMES = get_icon("times.svg")
151
+ REFRESH = get_icon("refresh.svg")
152
+ RELOAD = get_icon("reload.png")
153
+ CIRCLE_CHECK = get_icon("circle_check.svg")
154
+ CIRCLE_TIMES = get_icon("circle_x.svg")
155
+ CODE = get_icon("code.png")
156
+ EDIT = get_icon("font-selection-editor.png")
157
+ TRASH = get_icon("trash.png")
158
+ EDIT_DATA = get_icon("edit_data.svg")
159
+ PREVIEW = get_icon("preview.png")
160
+ SYNC = get_icon("sync.png")
161
+
162
+ @classmethod
163
+ def get(cls, name: str, default: str = "") -> str:
164
+ if hasattr(cls, name):
165
+ return getattr(cls, name)
166
+ else:
167
+ return default
@@ -0,0 +1,81 @@
1
+ from datetime import date
2
+ from typing import Any
3
+
4
+ from PyQt6.QtCore import Qt, QDate
5
+ from PyQt6.QtWidgets import (
6
+ QListWidgetItem,
7
+ QCalendarWidget,
8
+ QApplication,
9
+ QWidget,
10
+ QLineEdit,
11
+ QTextEdit,
12
+ QSpinBox,
13
+ QCheckBox,
14
+ QDoubleSpinBox,
15
+ )
16
+
17
+ from jbqt.common import consts
18
+ from jbqt.models import IChipsWidget
19
+
20
+
21
+ def get_item_value(item: QListWidgetItem) -> str:
22
+ value = item.data(consts.LIST_ITEM_ROLE) or item.text()
23
+ return value.strip()
24
+
25
+
26
+ def register_app(app: QApplication, icon_dir: str = "") -> None:
27
+ consts.GlobalRefs.app = app
28
+ consts.set_icon_dir(icon_dir)
29
+
30
+
31
+ def get_widget_value(widget: QWidget, key: str = "") -> Any:
32
+
33
+ if isinstance(widget, QLineEdit):
34
+ return widget.text()
35
+ if isinstance(widget, QTextEdit):
36
+ return widget.toPlainText().strip()
37
+ if isinstance(widget, (QSpinBox, QDoubleSpinBox)):
38
+ return widget.value()
39
+ if isinstance(widget, IChipsWidget):
40
+ return widget.values
41
+ if isinstance(widget, QCheckBox):
42
+ return widget.checkState == Qt.CheckState.Checked
43
+ if isinstance(widget, QCalendarWidget):
44
+ return widget.selectedDate().toString()
45
+
46
+ print(f"No handler defined for {key} of type `{type(widget)}`")
47
+
48
+
49
+ def set_widget_value(widget: QWidget, value: Any) -> None:
50
+ if isinstance(widget, (QLineEdit, QTextEdit)):
51
+ widget.setText(str(value))
52
+ elif isinstance(widget, QSpinBox):
53
+ try:
54
+ widget.setValue(int(value))
55
+ except Exception as e:
56
+ print(e)
57
+ print(type(e))
58
+
59
+ elif isinstance(widget, QDoubleSpinBox):
60
+ try:
61
+ widget.setValue(float(value))
62
+ except Exception as e:
63
+ print(e)
64
+ print(type(e))
65
+
66
+ elif isinstance(widget, IChipsWidget) and isinstance(value, list):
67
+ widget.add_chips(value)
68
+ elif isinstance(widget, QCheckBox):
69
+ state = Qt.CheckState.Unchecked
70
+ if isinstance(value, Qt.CheckState):
71
+ state = value
72
+ elif value is True:
73
+ state = Qt.CheckState.Checked
74
+ elif value is False:
75
+ state = Qt.CheckState.Unchecked
76
+
77
+ widget.setCheckState(state)
78
+
79
+ elif isinstance(widget, QCalendarWidget):
80
+ if isinstance(value, (date, QDate)):
81
+ widget.selectedDate(value)
@@ -0,0 +1,7 @@
1
+ """jbqt Dialog exports"""
2
+
3
+ from jbqt.dialogs.input_form import InputDialog
4
+ from jbqt.dialogs.text_preview import TextPreviewDialog
5
+ from jbqt.dialogs.file_dialog import JbFileDialog
6
+
7
+ __all__ = ["InputDialog", "TextPreviewDialog", "JbFileDialog"]
@@ -0,0 +1,27 @@
1
+ from PyQt6.QtCore import pyqtSignal
2
+ from PyQt6.QtWidgets import QWidget, QPushButton, QVBoxLayout, QFileDialog
3
+
4
+
5
+ class JbFileDialog(QWidget):
6
+ selectedFile = pyqtSignal(str)
7
+
8
+ def __init__(self, title: str = "File Dialog", directory: str = "") -> None:
9
+ """Initialize the main window with a button to open a file dialog."""
10
+ super().__init__()
11
+ self.setWindowTitle("QFileDialog Example")
12
+ self.directory = directory
13
+
14
+ self.button = QPushButton("Open File", self)
15
+ self.button.clicked.connect(self.open_file_dialog)
16
+
17
+ layout = QVBoxLayout()
18
+ layout.addWidget(self.button)
19
+ self.setLayout(layout)
20
+
21
+ def open_file_dialog(self) -> None:
22
+ """Open a file dialog to select a file."""
23
+ file_name, _ = QFileDialog.getOpenFileName(
24
+ self, "Open File", self.directory, "All Files (*);;Text Files (*.txt)"
25
+ )
26
+ if file_name:
27
+ self.selectedFile.emit(file_name)
@@ -0,0 +1,85 @@
1
+ from typing import Any, Callable
2
+
3
+ from PyQt6.QtCore import pyqtSignal
4
+ from PyQt6.QtWidgets import (
5
+ QDialog,
6
+ QDialogButtonBox,
7
+ QDoubleSpinBox,
8
+ QLabel,
9
+ QLineEdit,
10
+ QSpinBox,
11
+ QVBoxLayout,
12
+ )
13
+
14
+
15
+ INPUT_SIGNAL_TYPES: tuple[Any] = (str, int, float)
16
+
17
+
18
+ class InputDialog(QDialog):
19
+ signal_types: tuple[Any] = INPUT_SIGNAL_TYPES
20
+ """ reference value for external use """
21
+
22
+ value = pyqtSignal(*([ptype] for ptype in INPUT_SIGNAL_TYPES))
23
+
24
+ def __init__(
25
+ self,
26
+ parent=None,
27
+ title: str = "Input",
28
+ msg_str: str = "Enter Input:",
29
+ input_type: type = str,
30
+ **opts,
31
+ ):
32
+ super().__init__(parent)
33
+
34
+ self.setWindowTitle(title)
35
+
36
+ QBtn = (
37
+ QDialogButtonBox.StandardButton.Cancel
38
+ | QDialogButtonBox.StandardButton.Open
39
+ )
40
+
41
+ self.buttonBox = QDialogButtonBox(QBtn)
42
+ self.buttonBox.accepted.connect(self.accept)
43
+ self.buttonBox.rejected.connect(self.reject)
44
+
45
+ layout = QVBoxLayout()
46
+ message = QLabel(msg_str)
47
+ layout.addWidget(message)
48
+
49
+ self.input_widget = None
50
+ if input_type is str:
51
+ self.input_widget = QLineEdit()
52
+ elif input_type is int:
53
+ self.input_widget = QSpinBox()
54
+ elif input_type is float:
55
+ self.input_widget = QDoubleSpinBox()
56
+
57
+ self._init_widget(**opts)
58
+
59
+ layout.addWidget(self.input_widget)
60
+ layout.addWidget(self.buttonBox)
61
+ self.setLayout(layout)
62
+
63
+ def _init_widget(self, **opts) -> None:
64
+ for key, value in opts.items():
65
+ setter_key = f"set{key[0].upper() + key[1:]}"
66
+ if hasattr(self.input_widget, setter_key):
67
+ setter = getattr(self.input_widget, setter_key)
68
+ if setter:
69
+ setter(value)
70
+
71
+ def connect(self, callback: Callable) -> None:
72
+ for parm_type in self.signal_types:
73
+ self.value[parm_type].connect(callback)
74
+
75
+ def accept(self):
76
+ # Emit the custom signal with data when the dialog is accepted
77
+
78
+ if isinstance(self.input_widget, QLineEdit):
79
+ self.value[str].emit(self.input_widget.text())
80
+ elif isinstance(self.input_widget, QSpinBox):
81
+ self.value[int].emit(self.input_widget.value())
82
+ elif isinstance(self.input_widget, QDoubleSpinBox):
83
+ self.value[float].emit(self.input_widget.value())
84
+
85
+ super().accept()
@@ -0,0 +1,42 @@
1
+ from PyQt6.QtGui import QFontMetrics
2
+ from PyQt6.QtWidgets import (
3
+ QDialog,
4
+ QVBoxLayout,
5
+ QTextEdit,
6
+ QPushButton,
7
+ )
8
+
9
+
10
+ class TextPreviewDialog(QDialog):
11
+ def __init__(self, text: str, title: str = "", parent=None):
12
+ super().__init__(parent)
13
+
14
+ self.setWindowTitle("Text Preview")
15
+ if title:
16
+ self.setWindowTitle(f"Text Preview [{title}]")
17
+
18
+ layout = QVBoxLayout(self)
19
+
20
+ # QTextEdit to display the text
21
+ self.text_edit = QTextEdit(self)
22
+ self.text_edit.setPlainText(text)
23
+ self.text_edit.setReadOnly(True)
24
+ layout.addWidget(self.text_edit)
25
+
26
+ # Close button
27
+ close_button = QPushButton("Close", self)
28
+ close_button.clicked.connect(self.accept)
29
+ layout.addWidget(close_button)
30
+
31
+ # Adjust the dialog size based on the text
32
+ self.adjust_size_based_on_text(text)
33
+
34
+ def adjust_size_based_on_text(self, text):
35
+ # Calculate text size
36
+ font_metrics = QFontMetrics(self.text_edit.font())
37
+ text_size = font_metrics.size(0, text)
38
+
39
+ # Adjust size with some padding
40
+ h_padding = 20
41
+ v_padding = 100
42
+ self.resize(text_size.width() + h_padding, text_size.height() + v_padding)
@@ -0,0 +1,6 @@
1
+ """Model exports"""
2
+
3
+ from jbqt.models.chips import IChipsWidget
4
+ from jbqt.models.chip_button import IChipButton
5
+
6
+ __all__ = ["IChipButton", "IChipsWidget"]
@@ -0,0 +1,13 @@
1
+ """Model for chips widget"""
2
+
3
+ from abc import abstractmethod, ABC
4
+
5
+ from PyQt6.QtCore import QObject
6
+ from PyQt6.QtWidgets import QWidget
7
+
8
+
9
+ class IChipButton(QWidget):
10
+ """ChipButton model"""
11
+
12
+ def __init__(self, parent: QObject = None) -> None:
13
+ super().__init__(parent)
jbqt/models/chips.py ADDED
@@ -0,0 +1,31 @@
1
+ """Model for chips widget"""
2
+
3
+ from abc import abstractmethod, ABC
4
+
5
+ from PyQt6.QtCore import QObject
6
+ from PyQt6.QtWidgets import QWidget
7
+
8
+ from jbqt.models.chip_button import IChipButton
9
+
10
+
11
+ class IChipsWidget(QWidget):
12
+ """ChipsWidget model"""
13
+
14
+ def __init__(self, parent: QObject = None) -> None:
15
+ super().__init__(parent)
16
+ self.values: list = None
17
+
18
+ def add_chip(self):
19
+ pass
20
+
21
+ def remove_chip(self, button: IChipButton):
22
+ pass
23
+
24
+ def add_chips(self, items: list[str]) -> None:
25
+ pass
26
+
27
+ def remove_chips(self, items: list[str]) -> None:
28
+ pass
29
+
30
+ def remove_all(self, *_) -> None:
31
+ pass
jbqt/view_icons.py ADDED
@@ -0,0 +1,208 @@
1
+ import argparse
2
+ import os
3
+ import sys
4
+
5
+ from PyQt6.QtCore import QSize, Qt, QTimer
6
+ from PyQt6.QtGui import QIcon
7
+ from PyQt6.QtWidgets import (
8
+ QApplication,
9
+ QGridLayout,
10
+ QLabel,
11
+ QLineEdit,
12
+ QMainWindow,
13
+ QPushButton,
14
+ QScrollArea,
15
+ QStyle,
16
+ QTableWidget,
17
+ QTableWidgetItem,
18
+ QVBoxLayout,
19
+ QWidget,
20
+ )
21
+
22
+ from fuzzywuzzy import process # Needs fuzzywuzzy + python-Levenshtein
23
+
24
+ parser = argparse.ArgumentParser(description="Simple tool to display icons")
25
+ parser.add_argument(
26
+ "--from-theme",
27
+ "-f",
28
+ action="store_true",
29
+ help="Search and display all icons from the Ubuntu themes as opposed to the StandardIcon library in Qt6",
30
+ )
31
+ parser.add_argument(
32
+ "--theme-icons",
33
+ "-t",
34
+ action="store_true",
35
+ help="Pull icons from the QIcon.ThemeIcon list",
36
+ )
37
+ parser.add_argument(
38
+ "--add-quotes",
39
+ "-q",
40
+ action="store_true",
41
+ help="Wrap copied icon name with quotes",
42
+ )
43
+ args = parser.parse_args()
44
+
45
+ THEMES_DIR = "/usr/share/icons"
46
+
47
+
48
+ class Toast(QLabel):
49
+ def __init__(self, text: str, parent: QWidget = None, duration_ms: int = 1500):
50
+ super().__init__(parent)
51
+
52
+ self.setText(text)
53
+ self.setAlignment(Qt.AlignmentFlag.AlignCenter)
54
+ self.setStyleSheet(
55
+ """
56
+ background-color: rgba(50, 50, 50, 180);
57
+ color: white;
58
+ border-radius: 8px;
59
+ padding: 8px 16px;
60
+ font-size: 14px;
61
+ """
62
+ )
63
+ self.setWindowFlags(Qt.WindowType.ToolTip)
64
+ self.adjustSize()
65
+
66
+ # Center it over parent if available
67
+ if parent:
68
+ parent_rect = parent.rect()
69
+ toast_rect = self.rect()
70
+ self.move(
71
+ parent_rect.center().x() - toast_rect.width() // 2,
72
+ parent_rect.top() + 50, # appear slightly under the top
73
+ )
74
+
75
+ QTimer.singleShot(duration_ms, self.close)
76
+
77
+
78
+ def get_theme_icons(theme_dir: str) -> list[str]:
79
+ icon_names = set()
80
+ for root, _, files in os.walk(theme_dir):
81
+ for file in files:
82
+ if file.endswith((".svg", ".png")):
83
+ rel_path = os.path.relpath(os.path.join(root, file), theme_dir)
84
+ parts = rel_path.split(os.sep)
85
+ if len(parts) >= 2:
86
+ icon_name = os.path.splitext(parts[-1])[0]
87
+ icon_names.add(icon_name)
88
+ return sorted(icon_names)
89
+
90
+
91
+ class Window(QMainWindow):
92
+ def __init__(self):
93
+ super().__init__()
94
+
95
+ self.setGeometry(1000, 400, 1600, 800)
96
+
97
+ central_widget = QWidget()
98
+ self.setCentralWidget(central_widget)
99
+
100
+ self.layout = QVBoxLayout()
101
+ central_widget.setLayout(self.layout)
102
+
103
+ self.search_bar = QLineEdit()
104
+ self.search_bar.setPlaceholderText("Search icons...")
105
+ self.search_bar.textChanged.connect(self.update_display)
106
+
107
+ self.table = QTableWidget()
108
+ self.table.setColumnCount(4) # 4 columns of icons
109
+ self.table.horizontalHeader().setVisible(False)
110
+ self.table.verticalHeader().setVisible(False)
111
+ self.table.setIconSize(QSize(64, 64))
112
+ self.table.setShowGrid(False)
113
+ self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
114
+ self.table.setSelectionMode(QTableWidget.SelectionMode.NoSelection)
115
+ self.table.setFocusPolicy(Qt.FocusPolicy.NoFocus)
116
+ self.table.cellClicked.connect(self.copy_icon_name_to_clipboard)
117
+ self.table.setStyleSheet(
118
+ """
119
+ QTableWidget::item:hover {
120
+ background-color: #444444;
121
+ }
122
+ """
123
+ )
124
+
125
+ self.layout.addWidget(self.search_bar)
126
+ self.layout.addWidget(self.table)
127
+
128
+ cur_theme = QIcon.themeName()
129
+ self.icon_src = "standard"
130
+ if args.from_theme:
131
+ self.icon_src = "from_theme"
132
+ elif args.theme_icons:
133
+ self.icon_src = "theme_icons"
134
+
135
+ match self.icon_src:
136
+ case "from_theme":
137
+ self.icons = get_theme_icons(os.path.join(THEMES_DIR, cur_theme))
138
+ case "theme_icons":
139
+ self.icons = [
140
+ i for i in dir(QIcon.ThemeIcon) if not i.startswith("_")
141
+ ]
142
+ case _:
143
+ self.icons = sorted(
144
+ [
145
+ attr
146
+ for attr in dir(QStyle.StandardPixmap)
147
+ if attr.startswith("SP_")
148
+ ]
149
+ )
150
+
151
+ self.update_display()
152
+
153
+ def copy_icon_name_to_clipboard(self, row: int, column: int) -> None:
154
+ item = self.table.item(row, column)
155
+ if item and item.text():
156
+ text = f'"{item.text()}"' if args.add_quotes else item.text()
157
+ QApplication.clipboard().setText(text)
158
+ print(f"Copied '{item.text()}' to clipboard")
159
+ Toast(f"Copied '{item.text()}'", parent=self).show()
160
+
161
+ def update_display(self):
162
+ text = self.search_bar.text().strip()
163
+
164
+ if text:
165
+ # Use fuzzy matching
166
+ matches = process.extract(
167
+ text,
168
+ self.icons,
169
+ limit=200, # Don't explode the table even if 2400+ icons exist
170
+ )
171
+ filtered_icons = [match[0] for match in matches if match[1] >= 50]
172
+ else:
173
+ filtered_icons = self.icons
174
+
175
+ self.table.setRowCount((len(filtered_icons) + 3) // 4)
176
+
177
+ for i in range(self.table.rowCount()):
178
+ for j in range(4):
179
+ idx = i * 4 + j
180
+ if idx >= len(filtered_icons):
181
+ self.table.setItem(i, j, QTableWidgetItem())
182
+ continue
183
+
184
+ name = filtered_icons[idx]
185
+
186
+ match self.icon_src:
187
+ case "from_theme":
188
+ icon = QIcon.fromTheme(name)
189
+ case "theme_icons":
190
+ icon = QIcon.fromTheme(getattr(QIcon.ThemeIcon, name))
191
+ case _:
192
+
193
+ pixmapi = getattr(QStyle.StandardPixmap, name)
194
+ icon = self.style().standardIcon(pixmapi)
195
+
196
+ item = QTableWidgetItem(name)
197
+ item.setIcon(icon)
198
+ self.table.setItem(i, j, item)
199
+
200
+ self.table.resizeRowsToContents()
201
+ self.table.resizeColumnsToContents()
202
+
203
+
204
+ app = QApplication(sys.argv)
205
+ w = Window()
206
+ w.show()
207
+
208
+ app.exec()