jbqt 0.1.29__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.
- jbqt-0.1.29/PKG-INFO +18 -0
- jbqt-0.1.29/README.md +0 -0
- jbqt-0.1.29/jbqt/__init__.py +9 -0
- jbqt-0.1.29/jbqt/common/__init__.py +9 -0
- jbqt-0.1.29/jbqt/common/__pycache__/__init__.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/common/__pycache__/qt_utils.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/common/qt_utils.py +133 -0
- jbqt-0.1.29/jbqt/consts/__init__.py +239 -0
- jbqt-0.1.29/jbqt/consts/__pycache__/__init__.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/dialogs/__init__.py +8 -0
- jbqt-0.1.29/jbqt/dialogs/__pycache__/__init__.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/dialogs/__pycache__/file_dialog.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/dialogs/__pycache__/input_form.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/dialogs/__pycache__/single_input.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/dialogs/__pycache__/text_preview.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/dialogs/file_dialog.py +60 -0
- jbqt-0.1.29/jbqt/dialogs/input_form.py +140 -0
- jbqt-0.1.29/jbqt/dialogs/single_input.py +85 -0
- jbqt-0.1.29/jbqt/dialogs/text_preview.py +42 -0
- jbqt-0.1.29/jbqt/models/__init__.py +18 -0
- jbqt-0.1.29/jbqt/models/__pycache__/__init__.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/models/__pycache__/chip_button.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/models/__pycache__/chips.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/models/__pycache__/dialog_options.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/models/__pycache__/model_consts.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/models/__pycache__/model_utils.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/models/__pycache__/toolbar_button.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/models/chip_button.py +10 -0
- jbqt-0.1.29/jbqt/models/chips.py +29 -0
- jbqt-0.1.29/jbqt/models/dialog_options.py +28 -0
- jbqt-0.1.29/jbqt/models/model_consts.py +9 -0
- jbqt-0.1.29/jbqt/models/model_utils.py +27 -0
- jbqt-0.1.29/jbqt/models/toolbar_button.py +60 -0
- jbqt-0.1.29/jbqt/types/__init__.py +24 -0
- jbqt-0.1.29/jbqt/types/__pycache__/__init__.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/view_icons.py +209 -0
- jbqt-0.1.29/jbqt/widgets/__init__.py +20 -0
- jbqt-0.1.29/jbqt/widgets/__pycache__/__init__.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/widgets/__pycache__/chip_button.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/widgets/__pycache__/simple.cpython-312.pyc +0 -0
- jbqt-0.1.29/jbqt/widgets/chip_button.py +212 -0
- jbqt-0.1.29/jbqt/widgets/chips.py +261 -0
- jbqt-0.1.29/jbqt/widgets/multiselect.py +219 -0
- jbqt-0.1.29/jbqt/widgets/simple.py +71 -0
- jbqt-0.1.29/jbqt/widgets/toast.py +35 -0
- jbqt-0.1.29/jbqt/widgets/widget_utils.py +90 -0
- jbqt-0.1.29/pyproject.toml +19 -0
jbqt-0.1.29/PKG-INFO
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: jbqt
|
|
3
|
+
Version: 0.1.29
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Joseph Bochinski
|
|
6
|
+
Author-email: stirgejr@gmail.com
|
|
7
|
+
Requires-Python: >=3.12,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Requires-Dist: fuzzywuzzy (>=0.18.0,<0.19.0)
|
|
11
|
+
Requires-Dist: jbconsts (>=0.1.1,<0.2.0)
|
|
12
|
+
Requires-Dist: jbutils (>=0.2.2,<0.3.0)
|
|
13
|
+
Requires-Dist: pyqt6 (>=6.9.0,<7.0.0)
|
|
14
|
+
Requires-Dist: python-levenshtein (>=0.27.1,<0.28.0)
|
|
15
|
+
Requires-Dist: rich (>=14.0.0,<15.0.0)
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
|
jbqt-0.1.29/README.md
ADDED
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from jbutils import JbuConsole
|
|
5
|
+
from PyQt6.QtCore import Qt, QDate
|
|
6
|
+
from PyQt6.QtWidgets import (
|
|
7
|
+
QListWidgetItem,
|
|
8
|
+
QCalendarWidget,
|
|
9
|
+
QApplication,
|
|
10
|
+
QWidget,
|
|
11
|
+
QLineEdit,
|
|
12
|
+
QTextEdit,
|
|
13
|
+
QSpinBox,
|
|
14
|
+
QCheckBox,
|
|
15
|
+
QDoubleSpinBox,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
import jbqt.consts as consts
|
|
19
|
+
from jbqt.models import IChipsWidget
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_item_value(item: QListWidgetItem) -> str:
|
|
23
|
+
"""Retrieves the value from a list widget item
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
item (QListWidgetItem): Item object to retrieve data from
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
str: Value contained in the item
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
value = item.data(consts.LIST_ITEM_ROLE) or item.text()
|
|
33
|
+
return value.strip()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def register_app(app: QApplication, icon_dir: str = "") -> None:
|
|
37
|
+
"""Registers the PyQt app with the JbQt global config reference
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
app (QApplication): The PyQt application
|
|
41
|
+
icon_dir (str, optional): Directory containing custom icons. Defaults to "".
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
consts.QtGlobalRefs.app = app
|
|
45
|
+
consts.set_icon_dir(icon_dir)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_widget_value(widget: QWidget, key: str = "") -> Any:
|
|
49
|
+
"""Convenience function to retrieve the value from a variety of
|
|
50
|
+
common widgets
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
widget (QWidget): Widget to get the value from
|
|
54
|
+
key (str, optional): A key to retrieve a specific value for
|
|
55
|
+
certain widget types. Defaults to "".
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Any: Value contained in the widget, if any.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
if isinstance(widget, QLineEdit):
|
|
62
|
+
return widget.text()
|
|
63
|
+
if isinstance(widget, QTextEdit):
|
|
64
|
+
return widget.toPlainText().strip()
|
|
65
|
+
if isinstance(widget, (QSpinBox, QDoubleSpinBox)):
|
|
66
|
+
return widget.value()
|
|
67
|
+
if isinstance(widget, IChipsWidget):
|
|
68
|
+
return widget.values
|
|
69
|
+
if isinstance(widget, QCheckBox):
|
|
70
|
+
return widget.checkState == Qt.CheckState.Checked
|
|
71
|
+
if isinstance(widget, QCalendarWidget):
|
|
72
|
+
return widget.selectedDate().toString()
|
|
73
|
+
|
|
74
|
+
JbuConsole.warn(f"No handler defined for {key} of type `{type(widget)}`")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def set_widget_value(widget: QWidget, value: Any) -> None:
|
|
78
|
+
"""Set the value of a one of variety of common widgets
|
|
79
|
+
|
|
80
|
+
Currently Supported Widgets:
|
|
81
|
+
- QLineEdit
|
|
82
|
+
- QTextEdit
|
|
83
|
+
- QSpinBox
|
|
84
|
+
- QDoubleSpinBox
|
|
85
|
+
- ChipsWidget
|
|
86
|
+
- QCheckBox
|
|
87
|
+
- QCalendarWidget
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
widget (QWidget): Widget to assign value to
|
|
91
|
+
value (Any): Value to assign it
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
if isinstance(widget, (QLineEdit, QTextEdit)):
|
|
95
|
+
widget.setText(str(value))
|
|
96
|
+
elif isinstance(widget, QSpinBox):
|
|
97
|
+
try:
|
|
98
|
+
widget.setValue(int(value))
|
|
99
|
+
except Exception as e:
|
|
100
|
+
print(e)
|
|
101
|
+
print(type(e))
|
|
102
|
+
|
|
103
|
+
elif isinstance(widget, QDoubleSpinBox):
|
|
104
|
+
try:
|
|
105
|
+
widget.setValue(float(value))
|
|
106
|
+
except Exception as e:
|
|
107
|
+
print(e)
|
|
108
|
+
print(type(e))
|
|
109
|
+
|
|
110
|
+
elif isinstance(widget, IChipsWidget) and isinstance(value, list):
|
|
111
|
+
widget.add_chips(value)
|
|
112
|
+
elif isinstance(widget, QCheckBox):
|
|
113
|
+
state = Qt.CheckState.Unchecked
|
|
114
|
+
if isinstance(value, Qt.CheckState):
|
|
115
|
+
state = value
|
|
116
|
+
elif value is True:
|
|
117
|
+
state = Qt.CheckState.Checked
|
|
118
|
+
elif value is False:
|
|
119
|
+
state = Qt.CheckState.Unchecked
|
|
120
|
+
|
|
121
|
+
widget.setCheckState(state)
|
|
122
|
+
|
|
123
|
+
elif isinstance(widget, QCalendarWidget):
|
|
124
|
+
if isinstance(value, (date, QDate)):
|
|
125
|
+
widget.setSelectedDate(value)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def set_item_disabled(item: QListWidgetItem, disabled: bool = True) -> None:
|
|
129
|
+
if disabled:
|
|
130
|
+
item.setFlags(Qt.ItemFlag.NoItemFlags)
|
|
131
|
+
item.setCheckState(Qt.CheckState.Unchecked)
|
|
132
|
+
else:
|
|
133
|
+
item.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled)
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""Collection of constants and global references for the jbqt package."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from PyQt6.QtWidgets import QApplication, QMainWindow, QScrollArea, QWidget
|
|
10
|
+
from PyQt6.QtCore import Qt, QSize
|
|
11
|
+
from PyQt6.QtGui import QIcon, QPixmap, QPainter, QColor
|
|
12
|
+
|
|
13
|
+
from jbqt.types import IconGetter
|
|
14
|
+
|
|
15
|
+
TAG_RE = re.compile(r"^\((.+)\)(\d+\.\d+)$")
|
|
16
|
+
LIST_ITEM_ROLE = Qt.ItemDataRole.UserRole - 1
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class QtGlobalRefs:
|
|
20
|
+
"""Class that tracks various values globally for a given application"""
|
|
21
|
+
|
|
22
|
+
app: QApplication | None = None
|
|
23
|
+
main_window: QMainWindow | None = None
|
|
24
|
+
scroll_area: QScrollArea | None = None
|
|
25
|
+
server_active: bool = False
|
|
26
|
+
debug_set: bool = False
|
|
27
|
+
seed_widget: QWidget | None = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class QtPaths:
|
|
31
|
+
"""Global path reference storage for JbQt projects"""
|
|
32
|
+
|
|
33
|
+
icon_dir: str | None = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
THEME_ICONS: dict[str, QIcon] = {
|
|
37
|
+
"save": QIcon.fromTheme("media-floppy-symbolic"),
|
|
38
|
+
"new_file": QIcon.fromTheme("document-new-symbolic"),
|
|
39
|
+
"open": QIcon.fromTheme("folder-open-symbolic"),
|
|
40
|
+
"copy": QIcon.fromTheme("edit-paste-symbolic"),
|
|
41
|
+
"inspect": QIcon.fromTheme("docviewer-app-symbolic"),
|
|
42
|
+
"clone": QIcon.fromTheme("edit-copy-symbolic"),
|
|
43
|
+
"plus": QIcon.fromTheme("list-add-symbolic"),
|
|
44
|
+
"minus": QIcon.fromTheme("list-remove-symbolic"),
|
|
45
|
+
"times": QIcon.fromTheme("cancel-operation-symbolic"),
|
|
46
|
+
"refresh": QIcon.fromTheme("view-refresh-symbolic"),
|
|
47
|
+
"reload": QIcon.fromTheme("update-symbolic"),
|
|
48
|
+
"circle_check": QIcon.fromTheme("selection-checked"),
|
|
49
|
+
"circle_x": QIcon.fromTheme("application-exit"),
|
|
50
|
+
"code": QIcon.fromTheme("input-tablet-symbolic"),
|
|
51
|
+
"font-selection-editor": QIcon.fromTheme("font-select-symbolic"),
|
|
52
|
+
"trash": QIcon.fromTheme("trash-symbolic"),
|
|
53
|
+
"edit_data": QIcon.fromTheme("document-edit-symbolic"),
|
|
54
|
+
"preview": QIcon.fromTheme("view-layout-symbolic"),
|
|
55
|
+
"sync": QIcon.fromTheme("mail-send-receive-symbolic"),
|
|
56
|
+
}
|
|
57
|
+
""" Mapping of system fallback icons if STD_ICONS doesn't have one"""
|
|
58
|
+
|
|
59
|
+
STD_ICONS: dict[str, QIcon] = {
|
|
60
|
+
"save": QIcon.fromTheme(QIcon.ThemeIcon.DocumentSave),
|
|
61
|
+
"new_file": QIcon.fromTheme(QIcon.ThemeIcon.DocumentNew),
|
|
62
|
+
"open": QIcon.fromTheme(QIcon.ThemeIcon.FolderOpen),
|
|
63
|
+
"copy": QIcon.fromTheme(QIcon.ThemeIcon.EditPaste),
|
|
64
|
+
"inspect": QIcon.fromTheme(QIcon.ThemeIcon.DocumentPageSetup),
|
|
65
|
+
"clone": QIcon.fromTheme(QIcon.ThemeIcon.EditCopy),
|
|
66
|
+
"plus": QIcon.fromTheme(QIcon.ThemeIcon.ListAdd),
|
|
67
|
+
"minus": QIcon.fromTheme(QIcon.ThemeIcon.DialogError),
|
|
68
|
+
"times": QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit),
|
|
69
|
+
"refresh": QIcon.fromTheme(QIcon.ThemeIcon.SystemReboot),
|
|
70
|
+
"reload": QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaylistRepeat),
|
|
71
|
+
"circle_check": QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaybackStart),
|
|
72
|
+
"circle_x": QIcon.fromTheme(QIcon.ThemeIcon.ProcessStop),
|
|
73
|
+
"code": QIcon.fromTheme(QIcon.ThemeIcon.Computer),
|
|
74
|
+
"font-selection-editor": QIcon.fromTheme(QIcon.ThemeIcon.EditFind),
|
|
75
|
+
"trash": QIcon.fromTheme(QIcon.ThemeIcon.EditDelete),
|
|
76
|
+
"edit_data": QIcon.fromTheme(QIcon.ThemeIcon.InputTablet),
|
|
77
|
+
"preview": QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn),
|
|
78
|
+
"sync": QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaylistShuffle),
|
|
79
|
+
}
|
|
80
|
+
""" Mapping of preferred icon definitions """
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def set_icon_dir(path: str) -> None:
|
|
84
|
+
"""Convenience function to set the global icon directory"""
|
|
85
|
+
QtPaths.icon_dir = path
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class QtIconSizes:
|
|
89
|
+
"""Reference class to retrieve common PyQt icon size classes"""
|
|
90
|
+
|
|
91
|
+
ICON_XS = QSize(16, 16)
|
|
92
|
+
ICON_SM = QSize(20, 20)
|
|
93
|
+
ICON_MD = QSize(22, 22)
|
|
94
|
+
ICON_LG = QSize(24, 24)
|
|
95
|
+
ICON_XL = QSize(32, 32)
|
|
96
|
+
ICON_XXL = QSize(48, 48)
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def get(cls, key: str, default: QSize | None = None) -> QSize:
|
|
100
|
+
if hasattr(cls, key):
|
|
101
|
+
return getattr(cls, key)
|
|
102
|
+
return default or cls.ICON_MD
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def icon_dir(file_name: str) -> str:
|
|
106
|
+
"""Retrieve the filepath to an icon image
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
file_name (str): File name of the icon image to retrieve
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
str: Either the global icon directory joined with file_name if
|
|
113
|
+
the global icon directory has been defined, otherwise just
|
|
114
|
+
the file_name
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
if QtPaths.icon_dir:
|
|
118
|
+
return os.path.join(QtPaths.icon_dir, file_name)
|
|
119
|
+
return file_name
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def recolor_icon(
|
|
123
|
+
image_path: str, color: str | QColor, scale: QSize | None = None
|
|
124
|
+
) -> QIcon:
|
|
125
|
+
"""Define QIcon instance and adjust the color accordingly
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
image_path (str): Path to the image file to use, or a standard
|
|
129
|
+
system/theme reference string
|
|
130
|
+
color (str | QColor): Color value to set the icon to
|
|
131
|
+
scale (QSize | None, optional): Size to make the icon. If None,
|
|
132
|
+
gets set 0to 22px. Defaults to None.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
QIcon: QIcon instance with the provided size and color
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
if not color:
|
|
139
|
+
color = QColor("black")
|
|
140
|
+
if isinstance(color, str):
|
|
141
|
+
color = QColor(color)
|
|
142
|
+
|
|
143
|
+
if scale is None:
|
|
144
|
+
scale = QtIconSizes.ICON_MD
|
|
145
|
+
pixmap = QPixmap(image_path)
|
|
146
|
+
icon_key = os.path.splitext(os.path.basename(image_path))[0]
|
|
147
|
+
|
|
148
|
+
if pixmap.isNull():
|
|
149
|
+
fallback = THEME_ICONS.get(icon_key, STD_ICONS.get(icon_key))
|
|
150
|
+
if fallback:
|
|
151
|
+
pixmap = fallback.pixmap(scale)
|
|
152
|
+
|
|
153
|
+
recolored_pixmap = QPixmap(pixmap.size())
|
|
154
|
+
|
|
155
|
+
recolored_pixmap.fill(
|
|
156
|
+
QColor("transparent")
|
|
157
|
+
) # Ensure the background is transparent
|
|
158
|
+
painter = QPainter(recolored_pixmap)
|
|
159
|
+
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Source)
|
|
160
|
+
|
|
161
|
+
# Draw the original pixmap
|
|
162
|
+
painter.drawPixmap(0, 0, pixmap)
|
|
163
|
+
|
|
164
|
+
# Apply the desired color
|
|
165
|
+
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_SourceIn)
|
|
166
|
+
painter.fillRect(pixmap.rect(), color)
|
|
167
|
+
|
|
168
|
+
painter.end()
|
|
169
|
+
return QIcon(recolored_pixmap.scaled(scale, Qt.AspectRatioMode.KeepAspectRatio))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def get_icon(file_name: str) -> IconGetter:
|
|
173
|
+
"""Wrapper function that returns a function to build a QIcon instance
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
file_name (str): Icon image file name in the global icon directory,
|
|
177
|
+
or a standard system/theme icon string
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
IconGetter: Function that builds the actual QIcon instance based
|
|
181
|
+
on the provided file name
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
def getter(
|
|
185
|
+
color: Optional[str | QColor] = "", size: QSize | int | None = None
|
|
186
|
+
) -> QIcon:
|
|
187
|
+
color = color or ""
|
|
188
|
+
if isinstance(size, int):
|
|
189
|
+
size = QSize(size, size)
|
|
190
|
+
|
|
191
|
+
return recolor_icon(icon_dir(file_name), color, size)
|
|
192
|
+
|
|
193
|
+
return getter
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class ICONS:
|
|
197
|
+
"""Reference class that provides common predefined IconGetter functions"""
|
|
198
|
+
|
|
199
|
+
SAVE = get_icon("save.svg")
|
|
200
|
+
NEW = get_icon("new_file.svg")
|
|
201
|
+
OPEN = get_icon("open.png")
|
|
202
|
+
COPY = get_icon("copy.png")
|
|
203
|
+
INSPECT = get_icon("inspect.svg")
|
|
204
|
+
CLONE = get_icon("clone.svg")
|
|
205
|
+
PLUS = get_icon("plus.png")
|
|
206
|
+
MINUS = get_icon("minus.png")
|
|
207
|
+
TIMES = get_icon("times.svg")
|
|
208
|
+
REFRESH = get_icon("refresh.svg")
|
|
209
|
+
RELOAD = get_icon("reload.png")
|
|
210
|
+
CIRCLE_CHECK = get_icon("circle_check.svg")
|
|
211
|
+
CIRCLE_TIMES = get_icon("circle_x.svg")
|
|
212
|
+
CODE = get_icon("code.png")
|
|
213
|
+
EDIT = get_icon("font-selection-editor.png")
|
|
214
|
+
TRASH = get_icon("trash.png")
|
|
215
|
+
EDIT_DATA = get_icon("edit_data.svg")
|
|
216
|
+
PREVIEW = get_icon("preview.png")
|
|
217
|
+
SYNC = get_icon("sync.png")
|
|
218
|
+
|
|
219
|
+
@classmethod
|
|
220
|
+
def get(cls, name: str, default: str = "") -> IconGetter:
|
|
221
|
+
"""Function to retrieve an IconGetter reference by name.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
name (str): _description_
|
|
225
|
+
default (str, optional): _description_. Defaults to "".
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
IconGetter | str: _description_
|
|
229
|
+
"""
|
|
230
|
+
if hasattr(cls, name):
|
|
231
|
+
return getattr(cls, name)
|
|
232
|
+
else:
|
|
233
|
+
return get_icon(default)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
__all__ = [
|
|
237
|
+
"ICONS",
|
|
238
|
+
"QtIconSizes",
|
|
239
|
+
]
|
|
Binary file
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""jbqt Dialog exports"""
|
|
2
|
+
|
|
3
|
+
from jbqt.dialogs.input_form import InputFormDialog
|
|
4
|
+
from jbqt.dialogs.single_input import InputDialog
|
|
5
|
+
from jbqt.dialogs.text_preview import TextPreviewDialog
|
|
6
|
+
from jbqt.dialogs.file_dialog import JbFileDialog
|
|
7
|
+
|
|
8
|
+
__all__ = ["InputDialog", "InputFormDialog", "TextPreviewDialog", "JbFileDialog"]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from PyQt6.QtCore import pyqtSignal
|
|
2
|
+
from PyQt6.QtWidgets import QWidget, QPushButton, QVBoxLayout, QFileDialog
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class JbFileDialog(QWidget):
|
|
6
|
+
"""A simple widget that opens a file dialog in either 'open' or 'save' mode.
|
|
7
|
+
|
|
8
|
+
Emits:
|
|
9
|
+
selectedFile (str): Emitted when a file path is chosen.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
title (str): The dialog window title.
|
|
13
|
+
directory (str): The starting directory.
|
|
14
|
+
mode (str): 'open' to open an existing file, 'save' to choose a save location.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
selectedFile = pyqtSignal(str)
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self, title: str = "File Dialog", directory: str = "", mode: str = "open"
|
|
21
|
+
) -> None:
|
|
22
|
+
super().__init__()
|
|
23
|
+
|
|
24
|
+
self.title = title
|
|
25
|
+
self.directory = directory
|
|
26
|
+
self.mode = mode.lower()
|
|
27
|
+
|
|
28
|
+
if self.mode not in {"open", "save"}:
|
|
29
|
+
raise ValueError("mode must be either 'open' or 'save'")
|
|
30
|
+
|
|
31
|
+
self.setWindowTitle(title)
|
|
32
|
+
|
|
33
|
+
self.button = QPushButton(
|
|
34
|
+
"Open File" if self.mode == "open" else "Save File", self
|
|
35
|
+
)
|
|
36
|
+
self.button.clicked.connect(self.open_file_dialog)
|
|
37
|
+
|
|
38
|
+
layout = QVBoxLayout()
|
|
39
|
+
layout.addWidget(self.button)
|
|
40
|
+
self.setLayout(layout)
|
|
41
|
+
|
|
42
|
+
def open_file_dialog(self) -> None:
|
|
43
|
+
"""Open a file dialog based on the selected mode."""
|
|
44
|
+
if self.mode == "open":
|
|
45
|
+
file_name, _ = QFileDialog.getOpenFileName(
|
|
46
|
+
self,
|
|
47
|
+
self.title,
|
|
48
|
+
self.directory,
|
|
49
|
+
"All Files (*);;Text Files (*.txt)",
|
|
50
|
+
)
|
|
51
|
+
else:
|
|
52
|
+
file_name, _ = QFileDialog.getSaveFileName(
|
|
53
|
+
self,
|
|
54
|
+
self.title,
|
|
55
|
+
self.directory,
|
|
56
|
+
"All Files (*);;Text Files (*.txt)",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if file_name:
|
|
60
|
+
self.selectedFile.emit(file_name)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, Callable
|
|
3
|
+
|
|
4
|
+
from PyQt6.QtCore import pyqtSignal
|
|
5
|
+
from PyQt6.QtWidgets import (
|
|
6
|
+
QDialog,
|
|
7
|
+
QDialogButtonBox,
|
|
8
|
+
QDoubleSpinBox,
|
|
9
|
+
QHBoxLayout,
|
|
10
|
+
QLabel,
|
|
11
|
+
QLayout,
|
|
12
|
+
QLineEdit,
|
|
13
|
+
QPushButton,
|
|
14
|
+
QSpinBox,
|
|
15
|
+
QVBoxLayout,
|
|
16
|
+
QWidget,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from PyQt6 import QtWidgets
|
|
20
|
+
|
|
21
|
+
from jbqt.models import DialogOptions
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_widget(name: str) -> QWidget | None:
|
|
25
|
+
widget: QWidget | None = None
|
|
26
|
+
if hasattr(QtWidgets, name):
|
|
27
|
+
widget = getattr(QtWidgets, name)
|
|
28
|
+
""" elif hasattr(widgets, name):
|
|
29
|
+
widget = getattr(widgets, name) """
|
|
30
|
+
|
|
31
|
+
if widget:
|
|
32
|
+
if not callable(widget):
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
widget = widget()
|
|
36
|
+
if isinstance(widget, QWidget):
|
|
37
|
+
return widget
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_widget_val(widget: QWidget) -> str | int | float | None:
|
|
42
|
+
# Emit the custom signal with data when the dialog is accepted
|
|
43
|
+
value = None
|
|
44
|
+
if isinstance(widget, QLineEdit):
|
|
45
|
+
value = widget.text()
|
|
46
|
+
elif isinstance(widget, QSpinBox):
|
|
47
|
+
value = widget.value()
|
|
48
|
+
elif isinstance(widget, QDoubleSpinBox):
|
|
49
|
+
value = widget.value()
|
|
50
|
+
|
|
51
|
+
return value
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class InputFormDialog(QDialog):
|
|
55
|
+
"""reference value for external use"""
|
|
56
|
+
|
|
57
|
+
value = pyqtSignal(dict)
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
parent=None,
|
|
62
|
+
form_data: list[dict] | None = None,
|
|
63
|
+
opts: DialogOptions | None = None,
|
|
64
|
+
):
|
|
65
|
+
super().__init__(parent)
|
|
66
|
+
|
|
67
|
+
opts = opts or DialogOptions(title="Input")
|
|
68
|
+
opts.apply(self)
|
|
69
|
+
|
|
70
|
+
self.form_data: list[dict] = [obj.copy() for obj in form_data or []]
|
|
71
|
+
self.input_widgets: dict[str, QWidget] = {}
|
|
72
|
+
|
|
73
|
+
self.main_layout = QVBoxLayout()
|
|
74
|
+
self.construct_widgets()
|
|
75
|
+
|
|
76
|
+
button_box = QHBoxLayout()
|
|
77
|
+
submit_btn = QPushButton("Submit", parent=self)
|
|
78
|
+
submit_btn.clicked.connect(self.submit)
|
|
79
|
+
|
|
80
|
+
cancel_btn = QPushButton("Cancel", parent=self)
|
|
81
|
+
cancel_btn.clicked.connect(self.close)
|
|
82
|
+
|
|
83
|
+
button_box.addWidget(submit_btn)
|
|
84
|
+
button_box.addWidget(cancel_btn)
|
|
85
|
+
self.main_layout.addLayout(button_box)
|
|
86
|
+
|
|
87
|
+
self.setLayout(self.main_layout)
|
|
88
|
+
|
|
89
|
+
def construct_widgets(self) -> None:
|
|
90
|
+
for widget_def in self.form_data:
|
|
91
|
+
if not "type" in widget_def:
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
layout = QHBoxLayout()
|
|
95
|
+
|
|
96
|
+
w_type = widget_def.pop("type")
|
|
97
|
+
label = widget_def.pop("label", "")
|
|
98
|
+
key = widget_def.pop("key", "")
|
|
99
|
+
if not key:
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
widget = get_widget(w_type)
|
|
103
|
+
if not widget:
|
|
104
|
+
continue
|
|
105
|
+
if label and isinstance(label, str):
|
|
106
|
+
layout.addWidget(QLabel(label))
|
|
107
|
+
|
|
108
|
+
if widget_def:
|
|
109
|
+
self._init_widget(widget, **widget_def)
|
|
110
|
+
layout.addWidget(widget)
|
|
111
|
+
self.main_layout.addLayout(layout)
|
|
112
|
+
self.input_widgets[key] = widget
|
|
113
|
+
|
|
114
|
+
def get_form_data(self) -> dict:
|
|
115
|
+
form_values: dict[str, Any] = {}
|
|
116
|
+
|
|
117
|
+
for key, widget in self.input_widgets.items():
|
|
118
|
+
value = get_widget_val(widget)
|
|
119
|
+
if value:
|
|
120
|
+
form_values[key] = value
|
|
121
|
+
|
|
122
|
+
return form_values
|
|
123
|
+
|
|
124
|
+
def submit(self) -> None:
|
|
125
|
+
form_data = self.get_form_data()
|
|
126
|
+
self.value.emit(form_data)
|
|
127
|
+
self.close()
|
|
128
|
+
|
|
129
|
+
def close(self) -> bool:
|
|
130
|
+
self.deleteLater()
|
|
131
|
+
|
|
132
|
+
return super().close()
|
|
133
|
+
|
|
134
|
+
def _init_widget(self, widget: QWidget, **opts) -> None:
|
|
135
|
+
for key, value in opts.items():
|
|
136
|
+
setter_key = f"set{key[0].upper() + key[1:]}"
|
|
137
|
+
if hasattr(widget, setter_key):
|
|
138
|
+
setter = getattr(widget, setter_key)
|
|
139
|
+
if setter:
|
|
140
|
+
setter(value)
|