pygpt-net 2.6.3__py3-none-any.whl → 2.6.6__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 +14 -0
- pygpt_net/__init__.py +2 -2
- pygpt_net/app.py +6 -1
- pygpt_net/config.py +55 -65
- pygpt_net/controller/__init__.py +5 -2
- pygpt_net/controller/chat/chat.py +38 -35
- pygpt_net/controller/chat/render.py +144 -217
- pygpt_net/controller/chat/stream.py +52 -25
- pygpt_net/controller/config/config.py +39 -42
- pygpt_net/controller/config/field/checkbox.py +16 -12
- pygpt_net/controller/config/field/checkbox_list.py +36 -31
- pygpt_net/controller/config/field/cmd.py +51 -57
- pygpt_net/controller/config/field/combo.py +33 -16
- pygpt_net/controller/config/field/dictionary.py +48 -55
- pygpt_net/controller/config/field/input.py +50 -32
- pygpt_net/controller/config/field/slider.py +40 -45
- pygpt_net/controller/config/field/textarea.py +20 -6
- pygpt_net/controller/config/placeholder.py +110 -231
- pygpt_net/controller/ctx/common.py +48 -49
- pygpt_net/controller/ctx/ctx.py +24 -4
- pygpt_net/controller/lang/mapping.py +57 -95
- pygpt_net/controller/lang/plugins.py +64 -55
- pygpt_net/controller/lang/settings.py +39 -38
- pygpt_net/controller/layout/layout.py +11 -2
- pygpt_net/controller/plugins/plugins.py +19 -1
- pygpt_net/controller/settings/profile.py +16 -4
- pygpt_net/controller/ui/mode.py +107 -125
- pygpt_net/core/bridge/bridge.py +5 -5
- pygpt_net/core/command/command.py +149 -219
- pygpt_net/core/ctx/ctx.py +94 -146
- pygpt_net/core/debug/debug.py +48 -58
- pygpt_net/core/models/models.py +74 -112
- pygpt_net/core/modes/modes.py +13 -21
- pygpt_net/core/plugins/plugins.py +154 -177
- pygpt_net/core/presets/presets.py +103 -176
- pygpt_net/core/render/web/body.py +50 -39
- pygpt_net/core/render/web/renderer.py +154 -251
- pygpt_net/core/text/utils.py +28 -44
- pygpt_net/core/tokens/tokens.py +104 -203
- pygpt_net/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/item/ctx.py +141 -139
- pygpt_net/plugin/agent/plugin.py +2 -1
- pygpt_net/plugin/audio_output/plugin.py +5 -2
- pygpt_net/plugin/base/plugin.py +77 -93
- pygpt_net/plugin/bitbucket/plugin.py +3 -2
- pygpt_net/plugin/cmd_code_interpreter/plugin.py +3 -2
- pygpt_net/plugin/cmd_custom/plugin.py +3 -2
- pygpt_net/plugin/cmd_files/plugin.py +3 -2
- pygpt_net/plugin/cmd_history/plugin.py +3 -2
- pygpt_net/plugin/cmd_mouse_control/plugin.py +5 -2
- pygpt_net/plugin/cmd_serial/plugin.py +3 -2
- pygpt_net/plugin/cmd_system/plugin.py +3 -6
- pygpt_net/plugin/cmd_web/plugin.py +3 -2
- pygpt_net/plugin/experts/plugin.py +2 -2
- pygpt_net/plugin/facebook/plugin.py +3 -4
- pygpt_net/plugin/github/plugin.py +4 -2
- pygpt_net/plugin/google/plugin.py +3 -3
- pygpt_net/plugin/idx_llama_index/plugin.py +3 -2
- pygpt_net/plugin/mailer/plugin.py +3 -5
- pygpt_net/plugin/openai_vision/plugin.py +3 -2
- pygpt_net/plugin/real_time/plugin.py +52 -60
- pygpt_net/plugin/slack/plugin.py +3 -4
- pygpt_net/plugin/telegram/plugin.py +3 -4
- pygpt_net/plugin/twitter/plugin.py +3 -4
- pygpt_net/tools/code_interpreter/tool.py +0 -1
- pygpt_net/tools/translator/tool.py +1 -1
- pygpt_net/ui/layout/ctx/ctx_list.py +10 -6
- pygpt_net/ui/main.py +46 -30
- pygpt_net/ui/tray.py +61 -60
- pygpt_net/ui/widget/lists/context.py +2 -2
- pygpt_net/ui/widget/textarea/web.py +161 -48
- pygpt_net/utils.py +8 -1
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.6.dist-info}/METADATA +16 -2
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.6.dist-info}/RECORD +78 -78
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.6.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.6.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.6.dist-info}/entry_points.txt +0 -0
|
@@ -6,11 +6,13 @@
|
|
|
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.16 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import base64
|
|
13
|
-
|
|
13
|
+
import gc
|
|
14
|
+
import io
|
|
15
|
+
from typing import Optional, Literal
|
|
14
16
|
|
|
15
17
|
from PySide6.QtCore import QObject, Signal, Slot, QRunnable
|
|
16
18
|
|
|
@@ -20,6 +22,32 @@ from pygpt_net.core.types import MODE_ASSISTANT
|
|
|
20
22
|
from pygpt_net.core.text.utils import has_unclosed_code_tag
|
|
21
23
|
from pygpt_net.item.ctx import CtxItem
|
|
22
24
|
|
|
25
|
+
EventType = Literal[
|
|
26
|
+
"response.completed",
|
|
27
|
+
"response.output_text.delta",
|
|
28
|
+
"response.output_item.added",
|
|
29
|
+
"response.function_call_arguments.delta",
|
|
30
|
+
"response.function_call_arguments.done",
|
|
31
|
+
"response.output_text.annotation.added",
|
|
32
|
+
"response.reasoning_summary_text.delta",
|
|
33
|
+
"response.output_item.done",
|
|
34
|
+
"response.code_interpreter_call_code.delta",
|
|
35
|
+
"response.code_interpreter_call_code.done",
|
|
36
|
+
"response.image_generation_call.partial_image",
|
|
37
|
+
"response.created",
|
|
38
|
+
"response.done",
|
|
39
|
+
"response.failed",
|
|
40
|
+
"error",
|
|
41
|
+
]
|
|
42
|
+
ChunkType = Literal[
|
|
43
|
+
"api_chat",
|
|
44
|
+
"api_chat_responses",
|
|
45
|
+
"api_completion",
|
|
46
|
+
"langchain_chat",
|
|
47
|
+
"llama_chat",
|
|
48
|
+
"raw",
|
|
49
|
+
]
|
|
50
|
+
|
|
23
51
|
class WorkerSignals(QObject):
|
|
24
52
|
"""
|
|
25
53
|
Defines the signals available from a running worker thread.
|
|
@@ -54,15 +82,15 @@ class StreamWorker(QRunnable):
|
|
|
54
82
|
output_tokens = 0
|
|
55
83
|
begin = True
|
|
56
84
|
error = None
|
|
57
|
-
fn_args_buffers = {}
|
|
58
|
-
citations = []
|
|
85
|
+
fn_args_buffers: dict[str, io.StringIO] = {}
|
|
86
|
+
citations: Optional[list] = []
|
|
59
87
|
files = []
|
|
60
88
|
img_path = core.image.gen_unique_path(ctx)
|
|
61
89
|
is_image = False
|
|
62
90
|
is_code = False
|
|
63
91
|
force_func_call = False
|
|
64
92
|
stopped = False
|
|
65
|
-
chunk_type = "raw"
|
|
93
|
+
chunk_type: ChunkType = "raw"
|
|
66
94
|
generator = ctx.stream
|
|
67
95
|
ctx.stream = None
|
|
68
96
|
|
|
@@ -101,12 +129,12 @@ class StreamWorker(QRunnable):
|
|
|
101
129
|
stopped = True
|
|
102
130
|
break
|
|
103
131
|
|
|
104
|
-
etype = None
|
|
132
|
+
etype: Optional[EventType] = None
|
|
105
133
|
response = None
|
|
106
134
|
|
|
107
135
|
if ctx.use_responses_api:
|
|
108
136
|
if hasattr(chunk, 'type'):
|
|
109
|
-
etype = chunk.type
|
|
137
|
+
etype = chunk.type # type: ignore[assignment]
|
|
110
138
|
chunk_type = "api_chat_responses"
|
|
111
139
|
else:
|
|
112
140
|
continue
|
|
@@ -128,7 +156,6 @@ class StreamWorker(QRunnable):
|
|
|
128
156
|
else:
|
|
129
157
|
chunk_type = "raw"
|
|
130
158
|
|
|
131
|
-
# OpenAI chat completion
|
|
132
159
|
if chunk_type == "api_chat":
|
|
133
160
|
citations = None
|
|
134
161
|
delta = chunk.choices[0].delta
|
|
@@ -158,7 +185,6 @@ class StreamWorker(QRunnable):
|
|
|
158
185
|
if getattr(tool_chunk.function, "arguments", None):
|
|
159
186
|
tool_call["function"]["arguments"] += tool_chunk.function.arguments
|
|
160
187
|
|
|
161
|
-
# OpenAI Responses API
|
|
162
188
|
elif chunk_type == "api_chat_responses":
|
|
163
189
|
if etype == "response.completed":
|
|
164
190
|
for item in chunk.response.output:
|
|
@@ -197,7 +223,6 @@ class StreamWorker(QRunnable):
|
|
|
197
223
|
elif etype == "response.output_text.delta":
|
|
198
224
|
response = chunk.delta
|
|
199
225
|
|
|
200
|
-
# function_call
|
|
201
226
|
elif etype == "response.output_item.added" and chunk.item.type == "function_call":
|
|
202
227
|
tool_calls.append({
|
|
203
228
|
"id": chunk.item.id,
|
|
@@ -205,18 +230,23 @@ class StreamWorker(QRunnable):
|
|
|
205
230
|
"type": "function",
|
|
206
231
|
"function": {"name": chunk.item.name, "arguments": ""}
|
|
207
232
|
})
|
|
208
|
-
fn_args_buffers[chunk.item.id] =
|
|
233
|
+
fn_args_buffers[chunk.item.id] = io.StringIO()
|
|
209
234
|
elif etype == "response.function_call_arguments.delta":
|
|
210
|
-
fn_args_buffers
|
|
235
|
+
buf = fn_args_buffers.get(chunk.item_id)
|
|
236
|
+
if buf is not None:
|
|
237
|
+
buf.write(chunk.delta)
|
|
211
238
|
elif etype == "response.function_call_arguments.done":
|
|
212
239
|
buf = fn_args_buffers.pop(chunk.item_id, None)
|
|
213
240
|
if buf is not None:
|
|
241
|
+
try:
|
|
242
|
+
args_val = buf.getvalue()
|
|
243
|
+
finally:
|
|
244
|
+
buf.close()
|
|
214
245
|
for tc in tool_calls:
|
|
215
246
|
if tc["id"] == chunk.item_id:
|
|
216
|
-
tc["function"]["arguments"] =
|
|
247
|
+
tc["function"]["arguments"] = args_val
|
|
217
248
|
break
|
|
218
249
|
|
|
219
|
-
# annotations
|
|
220
250
|
elif etype == "response.output_text.annotation.added":
|
|
221
251
|
ann = chunk.annotation
|
|
222
252
|
if ann['type'] == "url_citation":
|
|
@@ -231,7 +261,6 @@ class StreamWorker(QRunnable):
|
|
|
231
261
|
"file_id": ann['file_id'],
|
|
232
262
|
})
|
|
233
263
|
|
|
234
|
-
# computer use
|
|
235
264
|
elif etype == "response.reasoning_summary_text.delta":
|
|
236
265
|
response = chunk.delta
|
|
237
266
|
|
|
@@ -240,7 +269,6 @@ class StreamWorker(QRunnable):
|
|
|
240
269
|
if has_calls:
|
|
241
270
|
force_func_call = True
|
|
242
271
|
|
|
243
|
-
# code interpreter
|
|
244
272
|
elif etype == "response.code_interpreter_call_code.delta":
|
|
245
273
|
if not is_code:
|
|
246
274
|
response = "\n\n**Code interpreter**\n```python\n" + chunk.delta
|
|
@@ -250,15 +278,14 @@ class StreamWorker(QRunnable):
|
|
|
250
278
|
elif etype == "response.code_interpreter_call_code.done":
|
|
251
279
|
response = "\n\n```\n-----------\n"
|
|
252
280
|
|
|
253
|
-
# image gen
|
|
254
281
|
elif etype == "response.image_generation_call.partial_image":
|
|
255
282
|
image_base64 = chunk.partial_image_b64
|
|
256
283
|
image_bytes = base64.b64decode(image_base64)
|
|
257
284
|
with open(img_path, "wb") as f:
|
|
258
285
|
f.write(image_bytes)
|
|
286
|
+
del image_bytes
|
|
259
287
|
is_image = True
|
|
260
288
|
|
|
261
|
-
# response ID
|
|
262
289
|
elif etype == "response.created":
|
|
263
290
|
ctx.msg_id = str(chunk.response.id)
|
|
264
291
|
core.ctx.update_item(ctx)
|
|
@@ -266,18 +293,15 @@ class StreamWorker(QRunnable):
|
|
|
266
293
|
elif etype in {"response.done", "response.failed", "error"}:
|
|
267
294
|
pass
|
|
268
295
|
|
|
269
|
-
# OpenAI completion
|
|
270
296
|
elif chunk_type == "api_completion":
|
|
271
297
|
choice0 = chunk.choices[0]
|
|
272
298
|
if choice0.text is not None:
|
|
273
299
|
response = choice0.text
|
|
274
300
|
|
|
275
|
-
# langchain chat
|
|
276
301
|
elif chunk_type == "langchain_chat":
|
|
277
302
|
if chunk.content is not None:
|
|
278
303
|
response = str(chunk.content)
|
|
279
304
|
|
|
280
|
-
# llama chat
|
|
281
305
|
elif chunk_type == "llama_chat":
|
|
282
306
|
if chunk.delta is not None:
|
|
283
307
|
response = str(chunk.delta)
|
|
@@ -301,7 +325,6 @@ class StreamWorker(QRunnable):
|
|
|
301
325
|
tool_calls.clear()
|
|
302
326
|
tool_calls.append(tool_call)
|
|
303
327
|
|
|
304
|
-
# raw text (llama-index / langchain completion)
|
|
305
328
|
else:
|
|
306
329
|
if chunk is not None:
|
|
307
330
|
response = str(chunk)
|
|
@@ -326,13 +349,11 @@ class StreamWorker(QRunnable):
|
|
|
326
349
|
|
|
327
350
|
chunk = None
|
|
328
351
|
|
|
329
|
-
# tool calls
|
|
330
352
|
if tool_calls:
|
|
331
353
|
ctx.force_call = force_func_call
|
|
332
354
|
core.debug.info("[chat] Tool calls found, unpacking...")
|
|
333
355
|
core.command.unpack_tool_calls_chunks(ctx, tool_calls)
|
|
334
356
|
|
|
335
|
-
# image
|
|
336
357
|
if is_image:
|
|
337
358
|
core.debug.info("[chat] Image generation call found")
|
|
338
359
|
ctx.images = [img_path]
|
|
@@ -371,11 +392,17 @@ class StreamWorker(QRunnable):
|
|
|
371
392
|
|
|
372
393
|
emit_end(ctx)
|
|
373
394
|
|
|
395
|
+
for _buf in fn_args_buffers.values():
|
|
396
|
+
try:
|
|
397
|
+
_buf.close()
|
|
398
|
+
except Exception:
|
|
399
|
+
pass
|
|
374
400
|
fn_args_buffers.clear()
|
|
375
401
|
files.clear()
|
|
376
402
|
tool_calls.clear()
|
|
377
|
-
if citations is not None:
|
|
403
|
+
if citations is not None and citations is not ctx.urls:
|
|
378
404
|
citations.clear()
|
|
405
|
+
citations = None
|
|
379
406
|
|
|
380
407
|
self.cleanup()
|
|
381
408
|
|
|
@@ -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: 2025.
|
|
9
|
+
# Updated Date: 2025.08.15 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Any, Dict, List
|
|
@@ -40,6 +40,24 @@ class Config:
|
|
|
40
40
|
self.slider = Slider(window)
|
|
41
41
|
self.textarea = Textarea(window)
|
|
42
42
|
|
|
43
|
+
self._apply_map = {
|
|
44
|
+
'text': self.input.apply,
|
|
45
|
+
'textarea': self.textarea.apply,
|
|
46
|
+
'bool': self.checkbox.apply,
|
|
47
|
+
'bool_list': self.checkbox_list.apply,
|
|
48
|
+
'dict': self.dictionary.apply,
|
|
49
|
+
'combo': self.combo.apply,
|
|
50
|
+
'cmd': self.cmd.apply,
|
|
51
|
+
}
|
|
52
|
+
self._get_map = {
|
|
53
|
+
'text': self.input.get_value,
|
|
54
|
+
'textarea': self.textarea.get_value,
|
|
55
|
+
'bool': self.checkbox.get_value,
|
|
56
|
+
'bool_list': self.checkbox_list.get_value,
|
|
57
|
+
'dict': self.dictionary.get_value,
|
|
58
|
+
'cmd': self.cmd.get_value,
|
|
59
|
+
}
|
|
60
|
+
|
|
43
61
|
def load_options(
|
|
44
62
|
self,
|
|
45
63
|
parent_id: str,
|
|
@@ -51,8 +69,7 @@ class Config:
|
|
|
51
69
|
:param parent_id: Parent ID
|
|
52
70
|
:param options: Options dict
|
|
53
71
|
"""
|
|
54
|
-
for key in options:
|
|
55
|
-
option = options[key]
|
|
72
|
+
for key, option in options.items():
|
|
56
73
|
self.apply(parent_id, key, option)
|
|
57
74
|
|
|
58
75
|
def apply(
|
|
@@ -68,25 +85,16 @@ class Config:
|
|
|
68
85
|
:param key: Option key
|
|
69
86
|
:param option: Option dict
|
|
70
87
|
"""
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
t = option['type']
|
|
89
|
+
if t in ('int', 'float'):
|
|
90
|
+
if option.get('slider'):
|
|
73
91
|
self.slider.apply(parent_id, key, option)
|
|
74
92
|
else:
|
|
75
93
|
self.input.apply(parent_id, key, option)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
elif option['type'] == 'bool':
|
|
81
|
-
self.checkbox.apply(parent_id, key, option)
|
|
82
|
-
elif option['type'] == 'bool_list':
|
|
83
|
-
self.checkbox_list.apply(parent_id, key, option)
|
|
84
|
-
elif option['type'] == 'dict':
|
|
85
|
-
self.dictionary.apply(parent_id, key, option)
|
|
86
|
-
elif option['type'] == 'combo':
|
|
87
|
-
self.combo.apply(parent_id, key, option)
|
|
88
|
-
elif option['type'] == 'cmd':
|
|
89
|
-
self.cmd.apply(parent_id, key, option)
|
|
94
|
+
return
|
|
95
|
+
func = self._apply_map.get(t)
|
|
96
|
+
if func:
|
|
97
|
+
func(parent_id, key, option)
|
|
90
98
|
|
|
91
99
|
def apply_value(
|
|
92
100
|
self,
|
|
@@ -122,25 +130,16 @@ class Config:
|
|
|
122
130
|
:param idx: return selected idx, not the value
|
|
123
131
|
:return: Option value
|
|
124
132
|
"""
|
|
125
|
-
|
|
126
|
-
|
|
133
|
+
t = option['type']
|
|
134
|
+
if t in ('int', 'float'):
|
|
135
|
+
if option.get('slider'):
|
|
127
136
|
return self.slider.get_value(parent_id, key, option)
|
|
128
|
-
else:
|
|
129
|
-
return self.input.get_value(parent_id, key, option)
|
|
130
|
-
elif option['type'] == 'text':
|
|
131
137
|
return self.input.get_value(parent_id, key, option)
|
|
132
|
-
|
|
133
|
-
return self.textarea.get_value(parent_id, key, option)
|
|
134
|
-
elif option['type'] == 'bool':
|
|
135
|
-
return self.checkbox.get_value(parent_id, key, option)
|
|
136
|
-
elif option['type'] == 'bool_list':
|
|
137
|
-
return self.checkbox_list.get_value(parent_id, key, option)
|
|
138
|
-
elif option['type'] == 'dict':
|
|
139
|
-
return self.dictionary.get_value(parent_id, key, option)
|
|
140
|
-
elif option['type'] == 'combo':
|
|
138
|
+
if t == 'combo':
|
|
141
139
|
return self.combo.get_value(parent_id, key, option, idx)
|
|
142
|
-
|
|
143
|
-
|
|
140
|
+
func = self._get_map.get(t)
|
|
141
|
+
if func:
|
|
142
|
+
return func(parent_id, key, option)
|
|
144
143
|
|
|
145
144
|
def update_list(
|
|
146
145
|
self,
|
|
@@ -159,13 +158,11 @@ class Config:
|
|
|
159
158
|
"""
|
|
160
159
|
if "type" not in option:
|
|
161
160
|
return
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
for
|
|
165
|
-
for k, v in item.items():
|
|
166
|
-
as_dict[k] = v
|
|
161
|
+
t = option['type']
|
|
162
|
+
if t == 'combo':
|
|
163
|
+
as_dict = {k: v for d in items for k, v in d.items()}
|
|
167
164
|
self.update_combo(parent_id, key, as_dict)
|
|
168
|
-
elif
|
|
165
|
+
elif t == 'bool_list':
|
|
169
166
|
self.update_bool_list(parent_id, key, items)
|
|
170
167
|
|
|
171
168
|
def update_combo(
|
|
@@ -196,4 +193,4 @@ class Config:
|
|
|
196
193
|
:param key: Option key
|
|
197
194
|
:param items: Items dict
|
|
198
195
|
"""
|
|
199
|
-
self.checkbox_list.update_list(parent_id, key, items)
|
|
196
|
+
self.checkbox_list.update_list(parent_id, key, items)
|
|
@@ -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: 2025.
|
|
9
|
+
# Updated Date: 2025.08.15 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Any, Dict
|
|
@@ -26,7 +26,7 @@ class Checkbox:
|
|
|
26
26
|
parent_id: str,
|
|
27
27
|
key: str,
|
|
28
28
|
option: Dict[str, Any]
|
|
29
|
-
):
|
|
29
|
+
) -> None:
|
|
30
30
|
"""
|
|
31
31
|
Apply value to checkbox
|
|
32
32
|
|
|
@@ -39,17 +39,19 @@ class Checkbox:
|
|
|
39
39
|
value = option["value"]
|
|
40
40
|
if value is None:
|
|
41
41
|
value = False
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
cfg_parent = self.window.ui.config[parent_id]
|
|
43
|
+
row = cfg_parent.get(key)
|
|
44
|
+
if row is not None:
|
|
45
|
+
row.box.setChecked(bool(value))
|
|
44
46
|
|
|
45
47
|
def on_update(
|
|
46
48
|
self,
|
|
47
49
|
parent_id: str,
|
|
48
50
|
key: str,
|
|
49
|
-
option:
|
|
51
|
+
option: Dict[str, Any],
|
|
50
52
|
value: Any,
|
|
51
53
|
hooks: bool = True
|
|
52
|
-
):
|
|
54
|
+
) -> None:
|
|
53
55
|
"""
|
|
54
56
|
Event: on update checkbox value
|
|
55
57
|
|
|
@@ -59,11 +61,11 @@ class Checkbox:
|
|
|
59
61
|
:param value: Option value
|
|
60
62
|
:param hooks: Run hooks
|
|
61
63
|
"""
|
|
62
|
-
# on update hooks
|
|
63
64
|
if hooks:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
ui = self.window.ui
|
|
66
|
+
hook_name = f"update.{parent_id}.{key}"
|
|
67
|
+
if ui.has_hook(hook_name):
|
|
68
|
+
hook = ui.get_hook(hook_name)
|
|
67
69
|
try:
|
|
68
70
|
hook(key, value, 'checkbox')
|
|
69
71
|
except Exception as e:
|
|
@@ -83,6 +85,8 @@ class Checkbox:
|
|
|
83
85
|
:param option: Option data dict
|
|
84
86
|
:return: Option value
|
|
85
87
|
"""
|
|
86
|
-
|
|
88
|
+
cfg_parent = self.window.ui.config[parent_id]
|
|
89
|
+
row = cfg_parent.get(key)
|
|
90
|
+
if row is None:
|
|
87
91
|
return False
|
|
88
|
-
return
|
|
92
|
+
return row.box.isChecked()
|
|
@@ -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: 2025.
|
|
9
|
+
# Updated Date: 2025.08.15 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Any, Dict, List
|
|
@@ -37,19 +37,24 @@ class CheckboxList:
|
|
|
37
37
|
if "value" not in option:
|
|
38
38
|
return
|
|
39
39
|
value = option["value"]
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
selection = {s.strip() for s in value.split(",")} if isinstance(value, str) else set()
|
|
41
|
+
selection.discard("")
|
|
42
|
+
|
|
43
|
+
ui = self.window.ui
|
|
44
|
+
cfg_parent = ui.config.get(parent_id)
|
|
45
|
+
if not cfg_parent:
|
|
46
|
+
return
|
|
47
|
+
entry = cfg_parent.get(key)
|
|
48
|
+
if entry is None or not hasattr(entry, "boxes"):
|
|
49
|
+
return
|
|
50
|
+
boxes = entry.boxes
|
|
51
|
+
|
|
52
|
+
for name, cb in boxes.items():
|
|
53
|
+
if cb is None:
|
|
51
54
|
continue
|
|
52
|
-
|
|
55
|
+
desired = name in selection
|
|
56
|
+
if cb.isChecked() != desired:
|
|
57
|
+
cb.setChecked(desired)
|
|
53
58
|
|
|
54
59
|
def on_update(
|
|
55
60
|
self,
|
|
@@ -70,15 +75,16 @@ class CheckboxList:
|
|
|
70
75
|
:param subkey: Subkey for specific checkbox
|
|
71
76
|
:param hooks: Run hooks
|
|
72
77
|
"""
|
|
73
|
-
# on update hooks
|
|
74
78
|
if hooks:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
ui = self.window.ui
|
|
80
|
+
hook_name = f"update.{parent_id}.{key}"
|
|
81
|
+
if ui.has_hook(hook_name):
|
|
82
|
+
hook = ui.get_hook(hook_name)
|
|
83
|
+
if hook:
|
|
84
|
+
try:
|
|
85
|
+
hook(key, value, 'bool_list')
|
|
86
|
+
except Exception as e:
|
|
87
|
+
self.window.core.debug.log(e)
|
|
82
88
|
|
|
83
89
|
def get_value(
|
|
84
90
|
self,
|
|
@@ -94,16 +100,15 @@ class CheckboxList:
|
|
|
94
100
|
:param option: Option data dict
|
|
95
101
|
:return: Option value
|
|
96
102
|
"""
|
|
97
|
-
|
|
103
|
+
ui = self.window.ui
|
|
104
|
+
cfg_parent = ui.config.get(parent_id)
|
|
105
|
+
if not cfg_parent:
|
|
98
106
|
return ""
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
imploded_list.append(item)
|
|
105
|
-
return ",".join(imploded_list)
|
|
106
|
-
|
|
107
|
+
entry = cfg_parent.get(key)
|
|
108
|
+
if entry is None or not hasattr(entry, "boxes"):
|
|
109
|
+
return ""
|
|
110
|
+
boxes = entry.boxes
|
|
111
|
+
return ",".join(name for name, cb in boxes.items() if cb is not None and cb.isChecked())
|
|
107
112
|
|
|
108
113
|
def update_list(
|
|
109
114
|
self,
|
|
@@ -118,4 +123,4 @@ class CheckboxList:
|
|
|
118
123
|
:param key: Option key
|
|
119
124
|
:param items: Items dict
|
|
120
125
|
"""
|
|
121
|
-
self.window.ui.config[parent_id][key].update_boxes_list(items)
|
|
126
|
+
self.window.ui.config[parent_id][key].update_boxes_list(items)
|