pygpt-net 2.6.21__py3-none-any.whl → 2.6.23__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.
- pygpt_net/CHANGELOG.txt +12 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +3 -1
- pygpt_net/controller/__init__.py +4 -8
- pygpt_net/controller/access/voice.py +2 -2
- pygpt_net/controller/agent/llama.py +3 -0
- pygpt_net/controller/assistant/batch.py +2 -3
- pygpt_net/controller/assistant/editor.py +2 -2
- pygpt_net/controller/assistant/files.py +2 -3
- pygpt_net/controller/assistant/store.py +2 -2
- pygpt_net/controller/audio/audio.py +2 -2
- pygpt_net/controller/chat/response.py +4 -0
- pygpt_net/controller/ctx/ctx.py +2 -1
- pygpt_net/controller/files/files.py +24 -55
- pygpt_net/controller/idx/indexer.py +85 -76
- pygpt_net/controller/lang/lang.py +52 -34
- pygpt_net/controller/model/importer.py +2 -2
- pygpt_net/controller/notepad/notepad.py +86 -84
- pygpt_net/controller/plugins/settings.py +3 -4
- pygpt_net/controller/settings/profile.py +105 -124
- pygpt_net/controller/theme/menu.py +154 -57
- pygpt_net/controller/theme/nodes.py +51 -44
- pygpt_net/controller/theme/theme.py +33 -9
- pygpt_net/controller/tools/tools.py +2 -2
- pygpt_net/controller/ui/tabs.py +2 -3
- pygpt_net/core/agents/observer/evaluation.py +2 -2
- pygpt_net/core/agents/runners/loop.py +1 -0
- pygpt_net/core/bridge/bridge.py +2 -0
- pygpt_net/core/ctx/container.py +13 -12
- pygpt_net/core/ctx/output.py +7 -4
- pygpt_net/core/debug/console/console.py +2 -2
- pygpt_net/core/filesystem/actions.py +1 -2
- pygpt_net/core/filesystem/opener.py +261 -0
- pygpt_net/core/filesystem/url.py +13 -10
- pygpt_net/core/platforms/platforms.py +5 -4
- pygpt_net/core/render/plain/helpers.py +2 -5
- pygpt_net/core/render/plain/renderer.py +26 -30
- pygpt_net/core/render/web/body.py +1 -1
- pygpt_net/core/settings/settings.py +43 -13
- pygpt_net/core/tabs/tabs.py +20 -13
- pygpt_net/data/config/config.json +4 -4
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/css/web-blocks.dark.css +7 -1
- pygpt_net/data/css/web-blocks.light.css +5 -2
- pygpt_net/data/css/web-chatgpt.dark.css +7 -1
- pygpt_net/data/css/web-chatgpt.light.css +3 -0
- pygpt_net/data/css/web-chatgpt_wide.dark.css +7 -1
- pygpt_net/data/css/web-chatgpt_wide.light.css +3 -0
- pygpt_net/data/locale/locale.de.ini +5 -1
- pygpt_net/data/locale/locale.en.ini +5 -1
- pygpt_net/data/locale/locale.es.ini +5 -1
- pygpt_net/data/locale/locale.fr.ini +5 -1
- pygpt_net/data/locale/locale.it.ini +5 -1
- pygpt_net/data/locale/locale.pl.ini +6 -4
- pygpt_net/data/locale/locale.uk.ini +5 -1
- pygpt_net/data/locale/locale.zh.ini +5 -1
- pygpt_net/plugin/twitter/plugin.py +2 -2
- pygpt_net/provider/core/config/patch.py +12 -1
- pygpt_net/tools/audio_transcriber/ui/dialogs.py +44 -54
- pygpt_net/tools/code_interpreter/body.py +1 -2
- pygpt_net/tools/code_interpreter/tool.py +7 -4
- pygpt_net/tools/code_interpreter/ui/html.py +1 -3
- pygpt_net/tools/code_interpreter/ui/widgets.py +2 -3
- pygpt_net/tools/html_canvas/ui/widgets.py +1 -3
- pygpt_net/tools/image_viewer/ui/dialogs.py +40 -37
- pygpt_net/tools/indexer/ui/widgets.py +2 -4
- pygpt_net/tools/media_player/tool.py +2 -5
- pygpt_net/tools/media_player/ui/widgets.py +60 -36
- pygpt_net/tools/text_editor/ui/widgets.py +18 -19
- pygpt_net/tools/translator/ui/widgets.py +39 -35
- pygpt_net/ui/base/context_menu.py +9 -4
- pygpt_net/ui/dialog/db.py +1 -3
- pygpt_net/ui/dialog/models.py +1 -3
- pygpt_net/ui/dialog/models_importer.py +2 -4
- pygpt_net/ui/dialogs.py +34 -30
- pygpt_net/ui/layout/chat/attachments.py +72 -84
- pygpt_net/ui/layout/chat/attachments_ctx.py +40 -44
- pygpt_net/ui/layout/chat/attachments_uploaded.py +36 -39
- pygpt_net/ui/layout/chat/calendar.py +100 -70
- pygpt_net/ui/layout/chat/chat.py +23 -17
- pygpt_net/ui/layout/chat/input.py +95 -118
- pygpt_net/ui/layout/chat/output.py +100 -162
- pygpt_net/ui/layout/chat/painter.py +89 -61
- pygpt_net/ui/layout/ctx/ctx_list.py +43 -52
- pygpt_net/ui/layout/status.py +23 -14
- pygpt_net/ui/layout/toolbox/agent.py +27 -38
- pygpt_net/ui/layout/toolbox/agent_llama.py +41 -45
- pygpt_net/ui/layout/toolbox/assistants.py +42 -38
- pygpt_net/ui/layout/toolbox/computer_env.py +32 -23
- pygpt_net/ui/layout/toolbox/footer.py +13 -16
- pygpt_net/ui/layout/toolbox/image.py +18 -21
- pygpt_net/ui/layout/toolbox/indexes.py +46 -89
- pygpt_net/ui/layout/toolbox/mode.py +20 -7
- pygpt_net/ui/layout/toolbox/model.py +12 -10
- pygpt_net/ui/layout/toolbox/presets.py +68 -52
- pygpt_net/ui/layout/toolbox/prompt.py +31 -58
- pygpt_net/ui/layout/toolbox/toolbox.py +25 -21
- pygpt_net/ui/layout/toolbox/vision.py +20 -22
- pygpt_net/ui/main.py +2 -4
- pygpt_net/ui/menu/about.py +64 -84
- pygpt_net/ui/menu/audio.py +87 -63
- pygpt_net/ui/menu/config.py +121 -127
- pygpt_net/ui/menu/debug.py +69 -76
- pygpt_net/ui/menu/file.py +32 -35
- pygpt_net/ui/menu/menu.py +2 -3
- pygpt_net/ui/menu/plugins.py +69 -33
- pygpt_net/ui/menu/theme.py +45 -46
- pygpt_net/ui/menu/tools.py +56 -60
- pygpt_net/ui/menu/video.py +20 -25
- pygpt_net/ui/tray.py +1 -2
- pygpt_net/ui/widget/audio/bar.py +1 -3
- pygpt_net/ui/widget/audio/input_button.py +3 -4
- pygpt_net/ui/widget/calendar/select.py +1 -2
- pygpt_net/ui/widget/dialog/base.py +12 -9
- pygpt_net/ui/widget/dialog/editor_file.py +20 -23
- pygpt_net/ui/widget/dialog/find.py +25 -24
- pygpt_net/ui/widget/dialog/profile.py +57 -53
- pygpt_net/ui/widget/draw/painter.py +62 -93
- pygpt_net/ui/widget/element/button.py +42 -30
- pygpt_net/ui/widget/element/checkbox.py +23 -15
- pygpt_net/ui/widget/element/group.py +6 -5
- pygpt_net/ui/widget/element/labels.py +1 -2
- pygpt_net/ui/widget/filesystem/explorer.py +93 -102
- pygpt_net/ui/widget/image/display.py +1 -2
- pygpt_net/ui/widget/lists/assistant.py +1 -2
- pygpt_net/ui/widget/lists/attachment.py +1 -2
- pygpt_net/ui/widget/lists/attachment_ctx.py +1 -2
- pygpt_net/ui/widget/lists/context.py +2 -4
- pygpt_net/ui/widget/lists/index.py +1 -2
- pygpt_net/ui/widget/lists/model.py +1 -2
- pygpt_net/ui/widget/lists/model_editor.py +1 -2
- pygpt_net/ui/widget/lists/model_importer.py +1 -2
- pygpt_net/ui/widget/lists/preset.py +1 -2
- pygpt_net/ui/widget/lists/preset_plugins.py +1 -2
- pygpt_net/ui/widget/lists/profile.py +1 -2
- pygpt_net/ui/widget/lists/uploaded.py +1 -2
- pygpt_net/ui/widget/option/checkbox.py +2 -4
- pygpt_net/ui/widget/option/checkbox_list.py +1 -4
- pygpt_net/ui/widget/option/cmd.py +1 -4
- pygpt_net/ui/widget/option/dictionary.py +25 -28
- pygpt_net/ui/widget/option/input.py +1 -3
- pygpt_net/ui/widget/tabs/Input.py +16 -12
- pygpt_net/ui/widget/tabs/body.py +5 -3
- pygpt_net/ui/widget/tabs/layout.py +41 -28
- pygpt_net/ui/widget/tabs/output.py +442 -85
- pygpt_net/ui/widget/textarea/calendar_note.py +1 -2
- pygpt_net/ui/widget/textarea/editor.py +41 -73
- pygpt_net/ui/widget/textarea/find.py +11 -10
- pygpt_net/ui/widget/textarea/html.py +3 -6
- pygpt_net/ui/widget/textarea/input.py +134 -69
- pygpt_net/ui/widget/textarea/notepad.py +54 -38
- pygpt_net/ui/widget/textarea/output.py +65 -54
- pygpt_net/ui/widget/textarea/search_input.py +5 -4
- pygpt_net/ui/widget/textarea/web.py +2 -4
- pygpt_net/ui/widget/vision/camera.py +2 -31
- {pygpt_net-2.6.21.dist-info → pygpt_net-2.6.23.dist-info}/METADATA +38 -174
- {pygpt_net-2.6.21.dist-info → pygpt_net-2.6.23.dist-info}/RECORD +160 -159
- {pygpt_net-2.6.21.dist-info → pygpt_net-2.6.23.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.21.dist-info → pygpt_net-2.6.23.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.21.dist-info → pygpt_net-2.6.23.dist-info}/entry_points.txt +0 -0
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2025.08.24 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6 import QtCore
|
|
@@ -15,7 +15,6 @@ from PySide6.QtWidgets import QDialog, QPushButton, QHBoxLayout, QLabel, QVBoxLa
|
|
|
15
15
|
|
|
16
16
|
from pygpt_net.utils import trans
|
|
17
17
|
from .base import BaseDialog
|
|
18
|
-
from ..element.labels import HelpLabel
|
|
19
18
|
from ..option.input import DirectoryInput
|
|
20
19
|
|
|
21
20
|
|
|
@@ -27,7 +26,7 @@ class ProfileDialog(BaseDialog):
|
|
|
27
26
|
:param window: main window
|
|
28
27
|
:param id: settings id
|
|
29
28
|
"""
|
|
30
|
-
super(
|
|
29
|
+
super().__init__(window, id)
|
|
31
30
|
self.window = window
|
|
32
31
|
self.id = id
|
|
33
32
|
|
|
@@ -39,7 +38,7 @@ class ProfileDialog(BaseDialog):
|
|
|
39
38
|
"""
|
|
40
39
|
self.window.controller.settings.profile.dialog = False
|
|
41
40
|
self.window.controller.settings.profile.update_menu()
|
|
42
|
-
super(
|
|
41
|
+
super().closeEvent(event)
|
|
43
42
|
|
|
44
43
|
def keyPressEvent(self, event):
|
|
45
44
|
"""
|
|
@@ -48,10 +47,9 @@ class ProfileDialog(BaseDialog):
|
|
|
48
47
|
:param event: key press event
|
|
49
48
|
"""
|
|
50
49
|
if event.key() == Qt.Key_Escape:
|
|
51
|
-
self.
|
|
52
|
-
self.close() # close dialog when the Esc key is pressed.
|
|
50
|
+
self.close()
|
|
53
51
|
else:
|
|
54
|
-
super(
|
|
52
|
+
super().keyPressEvent(event)
|
|
55
53
|
|
|
56
54
|
def cleanup(self):
|
|
57
55
|
"""
|
|
@@ -61,7 +59,6 @@ class ProfileDialog(BaseDialog):
|
|
|
61
59
|
self.window.controller.settings.profile.update_menu()
|
|
62
60
|
|
|
63
61
|
|
|
64
|
-
|
|
65
62
|
class ProfileEditDialog(QDialog):
|
|
66
63
|
def __init__(self, window=None, id=None):
|
|
67
64
|
"""
|
|
@@ -69,7 +66,7 @@ class ProfileEditDialog(QDialog):
|
|
|
69
66
|
|
|
70
67
|
:param window: main window
|
|
71
68
|
"""
|
|
72
|
-
super(
|
|
69
|
+
super().__init__(window)
|
|
73
70
|
self.window = window
|
|
74
71
|
self.id = id
|
|
75
72
|
self.uuid = None
|
|
@@ -77,29 +74,25 @@ class ProfileEditDialog(QDialog):
|
|
|
77
74
|
self.name = ""
|
|
78
75
|
self.mode = "create" # create | update
|
|
79
76
|
self.input = ProfileNameInput(window, id)
|
|
77
|
+
self.input.setParent(self)
|
|
80
78
|
self.input.setMinimumWidth(400)
|
|
81
79
|
|
|
82
|
-
self.window.ui.nodes
|
|
83
|
-
self.window.ui.nodes['dialog.profile.item.btn.update'].clicked.connect(
|
|
84
|
-
lambda: self.window.controller.settings.profile.handle_update(
|
|
85
|
-
self.mode,
|
|
86
|
-
self.input.text(),
|
|
87
|
-
self.workdir.text(),
|
|
88
|
-
self.uuid,
|
|
89
|
-
)
|
|
90
|
-
)
|
|
80
|
+
nodes = self.window.ui.nodes
|
|
91
81
|
|
|
92
|
-
self.
|
|
93
|
-
self.
|
|
94
|
-
|
|
95
|
-
|
|
82
|
+
self.btn_update = QPushButton(trans('dialog.profile.item.btn.update'), self)
|
|
83
|
+
self.btn_update.clicked.connect(self._on_update_clicked)
|
|
84
|
+
nodes['dialog.profile.item.btn.update'] = self.btn_update
|
|
85
|
+
|
|
86
|
+
self.btn_dismiss = QPushButton(trans('dialog.profile.item.btn.dismiss'), self)
|
|
87
|
+
self.btn_dismiss.clicked.connect(self._on_dismiss_clicked)
|
|
88
|
+
nodes['dialog.profile.item.btn.dismiss'] = self.btn_dismiss
|
|
96
89
|
|
|
97
90
|
bottom = QHBoxLayout()
|
|
98
|
-
bottom.addWidget(
|
|
99
|
-
bottom.addWidget(
|
|
91
|
+
bottom.addWidget(nodes['dialog.profile.item.btn.dismiss'])
|
|
92
|
+
bottom.addWidget(nodes['dialog.profile.item.btn.update'])
|
|
100
93
|
|
|
101
|
-
|
|
102
|
-
|
|
94
|
+
nodes['dialog.profile.name.label'] = QLabel(trans("dialog.profile.name.label"), self)
|
|
95
|
+
nodes['dialog.profile.workdir.label'] = QLabel(trans("dialog.profile.workdir.label"), self)
|
|
103
96
|
|
|
104
97
|
option = {
|
|
105
98
|
'type': 'text',
|
|
@@ -107,42 +100,56 @@ class ProfileEditDialog(QDialog):
|
|
|
107
100
|
'value': "",
|
|
108
101
|
}
|
|
109
102
|
self.workdir = DirectoryInput(self.window, 'profile', 'item', option)
|
|
103
|
+
self.workdir.setParent(self)
|
|
110
104
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
self.window.ui.nodes['dialog.profile.checkbox.db'] = QCheckBox(trans("dialog.profile.checkbox.include_db"))
|
|
114
|
-
self.window.ui.nodes['dialog.profile.checkbox.db'].setChecked(True)
|
|
115
|
-
self.window.ui.nodes['dialog.profile.checkbox.data'] = QCheckBox(trans("dialog.profile.checkbox.include_datadir"))
|
|
116
|
-
self.window.ui.nodes['dialog.profile.checkbox.data'].setChecked(True)
|
|
105
|
+
nodes['dialog.profile.checkbox.switch'] = QCheckBox(trans("dialog.profile.checkbox.switch"), self)
|
|
106
|
+
nodes['dialog.profile.checkbox.switch'].setChecked(True)
|
|
117
107
|
|
|
118
|
-
|
|
108
|
+
self.checkboxes = QWidget(self)
|
|
119
109
|
checkboxes_layout = QHBoxLayout()
|
|
120
110
|
checkboxes_layout.setContentsMargins(0, 0, 0, 0)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
111
|
+
|
|
112
|
+
nodes['dialog.profile.checkbox.db'] = QCheckBox(trans("dialog.profile.checkbox.include_db"), self.checkboxes)
|
|
113
|
+
nodes['dialog.profile.checkbox.db'].setChecked(True)
|
|
114
|
+
nodes['dialog.profile.checkbox.data'] = QCheckBox(trans("dialog.profile.checkbox.include_datadir"), self.checkboxes)
|
|
115
|
+
nodes['dialog.profile.checkbox.data'].setChecked(True)
|
|
116
|
+
|
|
117
|
+
checkboxes_layout.addWidget(nodes['dialog.profile.checkbox.db'])
|
|
118
|
+
checkboxes_layout.addWidget(nodes['dialog.profile.checkbox.data'])
|
|
124
119
|
self.checkboxes.setLayout(checkboxes_layout)
|
|
125
120
|
|
|
126
121
|
layout = QVBoxLayout()
|
|
127
|
-
layout.addWidget(
|
|
122
|
+
layout.addWidget(nodes['dialog.profile.name.label'])
|
|
128
123
|
layout.addWidget(self.input)
|
|
129
|
-
layout.addWidget(
|
|
124
|
+
layout.addWidget(nodes['dialog.profile.workdir.label'])
|
|
130
125
|
layout.addWidget(self.workdir)
|
|
131
126
|
layout.addWidget(self.checkboxes)
|
|
132
|
-
layout.addWidget(
|
|
127
|
+
layout.addWidget(nodes['dialog.profile.checkbox.switch'])
|
|
133
128
|
layout.addLayout(bottom)
|
|
134
129
|
|
|
135
130
|
self.setLayout(layout)
|
|
136
131
|
|
|
132
|
+
def _on_update_clicked(self):
|
|
133
|
+
self.window.controller.settings.profile.handle_update(
|
|
134
|
+
self.mode,
|
|
135
|
+
self.input.text(),
|
|
136
|
+
self.workdir.text(),
|
|
137
|
+
self.uuid,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def _on_dismiss_clicked(self):
|
|
141
|
+
self.window.controller.settings.profile.dismiss_update()
|
|
142
|
+
|
|
137
143
|
def prepare(self):
|
|
138
144
|
"""Prepare dialog before show"""
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
mapping = {
|
|
146
|
+
'create': 'dialog.profile.item.btn.create',
|
|
147
|
+
'edit': 'dialog.profile.item.btn.update',
|
|
148
|
+
'duplicate': 'dialog.profile.item.btn.duplicate',
|
|
149
|
+
}
|
|
150
|
+
key = mapping.get(self.mode)
|
|
151
|
+
if key:
|
|
152
|
+
self.window.ui.nodes['dialog.profile.item.btn.update'].setText(trans(key))
|
|
146
153
|
self.workdir.setText(self.path)
|
|
147
154
|
|
|
148
155
|
|
|
@@ -154,8 +161,7 @@ class ProfileNameInput(QLineEdit):
|
|
|
154
161
|
:param window: main window
|
|
155
162
|
:param id: info window id
|
|
156
163
|
"""
|
|
157
|
-
super(
|
|
158
|
-
|
|
164
|
+
super().__init__(window)
|
|
159
165
|
self.window = window
|
|
160
166
|
self.id = id
|
|
161
167
|
|
|
@@ -165,13 +171,11 @@ class ProfileNameInput(QLineEdit):
|
|
|
165
171
|
|
|
166
172
|
:param event: key event
|
|
167
173
|
"""
|
|
168
|
-
super(
|
|
169
|
-
|
|
170
|
-
# save on Enter
|
|
171
|
-
if event.key() == QtCore.Qt.Key_Return or event.key() == QtCore.Qt.Key_Enter:
|
|
174
|
+
super().keyPressEvent(event)
|
|
175
|
+
if event.key() in (Qt.Key_Return, Qt.Key_Enter):
|
|
172
176
|
self.window.controller.settings.profile.handle_update(
|
|
173
177
|
self.window.ui.dialog['profile.item'].mode,
|
|
174
178
|
self.window.ui.dialog['profile.item'].input.text(),
|
|
175
179
|
self.window.ui.dialog['profile.item'].workdir.text(),
|
|
176
180
|
self.window.ui.dialog['profile.item'].uuid,
|
|
177
|
-
)
|
|
181
|
+
)
|
|
@@ -6,18 +6,18 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.
|
|
9
|
+
# Updated Date: 2025.08.24 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
13
|
+
from collections import deque
|
|
13
14
|
|
|
14
15
|
from PySide6.QtCore import Qt, QPoint
|
|
15
|
-
from PySide6.QtGui import QImage, QPainter, QPen, QAction, QIcon
|
|
16
|
+
from PySide6.QtGui import QImage, QPainter, QPen, QAction, QIcon
|
|
16
17
|
from PySide6.QtWidgets import QMenu, QWidget, QFileDialog, QMessageBox, QApplication
|
|
17
18
|
|
|
18
19
|
from pygpt_net.core.tabs.tab import Tab
|
|
19
20
|
from pygpt_net.utils import trans
|
|
20
|
-
import pygpt_net.icons_rc
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class PainterWidget(QWidget):
|
|
@@ -31,13 +31,50 @@ class PainterWidget(QWidget):
|
|
|
31
31
|
self.brushColor = Qt.black
|
|
32
32
|
self.lastPoint = QPoint()
|
|
33
33
|
self.originalImage = None
|
|
34
|
-
self.undoStack = []
|
|
35
|
-
self.redoStack = []
|
|
36
34
|
self.undoLimit = 10
|
|
35
|
+
self.undoStack = deque(maxlen=self.undoLimit)
|
|
36
|
+
self.redoStack = deque()
|
|
37
37
|
self.setFocusPolicy(Qt.StrongFocus)
|
|
38
38
|
self.setFocus()
|
|
39
39
|
self.installEventFilter(self)
|
|
40
40
|
self.tab = None
|
|
41
|
+
self._pen = QPen(self.brushColor, self.brushSize, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
|
|
42
|
+
self.setAttribute(Qt.WA_OpaquePaintEvent, True)
|
|
43
|
+
self.setAttribute(Qt.WA_StaticContents, True)
|
|
44
|
+
|
|
45
|
+
self._act_undo = QAction(QIcon(":/icons/undo.svg"), trans('action.undo'), self)
|
|
46
|
+
self._act_undo.triggered.connect(self.undo)
|
|
47
|
+
|
|
48
|
+
self._act_redo = QAction(QIcon(":/icons/redo.svg"), trans('action.redo'), self)
|
|
49
|
+
self._act_redo.triggered.connect(self.redo)
|
|
50
|
+
|
|
51
|
+
self._act_copy = QAction(QIcon(":/icons/copy.svg"), trans('action.copy'), self)
|
|
52
|
+
self._act_copy.triggered.connect(self.handle_copy)
|
|
53
|
+
|
|
54
|
+
self._act_paste = QAction(QIcon(":/icons/paste.svg"), trans('action.paste'), self)
|
|
55
|
+
self._act_paste.triggered.connect(self.handle_paste)
|
|
56
|
+
|
|
57
|
+
self._act_open = QAction(QIcon(":/icons/folder_filled.svg"), trans('action.open'), self)
|
|
58
|
+
self._act_open.triggered.connect(self.action_open)
|
|
59
|
+
|
|
60
|
+
self._act_capture = QAction(QIcon(":/icons/fullscreen.svg"), trans('painter.btn.capture'), self)
|
|
61
|
+
self._act_capture.triggered.connect(self.action_capture)
|
|
62
|
+
|
|
63
|
+
self._act_save = QAction(QIcon(":/icons/save.svg"), trans('img.action.save'), self)
|
|
64
|
+
self._act_save.triggered.connect(self.action_save)
|
|
65
|
+
|
|
66
|
+
self._act_clear = QAction(QIcon(":/icons/close.svg"), trans('painter.btn.clear'), self)
|
|
67
|
+
self._act_clear.triggered.connect(self.action_clear)
|
|
68
|
+
|
|
69
|
+
self._ctx_menu = QMenu(self)
|
|
70
|
+
self._ctx_menu.addAction(self._act_undo)
|
|
71
|
+
self._ctx_menu.addAction(self._act_redo)
|
|
72
|
+
self._ctx_menu.addAction(self._act_open)
|
|
73
|
+
self._ctx_menu.addAction(self._act_capture)
|
|
74
|
+
self._ctx_menu.addAction(self._act_copy)
|
|
75
|
+
self._ctx_menu.addAction(self._act_paste)
|
|
76
|
+
self._ctx_menu.addAction(self._act_save)
|
|
77
|
+
self._ctx_menu.addAction(self._act_clear)
|
|
41
78
|
|
|
42
79
|
def set_tab(self, tab: Tab):
|
|
43
80
|
"""
|
|
@@ -67,66 +104,14 @@ class PainterWidget(QWidget):
|
|
|
67
104
|
|
|
68
105
|
:param event: Event
|
|
69
106
|
"""
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
actions['undo'].triggered.connect(
|
|
73
|
-
lambda: self.undo())
|
|
74
|
-
if self.has_undo():
|
|
75
|
-
actions['undo'].setEnabled(True)
|
|
76
|
-
else:
|
|
77
|
-
actions['undo'].setEnabled(False)
|
|
78
|
-
|
|
79
|
-
actions['redo'] = QAction(QIcon(":/icons/redo.svg"), trans('action.redo'), self)
|
|
80
|
-
actions['redo'].triggered.connect(
|
|
81
|
-
lambda: self.redo())
|
|
82
|
-
if self.has_redo():
|
|
83
|
-
actions['redo'].setEnabled(True)
|
|
84
|
-
else:
|
|
85
|
-
actions['redo'].setEnabled(False)
|
|
86
|
-
|
|
87
|
-
actions['copy'] = QAction(QIcon(":/icons/copy.svg"), trans('action.copy'), self)
|
|
88
|
-
actions['copy'].triggered.connect(
|
|
89
|
-
lambda: self.handle_copy()
|
|
90
|
-
)
|
|
107
|
+
self._act_undo.setEnabled(self.has_undo())
|
|
108
|
+
self._act_redo.setEnabled(self.has_redo())
|
|
91
109
|
|
|
92
|
-
is_paste = False
|
|
93
110
|
clipboard = QApplication.clipboard()
|
|
94
111
|
mime_data = clipboard.mimeData()
|
|
95
|
-
|
|
96
|
-
is_paste = True
|
|
112
|
+
self._act_paste.setEnabled(bool(mime_data.hasImage()))
|
|
97
113
|
|
|
98
|
-
|
|
99
|
-
actions['paste'].triggered.connect(
|
|
100
|
-
lambda: self.handle_paste()
|
|
101
|
-
)
|
|
102
|
-
if is_paste:
|
|
103
|
-
actions['paste'].setEnabled(True)
|
|
104
|
-
else:
|
|
105
|
-
actions['paste'].setEnabled(False)
|
|
106
|
-
|
|
107
|
-
actions['open'] = QAction(QIcon(":/icons/folder_filled.svg"), trans('action.open'), self)
|
|
108
|
-
actions['open'].triggered.connect(
|
|
109
|
-
lambda: self.action_open())
|
|
110
|
-
actions['capture'] = QAction(QIcon(":/icons/fullscreen.svg"), trans('painter.btn.capture'), self)
|
|
111
|
-
actions['capture'].triggered.connect(
|
|
112
|
-
lambda: self.action_capture())
|
|
113
|
-
actions['save'] = QAction(QIcon(":/icons/save.svg"), trans('img.action.save'), self)
|
|
114
|
-
actions['save'].triggered.connect(
|
|
115
|
-
lambda: self.action_save())
|
|
116
|
-
actions['clear'] = QAction(QIcon(":/icons/close.svg"), trans('painter.btn.clear'), self)
|
|
117
|
-
actions['clear'].triggered.connect(
|
|
118
|
-
lambda: self.action_clear())
|
|
119
|
-
|
|
120
|
-
menu = QMenu(self)
|
|
121
|
-
menu.addAction(actions['undo'])
|
|
122
|
-
menu.addAction(actions['redo'])
|
|
123
|
-
menu.addAction(actions['open'])
|
|
124
|
-
menu.addAction(actions['capture'])
|
|
125
|
-
menu.addAction(actions['copy'])
|
|
126
|
-
menu.addAction(actions['paste'])
|
|
127
|
-
menu.addAction(actions['save'])
|
|
128
|
-
menu.addAction(actions['clear'])
|
|
129
|
-
menu.exec_(event.globalPos())
|
|
114
|
+
self._ctx_menu.exec(event.globalPos())
|
|
130
115
|
|
|
131
116
|
def action_open(self):
|
|
132
117
|
"""Open the image"""
|
|
@@ -195,7 +180,6 @@ class PainterWidget(QWidget):
|
|
|
195
180
|
if image.width() == self.width():
|
|
196
181
|
new = image
|
|
197
182
|
else:
|
|
198
|
-
# to fit the width
|
|
199
183
|
if image.width() > image.height():
|
|
200
184
|
width = self.width()
|
|
201
185
|
height = (image.height() * self.width()) / image.width()
|
|
@@ -215,11 +199,8 @@ class PainterWidget(QWidget):
|
|
|
215
199
|
Qt.SmoothTransformation,
|
|
216
200
|
)
|
|
217
201
|
|
|
218
|
-
self.image
|
|
219
|
-
self.
|
|
220
|
-
self.height(),
|
|
221
|
-
QImage.Format_RGB32,
|
|
222
|
-
)
|
|
202
|
+
if self.image.size() != self.size():
|
|
203
|
+
self.image = QImage(self.size(), QImage.Format_RGB32)
|
|
223
204
|
self.image.fill(Qt.white)
|
|
224
205
|
painter = QPainter(self.image)
|
|
225
206
|
painter.drawImage(0, 0, new)
|
|
@@ -229,22 +210,20 @@ class PainterWidget(QWidget):
|
|
|
229
210
|
|
|
230
211
|
def saveForUndo(self):
|
|
231
212
|
"""Save current state for undo"""
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
self.undoStack.append(self.image.copy())
|
|
235
|
-
self.redoStack.clear() # clear redo on new action
|
|
213
|
+
self.undoStack.append(QImage(self.image))
|
|
214
|
+
self.redoStack.clear()
|
|
236
215
|
|
|
237
216
|
def undo(self):
|
|
238
217
|
"""Undo the last action"""
|
|
239
218
|
if self.undoStack:
|
|
240
|
-
self.redoStack.append(self.image
|
|
219
|
+
self.redoStack.append(QImage(self.image))
|
|
241
220
|
self.image = self.undoStack.pop()
|
|
242
221
|
self.update()
|
|
243
222
|
|
|
244
223
|
def redo(self):
|
|
245
224
|
"""Redo the last undo action"""
|
|
246
225
|
if self.redoStack:
|
|
247
|
-
self.undoStack.append(self.image
|
|
226
|
+
self.undoStack.append(QImage(self.image))
|
|
248
227
|
self.image = self.redoStack.pop()
|
|
249
228
|
self.update()
|
|
250
229
|
|
|
@@ -263,6 +242,7 @@ class PainterWidget(QWidget):
|
|
|
263
242
|
:param color: Color
|
|
264
243
|
"""
|
|
265
244
|
self.brushColor = color
|
|
245
|
+
self._pen.setColor(color)
|
|
266
246
|
|
|
267
247
|
def set_brush_size(self, size):
|
|
268
248
|
"""
|
|
@@ -271,6 +251,7 @@ class PainterWidget(QWidget):
|
|
|
271
251
|
:param size: Brush size
|
|
272
252
|
"""
|
|
273
253
|
self.brushSize = size
|
|
254
|
+
self._pen.setWidth(size)
|
|
274
255
|
|
|
275
256
|
def clear_image(self):
|
|
276
257
|
"""Clear the image"""
|
|
@@ -289,16 +270,9 @@ class PainterWidget(QWidget):
|
|
|
289
270
|
self.lastPoint = event.pos()
|
|
290
271
|
self.saveForUndo()
|
|
291
272
|
painter = QPainter(self.image)
|
|
292
|
-
painter.setPen(
|
|
293
|
-
QPen(
|
|
294
|
-
self.brushColor,
|
|
295
|
-
self.brushSize,
|
|
296
|
-
Qt.SolidLine,
|
|
297
|
-
Qt.RoundCap,
|
|
298
|
-
Qt.RoundJoin,
|
|
299
|
-
)
|
|
300
|
-
)
|
|
273
|
+
painter.setPen(self._pen)
|
|
301
274
|
painter.drawPoint(self.lastPoint)
|
|
275
|
+
painter.end()
|
|
302
276
|
self.update()
|
|
303
277
|
|
|
304
278
|
def mouseMoveEvent(self, event):
|
|
@@ -309,16 +283,9 @@ class PainterWidget(QWidget):
|
|
|
309
283
|
"""
|
|
310
284
|
if (event.buttons() & Qt.LeftButton) and self.drawing:
|
|
311
285
|
painter = QPainter(self.image)
|
|
312
|
-
painter.setPen(
|
|
313
|
-
QPen(
|
|
314
|
-
self.brushColor,
|
|
315
|
-
self.brushSize,
|
|
316
|
-
Qt.SolidLine,
|
|
317
|
-
Qt.RoundCap,
|
|
318
|
-
Qt.RoundJoin,
|
|
319
|
-
)
|
|
320
|
-
)
|
|
286
|
+
painter.setPen(self._pen)
|
|
321
287
|
painter.drawLine(self.lastPoint, event.pos())
|
|
288
|
+
painter.end()
|
|
322
289
|
self.lastPoint = event.pos()
|
|
323
290
|
self.update()
|
|
324
291
|
|
|
@@ -350,12 +317,13 @@ class PainterWidget(QWidget):
|
|
|
350
317
|
"""
|
|
351
318
|
painter = QPainter(self)
|
|
352
319
|
painter.drawImage(self.rect(), self.image, self.image.rect())
|
|
320
|
+
painter.end()
|
|
353
321
|
self.originalImage = self.image
|
|
354
322
|
|
|
355
323
|
def resizeEvent(self, event):
|
|
356
324
|
"""
|
|
357
325
|
Update coords on resize
|
|
358
|
-
|
|
326
|
+
|
|
359
327
|
:param event: Event
|
|
360
328
|
"""
|
|
361
329
|
if self.image.size() != self.size():
|
|
@@ -363,6 +331,7 @@ class PainterWidget(QWidget):
|
|
|
363
331
|
new.fill(Qt.white)
|
|
364
332
|
painter = QPainter(new)
|
|
365
333
|
painter.drawImage(QPoint(0, 0), self.image)
|
|
334
|
+
painter.end()
|
|
366
335
|
self.image = new
|
|
367
336
|
|
|
368
337
|
def eventFilter(self, source, event):
|
|
@@ -376,4 +345,4 @@ class PainterWidget(QWidget):
|
|
|
376
345
|
if self.tab is not None:
|
|
377
346
|
col_idx = self.tab.column_idx
|
|
378
347
|
self.window.controller.ui.tabs.on_column_focus(col_idx)
|
|
379
|
-
return super().eventFilter(source, event)
|
|
348
|
+
return super().eventFilter(source, event)
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2025.08.24 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
13
|
-
from PySide6.QtGui import
|
|
13
|
+
from PySide6.QtGui import QIcon
|
|
14
14
|
from PySide6.QtWidgets import QPushButton, QMenu
|
|
15
15
|
|
|
16
16
|
from pygpt_net.utils import trans
|
|
@@ -22,13 +22,17 @@ class ContextMenuButton(QPushButton):
|
|
|
22
22
|
self.action = action
|
|
23
23
|
|
|
24
24
|
def mousePressEvent(self, event):
|
|
25
|
-
|
|
25
|
+
btn = event.button()
|
|
26
|
+
if btn == Qt.LeftButton or btn == Qt.RightButton:
|
|
26
27
|
self.action(self, event.pos())
|
|
27
28
|
else:
|
|
28
29
|
super().mousePressEvent(event)
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
class NewCtxButton(QPushButton):
|
|
33
|
+
_icon_add = None
|
|
34
|
+
_icon_folder_filled = None
|
|
35
|
+
|
|
32
36
|
def __init__(self, title: str = None, window=None):
|
|
33
37
|
super().__init__(title)
|
|
34
38
|
self.window = window
|
|
@@ -36,6 +40,12 @@ class NewCtxButton(QPushButton):
|
|
|
36
40
|
lambda: self.window.controller.ctx.new()
|
|
37
41
|
)
|
|
38
42
|
|
|
43
|
+
@classmethod
|
|
44
|
+
def _ensure_icons(cls):
|
|
45
|
+
if cls._icon_add is None:
|
|
46
|
+
cls._icon_add = QIcon(":/icons/add.svg")
|
|
47
|
+
cls._icon_folder_filled = QIcon(":/icons/folder_filled.svg")
|
|
48
|
+
|
|
39
49
|
def mousePressEvent(self, event):
|
|
40
50
|
if event.button() == Qt.RightButton:
|
|
41
51
|
self.new_context_menu(self, event.pos())
|
|
@@ -49,32 +59,30 @@ class NewCtxButton(QPushButton):
|
|
|
49
59
|
:param parent: parent widget
|
|
50
60
|
:param pos: mouse position
|
|
51
61
|
"""
|
|
62
|
+
type(self)._ensure_icons()
|
|
52
63
|
group_id = self.window.controller.ctx.group_id
|
|
53
|
-
menu = QMenu(
|
|
54
|
-
actions = {}
|
|
55
|
-
actions['new'] = QAction(QIcon(":/icons/add.svg"), trans('action.ctx.new'), self)
|
|
56
|
-
actions['new'].triggered.connect(
|
|
57
|
-
lambda: self.window.controller.ctx.new_ungrouped()
|
|
58
|
-
)
|
|
64
|
+
menu = QMenu(parent)
|
|
59
65
|
if group_id is not None and group_id > 0:
|
|
60
66
|
group_name = self.window.controller.ctx.get_group_name(group_id)
|
|
61
|
-
|
|
62
|
-
|
|
67
|
+
act_new_in_group = menu.addAction(type(self)._icon_add, trans('action.ctx.new.in_group').format(group=group_name))
|
|
68
|
+
act_new_in_group.triggered.connect(
|
|
63
69
|
lambda checked=False, id=group_id: self.window.controller.ctx.new(force=False, group_id=id)
|
|
64
70
|
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
lambda: self.window.controller.ctx.
|
|
71
|
+
act_new = menu.addAction(type(self)._icon_add, trans('action.ctx.new'))
|
|
72
|
+
act_new.triggered.connect(
|
|
73
|
+
lambda checked=False: self.window.controller.ctx.new_ungrouped()
|
|
74
|
+
)
|
|
75
|
+
act_new_group = menu.addAction(type(self)._icon_folder_filled, trans('menu.file.group.new'))
|
|
76
|
+
act_new_group.triggered.connect(
|
|
77
|
+
lambda checked=False: self.window.controller.ctx.new_group()
|
|
68
78
|
)
|
|
69
|
-
|
|
70
|
-
if group_id is not None and group_id > 0:
|
|
71
|
-
menu.addAction(actions['new_in_group'])
|
|
72
|
-
menu.addAction(actions['new'])
|
|
73
|
-
menu.addAction(actions['new_group'])
|
|
74
79
|
menu.exec_(parent.mapToGlobal(pos))
|
|
80
|
+
menu.deleteLater()
|
|
75
81
|
|
|
76
82
|
|
|
77
83
|
class SyncButton(QPushButton):
|
|
84
|
+
_icon_download = None
|
|
85
|
+
|
|
78
86
|
def __init__(self, title: str = None, window=None):
|
|
79
87
|
super().__init__(title)
|
|
80
88
|
self.window = window
|
|
@@ -82,6 +90,11 @@ class SyncButton(QPushButton):
|
|
|
82
90
|
lambda: self.window.controller.assistant.batch.import_files_current()
|
|
83
91
|
)
|
|
84
92
|
|
|
93
|
+
@classmethod
|
|
94
|
+
def _ensure_icons(cls):
|
|
95
|
+
if cls._icon_download is None:
|
|
96
|
+
cls._icon_download = QIcon(":/icons/download.svg")
|
|
97
|
+
|
|
85
98
|
def mousePressEvent(self, event):
|
|
86
99
|
if event.button() == Qt.RightButton:
|
|
87
100
|
self.new_context_menu(self, event.pos())
|
|
@@ -95,16 +108,15 @@ class SyncButton(QPushButton):
|
|
|
95
108
|
:param parent: parent widget
|
|
96
109
|
:param pos: mouse position
|
|
97
110
|
"""
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
lambda: self.window.controller.assistant.batch.import_files_current()
|
|
111
|
+
type(self)._ensure_icons()
|
|
112
|
+
menu = QMenu(parent)
|
|
113
|
+
act_current = menu.addAction(type(self)._icon_download, trans('attachments_uploaded.btn.sync.current'))
|
|
114
|
+
act_current.triggered.connect(
|
|
115
|
+
lambda checked=False: self.window.controller.assistant.batch.import_files_current()
|
|
103
116
|
)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
lambda: self.window.controller.assistant.batch.import_files()
|
|
117
|
+
act_all = menu.addAction(type(self)._icon_download, trans('attachments_uploaded.btn.sync.all'))
|
|
118
|
+
act_all.triggered.connect(
|
|
119
|
+
lambda checked=False: self.window.controller.assistant.batch.import_files()
|
|
107
120
|
)
|
|
108
|
-
menu.
|
|
109
|
-
menu.
|
|
110
|
-
menu.exec_(parent.mapToGlobal(pos))
|
|
121
|
+
menu.exec_(parent.mapToGlobal(pos))
|
|
122
|
+
menu.deleteLater()
|