pygpt-net 2.6.33__py3-none-any.whl → 2.6.34__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 +7 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/controller/assistant/batch.py +14 -4
- pygpt_net/controller/assistant/files.py +1 -0
- pygpt_net/controller/assistant/store.py +195 -1
- pygpt_net/controller/camera/camera.py +1 -1
- pygpt_net/controller/chat/common.py +50 -46
- pygpt_net/controller/config/placeholder.py +95 -75
- pygpt_net/controller/dialogs/confirm.py +3 -1
- pygpt_net/controller/media/media.py +11 -3
- pygpt_net/controller/painter/common.py +231 -13
- pygpt_net/core/assistants/files.py +18 -0
- pygpt_net/core/camera/camera.py +31 -402
- pygpt_net/core/camera/worker.py +430 -0
- pygpt_net/core/filesystem/url.py +3 -0
- pygpt_net/core/render/web/body.py +65 -9
- pygpt_net/core/text/utils.py +3 -0
- pygpt_net/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/settings.json +10 -5
- pygpt_net/data/locale/locale.de.ini +8 -7
- pygpt_net/data/locale/locale.en.ini +9 -6
- pygpt_net/data/locale/locale.es.ini +8 -7
- pygpt_net/data/locale/locale.fr.ini +8 -7
- pygpt_net/data/locale/locale.it.ini +8 -7
- pygpt_net/data/locale/locale.pl.ini +8 -7
- pygpt_net/data/locale/locale.uk.ini +8 -7
- pygpt_net/data/locale/locale.zh.ini +8 -7
- pygpt_net/item/assistant.py +13 -1
- pygpt_net/provider/api/google/__init__.py +32 -23
- pygpt_net/provider/api/openai/store.py +45 -1
- pygpt_net/provider/llms/google.py +4 -0
- pygpt_net/ui/dialog/assistant_store.py +213 -203
- pygpt_net/ui/layout/chat/input.py +3 -3
- pygpt_net/ui/widget/draw/painter.py +16 -1
- pygpt_net/ui/widget/option/combo.py +5 -1
- pygpt_net/ui/widget/textarea/input.py +273 -3
- {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.34.dist-info}/METADATA +9 -2
- {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.34.dist-info}/RECORD +42 -41
- {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.34.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.34.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.34.dist-info}/entry_points.txt +0 -0
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# ================================================== #
|
|
4
|
+
# This file is a part of PYGPT package #
|
|
5
|
+
# Website: https://pygpt.net #
|
|
6
|
+
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
|
+
# MIT License #
|
|
8
|
+
# Created By : Marcin Szczygliński #
|
|
9
|
+
# Updated Date: 2025.09.02 20:00:00 #
|
|
10
|
+
# ================================================== #
|
|
2
11
|
|
|
3
12
|
from typing import Tuple, Optional, Dict, List
|
|
4
13
|
|
|
5
14
|
from PySide6.QtCore import Qt, QSize
|
|
6
15
|
from PySide6.QtGui import QColor
|
|
16
|
+
from PySide6.QtWidgets import QComboBox
|
|
7
17
|
|
|
8
18
|
|
|
9
19
|
class Common:
|
|
@@ -14,6 +24,12 @@ class Common:
|
|
|
14
24
|
:param window: Window instance
|
|
15
25
|
"""
|
|
16
26
|
self.window = window
|
|
27
|
+
# Guard to prevent re-entrancy when programmatically changing the combo/size
|
|
28
|
+
self._changing_canvas_size = False
|
|
29
|
+
# Cached set for predefined canvas sizes
|
|
30
|
+
self._predef_canvas_sizes_set = None
|
|
31
|
+
# Sticky custom value derived from the current "source" image (kept at index 0 when present)
|
|
32
|
+
self._sticky_custom_value: Optional[str] = None
|
|
17
33
|
|
|
18
34
|
def convert_to_size(self, canvas_size: str) -> Tuple[int, int]:
|
|
19
35
|
"""
|
|
@@ -28,8 +44,8 @@ class Common:
|
|
|
28
44
|
"""
|
|
29
45
|
Set canvas size
|
|
30
46
|
|
|
31
|
-
:param width:
|
|
32
|
-
:param height:
|
|
47
|
+
:param width: Canvas width
|
|
48
|
+
:param height: Canvas height
|
|
33
49
|
"""
|
|
34
50
|
self.window.ui.painter.setFixedSize(QSize(width, height))
|
|
35
51
|
|
|
@@ -69,16 +85,64 @@ class Common:
|
|
|
69
85
|
|
|
70
86
|
:param selected: Selected size
|
|
71
87
|
"""
|
|
88
|
+
# Re-entrancy guard to avoid loops when we adjust the combo programmatically
|
|
89
|
+
if self._changing_canvas_size:
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
combo: QComboBox = self.window.ui.nodes['painter.select.canvas.size']
|
|
93
|
+
painter = self.window.ui.painter
|
|
94
|
+
|
|
95
|
+
# Heuristic to detect manual UI change vs programmatic call
|
|
96
|
+
# - manual if: no arg, or int index (Qt int overload), or arg equals currentText/currentData
|
|
97
|
+
raw_arg = selected
|
|
98
|
+
current_text = combo.currentText()
|
|
99
|
+
current_data = combo.currentData()
|
|
100
|
+
current_data_str = current_data if isinstance(current_data, str) else None
|
|
101
|
+
is_manual = (
|
|
102
|
+
raw_arg is None
|
|
103
|
+
or isinstance(raw_arg, int)
|
|
104
|
+
or (isinstance(raw_arg, str) and (raw_arg == current_text or (current_data_str and raw_arg == current_data_str)))
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Resolve selection if not passed explicitly; fallback to currentText if userData is missing
|
|
72
108
|
if not selected:
|
|
73
|
-
selected =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
109
|
+
selected = current_data_str or current_text
|
|
110
|
+
|
|
111
|
+
# Normalize to "WxH" strictly; if invalid, do nothing
|
|
112
|
+
selected_norm = self._normalize_canvas_value(selected)
|
|
113
|
+
if not selected_norm:
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
# Save undo only for manual changes and only if size will change
|
|
117
|
+
will_change = selected_norm != f"{painter.width()}x{painter.height()}"
|
|
118
|
+
if is_manual and will_change:
|
|
119
|
+
painter.saveForUndo()
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
self._changing_canvas_size = True
|
|
123
|
+
|
|
124
|
+
predef = self._get_predef_canvas_set()
|
|
125
|
+
|
|
126
|
+
# Sticky custom update only for programmatic (source-driven) changes
|
|
127
|
+
programmatic = not is_manual
|
|
128
|
+
if programmatic:
|
|
129
|
+
if selected_norm in predef:
|
|
130
|
+
self._sticky_custom_value = None
|
|
131
|
+
else:
|
|
132
|
+
self._sticky_custom_value = selected_norm
|
|
133
|
+
|
|
134
|
+
# Ensure combo reflects single custom at index 0 (sticky respected), then select current value
|
|
135
|
+
self._sync_canvas_size_combo(combo, selected_norm, sticky_to_keep=self._sticky_custom_value)
|
|
136
|
+
|
|
137
|
+
# Apply canvas size; PainterWidget handles rescaling in resizeEvent
|
|
138
|
+
w, h = self.convert_to_size(selected_norm)
|
|
139
|
+
self.set_canvas_size(w, h)
|
|
140
|
+
|
|
141
|
+
# Persist normalized value
|
|
142
|
+
self.window.core.config.set('painter.canvas.size', selected_norm)
|
|
81
143
|
self.window.core.config.save()
|
|
144
|
+
finally:
|
|
145
|
+
self._changing_canvas_size = False
|
|
82
146
|
|
|
83
147
|
def change_brush_size(self, size: int):
|
|
84
148
|
"""
|
|
@@ -141,7 +205,7 @@ class Common:
|
|
|
141
205
|
if self.window.core.config.has('painter.brush.size'):
|
|
142
206
|
size = int(self.window.core.config.get('painter.brush.size', 3))
|
|
143
207
|
self.window.ui.nodes['painter.select.brush.size'].setCurrentIndex(
|
|
144
|
-
|
|
208
|
+
self.window.ui.nodes['painter.select.brush.size'].findText(str(size))
|
|
145
209
|
)
|
|
146
210
|
|
|
147
211
|
def get_colors(self) -> Dict[str, QColor]:
|
|
@@ -191,4 +255,158 @@ class Common:
|
|
|
191
255
|
|
|
192
256
|
:return: path to capture directory
|
|
193
257
|
"""
|
|
194
|
-
return self.window.core.config.get_user_dir('capture')
|
|
258
|
+
return self.window.core.config.get_user_dir('capture')
|
|
259
|
+
|
|
260
|
+
# ---------- Public sync helper (used by PainterWidget undo/redo) ----------
|
|
261
|
+
|
|
262
|
+
def sync_canvas_combo_from_widget(self):
|
|
263
|
+
"""
|
|
264
|
+
Sync the size combobox with current PainterWidget canvas size.
|
|
265
|
+
Also derive sticky custom from the current source image if it is custom.
|
|
266
|
+
This method does not change the canvas size (UI-only sync).
|
|
267
|
+
"""
|
|
268
|
+
if self._changing_canvas_size:
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
combo: QComboBox = self.window.ui.nodes['painter.select.canvas.size']
|
|
272
|
+
painter = self.window.ui.painter
|
|
273
|
+
|
|
274
|
+
canvas_value = f"{painter.width()}x{painter.height()}"
|
|
275
|
+
canvas_norm = self._normalize_canvas_value(canvas_value)
|
|
276
|
+
if not canvas_norm:
|
|
277
|
+
return
|
|
278
|
+
|
|
279
|
+
# Derive sticky from current source image (if custom)
|
|
280
|
+
predef = self._get_predef_canvas_set()
|
|
281
|
+
sticky = None
|
|
282
|
+
if painter.sourceImageOriginal is not None and not painter.sourceImageOriginal.isNull():
|
|
283
|
+
src_val = f"{painter.sourceImageOriginal.width()}x{painter.sourceImageOriginal.height()}"
|
|
284
|
+
src_val = self._normalize_canvas_value(src_val)
|
|
285
|
+
if src_val and src_val not in predef:
|
|
286
|
+
sticky = src_val
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
self._changing_canvas_size = True
|
|
290
|
+
self._sticky_custom_value = sticky
|
|
291
|
+
self._sync_canvas_size_combo(combo, canvas_norm, sticky_to_keep=sticky)
|
|
292
|
+
|
|
293
|
+
# Persist canvas size only (do not change sticky config-scope)
|
|
294
|
+
self.window.core.config.set('painter.canvas.size', canvas_norm)
|
|
295
|
+
self.window.core.config.save()
|
|
296
|
+
finally:
|
|
297
|
+
self._changing_canvas_size = False
|
|
298
|
+
|
|
299
|
+
# ---------- Internal helpers ----------
|
|
300
|
+
|
|
301
|
+
def _normalize_canvas_value(self, value: Optional[str]) -> Optional[str]:
|
|
302
|
+
"""
|
|
303
|
+
Normalize arbitrary canvas string to canonical 'WxH'. Returns None if invalid.
|
|
304
|
+
Accepts variants like ' 1024 x 768 ', '1024×768', etc.
|
|
305
|
+
|
|
306
|
+
:param value: input value
|
|
307
|
+
:return: normalized value or None
|
|
308
|
+
"""
|
|
309
|
+
if not value:
|
|
310
|
+
return None
|
|
311
|
+
s = str(value).strip().lower().replace(' ', '').replace('×', 'x')
|
|
312
|
+
if 'x' not in s:
|
|
313
|
+
return None
|
|
314
|
+
parts = s.split('x', 1)
|
|
315
|
+
try:
|
|
316
|
+
w = int(parts[0])
|
|
317
|
+
h = int(parts[1])
|
|
318
|
+
except Exception:
|
|
319
|
+
return None
|
|
320
|
+
if w <= 0 or h <= 0:
|
|
321
|
+
return None
|
|
322
|
+
return f"{w}x{h}"
|
|
323
|
+
|
|
324
|
+
def _get_predef_canvas_set(self) -> set:
|
|
325
|
+
"""
|
|
326
|
+
Return cached set of predefined sizes for O(1) lookups.
|
|
327
|
+
|
|
328
|
+
:return: set of predefined sizes
|
|
329
|
+
"""
|
|
330
|
+
if self._predef_canvas_sizes_set is None:
|
|
331
|
+
self._predef_canvas_sizes_set = set(self.get_canvas_sizes())
|
|
332
|
+
return self._predef_canvas_sizes_set
|
|
333
|
+
|
|
334
|
+
def _find_index_for_value(self, combo: QComboBox, value: str) -> int:
|
|
335
|
+
"""
|
|
336
|
+
Find index by userData first, then by text. Returns -1 if not found.
|
|
337
|
+
|
|
338
|
+
:param combo: QComboBox
|
|
339
|
+
:param value: value to find
|
|
340
|
+
:return: index or -1
|
|
341
|
+
"""
|
|
342
|
+
idx = combo.findData(value)
|
|
343
|
+
if idx == -1:
|
|
344
|
+
idx = combo.findText(value, Qt.MatchFixedString)
|
|
345
|
+
return idx
|
|
346
|
+
|
|
347
|
+
def _remove_extra_custom_items(self, combo: QComboBox, predef: set, keep_index: int = -1):
|
|
348
|
+
"""
|
|
349
|
+
Remove all non-predefined items except one at keep_index (if set).
|
|
350
|
+
|
|
351
|
+
:param combo: QComboBox
|
|
352
|
+
:param predef: set of predefined values
|
|
353
|
+
:param keep_index: index to keep even if custom, or -1 to remove all custom
|
|
354
|
+
"""
|
|
355
|
+
for i in range(combo.count() - 1, -1, -1):
|
|
356
|
+
if i == keep_index:
|
|
357
|
+
continue
|
|
358
|
+
txt = combo.itemText(i)
|
|
359
|
+
if txt not in predef:
|
|
360
|
+
combo.removeItem(i)
|
|
361
|
+
|
|
362
|
+
def _ensure_custom_index0(self, combo: QComboBox, custom_value: str, predef: set):
|
|
363
|
+
"""
|
|
364
|
+
Ensure exactly one custom item exists at index 0 with given value.
|
|
365
|
+
|
|
366
|
+
:param combo: QComboBox
|
|
367
|
+
:param custom_value: custom value to set at index 0
|
|
368
|
+
:param predef: set of predefined values
|
|
369
|
+
"""
|
|
370
|
+
if combo.count() > 0 and combo.itemText(0) not in predef:
|
|
371
|
+
if combo.itemText(0) != custom_value:
|
|
372
|
+
combo.setItemText(0, custom_value)
|
|
373
|
+
combo.setItemData(0, custom_value)
|
|
374
|
+
else:
|
|
375
|
+
combo.insertItem(0, custom_value, custom_value)
|
|
376
|
+
self._remove_extra_custom_items(combo, predef, keep_index=0)
|
|
377
|
+
|
|
378
|
+
def _sync_canvas_size_combo(self, combo: QComboBox, value: str, sticky_to_keep: Optional[str]):
|
|
379
|
+
"""
|
|
380
|
+
Enforce invariant and selection:
|
|
381
|
+
- If sticky_to_keep is a custom value -> keep it as single custom item at index 0.
|
|
382
|
+
- If sticky_to_keep is None -> remove all custom items.
|
|
383
|
+
- Select 'value' in the combo. If value is custom and sticky_to_keep differs or is None,
|
|
384
|
+
ensure index 0 matches 'value' and select it.
|
|
385
|
+
|
|
386
|
+
:param combo: QComboBox
|
|
387
|
+
:param value: current canvas size value to select
|
|
388
|
+
:param sticky_to_keep: sticky custom value to keep at index 0, or None
|
|
389
|
+
"""
|
|
390
|
+
predef = self._get_predef_canvas_set()
|
|
391
|
+
|
|
392
|
+
# Maintain sticky custom slot (index 0) if provided
|
|
393
|
+
if sticky_to_keep and sticky_to_keep not in predef:
|
|
394
|
+
self._ensure_custom_index0(combo, sticky_to_keep, predef)
|
|
395
|
+
else:
|
|
396
|
+
self._remove_extra_custom_items(combo, predef, keep_index=-1)
|
|
397
|
+
|
|
398
|
+
# Select the current canvas value
|
|
399
|
+
if value in predef:
|
|
400
|
+
idx = self._find_index_for_value(combo, value)
|
|
401
|
+
if idx != -1 and idx != combo.currentIndex():
|
|
402
|
+
combo.setCurrentIndex(idx)
|
|
403
|
+
elif idx == -1:
|
|
404
|
+
# Fallback: set text (should not normally happen if combo prepopulates predefined sizes)
|
|
405
|
+
combo.setCurrentText(value)
|
|
406
|
+
else:
|
|
407
|
+
# Current value is custom: ensure it exists at index 0 and select it
|
|
408
|
+
# If sticky differs or is None, overwrite/create the custom at index 0 to reflect true current value.
|
|
409
|
+
if not sticky_to_keep or sticky_to_keep != value:
|
|
410
|
+
self._ensure_custom_index0(combo, value, predef)
|
|
411
|
+
if combo.currentIndex() != 0:
|
|
412
|
+
combo.setCurrentIndex(0)
|
|
@@ -257,6 +257,24 @@ class Files:
|
|
|
257
257
|
del self.items[file.record_id]
|
|
258
258
|
return True
|
|
259
259
|
|
|
260
|
+
def delete_by_file_id(self, file_id: str) -> bool:
|
|
261
|
+
"""
|
|
262
|
+
Delete file by file ID
|
|
263
|
+
|
|
264
|
+
:param file_id: file ID
|
|
265
|
+
:return: True if deleted
|
|
266
|
+
"""
|
|
267
|
+
res = self.provider.delete_by_file_id(file_id)
|
|
268
|
+
if res:
|
|
269
|
+
# remove from items
|
|
270
|
+
to_delete = []
|
|
271
|
+
for id in self.items:
|
|
272
|
+
if self.items[id].file_id == file_id:
|
|
273
|
+
to_delete.append(id)
|
|
274
|
+
for id in to_delete:
|
|
275
|
+
del self.items[id]
|
|
276
|
+
return res
|
|
277
|
+
|
|
260
278
|
def on_store_deleted(self, store_id: str):
|
|
261
279
|
"""
|
|
262
280
|
Clear deleted store from files
|