pygpt-net 2.6.37__py3-none-any.whl → 2.6.39__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.
Files changed (54) hide show
  1. pygpt_net/CHANGELOG.txt +12 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/controller/chat/handler/anthropic_stream.py +0 -2
  4. pygpt_net/controller/chat/handler/worker.py +6 -2
  5. pygpt_net/controller/debug/debug.py +6 -6
  6. pygpt_net/controller/model/editor.py +20 -42
  7. pygpt_net/controller/model/importer.py +9 -2
  8. pygpt_net/controller/painter/common.py +0 -8
  9. pygpt_net/controller/plugins/plugins.py +11 -3
  10. pygpt_net/controller/presets/presets.py +2 -2
  11. pygpt_net/core/ctx/bag.py +7 -2
  12. pygpt_net/core/ctx/reply.py +17 -2
  13. pygpt_net/core/db/viewer.py +19 -34
  14. pygpt_net/core/render/plain/pid.py +12 -1
  15. pygpt_net/core/render/web/body.py +292 -445
  16. pygpt_net/core/tabs/tab.py +24 -1
  17. pygpt_net/data/config/config.json +3 -3
  18. pygpt_net/data/config/models.json +3 -3
  19. pygpt_net/item/assistant.py +51 -2
  20. pygpt_net/item/attachment.py +21 -20
  21. pygpt_net/item/calendar_note.py +19 -2
  22. pygpt_net/item/ctx.py +115 -2
  23. pygpt_net/item/index.py +9 -2
  24. pygpt_net/item/mode.py +9 -6
  25. pygpt_net/item/model.py +20 -3
  26. pygpt_net/item/notepad.py +14 -2
  27. pygpt_net/item/preset.py +42 -2
  28. pygpt_net/item/prompt.py +8 -2
  29. pygpt_net/plugin/cmd_files/plugin.py +2 -2
  30. pygpt_net/provider/api/anthropic/tools.py +1 -1
  31. pygpt_net/provider/api/google/realtime/client.py +2 -2
  32. pygpt_net/provider/core/attachment/json_file.py +2 -2
  33. pygpt_net/tools/text_editor/tool.py +4 -1
  34. pygpt_net/tools/text_editor/ui/dialogs.py +1 -1
  35. pygpt_net/ui/dialog/db.py +177 -59
  36. pygpt_net/ui/dialog/dictionary.py +57 -59
  37. pygpt_net/ui/dialog/editor.py +3 -2
  38. pygpt_net/ui/dialog/image.py +1 -1
  39. pygpt_net/ui/dialog/logger.py +3 -2
  40. pygpt_net/ui/dialog/models.py +171 -21
  41. pygpt_net/ui/dialog/plugins.py +26 -20
  42. pygpt_net/ui/layout/ctx/ctx_list.py +3 -4
  43. pygpt_net/ui/layout/toolbox/__init__.py +2 -2
  44. pygpt_net/ui/layout/toolbox/assistants.py +8 -9
  45. pygpt_net/ui/layout/toolbox/presets.py +2 -2
  46. pygpt_net/ui/main.py +9 -4
  47. pygpt_net/ui/widget/element/labels.py +2 -2
  48. pygpt_net/ui/widget/textarea/editor.py +0 -4
  49. pygpt_net/utils.py +12 -13
  50. {pygpt_net-2.6.37.dist-info → pygpt_net-2.6.39.dist-info}/METADATA +14 -2
  51. {pygpt_net-2.6.37.dist-info → pygpt_net-2.6.39.dist-info}/RECORD +54 -54
  52. {pygpt_net-2.6.37.dist-info → pygpt_net-2.6.39.dist-info}/LICENSE +0 -0
  53. {pygpt_net-2.6.37.dist-info → pygpt_net-2.6.39.dist-info}/WHEEL +0 -0
  54. {pygpt_net-2.6.37.dist-info → pygpt_net-2.6.39.dist-info}/entry_points.txt +0 -0
@@ -890,7 +890,7 @@ class GoogleLiveClient:
890
890
  "arguments": json.dumps(args_dict, ensure_ascii=False),
891
891
  }
892
892
  })
893
- self._rt_state["force_func_call"] = True
893
+ # self._rt_state["force_func_call"] = True
894
894
  self._last_tool_calls = list(self._rt_state["tool_calls"])
895
895
  turn_finished = True # let the app run tools now
896
896
 
@@ -1004,7 +1004,7 @@ class GoogleLiveClient:
1004
1004
  if key not in seen:
1005
1005
  self._rt_state["tool_calls"].append(c)
1006
1006
  seen.add(key)
1007
- self._rt_state["force_func_call"] = True
1007
+ # self._rt_state["force_func_call"] = True
1008
1008
  self._last_tool_calls = list(self._rt_state["tool_calls"])
1009
1009
  turn_finished = True
1010
1010
 
@@ -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: 2024.12.16 01:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import json
@@ -162,7 +162,7 @@ class JsonFileProvider(BaseProvider):
162
162
  attachment.name = data['name']
163
163
  if 'path' in data:
164
164
  attachment.path = data['path']
165
- if 'remote_id' in data:
165
+ if 'remote' in data:
166
166
  attachment.remote = data['remote']
167
167
  if 'send' in data:
168
168
  attachment.send = data['send']
@@ -183,16 +183,19 @@ class TextEditor(BaseTool):
183
183
  self.window.core.filesystem.editor.destroy(id) # unregister from memory
184
184
  self.window.ui.dialogs.close(id)
185
185
 
186
- def save(self, id: str) -> str:
186
+ def save(self, id: str, close: bool = False) -> str:
187
187
  """
188
188
  Save content to current file
189
189
 
190
190
  :param id: editor id
191
+ :param close: close editor after save
191
192
  :return: new editor id
192
193
  """
193
194
  file = self.window.ui.dialog[id].file
194
195
  if file:
195
196
  self.window.core.filesystem.editor.save(id)
197
+ if close:
198
+ self.close(id)
196
199
  else:
197
200
  id = self.save_as_file(id)
198
201
  return id
@@ -47,7 +47,7 @@ class DialogSpawner:
47
47
  lambda: self.window.tools.get("editor").restore(id)
48
48
  )
49
49
  self.window.ui.nodes['editor.custom.btn.save'].clicked.connect(
50
- lambda: self.window.tools.get("editor").save(id)
50
+ lambda: self.window.tools.get("editor").save(id, close=True)
51
51
  )
52
52
 
53
53
  bottom_layout = QHBoxLayout()
pygpt_net/ui/dialog/db.py CHANGED
@@ -6,13 +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.24 23:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from PySide6.QtCore import Qt
13
- from PySide6.QtGui import QAction, QIcon
12
+ from PySide6.QtCore import Qt, QTimer, QSignalBlocker
13
+ from PySide6.QtGui import QAction, QIcon, QIntValidator
14
14
  from PySide6.QtWidgets import QGridLayout, QScrollArea, QSplitter, QComboBox, QLineEdit, QPushButton, \
15
- QHBoxLayout, QVBoxLayout, QLabel, QWidget, QSizePolicy, QCheckBox, QMenuBar
15
+ QHBoxLayout, QVBoxLayout, QLabel, QWidget, QSizePolicy, QCheckBox, QMenuBar, QAbstractItemView, QHeaderView, QStyledItemDelegate
16
16
 
17
17
  from pygpt_net.ui.widget.dialog.db import DatabaseDialog
18
18
  from pygpt_net.ui.widget.lists.db import DatabaseList, DatabaseTableModel
@@ -20,6 +20,18 @@ from pygpt_net.ui.widget.textarea.editor import CodeEditor
20
20
  from pygpt_net.utils import trans
21
21
 
22
22
 
23
+ class _FastTextDelegate(QStyledItemDelegate):
24
+ def __init__(self, max_chars=1200, parent=None):
25
+ super().__init__(parent)
26
+ self.max_chars = max_chars
27
+
28
+ def displayText(self, value, locale):
29
+ s = "" if value is None else str(value)
30
+ if len(s) > self.max_chars:
31
+ return s[:self.max_chars] + "…"
32
+ return s
33
+
34
+
23
35
  class Database:
24
36
  def __init__(self, window=None):
25
37
  """
@@ -29,6 +41,8 @@ class Database:
29
41
  """
30
42
  self.window = window
31
43
  self.viewer = None
44
+ self._splitter_timer = None
45
+ self._text_viewer = None
32
46
 
33
47
  def setup(self, id: str = "db"):
34
48
  """
@@ -45,6 +59,7 @@ class Database:
45
59
  # data viewer
46
60
  text_viewer = CodeEditor(self.window)
47
61
  text_viewer.setReadOnly(False)
62
+ self._text_viewer = text_viewer
48
63
 
49
64
  self.window.ui.debug[id].browser = self.viewer
50
65
  self.window.ui.debug[id].viewer = text_viewer
@@ -63,6 +78,12 @@ class Database:
63
78
  splitter.addWidget(editor_widget) # Value viewer
64
79
  splitter.setStretchFactor(0, 3)
65
80
  splitter.setStretchFactor(1, 1)
81
+ splitter.setOpaqueResize(False)
82
+
83
+ self._splitter_timer = QTimer(self.window)
84
+ self._splitter_timer.setSingleShot(True)
85
+ self._splitter_timer.timeout.connect(self._finish_splitter_move)
86
+ splitter.splitterMoved.connect(self._on_splitter_moved)
66
87
 
67
88
  self.menu_bar = QMenuBar()
68
89
  self.batch_actions_menu = self.menu_bar.addMenu("Actions")
@@ -81,6 +102,18 @@ class Database:
81
102
  self.window.ui.dialog['debug.db'].setLayout(layout)
82
103
  self.window.ui.dialog['debug.db'].setWindowTitle("Debug: Database (SQLite)")
83
104
 
105
+ def _on_splitter_moved(self, pos, index):
106
+ if self._text_viewer is not None:
107
+ self._text_viewer.setUpdatesEnabled(False)
108
+ self._splitter_timer.start(120)
109
+
110
+ def _finish_splitter_move(self):
111
+ if self._text_viewer is not None:
112
+ self._text_viewer.setUpdatesEnabled(True)
113
+ if hasattr(self._text_viewer, 'viewport'):
114
+ self._text_viewer.viewport().update()
115
+
116
+
84
117
  class DataBrowser(QWidget):
85
118
  def __init__(self, window=None):
86
119
  super().__init__()
@@ -111,7 +144,7 @@ class DataBrowser(QWidget):
111
144
 
112
145
  # buttons
113
146
  self.refresh_button = QPushButton(trans("db.refresh"))
114
- self.refresh_button.clicked.connect(self.update_table_view)
147
+ self.refresh_button.clicked.connect(self.force_refresh)
115
148
  self.prev_button = QPushButton(trans("db.prev"))
116
149
  self.next_button = QPushButton(trans("db.next"))
117
150
  self.limit_input = QLineEdit("100")
@@ -126,7 +159,7 @@ class DataBrowser(QWidget):
126
159
  self.page_input_label = QLabel(trans("db.page") + ":")
127
160
  self.page_input_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
128
161
  self.page_input.editingFinished.connect(self.on_page_input_change)
129
- self.page_info_label = QLabel(" / 1") # total pages
162
+ self.page_info_label = QLabel(" / 1")
130
163
  self.page_info_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
131
164
 
132
165
  # checkboxes
@@ -155,18 +188,30 @@ class DataBrowser(QWidget):
155
188
  self.table_select.addItems(self.get_table_names())
156
189
  self.sort_order_select.addItems(['ASC', 'DESC'])
157
190
 
191
+ self._search_timer = QTimer(self)
192
+ self._search_timer.setSingleShot(True)
193
+ self._search_timer.timeout.connect(self.update_table_view)
194
+ self._last_fetch_sig = None
195
+ self._last_count_sig = None
196
+ self._cached_total_rows = 0
197
+ self._last_columns_sig = None
198
+
199
+ v = QIntValidator(1, 1000000000, self)
200
+ self.limit_input.setValidator(v)
201
+ self.page_input.setValidator(v)
202
+
158
203
  # signals
159
204
  self.table_select.currentIndexChanged.connect(self.on_table_select_changed)
160
205
  self.sort_by_select.currentIndexChanged.connect(self.update_table_view)
161
206
  self.sort_order_select.currentIndexChanged.connect(self.update_table_view)
162
- self.search_input.textChanged.connect(self.update_table_view)
207
+ self.search_input.textChanged.connect(self.on_search_text_changed)
163
208
  self.search_column_select.currentIndexChanged.connect(self.update_table_view)
164
209
 
165
210
  self.prev_button.clicked.connect(self.prev_page)
166
211
  self.next_button.clicked.connect(self.next_page)
167
212
 
168
213
  self.table_select.setCurrentText(self.get_default_table())
169
- self.on_table_select_changed() # load data
214
+ self.on_table_select_changed()
170
215
 
171
216
  # setup layouts
172
217
  if not self.is_inline():
@@ -221,6 +266,27 @@ class DataBrowser(QWidget):
221
266
 
222
267
  self.setLayout(main_layout)
223
268
 
269
+ view = self.get_list_widget()
270
+ if hasattr(view, "setUniformRowHeights"):
271
+ view.setUniformRowHeights(True)
272
+ if hasattr(view, "setWordWrap"):
273
+ view.setWordWrap(False)
274
+ if hasattr(view, "setTextElideMode"):
275
+ view.setTextElideMode(Qt.ElideRight)
276
+ if hasattr(view, "setVerticalScrollMode"):
277
+ view.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
278
+ if hasattr(view, "setHorizontalScrollMode"):
279
+ view.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
280
+ if hasattr(view, "setViewportUpdateMode"):
281
+ view.setViewportUpdateMode(QAbstractItemView.MinimalViewportUpdate)
282
+ if hasattr(view, "horizontalHeader"):
283
+ hh = view.horizontalHeader()
284
+ if hh is not None:
285
+ hh.setSectionResizeMode(QHeaderView.Interactive)
286
+ self._fast_delegate = _FastTextDelegate(parent=view)
287
+ if hasattr(view, "setItemDelegate"):
288
+ view.setItemDelegate(self._fast_delegate)
289
+
224
290
  def is_inline(self) -> bool:
225
291
  return False
226
292
 
@@ -339,6 +405,8 @@ class DataBrowser(QWidget):
339
405
  self.limit_input.setText(str(current_limit))
340
406
  return
341
407
  self.current_offset = 0
408
+ self._last_fetch_sig = None
409
+ self._last_count_sig = None
342
410
  self.update_table_view()
343
411
 
344
412
  def get_page_size(self) -> int:
@@ -358,27 +426,18 @@ class DataBrowser(QWidget):
358
426
 
359
427
  :param update: Update table view
360
428
  """
361
- page = int(self.page_input.text()) - 1
362
- limit = int(self.limit_input.text())
363
- search_query = self.search_input.text()
364
- search_column = self.search_column_select.currentText()
365
- if search_column == "*":
366
- search_column = None
367
- current_table = self.get_table_name(self.table_select.currentText())
368
- total_rows = self.get_viewer().count_rows(
369
- current_table,
370
- search_query=search_query,
371
- search_column=search_column,
372
- filters=self.get_filters(),
373
- )
429
+ try:
430
+ page = int(self.page_input.text()) - 1
431
+ except ValueError:
432
+ page = 0
433
+ limit = self.get_page_size()
434
+ total_rows = self._get_total_rows()
374
435
 
375
- # check if page is in range
376
436
  if page * limit < total_rows and page >= 0:
377
437
  if update:
378
438
  self.current_offset = page * limit
379
439
  self.update_table_view()
380
440
  else:
381
- # reset page
382
441
  current_page = self.current_offset // limit + 1
383
442
  self.page_input.setText(str(current_page))
384
443
 
@@ -395,38 +454,33 @@ class DataBrowser(QWidget):
395
454
  """Table select change event"""
396
455
  tables = self.get_tables()
397
456
  current_table = self.get_table_name(self.table_select.currentText())
398
- self.sort_by_select.clear()
399
- if current_table in tables:
400
- self.sort_by_select.addItems(tables[current_table]['sort_by'])
401
- self.sort_by_select.setCurrentText(tables[current_table]['default_sort'])
402
- self.sort_order_select.setCurrentText(tables[current_table]['default_order'])
403
- self.page_input.setText("1") # reset page
404
- self.on_page_input_change()
405
- self.update_table_view() # update view
406
- self.update_search_columns() # update search columns
457
+ with QSignalBlocker(self.sort_by_select), QSignalBlocker(self.sort_order_select):
458
+ self.sort_by_select.clear()
459
+ if current_table in tables:
460
+ self.sort_by_select.addItems(tables[current_table]['sort_by'])
461
+ self.sort_by_select.setCurrentText(tables[current_table]['default_sort'])
462
+ self.sort_order_select.setCurrentText(tables[current_table]['default_order'])
463
+ self.page_input.setText("1")
464
+ self.current_offset = 0
465
+ self.update_search_columns()
466
+ self._last_fetch_sig = None
467
+ self._last_count_sig = None
468
+ self._last_columns_sig = None
469
+ self.update_table_view()
407
470
 
408
471
  def update_search_columns(self):
409
472
  """Update search columns"""
410
473
  tables = self.get_tables()
411
474
  current_table = self.get_table_name(self.table_select.currentText())
412
- self.search_column_select.clear()
413
- self.search_column_select.addItem("*", None)
414
- self.search_column_select.addItems(tables[current_table]['columns'])
475
+ with QSignalBlocker(self.search_column_select):
476
+ self.search_column_select.clear()
477
+ self.search_column_select.addItem("*", None)
478
+ self.search_column_select.addItems(tables[current_table]['columns'])
415
479
 
416
480
  def update_pagination_info(self):
417
481
  """Update pagination info"""
418
- limit = int(self.limit_input.text())
419
- search_query = self.search_input.text()
420
- search_column = self.search_column_select.currentText()
421
- if search_column == "*":
422
- search_column = None
423
- current_table = self.get_table_name(self.table_select.currentText())
424
- total_rows = self.get_viewer().count_rows(
425
- current_table,
426
- search_query=search_query,
427
- search_column=search_column,
428
- filters=self.get_filters(),
429
- )
482
+ limit = self.get_page_size()
483
+ total_rows = self._get_total_rows()
430
484
  total_pages = (total_rows - 1) // limit + 1
431
485
  self.page_info_label.setText(f" / {total_pages} ({total_rows} rows)")
432
486
 
@@ -435,21 +489,24 @@ class DataBrowser(QWidget):
435
489
  self.prev_button.setEnabled(self.current_offset > 0)
436
490
  self.next_button.setEnabled(self.current_offset + limit < total_rows)
437
491
 
438
- def update_table_view(self):
492
+ def update_table_view(self, *args, force: bool = False):
439
493
  """Update table view"""
440
494
  tables = self.get_tables()
441
495
  current_table = self.get_table_name(self.table_select.currentText())
442
496
  sort_by = self.sort_by_select.currentText()
443
497
  sort_order = self.sort_order_select.currentText()
444
498
  search_query = self.search_input.text()
445
- search_column = self.search_column_select.currentText()
446
- if search_column == "*":
447
- search_column = None
448
- limit = int(self.limit_input.text())
499
+ search_column = self.search_column_select.currentData()
500
+ limit = self.get_page_size()
449
501
 
450
502
  if current_table not in tables or sort_by == '' or sort_order == '' or limit <= 0:
451
503
  return
452
504
 
505
+ sig = self._params_signature_fetch()
506
+ if not force and self._last_fetch_sig == sig:
507
+ self.update_pagination_info()
508
+ return
509
+
453
510
  data = self.get_viewer().fetch_data(
454
511
  table=current_table,
455
512
  columns=tables[current_table]['columns'],
@@ -462,11 +519,12 @@ class DataBrowser(QWidget):
462
519
  filters=self.get_filters(),
463
520
  )
464
521
  self.load_data(data, tables[current_table])
522
+ self._last_fetch_sig = sig
465
523
  self.update_pagination_info()
466
524
 
467
525
  def prev_page(self):
468
526
  """Previous page event"""
469
- limit = int(self.limit_input.text())
527
+ limit = self.get_page_size()
470
528
  if self.current_offset - limit >= 0:
471
529
  self.current_offset -= limit
472
530
  else:
@@ -475,10 +533,8 @@ class DataBrowser(QWidget):
475
533
 
476
534
  def next_page(self):
477
535
  """Next page event"""
478
- limit = int(self.limit_input.text())
479
- current_table = self.get_table_name(self.table_select.currentText())
480
- total_rows = self.get_viewer().count_rows(current_table)
481
-
536
+ limit = self.get_page_size()
537
+ total_rows = self._get_total_rows()
482
538
  if self.current_offset + limit < total_rows:
483
539
  self.current_offset += limit
484
540
  self.update_table_view()
@@ -498,11 +554,73 @@ class DataBrowser(QWidget):
498
554
  self.db_path_label.setText(self.window.core.db.db_path)
499
555
  if not self.is_inline():
500
556
  convert_timestamps = self.convert_timestamps_checkbox.isChecked()
557
+ view = self.get_list_widget()
558
+ if hasattr(view, 'setSortingEnabled'):
559
+ view.setSortingEnabled(False)
560
+ view.setUpdatesEnabled(False)
501
561
  model = DatabaseTableModel(
502
562
  data,
503
563
  table['columns'],
504
564
  timestamp_columns=table.get('timestamp_columns', []),
505
565
  convert_timestamps=convert_timestamps,
506
566
  )
507
- self.get_list_widget().setModel(model)
508
- self.get_list_widget().adjustColumns()
567
+ if hasattr(model, 'setParent'):
568
+ model.setParent(view)
569
+ view.setModel(model)
570
+ if self._last_columns_sig != tuple(table['columns']):
571
+ if hasattr(view, 'adjustColumns'):
572
+ view.adjustColumns()
573
+ if hasattr(view, "horizontalHeader"):
574
+ hh = view.horizontalHeader()
575
+ if hh is not None:
576
+ hh.setSectionResizeMode(QHeaderView.Interactive)
577
+ self._last_columns_sig = tuple(table['columns'])
578
+ view.setUpdatesEnabled(True)
579
+
580
+ def on_search_text_changed(self, text):
581
+ self._search_timer.start(200)
582
+
583
+ def _params_signature_base(self):
584
+ table = self.get_table_name(self.table_select.currentText())
585
+ search_query = self.search_input.text()
586
+ search_column = self.search_column_select.currentData()
587
+ filters = self.get_filters()
588
+ if isinstance(filters, dict):
589
+ filters_key = tuple(sorted(filters.items()))
590
+ else:
591
+ filters_key = str(filters)
592
+ return table, search_query, search_column, filters_key
593
+
594
+ def _params_signature_fetch(self):
595
+ table, search_query, search_column, filters_key = self._params_signature_base()
596
+ tables = self.get_tables()
597
+ columns = tuple(tables[table]['columns']) if table in tables else tuple()
598
+ sort_by = self.sort_by_select.currentText()
599
+ sort_order = self.sort_order_select.currentText()
600
+ limit = self.get_page_size()
601
+ offset = self.current_offset
602
+ return table, columns, sort_by, sort_order, search_query, search_column, filters_key, limit, offset
603
+
604
+ def _params_signature_count(self):
605
+ return self._params_signature_base()
606
+
607
+ def _get_total_rows(self):
608
+ sig = self._params_signature_count()
609
+ if self._last_count_sig == sig:
610
+ return self._cached_total_rows
611
+ table, search_query, search_column, _ = sig
612
+ total_rows = self.get_viewer().count_rows(
613
+ table,
614
+ search_query=search_query,
615
+ search_column=search_column,
616
+ filters=self.get_filters(),
617
+ )
618
+ self._last_count_sig = sig
619
+ self._cached_total_rows = total_rows
620
+ return total_rows
621
+
622
+ def force_refresh(self):
623
+ self._last_fetch_sig = None
624
+ self._last_count_sig = None
625
+ self._last_columns_sig = None
626
+ self.update_table_view()
@@ -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: 2024.03.12 06:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QPushButton, QHBoxLayout, QVBoxLayout, QScrollArea, QWidget, QSizePolicy
@@ -47,87 +47,85 @@ class Dictionary(BaseConfigDialog):
47
47
 
48
48
  def setup(self):
49
49
  """Setup dictionary editor dialogs"""
50
- for dict_id in self.dicts:
51
- parent_id = self.id + "." + dict_id
50
+ ui = self.window.ui
51
+ controller = self.window.controller
52
+ save_text = trans("dialog.preset.btn.save")
53
+ dismiss_text = trans("dialog.rename.dismiss")
54
+ edit_title = trans('action.edit')
55
+
56
+ for dict_id, data in self.dicts.items():
57
+ parent_id = f"{self.id}.{dict_id}"
52
58
  option_key = self.keys[dict_id]
53
59
  parent = self.parents[dict_id]
54
- data = self.dicts[dict_id]
55
- self.window.ui.config[parent_id] = {}
60
+ ui.config[parent_id] = {}
56
61
 
57
- # widgets
58
62
  fields = {}
59
-
60
- # option type: dict
61
63
  if data["type"] == 'dict':
62
- fields = self.window.controller.config.dictionary.to_options(
64
+ fields = controller.config.dictionary.to_options(
63
65
  parent_id,
64
66
  data,
65
- ) # item to options
66
-
67
- # option type: cmd
67
+ )
68
68
  elif data["type"] == 'cmd':
69
- fields = self.window.controller.config.cmd.to_options(
69
+ fields = controller.config.cmd.to_options(
70
70
  parent_id,
71
71
  data,
72
- ) # item to options
72
+ )
73
73
 
74
74
  widgets = self.build_widgets(
75
75
  parent_id,
76
76
  fields,
77
77
  stretch=True,
78
- ) # from base config dialog
79
-
80
- for key in widgets:
81
- self.window.ui.config[parent_id][key] = widgets[key]
82
-
83
- # apply widgets to layouts
84
- options = {}
85
- is_stretch = False
86
- for key in widgets:
87
- if fields[key]["type"] == 'int' or fields[key]["type"] == 'float':
88
- options[key] = self.add_option(widgets[key], fields[key])
89
- elif fields[key]["type"] == 'text' or fields[key]["type"] == 'textarea':
90
- options[key] = self.add_row_option(widgets[key], fields[key])
91
- elif fields[key]["type"] == 'bool':
92
- options[key] = self.add_raw_option(widgets[key], fields[key])
93
- elif fields[key]["type"] == 'dict':
94
- options[key] = self.add_row_option(widgets[key], fields[key])
95
- elif fields[key]["type"] == 'combo':
96
- options[key] = self.add_row_option(widgets[key], fields[key])
97
-
98
- # stretch all only if textarea is present
99
- if fields[key]["type"] == 'textarea':
100
- is_stretch = True
78
+ )
79
+ ui.config[parent_id] = widgets
101
80
 
102
- rows = QVBoxLayout()
103
- for key in options:
104
- rows.addLayout(options[key])
81
+ add_option = self.add_option
82
+ add_row_option = self.add_row_option
83
+ add_raw_option = self.add_raw_option
105
84
 
106
- if not is_stretch:
85
+ rows = QVBoxLayout()
86
+ has_textarea = False
87
+
88
+ for k, f in fields.items():
89
+ t = f.get("type")
90
+ w = widgets.get(k)
91
+ if t in ('int', 'float'):
92
+ row = add_option(w, f)
93
+ elif t in ('text', 'textarea', 'dict', 'combo'):
94
+ row = add_row_option(w, f)
95
+ elif t == 'bool':
96
+ row = add_raw_option(w, f)
97
+ else:
98
+ continue
99
+ rows.addLayout(row)
100
+ if t == 'textarea':
101
+ has_textarea = True
102
+
103
+ if not has_textarea:
107
104
  rows.addStretch()
108
105
 
109
- # footer
110
- self.window.ui.nodes[parent_id + '.btn.save'] = QPushButton(trans("dialog.preset.btn.save"))
111
- self.window.ui.nodes[parent_id + '.btn.save'].clicked.connect(
106
+ save_key = f"{parent_id}.btn.save"
107
+ dismiss_key = f"{parent_id}.btn.dismiss"
108
+
109
+ ui.nodes[save_key] = QPushButton(save_text)
110
+ ui.nodes[save_key].clicked.connect(
112
111
  lambda checked=True, option_key=option_key, parent=parent, fields=fields:
113
- self.window.controller.config.dictionary.save_editor(
112
+ controller.config.dictionary.save_editor(
114
113
  option_key,
115
114
  parent,
116
115
  fields,
117
116
  ))
118
- self.window.ui.nodes[parent_id + '.btn.save'].setAutoDefault(True)
117
+ ui.nodes[save_key].setAutoDefault(True)
119
118
 
120
- self.window.ui.nodes[parent_id + '.btn.dismiss'] = QPushButton(trans("dialog.rename.dismiss"))
121
- self.window.ui.nodes[parent_id + '.btn.dismiss'].clicked.connect(
122
- lambda checked=True, parent_id=parent_id: self.window.ui.dialogs.close('editor.' + parent_id)
119
+ ui.nodes[dismiss_key] = QPushButton(dismiss_text)
120
+ ui.nodes[dismiss_key].clicked.connect(
121
+ lambda checked=True, pid=parent_id: ui.dialogs.close(f'editor.{pid}')
123
122
  )
124
- self.window.ui.nodes[parent_id + '.btn.dismiss'].setAutoDefault(False)
123
+ ui.nodes[dismiss_key].setAutoDefault(False)
125
124
 
126
125
  footer = QHBoxLayout()
127
- footer.addWidget(self.window.ui.nodes[parent_id + '.btn.dismiss'])
128
- footer.addWidget(self.window.ui.nodes[parent_id + '.btn.save'])
126
+ footer.addWidget(ui.nodes[dismiss_key])
127
+ footer.addWidget(ui.nodes[save_key])
129
128
 
130
- # scroll area
131
129
  scroll = QScrollArea()
132
130
  scroll.setWidgetResizable(True)
133
131
  scroll.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
@@ -138,10 +136,10 @@ class Dictionary(BaseConfigDialog):
138
136
  widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
139
137
 
140
138
  layout = QVBoxLayout()
141
- layout.addWidget(scroll) # options
142
- layout.addLayout(footer) # footer
139
+ layout.addWidget(scroll)
140
+ layout.addLayout(footer)
143
141
 
144
- # dialog
145
- self.window.ui.dialog['editor.' + parent_id] = EditorDialog(self.window, parent_id) # current idx here
146
- self.window.ui.dialog['editor.' + parent_id].setLayout(layout)
147
- self.window.ui.dialog['editor.' + parent_id].setWindowTitle(trans('action.edit'))
142
+ dialog_key = f'editor.{parent_id}'
143
+ ui.dialog[dialog_key] = EditorDialog(self.window, parent_id)
144
+ ui.dialog[dialog_key].setLayout(layout)
145
+ ui.dialog[dialog_key].setWindowTitle(edit_title)
@@ -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: 2024.04.09 23:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QPushButton, QHBoxLayout, QLabel, QVBoxLayout
@@ -45,7 +45,8 @@ class Editor:
45
45
  lambda: self.window.controller.settings.editor.load_editor_defaults_app()
46
46
  )
47
47
  self.window.ui.nodes['editor.btn.save'].clicked.connect(
48
- lambda: self.window.core.settings.save_editor())
48
+ lambda: self.window.core.settings.save_editor()
49
+ )
49
50
 
50
51
  bottom_layout = QHBoxLayout()
51
52
  bottom_layout.addWidget(self.window.ui.nodes['editor.btn.default'])
@@ -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: 2024.03.26 15:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QCheckBox