pygpt-net 2.6.3__py3-none-any.whl → 2.6.5__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 +10 -0
- pygpt_net/__init__.py +2 -2
- 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 +51 -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 +2 -3
- pygpt_net/core/render/web/renderer.py +109 -180
- 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 +33 -29
- pygpt_net/ui/tray.py +61 -60
- pygpt_net/ui/widget/lists/context.py +2 -2
- pygpt_net/ui/widget/textarea/web.py +18 -14
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.5.dist-info}/METADATA +12 -2
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.5.dist-info}/RECORD +76 -76
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.5.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.5.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.5.dist-info}/entry_points.txt +0 -0
|
@@ -6,11 +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.15 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import base64
|
|
13
|
-
|
|
13
|
+
import io
|
|
14
|
+
from typing import Optional, Literal
|
|
14
15
|
|
|
15
16
|
from PySide6.QtCore import QObject, Signal, Slot, QRunnable
|
|
16
17
|
|
|
@@ -20,6 +21,32 @@ from pygpt_net.core.types import MODE_ASSISTANT
|
|
|
20
21
|
from pygpt_net.core.text.utils import has_unclosed_code_tag
|
|
21
22
|
from pygpt_net.item.ctx import CtxItem
|
|
22
23
|
|
|
24
|
+
EventType = Literal[
|
|
25
|
+
"response.completed",
|
|
26
|
+
"response.output_text.delta",
|
|
27
|
+
"response.output_item.added",
|
|
28
|
+
"response.function_call_arguments.delta",
|
|
29
|
+
"response.function_call_arguments.done",
|
|
30
|
+
"response.output_text.annotation.added",
|
|
31
|
+
"response.reasoning_summary_text.delta",
|
|
32
|
+
"response.output_item.done",
|
|
33
|
+
"response.code_interpreter_call_code.delta",
|
|
34
|
+
"response.code_interpreter_call_code.done",
|
|
35
|
+
"response.image_generation_call.partial_image",
|
|
36
|
+
"response.created",
|
|
37
|
+
"response.done",
|
|
38
|
+
"response.failed",
|
|
39
|
+
"error",
|
|
40
|
+
]
|
|
41
|
+
ChunkType = Literal[
|
|
42
|
+
"api_chat",
|
|
43
|
+
"api_chat_responses",
|
|
44
|
+
"api_completion",
|
|
45
|
+
"langchain_chat",
|
|
46
|
+
"llama_chat",
|
|
47
|
+
"raw",
|
|
48
|
+
]
|
|
49
|
+
|
|
23
50
|
class WorkerSignals(QObject):
|
|
24
51
|
"""
|
|
25
52
|
Defines the signals available from a running worker thread.
|
|
@@ -54,15 +81,15 @@ class StreamWorker(QRunnable):
|
|
|
54
81
|
output_tokens = 0
|
|
55
82
|
begin = True
|
|
56
83
|
error = None
|
|
57
|
-
fn_args_buffers = {}
|
|
58
|
-
citations = []
|
|
84
|
+
fn_args_buffers: dict[str, io.StringIO] = {}
|
|
85
|
+
citations: Optional[list] = []
|
|
59
86
|
files = []
|
|
60
87
|
img_path = core.image.gen_unique_path(ctx)
|
|
61
88
|
is_image = False
|
|
62
89
|
is_code = False
|
|
63
90
|
force_func_call = False
|
|
64
91
|
stopped = False
|
|
65
|
-
chunk_type = "raw"
|
|
92
|
+
chunk_type: ChunkType = "raw"
|
|
66
93
|
generator = ctx.stream
|
|
67
94
|
ctx.stream = None
|
|
68
95
|
|
|
@@ -101,12 +128,12 @@ class StreamWorker(QRunnable):
|
|
|
101
128
|
stopped = True
|
|
102
129
|
break
|
|
103
130
|
|
|
104
|
-
etype = None
|
|
131
|
+
etype: Optional[EventType] = None
|
|
105
132
|
response = None
|
|
106
133
|
|
|
107
134
|
if ctx.use_responses_api:
|
|
108
135
|
if hasattr(chunk, 'type'):
|
|
109
|
-
etype = chunk.type
|
|
136
|
+
etype = chunk.type # type: ignore[assignment]
|
|
110
137
|
chunk_type = "api_chat_responses"
|
|
111
138
|
else:
|
|
112
139
|
continue
|
|
@@ -128,7 +155,6 @@ class StreamWorker(QRunnable):
|
|
|
128
155
|
else:
|
|
129
156
|
chunk_type = "raw"
|
|
130
157
|
|
|
131
|
-
# OpenAI chat completion
|
|
132
158
|
if chunk_type == "api_chat":
|
|
133
159
|
citations = None
|
|
134
160
|
delta = chunk.choices[0].delta
|
|
@@ -158,7 +184,6 @@ class StreamWorker(QRunnable):
|
|
|
158
184
|
if getattr(tool_chunk.function, "arguments", None):
|
|
159
185
|
tool_call["function"]["arguments"] += tool_chunk.function.arguments
|
|
160
186
|
|
|
161
|
-
# OpenAI Responses API
|
|
162
187
|
elif chunk_type == "api_chat_responses":
|
|
163
188
|
if etype == "response.completed":
|
|
164
189
|
for item in chunk.response.output:
|
|
@@ -197,7 +222,6 @@ class StreamWorker(QRunnable):
|
|
|
197
222
|
elif etype == "response.output_text.delta":
|
|
198
223
|
response = chunk.delta
|
|
199
224
|
|
|
200
|
-
# function_call
|
|
201
225
|
elif etype == "response.output_item.added" and chunk.item.type == "function_call":
|
|
202
226
|
tool_calls.append({
|
|
203
227
|
"id": chunk.item.id,
|
|
@@ -205,18 +229,23 @@ class StreamWorker(QRunnable):
|
|
|
205
229
|
"type": "function",
|
|
206
230
|
"function": {"name": chunk.item.name, "arguments": ""}
|
|
207
231
|
})
|
|
208
|
-
fn_args_buffers[chunk.item.id] =
|
|
232
|
+
fn_args_buffers[chunk.item.id] = io.StringIO()
|
|
209
233
|
elif etype == "response.function_call_arguments.delta":
|
|
210
|
-
fn_args_buffers
|
|
234
|
+
buf = fn_args_buffers.get(chunk.item_id)
|
|
235
|
+
if buf is not None:
|
|
236
|
+
buf.write(chunk.delta)
|
|
211
237
|
elif etype == "response.function_call_arguments.done":
|
|
212
238
|
buf = fn_args_buffers.pop(chunk.item_id, None)
|
|
213
239
|
if buf is not None:
|
|
240
|
+
try:
|
|
241
|
+
args_val = buf.getvalue()
|
|
242
|
+
finally:
|
|
243
|
+
buf.close()
|
|
214
244
|
for tc in tool_calls:
|
|
215
245
|
if tc["id"] == chunk.item_id:
|
|
216
|
-
tc["function"]["arguments"] =
|
|
246
|
+
tc["function"]["arguments"] = args_val
|
|
217
247
|
break
|
|
218
248
|
|
|
219
|
-
# annotations
|
|
220
249
|
elif etype == "response.output_text.annotation.added":
|
|
221
250
|
ann = chunk.annotation
|
|
222
251
|
if ann['type'] == "url_citation":
|
|
@@ -231,7 +260,6 @@ class StreamWorker(QRunnable):
|
|
|
231
260
|
"file_id": ann['file_id'],
|
|
232
261
|
})
|
|
233
262
|
|
|
234
|
-
# computer use
|
|
235
263
|
elif etype == "response.reasoning_summary_text.delta":
|
|
236
264
|
response = chunk.delta
|
|
237
265
|
|
|
@@ -240,7 +268,6 @@ class StreamWorker(QRunnable):
|
|
|
240
268
|
if has_calls:
|
|
241
269
|
force_func_call = True
|
|
242
270
|
|
|
243
|
-
# code interpreter
|
|
244
271
|
elif etype == "response.code_interpreter_call_code.delta":
|
|
245
272
|
if not is_code:
|
|
246
273
|
response = "\n\n**Code interpreter**\n```python\n" + chunk.delta
|
|
@@ -250,15 +277,14 @@ class StreamWorker(QRunnable):
|
|
|
250
277
|
elif etype == "response.code_interpreter_call_code.done":
|
|
251
278
|
response = "\n\n```\n-----------\n"
|
|
252
279
|
|
|
253
|
-
# image gen
|
|
254
280
|
elif etype == "response.image_generation_call.partial_image":
|
|
255
281
|
image_base64 = chunk.partial_image_b64
|
|
256
282
|
image_bytes = base64.b64decode(image_base64)
|
|
257
283
|
with open(img_path, "wb") as f:
|
|
258
284
|
f.write(image_bytes)
|
|
285
|
+
del image_bytes
|
|
259
286
|
is_image = True
|
|
260
287
|
|
|
261
|
-
# response ID
|
|
262
288
|
elif etype == "response.created":
|
|
263
289
|
ctx.msg_id = str(chunk.response.id)
|
|
264
290
|
core.ctx.update_item(ctx)
|
|
@@ -266,18 +292,15 @@ class StreamWorker(QRunnable):
|
|
|
266
292
|
elif etype in {"response.done", "response.failed", "error"}:
|
|
267
293
|
pass
|
|
268
294
|
|
|
269
|
-
# OpenAI completion
|
|
270
295
|
elif chunk_type == "api_completion":
|
|
271
296
|
choice0 = chunk.choices[0]
|
|
272
297
|
if choice0.text is not None:
|
|
273
298
|
response = choice0.text
|
|
274
299
|
|
|
275
|
-
# langchain chat
|
|
276
300
|
elif chunk_type == "langchain_chat":
|
|
277
301
|
if chunk.content is not None:
|
|
278
302
|
response = str(chunk.content)
|
|
279
303
|
|
|
280
|
-
# llama chat
|
|
281
304
|
elif chunk_type == "llama_chat":
|
|
282
305
|
if chunk.delta is not None:
|
|
283
306
|
response = str(chunk.delta)
|
|
@@ -301,7 +324,6 @@ class StreamWorker(QRunnable):
|
|
|
301
324
|
tool_calls.clear()
|
|
302
325
|
tool_calls.append(tool_call)
|
|
303
326
|
|
|
304
|
-
# raw text (llama-index / langchain completion)
|
|
305
327
|
else:
|
|
306
328
|
if chunk is not None:
|
|
307
329
|
response = str(chunk)
|
|
@@ -326,13 +348,11 @@ class StreamWorker(QRunnable):
|
|
|
326
348
|
|
|
327
349
|
chunk = None
|
|
328
350
|
|
|
329
|
-
# tool calls
|
|
330
351
|
if tool_calls:
|
|
331
352
|
ctx.force_call = force_func_call
|
|
332
353
|
core.debug.info("[chat] Tool calls found, unpacking...")
|
|
333
354
|
core.command.unpack_tool_calls_chunks(ctx, tool_calls)
|
|
334
355
|
|
|
335
|
-
# image
|
|
336
356
|
if is_image:
|
|
337
357
|
core.debug.info("[chat] Image generation call found")
|
|
338
358
|
ctx.images = [img_path]
|
|
@@ -371,11 +391,17 @@ class StreamWorker(QRunnable):
|
|
|
371
391
|
|
|
372
392
|
emit_end(ctx)
|
|
373
393
|
|
|
394
|
+
for _buf in fn_args_buffers.values():
|
|
395
|
+
try:
|
|
396
|
+
_buf.close()
|
|
397
|
+
except Exception:
|
|
398
|
+
pass
|
|
374
399
|
fn_args_buffers.clear()
|
|
375
400
|
files.clear()
|
|
376
401
|
tool_calls.clear()
|
|
377
|
-
if citations is not None:
|
|
402
|
+
if citations is not None and citations is not ctx.urls:
|
|
378
403
|
citations.clear()
|
|
404
|
+
citations = None
|
|
379
405
|
|
|
380
406
|
self.cleanup()
|
|
381
407
|
|
|
@@ -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)
|