jbqt 0.1.1__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 jbqt might be problematic. Click here for more details.
- jbqt-0.1.1/PKG-INFO +16 -0
- jbqt-0.1.1/README.md +0 -0
- jbqt-0.1.1/jbqt/__init__.py +0 -0
- jbqt-0.1.1/jbqt/common/__init__.py +6 -0
- jbqt-0.1.1/jbqt/common/consts.py +167 -0
- jbqt-0.1.1/jbqt/common/qt_utils.py +81 -0
- jbqt-0.1.1/jbqt/dialogs/__init__.py +7 -0
- jbqt-0.1.1/jbqt/dialogs/file_dialog.py +27 -0
- jbqt-0.1.1/jbqt/dialogs/input_form.py +85 -0
- jbqt-0.1.1/jbqt/dialogs/text_preview.py +42 -0
- jbqt-0.1.1/jbqt/models/__init__.py +6 -0
- jbqt-0.1.1/jbqt/models/chip_button.py +13 -0
- jbqt-0.1.1/jbqt/models/chips.py +31 -0
- jbqt-0.1.1/jbqt/view_icons.py +208 -0
- jbqt-0.1.1/jbqt/widgets/__init__.py +16 -0
- jbqt-0.1.1/jbqt/widgets/chip_button.py +204 -0
- jbqt-0.1.1/jbqt/widgets/chips.py +232 -0
- jbqt-0.1.1/jbqt/widgets/multiselect.py +201 -0
- jbqt-0.1.1/jbqt/widgets/simple.py +67 -0
- jbqt-0.1.1/jbqt/widgets/toast.py +35 -0
- jbqt-0.1.1/jbqt/widgets/widget_utils.py +77 -0
- jbqt-0.1.1/pyproject.toml +17 -0
jbqt-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: jbqt
|
|
3
|
+
Version: 0.1.1
|
|
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: jb-utils (>=0.1.2,<0.2.0)
|
|
12
|
+
Requires-Dist: pyqt6 (>=6.9.0,<7.0.0)
|
|
13
|
+
Requires-Dist: python-levenshtein (>=0.27.1,<0.28.0)
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
|
jbqt-0.1.1/README.md
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -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,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,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)
|
|
@@ -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
|