pygpt-net 2.6.29__py3-none-any.whl → 2.6.31__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 +15 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +4 -0
- pygpt_net/{container.py → app_core.py} +5 -6
- pygpt_net/controller/__init__.py +5 -2
- pygpt_net/controller/access/control.py +1 -9
- pygpt_net/controller/assistant/assistant.py +4 -4
- pygpt_net/controller/assistant/batch.py +7 -7
- pygpt_net/controller/assistant/files.py +4 -4
- pygpt_net/controller/assistant/threads.py +3 -3
- pygpt_net/controller/attachment/attachment.py +4 -7
- pygpt_net/controller/audio/audio.py +25 -1
- pygpt_net/controller/audio/ui.py +2 -2
- pygpt_net/controller/chat/audio.py +1 -8
- pygpt_net/controller/chat/common.py +30 -4
- pygpt_net/controller/chat/handler/stream_worker.py +1124 -0
- pygpt_net/controller/chat/output.py +8 -3
- pygpt_net/controller/chat/stream.py +4 -405
- pygpt_net/controller/chat/text.py +3 -2
- pygpt_net/controller/chat/vision.py +11 -19
- pygpt_net/controller/config/placeholder.py +1 -1
- pygpt_net/controller/ctx/ctx.py +1 -1
- pygpt_net/controller/ctx/summarizer.py +1 -1
- pygpt_net/controller/kernel/kernel.py +11 -3
- pygpt_net/controller/kernel/reply.py +5 -1
- pygpt_net/controller/mode/mode.py +21 -12
- pygpt_net/controller/plugins/settings.py +3 -2
- pygpt_net/controller/presets/editor.py +112 -99
- pygpt_net/controller/realtime/__init__.py +12 -0
- pygpt_net/controller/realtime/manager.py +53 -0
- pygpt_net/controller/realtime/realtime.py +268 -0
- pygpt_net/controller/theme/theme.py +3 -2
- pygpt_net/controller/ui/mode.py +7 -0
- pygpt_net/controller/ui/ui.py +19 -1
- pygpt_net/controller/ui/vision.py +4 -4
- pygpt_net/core/agents/legacy.py +2 -2
- pygpt_net/core/agents/runners/openai_workflow.py +2 -2
- pygpt_net/core/assistants/files.py +5 -5
- pygpt_net/core/assistants/store.py +4 -4
- pygpt_net/core/audio/audio.py +6 -1
- pygpt_net/core/audio/backend/native/__init__.py +12 -0
- pygpt_net/core/audio/backend/{native.py → native/native.py} +426 -127
- pygpt_net/core/audio/backend/native/player.py +139 -0
- pygpt_net/core/audio/backend/native/realtime.py +250 -0
- pygpt_net/core/audio/backend/pyaudio/__init__.py +12 -0
- pygpt_net/core/audio/backend/pyaudio/playback.py +194 -0
- pygpt_net/core/audio/backend/pyaudio/pyaudio.py +923 -0
- pygpt_net/core/audio/backend/pyaudio/realtime.py +275 -0
- pygpt_net/core/audio/backend/pygame/__init__.py +12 -0
- pygpt_net/core/audio/backend/{pygame.py → pygame/pygame.py} +130 -19
- pygpt_net/core/audio/backend/shared/__init__.py +38 -0
- pygpt_net/core/audio/backend/shared/conversions.py +211 -0
- pygpt_net/core/audio/backend/shared/envelope.py +38 -0
- pygpt_net/core/audio/backend/shared/player.py +137 -0
- pygpt_net/core/audio/backend/shared/rt.py +52 -0
- pygpt_net/core/audio/capture.py +5 -0
- pygpt_net/core/audio/output.py +13 -2
- pygpt_net/core/audio/whisper.py +6 -2
- pygpt_net/core/bridge/bridge.py +4 -3
- pygpt_net/core/bridge/worker.py +31 -9
- pygpt_net/core/debug/console/console.py +2 -2
- pygpt_net/core/debug/presets.py +2 -2
- pygpt_net/core/dispatcher/dispatcher.py +37 -1
- pygpt_net/core/events/__init__.py +2 -1
- pygpt_net/core/events/realtime.py +55 -0
- pygpt_net/core/experts/experts.py +2 -2
- pygpt_net/core/image/image.py +51 -1
- pygpt_net/core/modes/modes.py +2 -2
- pygpt_net/core/presets/presets.py +3 -3
- pygpt_net/core/realtime/options.py +87 -0
- pygpt_net/core/realtime/shared/__init__.py +0 -0
- pygpt_net/core/realtime/shared/audio.py +213 -0
- pygpt_net/core/realtime/shared/loop.py +64 -0
- pygpt_net/core/realtime/shared/session.py +59 -0
- pygpt_net/core/realtime/shared/text.py +37 -0
- pygpt_net/core/realtime/shared/tools.py +276 -0
- pygpt_net/core/realtime/shared/turn.py +38 -0
- pygpt_net/core/realtime/shared/types.py +16 -0
- pygpt_net/core/realtime/worker.py +164 -0
- pygpt_net/core/tokens/tokens.py +4 -4
- pygpt_net/core/types/__init__.py +1 -0
- pygpt_net/core/types/image.py +48 -0
- pygpt_net/core/types/mode.py +5 -2
- pygpt_net/core/vision/analyzer.py +1 -1
- pygpt_net/data/config/config.json +13 -4
- pygpt_net/data/config/models.json +219 -101
- pygpt_net/data/config/modes.json +3 -9
- pygpt_net/data/config/settings.json +135 -27
- pygpt_net/data/config/settings_section.json +2 -2
- pygpt_net/data/locale/locale.de.ini +7 -7
- pygpt_net/data/locale/locale.en.ini +25 -12
- pygpt_net/data/locale/locale.es.ini +7 -7
- pygpt_net/data/locale/locale.fr.ini +7 -7
- pygpt_net/data/locale/locale.it.ini +7 -7
- pygpt_net/data/locale/locale.pl.ini +8 -8
- pygpt_net/data/locale/locale.uk.ini +7 -7
- pygpt_net/data/locale/locale.zh.ini +3 -3
- pygpt_net/data/locale/plugin.audio_input.en.ini +4 -0
- pygpt_net/data/locale/plugin.audio_output.en.ini +4 -0
- pygpt_net/item/model.py +23 -3
- pygpt_net/plugin/audio_input/plugin.py +37 -4
- pygpt_net/plugin/audio_input/simple.py +57 -8
- pygpt_net/plugin/cmd_files/worker.py +3 -0
- pygpt_net/plugin/openai_dalle/plugin.py +4 -4
- pygpt_net/plugin/openai_vision/plugin.py +12 -13
- pygpt_net/provider/agents/openai/agent.py +5 -5
- pygpt_net/provider/agents/openai/agent_b2b.py +5 -5
- pygpt_net/provider/agents/openai/agent_planner.py +5 -6
- pygpt_net/provider/agents/openai/agent_with_experts.py +5 -5
- pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +4 -4
- pygpt_net/provider/agents/openai/agent_with_feedback.py +4 -4
- pygpt_net/provider/agents/openai/bot_researcher.py +2 -2
- pygpt_net/provider/agents/openai/bots/research_bot/agents/planner_agent.py +1 -1
- pygpt_net/provider/agents/openai/bots/research_bot/agents/search_agent.py +1 -1
- pygpt_net/provider/agents/openai/bots/research_bot/agents/writer_agent.py +1 -1
- pygpt_net/provider/agents/openai/evolve.py +5 -5
- pygpt_net/provider/agents/openai/supervisor.py +4 -4
- pygpt_net/provider/api/__init__.py +27 -0
- pygpt_net/provider/api/anthropic/__init__.py +68 -0
- pygpt_net/provider/api/google/__init__.py +295 -0
- pygpt_net/provider/api/google/audio.py +121 -0
- pygpt_net/provider/api/google/chat.py +591 -0
- pygpt_net/provider/api/google/image.py +427 -0
- pygpt_net/provider/api/google/realtime/__init__.py +12 -0
- pygpt_net/provider/api/google/realtime/client.py +1945 -0
- pygpt_net/provider/api/google/realtime/realtime.py +186 -0
- pygpt_net/provider/api/google/tools.py +222 -0
- pygpt_net/provider/api/google/vision.py +129 -0
- pygpt_net/provider/{gpt → api/openai}/__init__.py +24 -4
- pygpt_net/provider/api/openai/agents/__init__.py +0 -0
- pygpt_net/provider/{gpt → api/openai}/agents/computer.py +1 -1
- pygpt_net/provider/{gpt → api/openai}/agents/experts.py +1 -1
- pygpt_net/provider/{gpt → api/openai}/agents/response.py +1 -1
- pygpt_net/provider/{gpt → api/openai}/assistants.py +1 -1
- pygpt_net/provider/{gpt → api/openai}/chat.py +15 -8
- pygpt_net/provider/{gpt → api/openai}/completion.py +1 -1
- pygpt_net/provider/{gpt → api/openai}/image.py +1 -1
- pygpt_net/provider/api/openai/realtime/__init__.py +12 -0
- pygpt_net/provider/api/openai/realtime/client.py +1828 -0
- pygpt_net/provider/api/openai/realtime/realtime.py +194 -0
- pygpt_net/provider/{gpt → api/openai}/remote_tools.py +1 -1
- pygpt_net/provider/{gpt → api/openai}/responses.py +34 -20
- pygpt_net/provider/{gpt → api/openai}/store.py +2 -2
- pygpt_net/provider/{gpt → api/openai}/vision.py +1 -1
- pygpt_net/provider/api/openai/worker/__init__.py +0 -0
- pygpt_net/provider/{gpt → api/openai}/worker/assistants.py +4 -4
- pygpt_net/provider/{gpt → api/openai}/worker/importer.py +10 -10
- pygpt_net/provider/audio_input/google_genai.py +103 -0
- pygpt_net/provider/audio_input/openai_whisper.py +1 -1
- pygpt_net/provider/audio_output/google_genai_tts.py +229 -0
- pygpt_net/provider/audio_output/openai_tts.py +9 -6
- pygpt_net/provider/core/config/patch.py +26 -0
- pygpt_net/provider/core/model/patch.py +20 -0
- pygpt_net/provider/core/preset/json_file.py +2 -4
- pygpt_net/provider/llms/anthropic.py +2 -5
- pygpt_net/provider/llms/base.py +4 -3
- pygpt_net/provider/llms/google.py +8 -9
- pygpt_net/provider/llms/openai.py +1 -1
- pygpt_net/provider/loaders/hub/image_vision/base.py +1 -1
- pygpt_net/ui/dialog/preset.py +71 -55
- pygpt_net/ui/layout/toolbox/footer.py +16 -0
- pygpt_net/ui/layout/toolbox/image.py +5 -0
- pygpt_net/ui/main.py +6 -4
- pygpt_net/ui/widget/option/combo.py +15 -1
- pygpt_net/utils.py +9 -0
- {pygpt_net-2.6.29.dist-info → pygpt_net-2.6.31.dist-info}/METADATA +55 -55
- {pygpt_net-2.6.29.dist-info → pygpt_net-2.6.31.dist-info}/RECORD +181 -135
- pygpt_net/core/audio/backend/pyaudio.py +0 -554
- /pygpt_net/{provider/gpt/agents → controller/chat/handler}/__init__.py +0 -0
- /pygpt_net/{provider/gpt/worker → core/realtime}/__init__.py +0 -0
- /pygpt_net/provider/{gpt → api/openai}/agents/client.py +0 -0
- /pygpt_net/provider/{gpt → api/openai}/agents/remote_tools.py +0 -0
- /pygpt_net/provider/{gpt → api/openai}/agents/utils.py +0 -0
- /pygpt_net/provider/{gpt → api/openai}/audio.py +0 -0
- /pygpt_net/provider/{gpt → api/openai}/computer.py +0 -0
- /pygpt_net/provider/{gpt → api/openai}/container.py +0 -0
- /pygpt_net/provider/{gpt → api/openai}/summarizer.py +0 -0
- /pygpt_net/provider/{gpt → api/openai}/tools.py +0 -0
- /pygpt_net/provider/{gpt → api/openai}/utils.py +0 -0
- {pygpt_net-2.6.29.dist-info → pygpt_net-2.6.31.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.29.dist-info → pygpt_net-2.6.31.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.29.dist-info → pygpt_net-2.6.31.dist-info}/entry_points.txt +0 -0
pygpt_net/ui/dialog/preset.py
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
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.08.
|
|
9
|
+
# Updated Date: 2025.08.28 09:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
13
13
|
from PySide6.QtWidgets import QPushButton, QHBoxLayout, QLabel, QVBoxLayout, QSplitter, QWidget, QSizePolicy, \
|
|
14
|
-
QTabWidget,
|
|
14
|
+
QTabWidget, QFileDialog
|
|
15
15
|
|
|
16
16
|
from pygpt_net.core.types import (
|
|
17
17
|
MODE_AGENT,
|
|
@@ -51,12 +51,8 @@ class Preset(BaseConfigDialog):
|
|
|
51
51
|
"""Setup preset editor dialog"""
|
|
52
52
|
self.window.ui.nodes['preset.btn.current'] = QPushButton(trans("dialog.preset.btn.current"))
|
|
53
53
|
self.window.ui.nodes['preset.btn.save'] = QPushButton(trans("dialog.preset.btn.save"))
|
|
54
|
-
self.window.ui.nodes['preset.btn.current'].clicked.connect(
|
|
55
|
-
|
|
56
|
-
)
|
|
57
|
-
self.window.ui.nodes['preset.btn.save'].clicked.connect(
|
|
58
|
-
lambda: self.window.controller.presets.editor.save()
|
|
59
|
-
)
|
|
54
|
+
self.window.ui.nodes['preset.btn.current'].clicked.connect(self.window.controller.presets.editor.from_current)
|
|
55
|
+
self.window.ui.nodes['preset.btn.save'].clicked.connect(self.window.controller.presets.editor.save)
|
|
60
56
|
|
|
61
57
|
self.window.ui.nodes['preset.btn.current'].setAutoDefault(False)
|
|
62
58
|
self.window.ui.nodes['preset.btn.save'].setAutoDefault(True)
|
|
@@ -119,14 +115,14 @@ class Preset(BaseConfigDialog):
|
|
|
119
115
|
mode_keys_middle = [
|
|
120
116
|
MODE_COMPLETION,
|
|
121
117
|
MODE_IMAGE,
|
|
122
|
-
MODE_VISION,
|
|
118
|
+
# MODE_VISION,
|
|
123
119
|
MODE_COMPUTER,
|
|
120
|
+
MODE_EXPERT,
|
|
124
121
|
]
|
|
125
122
|
mode_keys_right = [
|
|
126
123
|
MODE_AGENT_LLAMA,
|
|
127
124
|
MODE_AGENT_OPENAI,
|
|
128
125
|
MODE_AGENT,
|
|
129
|
-
MODE_EXPERT,
|
|
130
126
|
]
|
|
131
127
|
|
|
132
128
|
rows_mode_left = QVBoxLayout()
|
|
@@ -156,17 +152,20 @@ class Preset(BaseConfigDialog):
|
|
|
156
152
|
rows_mode.addStretch(1)
|
|
157
153
|
|
|
158
154
|
# modes
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
155
|
+
modes = QWidget()
|
|
156
|
+
modes.setLayout(rows_mode)
|
|
157
|
+
modes.setContentsMargins(0, 0, 0, 0)
|
|
158
|
+
self.window.ui.nodes['preset.editor.modes'] = modes
|
|
162
159
|
|
|
163
160
|
# experts
|
|
164
161
|
self.window.ui.nodes['preset.editor.experts'] = ExpertsEditor(self.window)
|
|
165
162
|
|
|
166
163
|
# desc and prompt
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
164
|
+
desc = QWidget()
|
|
165
|
+
desc.setLayout(options['description'])
|
|
166
|
+
desc.setContentsMargins(0, 5, 0, 5)
|
|
167
|
+
self.window.ui.nodes['preset.editor.description'] = desc
|
|
168
|
+
|
|
170
169
|
|
|
171
170
|
# prompt + extra options
|
|
172
171
|
prompt_layout = QVBoxLayout()
|
|
@@ -195,6 +194,7 @@ class Preset(BaseConfigDialog):
|
|
|
195
194
|
"agent_provider_openai",
|
|
196
195
|
"idx",
|
|
197
196
|
]
|
|
197
|
+
# personalize tab
|
|
198
198
|
personalize_keys = [
|
|
199
199
|
"ai_name",
|
|
200
200
|
"user_name",
|
|
@@ -202,15 +202,18 @@ class Preset(BaseConfigDialog):
|
|
|
202
202
|
"ai_personalize",
|
|
203
203
|
]
|
|
204
204
|
for key in left_keys:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
205
|
+
node_key = f"preset.editor.{key}"
|
|
206
|
+
node = QWidget()
|
|
207
|
+
node.setLayout(options[key])
|
|
208
|
+
node.setContentsMargins(0, 0, 0, 0)
|
|
209
|
+
rows.addWidget(node)
|
|
210
|
+
self.window.ui.nodes[node_key] = node
|
|
209
211
|
|
|
210
212
|
# remote tools
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
remote_tools = QWidget()
|
|
214
|
+
remote_tools.setLayout(options['remote_tools'])
|
|
215
|
+
remote_tools.setContentsMargins(0, 0, 0, 0)
|
|
216
|
+
self.window.ui.nodes['preset.editor.remote_tools'] = remote_tools
|
|
214
217
|
|
|
215
218
|
rows_remote_tools = QVBoxLayout()
|
|
216
219
|
rows_remote_tools.addWidget(self.window.ui.nodes['preset.editor.remote_tools'])
|
|
@@ -222,10 +225,12 @@ class Preset(BaseConfigDialog):
|
|
|
222
225
|
# personalize
|
|
223
226
|
personalize_rows = QVBoxLayout()
|
|
224
227
|
for key in personalize_keys:
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
228
|
+
node_key = f"preset.editor.{key}"
|
|
229
|
+
node = QWidget()
|
|
230
|
+
node.setLayout(options[key])
|
|
231
|
+
node.setContentsMargins(0, 0, 0, 0)
|
|
232
|
+
personalize_rows.addWidget(node)
|
|
233
|
+
self.window.ui.nodes[node_key] = node
|
|
229
234
|
|
|
230
235
|
self.window.ui.nodes['preset.editor.ai_avatar'].setVisible(False)
|
|
231
236
|
|
|
@@ -255,11 +260,12 @@ class Preset(BaseConfigDialog):
|
|
|
255
260
|
widget_main = QWidget()
|
|
256
261
|
widget_main.setLayout(main)
|
|
257
262
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
+
splitter = QSplitter(Qt.Vertical)
|
|
264
|
+
splitter.addWidget(widget_main)
|
|
265
|
+
splitter.addWidget(widget_prompt)
|
|
266
|
+
splitter.setStretchFactor(0, 1)
|
|
267
|
+
splitter.setStretchFactor(1, 2)
|
|
268
|
+
self.window.ui.splitters['editor.presets'] = splitter
|
|
263
269
|
|
|
264
270
|
widget_personalize = QWidget()
|
|
265
271
|
widget_personalize.setLayout(personalize_rows)
|
|
@@ -270,24 +276,26 @@ class Preset(BaseConfigDialog):
|
|
|
270
276
|
widget_experts = QWidget()
|
|
271
277
|
widget_experts.setLayout(experts_rows)
|
|
272
278
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
279
|
+
tabs = QTabWidget()
|
|
280
|
+
tabs.addTab(splitter, trans("preset.tab.general"))
|
|
281
|
+
tabs.addTab(widget_personalize, trans("preset.tab.personalize"))
|
|
282
|
+
tabs.addTab(widget_experts, trans("preset.tab.experts"))
|
|
283
|
+
tabs.addTab(widget_remote_tools, trans("preset.tab.remote_tools"))
|
|
284
|
+
self.window.ui.tabs['preset.editor.tabs'] = tabs
|
|
278
285
|
|
|
279
286
|
layout = QVBoxLayout()
|
|
280
287
|
layout.addWidget(self.window.ui.tabs['preset.editor.tabs'])
|
|
281
288
|
layout.addLayout(footer)
|
|
282
289
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
290
|
+
dialog = EditorDialog(self.window, self.dialog_id)
|
|
291
|
+
dialog.setSizeGripEnabled(True)
|
|
292
|
+
dialog.setWindowFlags(
|
|
293
|
+
dialog.windowFlags() | Qt.WindowMaximizeButtonHint
|
|
287
294
|
)
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
295
|
+
dialog.setLayout(layout)
|
|
296
|
+
dialog.setWindowTitle(trans('dialog.preset'))
|
|
297
|
+
dialog.on_close_callback = self.on_close
|
|
298
|
+
self.window.ui.dialog['editor.' + self.dialog_id] = dialog
|
|
291
299
|
|
|
292
300
|
|
|
293
301
|
def prepare_extra_config(self, prompt_layout):
|
|
@@ -301,15 +309,17 @@ class Preset(BaseConfigDialog):
|
|
|
301
309
|
prompt_widget.setLayout(prompt_layout)
|
|
302
310
|
|
|
303
311
|
self.window.ui.nodes['preset.editor.extra'] = {}
|
|
304
|
-
|
|
305
|
-
|
|
312
|
+
|
|
313
|
+
tabs = QTabWidget()
|
|
314
|
+
tabs.addTab(
|
|
306
315
|
prompt_widget,
|
|
307
316
|
trans("preset.prompt"),
|
|
308
317
|
)
|
|
309
|
-
|
|
310
|
-
|
|
318
|
+
tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
319
|
+
tabs.setMinimumHeight(150)
|
|
311
320
|
layout = QVBoxLayout()
|
|
312
|
-
layout.addWidget(
|
|
321
|
+
layout.addWidget(tabs)
|
|
322
|
+
self.window.ui.tabs['preset.editor.extra'] = tabs
|
|
313
323
|
return layout
|
|
314
324
|
|
|
315
325
|
|
|
@@ -368,6 +378,7 @@ class AvatarWidget(QWidget):
|
|
|
368
378
|
self.init_ui()
|
|
369
379
|
|
|
370
380
|
def init_ui(self):
|
|
381
|
+
"""Initialize the avatar widget UI."""
|
|
371
382
|
main_layout = QVBoxLayout(self)
|
|
372
383
|
main_layout.setContentsMargins(0, 0, 0, 0)
|
|
373
384
|
|
|
@@ -397,13 +408,19 @@ class AvatarWidget(QWidget):
|
|
|
397
408
|
main_layout.addStretch()
|
|
398
409
|
|
|
399
410
|
def open_file_dialog(self):
|
|
411
|
+
"""Open a file dialog to select an avatar image file."""
|
|
400
412
|
file_name, _ = QFileDialog.getOpenFileName(
|
|
401
413
|
self, trans("preset.personalize.avatar.choose.title"), "", "Images (*.png *.jpg *.jpeg *.bmp *.gif *.webp)"
|
|
402
414
|
)
|
|
403
415
|
if file_name:
|
|
404
416
|
self.window.controller.presets.editor.upload_avatar(file_name)
|
|
405
417
|
|
|
406
|
-
def load_avatar(self, file_path):
|
|
418
|
+
def load_avatar(self, file_path: str):
|
|
419
|
+
"""
|
|
420
|
+
Load and display the avatar image from the given file path.
|
|
421
|
+
|
|
422
|
+
:param file_path: Path to the avatar image file
|
|
423
|
+
"""
|
|
407
424
|
from PySide6.QtGui import QPixmap
|
|
408
425
|
pixmap = QPixmap(file_path)
|
|
409
426
|
if not pixmap.isNull():
|
|
@@ -420,9 +437,7 @@ class AvatarWidget(QWidget):
|
|
|
420
437
|
self.remove_button.setEnabled(enabled)
|
|
421
438
|
|
|
422
439
|
def disable_remove_button(self):
|
|
423
|
-
"""
|
|
424
|
-
Disable the remove button.
|
|
425
|
-
"""
|
|
440
|
+
"""Disable the remove button."""
|
|
426
441
|
self.enable_remove_button(False)
|
|
427
442
|
|
|
428
443
|
def get_cover_pixmap(self, pixmap, target_width, target_height):
|
|
@@ -432,6 +447,7 @@ class AvatarWidget(QWidget):
|
|
|
432
447
|
:param pixmap: Original pixmap
|
|
433
448
|
:param target_width: Target width for the avatar preview
|
|
434
449
|
:param target_height: Target height for the avatar preview
|
|
450
|
+
:return: Scaled and cropped pixmap
|
|
435
451
|
"""
|
|
436
452
|
factor = max(target_width / pixmap.width(), target_height / pixmap.height())
|
|
437
453
|
new_width = int(pixmap.width() * factor)
|
|
@@ -439,9 +455,9 @@ class AvatarWidget(QWidget):
|
|
|
439
455
|
scaled_pix = pixmap.scaled(new_width, new_height, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
|
440
456
|
x = (scaled_pix.width() - target_width) // 2
|
|
441
457
|
y = (scaled_pix.height() - target_height) // 2
|
|
442
|
-
|
|
443
|
-
return cropped_pix
|
|
458
|
+
return scaled_pix.copy(x, y, target_width, target_height)
|
|
444
459
|
|
|
445
460
|
def remove_avatar(self):
|
|
461
|
+
"""Remove the current avatar image."""
|
|
446
462
|
self.avatar_preview.clear()
|
|
447
463
|
self.remove_button.setEnabled(False)
|
|
@@ -81,6 +81,21 @@ class Footer:
|
|
|
81
81
|
rows.addWidget(self.window.ui.nodes['voice.control.btn'])
|
|
82
82
|
rows.setContentsMargins(2, 0, 0, 0)
|
|
83
83
|
|
|
84
|
+
self.window.ui.nodes['audio.auto_turn'] = ToggleLabel(trans('audio.auto_turn'), label_position="left",
|
|
85
|
+
icon=":/icons/voice.svg",
|
|
86
|
+
parent=self.window)
|
|
87
|
+
self.window.ui.nodes['audio.auto_turn'].box.toggled.connect(
|
|
88
|
+
self.window.controller.audio.toggle_auto_turn
|
|
89
|
+
)
|
|
90
|
+
auto_turn_widget = QWidget(widget)
|
|
91
|
+
auto_turn_layout = QHBoxLayout(auto_turn_widget)
|
|
92
|
+
|
|
93
|
+
auto_turn_layout.addWidget(QLabel("", auto_turn_widget))
|
|
94
|
+
auto_turn_layout.addStretch(1)
|
|
95
|
+
auto_turn_layout.addWidget(self.window.ui.nodes['audio.auto_turn'])
|
|
96
|
+
auto_turn_layout.setContentsMargins(5, 0, 15, 0)
|
|
97
|
+
rows.addWidget(auto_turn_widget)
|
|
98
|
+
|
|
84
99
|
self.window.ui.nodes['layout.split'] = ToggleLabel(trans('layout.split'), label_position="left",
|
|
85
100
|
icon=":/icons/split_screen.svg",
|
|
86
101
|
parent=self.window)
|
|
@@ -90,6 +105,7 @@ class Footer:
|
|
|
90
105
|
split_widget = QWidget(widget)
|
|
91
106
|
split_layout = QHBoxLayout(split_widget)
|
|
92
107
|
|
|
108
|
+
|
|
93
109
|
split_layout.addWidget(QLabel("", split_widget))
|
|
94
110
|
split_layout.addStretch(1)
|
|
95
111
|
split_layout.addWidget(self.window.ui.nodes['layout.split'])
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
from PySide6.QtWidgets import QVBoxLayout, QLabel, QHBoxLayout, QWidget, QCheckBox
|
|
13
13
|
|
|
14
|
+
from pygpt_net.ui.widget.option.combo import OptionCombo
|
|
14
15
|
from pygpt_net.ui.widget.option.slider import OptionSlider
|
|
15
16
|
from pygpt_net.utils import trans
|
|
16
17
|
|
|
@@ -55,6 +56,9 @@ class Image:
|
|
|
55
56
|
conf_global['img_raw'] = QCheckBox(trans("img.raw"), parent=container)
|
|
56
57
|
conf_global['img_raw'].toggled.connect(self.window.controller.chat.common.img_toggle_raw)
|
|
57
58
|
|
|
59
|
+
option_resolutions = self.window.core.image.get_resolution_option()
|
|
60
|
+
conf_global['img_resolution'] = OptionCombo(self.window, 'global', 'img_resolution', option_resolutions)
|
|
61
|
+
|
|
58
62
|
cols = QHBoxLayout()
|
|
59
63
|
cols.addWidget(conf_global['img_raw'])
|
|
60
64
|
cols.addWidget(conf_global['img_variants'])
|
|
@@ -62,6 +66,7 @@ class Image:
|
|
|
62
66
|
rows = QVBoxLayout()
|
|
63
67
|
rows.addWidget(ui.nodes['img_variants.label'])
|
|
64
68
|
rows.addLayout(cols)
|
|
69
|
+
rows.addWidget(conf_global['img_resolution'])
|
|
65
70
|
rows.setContentsMargins(2, 5, 5, 5)
|
|
66
71
|
|
|
67
72
|
container.setLayout(rows)
|
pygpt_net/ui/main.py
CHANGED
|
@@ -17,12 +17,13 @@ from PySide6.QtWidgets import QMainWindow, QApplication
|
|
|
17
17
|
from qt_material import QtStyleTools
|
|
18
18
|
|
|
19
19
|
from pygpt_net.core.events import BaseEvent, KernelEvent, ControlEvent
|
|
20
|
-
from pygpt_net.
|
|
20
|
+
from pygpt_net.app_core import Core
|
|
21
21
|
from pygpt_net.controller import Controller
|
|
22
22
|
from pygpt_net.tools import Tools
|
|
23
23
|
from pygpt_net.ui import UI
|
|
24
24
|
from pygpt_net.ui.widget.textarea.web import ChatWebOutput
|
|
25
|
-
from pygpt_net.utils import get_app_meta
|
|
25
|
+
from pygpt_net.utils import get_app_meta, freeze_updates
|
|
26
|
+
|
|
26
27
|
|
|
27
28
|
class MainWindow(QMainWindow, QtStyleTools):
|
|
28
29
|
|
|
@@ -63,7 +64,7 @@ class MainWindow(QMainWindow, QtStyleTools):
|
|
|
63
64
|
self.meta = get_app_meta()
|
|
64
65
|
|
|
65
66
|
# setup service container
|
|
66
|
-
self.core =
|
|
67
|
+
self.core = Core(self)
|
|
67
68
|
self.core.init()
|
|
68
69
|
self.core.patch() # patch version if needed
|
|
69
70
|
self.core.post_setup()
|
|
@@ -85,7 +86,8 @@ class MainWindow(QMainWindow, QtStyleTools):
|
|
|
85
86
|
|
|
86
87
|
# setup UI
|
|
87
88
|
self.ui = UI(self)
|
|
88
|
-
self
|
|
89
|
+
with freeze_updates(self):
|
|
90
|
+
self.ui.init()
|
|
89
91
|
|
|
90
92
|
# global shortcuts
|
|
91
93
|
self.shortcuts = []
|
|
@@ -79,6 +79,7 @@ class OptionCombo(QWidget):
|
|
|
79
79
|
self.combo = NoScrollCombo()
|
|
80
80
|
self.combo.currentIndexChanged.connect(self.on_combo_change)
|
|
81
81
|
self.current_id = None
|
|
82
|
+
self.locked = False
|
|
82
83
|
|
|
83
84
|
# add items
|
|
84
85
|
self.update()
|
|
@@ -114,6 +115,12 @@ class OptionCombo(QWidget):
|
|
|
114
115
|
self.combo.addItem(value, key)
|
|
115
116
|
else:
|
|
116
117
|
self.combo.addItem(item, item)
|
|
118
|
+
elif type(self.keys) is dict:
|
|
119
|
+
for key, value in self.keys.items():
|
|
120
|
+
if key.startswith("separator::"):
|
|
121
|
+
self.combo.addSeparator(value)
|
|
122
|
+
else:
|
|
123
|
+
self.combo.addItem(value, key)
|
|
117
124
|
|
|
118
125
|
def set_value(self, value):
|
|
119
126
|
"""
|
|
@@ -135,16 +142,21 @@ class OptionCombo(QWidget):
|
|
|
135
142
|
"""
|
|
136
143
|
return self.current_id
|
|
137
144
|
|
|
138
|
-
def set_keys(self, keys):
|
|
145
|
+
def set_keys(self, keys, lock: bool = False):
|
|
139
146
|
"""
|
|
140
147
|
Set keys
|
|
141
148
|
|
|
142
149
|
:param keys: keys
|
|
150
|
+
:param lock: lock current value if True
|
|
143
151
|
"""
|
|
152
|
+
if lock:
|
|
153
|
+
self.locked = True # lock on_change
|
|
144
154
|
self.keys = keys
|
|
145
155
|
self.option["keys"] = keys
|
|
146
156
|
self.combo.clear()
|
|
147
157
|
self.update()
|
|
158
|
+
if lock:
|
|
159
|
+
self.locked = False
|
|
148
160
|
|
|
149
161
|
def on_combo_change(self, index):
|
|
150
162
|
"""
|
|
@@ -153,6 +165,8 @@ class OptionCombo(QWidget):
|
|
|
153
165
|
:param index: combo index
|
|
154
166
|
:return:
|
|
155
167
|
"""
|
|
168
|
+
if self.locked:
|
|
169
|
+
return
|
|
156
170
|
self.current_id = self.combo.itemData(index)
|
|
157
171
|
self.window.controller.config.combo.on_update(self.parent_id, self.id, self.option, self.current_id)
|
|
158
172
|
|
pygpt_net/utils.py
CHANGED
|
@@ -13,6 +13,7 @@ import json
|
|
|
13
13
|
import os
|
|
14
14
|
import re
|
|
15
15
|
from datetime import datetime
|
|
16
|
+
from contextlib import contextmanager
|
|
16
17
|
|
|
17
18
|
from PySide6 import QtCore, QtGui
|
|
18
19
|
from PySide6.QtWidgets import QApplication
|
|
@@ -61,6 +62,14 @@ def trans(key: str, reload: bool = False, domain: str = None) -> str:
|
|
|
61
62
|
return locale.get(key, domain)
|
|
62
63
|
|
|
63
64
|
|
|
65
|
+
@contextmanager
|
|
66
|
+
def freeze_updates(widget):
|
|
67
|
+
widget.setUpdatesEnabled(False)
|
|
68
|
+
try:
|
|
69
|
+
yield
|
|
70
|
+
finally:
|
|
71
|
+
widget.setUpdatesEnabled(True)
|
|
72
|
+
|
|
64
73
|
def get_init_value(key: str = "__version__") -> str:
|
|
65
74
|
"""
|
|
66
75
|
Return config value from __init__.py
|