pygpt-net 2.6.20__py3-none-any.whl → 2.6.22__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 (221) hide show
  1. pygpt_net/CHANGELOG.txt +13 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +3 -1
  4. pygpt_net/controller/__init__.py +4 -8
  5. pygpt_net/controller/access/voice.py +2 -2
  6. pygpt_net/controller/agent/agent.py +130 -2
  7. pygpt_net/controller/agent/experts.py +93 -96
  8. pygpt_net/controller/agent/llama.py +2 -1
  9. pygpt_net/controller/assistant/assistant.py +18 -1
  10. pygpt_net/controller/assistant/batch.py +2 -3
  11. pygpt_net/controller/assistant/editor.py +2 -2
  12. pygpt_net/controller/assistant/files.py +2 -3
  13. pygpt_net/controller/assistant/store.py +2 -2
  14. pygpt_net/controller/attachment/attachment.py +17 -1
  15. pygpt_net/controller/audio/audio.py +2 -2
  16. pygpt_net/controller/camera/camera.py +15 -7
  17. pygpt_net/controller/chat/chat.py +2 -2
  18. pygpt_net/controller/chat/common.py +50 -33
  19. pygpt_net/controller/chat/image.py +67 -77
  20. pygpt_net/controller/chat/input.py +94 -166
  21. pygpt_net/controller/chat/output.py +83 -140
  22. pygpt_net/controller/chat/response.py +83 -102
  23. pygpt_net/controller/chat/text.py +116 -149
  24. pygpt_net/controller/ctx/common.py +2 -1
  25. pygpt_net/controller/ctx/ctx.py +87 -6
  26. pygpt_net/controller/files/files.py +13 -1
  27. pygpt_net/controller/idx/idx.py +26 -2
  28. pygpt_net/controller/idx/indexer.py +85 -76
  29. pygpt_net/controller/kernel/reply.py +53 -66
  30. pygpt_net/controller/kernel/stack.py +16 -16
  31. pygpt_net/controller/lang/lang.py +52 -34
  32. pygpt_net/controller/model/importer.py +3 -2
  33. pygpt_net/controller/model/model.py +62 -3
  34. pygpt_net/controller/notepad/notepad.py +86 -84
  35. pygpt_net/controller/plugins/settings.py +3 -4
  36. pygpt_net/controller/settings/editor.py +4 -4
  37. pygpt_net/controller/settings/profile.py +105 -124
  38. pygpt_net/controller/theme/menu.py +154 -57
  39. pygpt_net/controller/theme/nodes.py +51 -44
  40. pygpt_net/controller/theme/theme.py +33 -9
  41. pygpt_net/controller/tools/tools.py +2 -2
  42. pygpt_net/controller/ui/tabs.py +2 -3
  43. pygpt_net/controller/ui/ui.py +16 -2
  44. pygpt_net/core/agents/observer/evaluation.py +3 -3
  45. pygpt_net/core/agents/provider.py +25 -3
  46. pygpt_net/core/agents/runner.py +4 -1
  47. pygpt_net/core/agents/runners/llama_workflow.py +19 -7
  48. pygpt_net/core/agents/runners/loop.py +3 -1
  49. pygpt_net/core/agents/runners/openai_workflow.py +17 -3
  50. pygpt_net/core/agents/tools.py +4 -1
  51. pygpt_net/core/bridge/context.py +34 -37
  52. pygpt_net/core/ctx/container.py +13 -12
  53. pygpt_net/core/ctx/ctx.py +1 -1
  54. pygpt_net/core/ctx/output.py +7 -4
  55. pygpt_net/core/db/database.py +2 -2
  56. pygpt_net/core/debug/console/console.py +2 -2
  57. pygpt_net/core/debug/debug.py +12 -1
  58. pygpt_net/core/dispatcher/dispatcher.py +24 -1
  59. pygpt_net/core/events/app.py +7 -7
  60. pygpt_net/core/events/control.py +26 -26
  61. pygpt_net/core/events/event.py +6 -3
  62. pygpt_net/core/events/kernel.py +2 -2
  63. pygpt_net/core/events/render.py +13 -13
  64. pygpt_net/core/experts/experts.py +76 -82
  65. pygpt_net/core/experts/worker.py +12 -12
  66. pygpt_net/core/filesystem/actions.py +1 -2
  67. pygpt_net/core/models/models.py +5 -1
  68. pygpt_net/core/models/ollama.py +14 -5
  69. pygpt_net/core/render/plain/helpers.py +2 -5
  70. pygpt_net/core/render/plain/renderer.py +26 -30
  71. pygpt_net/core/render/web/body.py +1 -1
  72. pygpt_net/core/render/web/helpers.py +2 -2
  73. pygpt_net/core/render/web/renderer.py +4 -4
  74. pygpt_net/core/settings/settings.py +43 -13
  75. pygpt_net/core/tabs/tabs.py +20 -13
  76. pygpt_net/core/types/__init__.py +2 -1
  77. pygpt_net/core/types/agent.py +4 -4
  78. pygpt_net/core/types/base.py +19 -0
  79. pygpt_net/core/types/console.py +6 -6
  80. pygpt_net/core/types/mode.py +8 -8
  81. pygpt_net/core/types/multimodal.py +3 -3
  82. pygpt_net/core/types/openai.py +2 -1
  83. pygpt_net/data/config/config.json +5 -5
  84. pygpt_net/data/config/models.json +19 -3
  85. pygpt_net/data/config/settings.json +14 -14
  86. pygpt_net/data/locale/locale.de.ini +4 -1
  87. pygpt_net/data/locale/locale.en.ini +6 -3
  88. pygpt_net/data/locale/locale.es.ini +4 -1
  89. pygpt_net/data/locale/locale.fr.ini +4 -1
  90. pygpt_net/data/locale/locale.it.ini +4 -1
  91. pygpt_net/data/locale/locale.pl.ini +5 -4
  92. pygpt_net/data/locale/locale.uk.ini +4 -1
  93. pygpt_net/data/locale/locale.zh.ini +4 -1
  94. pygpt_net/item/ctx.py +256 -240
  95. pygpt_net/item/model.py +59 -116
  96. pygpt_net/item/preset.py +122 -105
  97. pygpt_net/plugin/twitter/plugin.py +2 -2
  98. pygpt_net/provider/agents/llama_index/workflow/planner.py +3 -3
  99. pygpt_net/provider/agents/openai/agent.py +4 -12
  100. pygpt_net/provider/agents/openai/agent_b2b.py +10 -15
  101. pygpt_net/provider/agents/openai/agent_planner.py +4 -4
  102. pygpt_net/provider/agents/openai/agent_with_experts.py +3 -7
  103. pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +4 -8
  104. pygpt_net/provider/agents/openai/agent_with_feedback.py +4 -8
  105. pygpt_net/provider/agents/openai/bot_researcher.py +2 -18
  106. pygpt_net/provider/agents/openai/bots/__init__.py +0 -0
  107. pygpt_net/provider/agents/openai/bots/research_bot/__init__.py +0 -0
  108. pygpt_net/provider/agents/openai/bots/research_bot/agents/__init__.py +0 -0
  109. pygpt_net/provider/agents/openai/bots/research_bot/agents/planner_agent.py +1 -1
  110. pygpt_net/provider/agents/openai/bots/research_bot/agents/search_agent.py +1 -0
  111. pygpt_net/provider/agents/openai/bots/research_bot/agents/writer_agent.py +1 -1
  112. pygpt_net/provider/agents/openai/bots/research_bot/manager.py +1 -10
  113. pygpt_net/provider/agents/openai/evolve.py +5 -9
  114. pygpt_net/provider/agents/openai/supervisor.py +4 -8
  115. pygpt_net/provider/core/config/patch.py +10 -3
  116. pygpt_net/provider/core/ctx/db_sqlite/utils.py +43 -43
  117. pygpt_net/provider/core/model/patch.py +11 -1
  118. pygpt_net/provider/core/preset/json_file.py +47 -49
  119. pygpt_net/provider/gpt/agents/experts.py +2 -2
  120. pygpt_net/tools/audio_transcriber/ui/dialogs.py +44 -54
  121. pygpt_net/tools/code_interpreter/body.py +1 -2
  122. pygpt_net/tools/code_interpreter/tool.py +7 -4
  123. pygpt_net/tools/code_interpreter/ui/html.py +1 -3
  124. pygpt_net/tools/code_interpreter/ui/widgets.py +2 -3
  125. pygpt_net/tools/html_canvas/ui/widgets.py +1 -3
  126. pygpt_net/tools/image_viewer/ui/dialogs.py +40 -37
  127. pygpt_net/tools/indexer/ui/widgets.py +2 -4
  128. pygpt_net/tools/media_player/tool.py +2 -5
  129. pygpt_net/tools/media_player/ui/widgets.py +60 -36
  130. pygpt_net/tools/text_editor/ui/widgets.py +18 -19
  131. pygpt_net/tools/translator/ui/widgets.py +39 -35
  132. pygpt_net/ui/base/context_menu.py +9 -4
  133. pygpt_net/ui/dialog/db.py +1 -3
  134. pygpt_net/ui/dialog/models.py +1 -3
  135. pygpt_net/ui/dialog/models_importer.py +2 -4
  136. pygpt_net/ui/dialogs.py +34 -30
  137. pygpt_net/ui/layout/chat/attachments.py +72 -84
  138. pygpt_net/ui/layout/chat/attachments_ctx.py +40 -44
  139. pygpt_net/ui/layout/chat/attachments_uploaded.py +36 -39
  140. pygpt_net/ui/layout/chat/calendar.py +100 -70
  141. pygpt_net/ui/layout/chat/chat.py +23 -17
  142. pygpt_net/ui/layout/chat/input.py +95 -118
  143. pygpt_net/ui/layout/chat/output.py +100 -162
  144. pygpt_net/ui/layout/chat/painter.py +89 -61
  145. pygpt_net/ui/layout/ctx/ctx_list.py +43 -52
  146. pygpt_net/ui/layout/status.py +23 -14
  147. pygpt_net/ui/layout/toolbox/agent.py +27 -38
  148. pygpt_net/ui/layout/toolbox/agent_llama.py +42 -45
  149. pygpt_net/ui/layout/toolbox/assistants.py +42 -38
  150. pygpt_net/ui/layout/toolbox/computer_env.py +32 -23
  151. pygpt_net/ui/layout/toolbox/footer.py +13 -16
  152. pygpt_net/ui/layout/toolbox/image.py +18 -21
  153. pygpt_net/ui/layout/toolbox/indexes.py +46 -89
  154. pygpt_net/ui/layout/toolbox/mode.py +20 -7
  155. pygpt_net/ui/layout/toolbox/model.py +12 -10
  156. pygpt_net/ui/layout/toolbox/presets.py +68 -52
  157. pygpt_net/ui/layout/toolbox/prompt.py +31 -58
  158. pygpt_net/ui/layout/toolbox/toolbox.py +25 -21
  159. pygpt_net/ui/layout/toolbox/vision.py +20 -22
  160. pygpt_net/ui/main.py +2 -4
  161. pygpt_net/ui/menu/about.py +64 -84
  162. pygpt_net/ui/menu/audio.py +87 -63
  163. pygpt_net/ui/menu/config.py +121 -127
  164. pygpt_net/ui/menu/debug.py +69 -76
  165. pygpt_net/ui/menu/file.py +32 -35
  166. pygpt_net/ui/menu/menu.py +2 -3
  167. pygpt_net/ui/menu/plugins.py +69 -33
  168. pygpt_net/ui/menu/theme.py +45 -46
  169. pygpt_net/ui/menu/tools.py +56 -60
  170. pygpt_net/ui/menu/video.py +20 -25
  171. pygpt_net/ui/tray.py +1 -2
  172. pygpt_net/ui/widget/audio/bar.py +1 -3
  173. pygpt_net/ui/widget/audio/input_button.py +3 -4
  174. pygpt_net/ui/widget/calendar/select.py +1 -2
  175. pygpt_net/ui/widget/dialog/base.py +12 -9
  176. pygpt_net/ui/widget/dialog/editor_file.py +20 -23
  177. pygpt_net/ui/widget/dialog/find.py +25 -24
  178. pygpt_net/ui/widget/dialog/profile.py +57 -53
  179. pygpt_net/ui/widget/draw/painter.py +62 -93
  180. pygpt_net/ui/widget/element/button.py +42 -30
  181. pygpt_net/ui/widget/element/checkbox.py +23 -15
  182. pygpt_net/ui/widget/element/group.py +6 -5
  183. pygpt_net/ui/widget/element/labels.py +1 -2
  184. pygpt_net/ui/widget/filesystem/explorer.py +93 -102
  185. pygpt_net/ui/widget/image/display.py +1 -2
  186. pygpt_net/ui/widget/lists/assistant.py +1 -2
  187. pygpt_net/ui/widget/lists/attachment.py +1 -2
  188. pygpt_net/ui/widget/lists/attachment_ctx.py +1 -2
  189. pygpt_net/ui/widget/lists/context.py +2 -4
  190. pygpt_net/ui/widget/lists/index.py +1 -2
  191. pygpt_net/ui/widget/lists/model.py +1 -2
  192. pygpt_net/ui/widget/lists/model_editor.py +1 -2
  193. pygpt_net/ui/widget/lists/model_importer.py +1 -2
  194. pygpt_net/ui/widget/lists/preset.py +1 -2
  195. pygpt_net/ui/widget/lists/preset_plugins.py +1 -2
  196. pygpt_net/ui/widget/lists/profile.py +1 -2
  197. pygpt_net/ui/widget/lists/uploaded.py +1 -2
  198. pygpt_net/ui/widget/option/checkbox.py +2 -4
  199. pygpt_net/ui/widget/option/checkbox_list.py +1 -4
  200. pygpt_net/ui/widget/option/cmd.py +1 -4
  201. pygpt_net/ui/widget/option/dictionary.py +25 -28
  202. pygpt_net/ui/widget/option/input.py +1 -3
  203. pygpt_net/ui/widget/tabs/Input.py +16 -12
  204. pygpt_net/ui/widget/tabs/body.py +5 -3
  205. pygpt_net/ui/widget/tabs/layout.py +36 -25
  206. pygpt_net/ui/widget/tabs/output.py +96 -74
  207. pygpt_net/ui/widget/textarea/calendar_note.py +1 -2
  208. pygpt_net/ui/widget/textarea/editor.py +41 -73
  209. pygpt_net/ui/widget/textarea/find.py +11 -10
  210. pygpt_net/ui/widget/textarea/html.py +3 -6
  211. pygpt_net/ui/widget/textarea/input.py +63 -64
  212. pygpt_net/ui/widget/textarea/notepad.py +54 -38
  213. pygpt_net/ui/widget/textarea/output.py +65 -54
  214. pygpt_net/ui/widget/textarea/search_input.py +5 -4
  215. pygpt_net/ui/widget/textarea/web.py +2 -4
  216. pygpt_net/ui/widget/vision/camera.py +2 -31
  217. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/METADATA +25 -154
  218. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/RECORD +218 -217
  219. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/LICENSE +0 -0
  220. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/WHEEL +0 -0
  221. {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/entry_points.txt +0 -0
@@ -6,11 +6,11 @@
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.01.19 02:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt
13
- from PySide6.QtWidgets import QVBoxLayout, QLabel, QHBoxLayout, QWidget, QSplitter, QSizePolicy, QRadioButton, QCheckBox
13
+ from PySide6.QtWidgets import QVBoxLayout, QLabel, QHBoxLayout, QWidget, QSplitter, QSizePolicy, QRadioButton, QCheckBox, QButtonGroup
14
14
 
15
15
  from pygpt_net.ui.widget.calendar.select import CalendarSelect
16
16
  from pygpt_net.ui.widget.element.checkbox import ColorCheckbox
@@ -20,6 +20,8 @@ from pygpt_net.utils import trans
20
20
 
21
21
 
22
22
  class Calendar:
23
+ __slots__ = ('window',)
24
+
23
25
  def __init__(self, window=None):
24
26
  """
25
27
  Calendar UI
@@ -34,8 +36,11 @@ class Calendar:
34
36
 
35
37
  :return: QWidget
36
38
  """
37
- self.window.ui.calendar['select'] = CalendarSelect(self.window)
38
- self.window.ui.calendar['note'] = CalendarNote(self.window)
39
+ ui = self.window.ui
40
+ ui.calendar['select'] = CalendarSelect(self.window)
41
+ ui.calendar['select'].setMinimumSize(200, 200)
42
+ ui.calendar['select'].setGridVisible(True)
43
+ ui.calendar['note'] = CalendarNote(self.window)
39
44
 
40
45
  def setup(self) -> QWidget:
41
46
  """
@@ -49,58 +54,76 @@ class Calendar:
49
54
  body.append(self.window.ui.calendar['select'])
50
55
  return body
51
56
 
57
+ def _on_filter_id_clicked(self, id_: int) -> None:
58
+ key = "all" if id_ == 0 else ("pinned" if id_ == 1 else "indexed")
59
+ self.window.controller.ctx.common.toggle_display_filter(key)
60
+
61
+ def _on_counters_all_toggled(self, checked: bool) -> None:
62
+ self.window.controller.calendar.note.toggle_counters_all(checked)
63
+
52
64
  def setup_filters(self) -> QWidget:
53
65
  """
54
66
  Setup calendar filters
55
67
 
56
68
  :return: QWidget
57
69
  """
58
- self.window.ui.nodes['filter.ctx.label'] = QLabel(trans("filter.ctx.label"))
59
-
60
- # display: all
61
- self.window.ui.nodes['filter.ctx.radio.all'] = QRadioButton(trans("filter.ctx.radio.all"))
62
- self.window.ui.nodes['filter.ctx.radio.all'].clicked.connect(
63
- lambda: self.window.controller.ctx.common.toggle_display_filter("all"))
64
-
65
- # display: only pinned
66
- self.window.ui.nodes['filter.ctx.radio.pinned'] = QRadioButton(trans("filter.ctx.radio.pinned"))
67
- self.window.ui.nodes['filter.ctx.radio.pinned'].clicked.connect(
68
- lambda: self.window.controller.ctx.common.toggle_display_filter("pinned"))
69
-
70
- # display: only indexed
71
- self.window.ui.nodes['filter.ctx.radio.indexed'] = QRadioButton(trans("filter.ctx.radio.indexed"))
72
- self.window.ui.nodes['filter.ctx.radio.indexed'].clicked.connect(
73
- lambda: self.window.controller.ctx.common.toggle_display_filter("indexed"))
74
-
75
- # all counters
76
- self.window.ui.nodes['filter.ctx.counters.all'] = QCheckBox(trans("filter.ctx.counters.all"))
77
- self.window.ui.nodes['filter.ctx.counters.all'].stateChanged.connect(
78
- lambda: self.window.controller.calendar.note.toggle_counters_all(
79
- self.window.ui.nodes['filter.ctx.counters.all'].isChecked())
80
- )
81
- if self.window.core.config.get("ctx.counters.all"):
82
- self.window.ui.nodes['filter.ctx.counters.all'].setChecked(True)
70
+ ui = self.window.ui
71
+ nodes = ui.nodes
72
+
73
+ label_existing = nodes.get('filter.ctx.label')
74
+ if label_existing is None:
75
+ layout = QHBoxLayout()
76
+ widget = QWidget()
77
+ rows = QVBoxLayout()
78
+ widget.setLayout(rows)
79
+
80
+ label = QLabel(trans("filter.ctx.label"), widget)
81
+
82
+ radio_all = QRadioButton(trans("filter.ctx.radio.all"), widget)
83
+ radio_pinned = QRadioButton(trans("filter.ctx.radio.pinned"), widget)
84
+ radio_indexed = QRadioButton(trans("filter.ctx.radio.indexed"), widget)
85
+
86
+ counters_all = QCheckBox(trans("filter.ctx.counters.all"), widget)
87
+
88
+ layout.addWidget(label)
89
+ layout.addWidget(radio_all)
90
+ layout.addWidget(radio_pinned)
91
+ layout.addWidget(radio_indexed)
92
+ layout.addWidget(counters_all)
93
+ layout.addStretch()
94
+
95
+ nodes['filter.ctx.labels'] = ColorCheckbox(self.window)
96
+
97
+ rows.addLayout(layout)
98
+ rows.addWidget(nodes['filter.ctx.labels'])
99
+
100
+ group = QButtonGroup(widget)
101
+ group.setExclusive(True)
102
+ group.addButton(radio_all, 0)
103
+ group.addButton(radio_pinned, 1)
104
+ group.addButton(radio_indexed, 2)
105
+ group.idClicked.connect(self._on_filter_id_clicked)
106
+
107
+ counters_all.toggled.connect(self._on_counters_all_toggled)
108
+
109
+ nodes['filter.ctx.label'] = label
110
+ nodes['filter.ctx.radio.all'] = radio_all
111
+ nodes['filter.ctx.radio.pinned'] = radio_pinned
112
+ nodes['filter.ctx.radio.indexed'] = radio_indexed
113
+ nodes['filter.ctx.counters.all'] = counters_all
83
114
  else:
84
- self.window.ui.nodes['filter.ctx.counters.all'].setChecked(False)
85
-
86
- # layout
87
- layout = QHBoxLayout()
88
- layout.addWidget(self.window.ui.nodes['filter.ctx.label'])
89
- layout.addWidget(self.window.ui.nodes['filter.ctx.radio.all'])
90
- layout.addWidget(self.window.ui.nodes['filter.ctx.radio.pinned'])
91
- layout.addWidget(self.window.ui.nodes['filter.ctx.radio.indexed'])
92
- layout.addWidget(self.window.ui.nodes['filter.ctx.counters.all'])
93
- layout.addStretch()
94
-
95
- self.window.ui.nodes['filter.ctx.labels'] = ColorCheckbox(self.window)
96
-
97
- rows = QVBoxLayout()
98
- rows.addLayout(layout)
99
- rows.addWidget(self.window.ui.nodes['filter.ctx.labels'])
100
-
101
- widget = QWidget()
102
- widget.setLayout(rows)
103
-
115
+ widget = nodes['filter.ctx.label'].parentWidget()
116
+ nodes['filter.ctx.label'].setText(trans("filter.ctx.label"))
117
+ nodes['filter.ctx.radio.all'].setText(trans("filter.ctx.radio.all"))
118
+ nodes['filter.ctx.radio.pinned'].setText(trans("filter.ctx.radio.pinned"))
119
+ nodes['filter.ctx.radio.indexed'].setText(trans("filter.ctx.radio.indexed"))
120
+ nodes['filter.ctx.counters.all'].setText(trans("filter.ctx.counters.all"))
121
+
122
+ desired = bool(self.window.core.config.get("ctx.counters.all"))
123
+ if nodes['filter.ctx.counters.all'].isChecked() != desired:
124
+ nodes['filter.ctx.counters.all'].setChecked(desired)
125
+
126
+ widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
104
127
  return widget
105
128
 
106
129
  def setup_calendar(self) -> QWidget:
@@ -109,22 +132,32 @@ class Calendar:
109
132
 
110
133
  :return: QSplitter
111
134
  """
112
- # calendar
135
+ ui = self.window.ui
136
+ nodes = ui.nodes
137
+ calendar = ui.calendar
138
+
113
139
  select_layout = QVBoxLayout()
114
- self.window.ui.calendar['select'].setMinimumHeight(200)
115
- self.window.ui.calendar['select'].setMinimumWidth(200)
116
- self.window.ui.calendar['select'].setGridVisible(True)
117
- select_layout.addWidget(self.window.ui.calendar['select'])
140
+ select_layout.addWidget(calendar['select'])
118
141
  select_layout.setContentsMargins(5, 0, 5, 0)
119
142
 
120
- self.window.ui.nodes['tip.output.tab.calendar'] = HelpLabel(trans('tip.output.tab.calendar'), self.window)
143
+ tip = nodes.get('tip.output.tab.calendar')
144
+ if tip is None:
145
+ tip = HelpLabel(trans('tip.output.tab.calendar'), self.window)
146
+ nodes['tip.output.tab.calendar'] = tip
147
+ else:
148
+ tip.setText(trans('tip.output.tab.calendar'))
149
+
150
+ note_label = calendar.get('note.label')
151
+ if note_label is None:
152
+ note_label = QLabel(trans('calendar.note.label'))
153
+ calendar['note.label'] = note_label
154
+ else:
155
+ note_label.setText(trans('calendar.note.label'))
121
156
 
122
- # note
123
- self.window.ui.calendar['note.label'] = QLabel(trans('calendar.note.label'))
124
157
  layout = QVBoxLayout()
125
- layout.addWidget(self.window.ui.calendar['note.label'])
126
- layout.addWidget(self.window.ui.calendar['note'])
127
- layout.addWidget(self.window.ui.nodes['tip.output.tab.calendar'])
158
+ layout.addWidget(note_label)
159
+ layout.addWidget(calendar['note'])
160
+ layout.addWidget(tip)
128
161
  layout.setContentsMargins(0, 0, 0, 0)
129
162
 
130
163
  widget = QWidget()
@@ -136,16 +169,13 @@ class Calendar:
136
169
  select_widget = QWidget()
137
170
  select_widget.setLayout(select_layout)
138
171
 
139
- # layout / splitter
140
- self.window.ui.splitters['calendar'] = QSplitter(Qt.Horizontal)
141
- self.window.ui.splitters['calendar'].addWidget(select_widget)
142
- self.window.ui.splitters['calendar'].addWidget(widget)
143
- self.window.ui.splitters['calendar'].setStretchFactor(0, 6) # 60%
144
- self.window.ui.splitters['calendar'].setStretchFactor(1, 4) # 40%
172
+ ui.splitters['calendar'] = QSplitter(Qt.Horizontal)
173
+ ui.splitters['calendar'].addWidget(select_widget)
174
+ ui.splitters['calendar'].addWidget(widget)
175
+ ui.splitters['calendar'].setStretchFactor(0, 6)
176
+ ui.splitters['calendar'].setStretchFactor(1, 4)
145
177
 
146
- self.window.ui.splitters['calendar'].setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
178
+ ui.splitters['calendar'].setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
147
179
  filters.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
148
180
 
149
- # build tab body
150
- return self.window.core.tabs.from_widget(self.window.ui.splitters['calendar'])
151
-
181
+ return self.window.core.tabs.from_widget(ui.splitters['calendar'])
@@ -6,10 +6,10 @@
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.11.17 03:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from PySide6.QtCore import Qt
12
+ from PySide6.QtCore import Qt, Slot
13
13
  from PySide6.QtWidgets import QSplitter
14
14
 
15
15
  from pygpt_net.ui.layout.chat.input import Input
@@ -34,28 +34,34 @@ class ChatMain:
34
34
  :return: QSplitter
35
35
  :rtype: QSplitter
36
36
  """
37
- input = self.input.setup()
38
- output = self.output.setup()
37
+ input_widget = self.input.setup()
38
+ output_widget = self.output.setup()
39
39
 
40
- # main vertical splitter
41
- self.window.ui.splitters['main.output'] = QSplitter(Qt.Vertical)
42
- self.window.ui.splitters['main.output'].addWidget(output)
43
- self.window.ui.splitters['main.output'].addWidget(input)
44
- self.window.ui.splitters['main.output'].setStretchFactor(0, 9) # Output widget stretch factor
45
- self.window.ui.splitters['main.output'].setStretchFactor(1, 1) # Input widget stretch factor
46
- self.window.ui.splitters['main.output'].splitterMoved.connect(self.on_splitter_moved)
47
- self.window.controller.ui.splitter_output_size_input = self.window.ui.splitters['main.output'].sizes()
40
+ splitter = QSplitter(Qt.Vertical)
41
+ self.window.ui.splitters['main.output'] = splitter
42
+ splitter.addWidget(output_widget)
43
+ splitter.addWidget(input_widget)
44
+ splitter.setStretchFactor(0, 9) # Output widget stretch factor
45
+ splitter.setStretchFactor(1, 1) # Input widget stretch factor
46
+ splitter.splitterMoved.connect(self.on_splitter_moved)
47
+ self.window.controller.ui.splitter_output_size_input = splitter.sizes()
48
48
 
49
- return self.window.ui.splitters['main.output']
49
+ return splitter
50
50
 
51
+ @Slot(int, int)
51
52
  def on_splitter_moved(self, pos, index):
52
53
  """
53
54
  Store the size of the output splitter when it is moved
54
55
  """
55
- if "input" not in self.window.ui.tabs:
56
+ tabs = self.window.ui.tabs
57
+ if "input" not in tabs:
56
58
  return
57
- idx = self.window.ui.tabs['input'].currentIndex()
59
+ splitter = self.window.ui.splitters.get('main.output')
60
+ if splitter is None:
61
+ return
62
+ idx = tabs['input'].currentIndex()
63
+ sizes = splitter.sizes()
58
64
  if idx != 0:
59
- self.window.controller.ui.splitter_output_size_files = self.window.ui.splitters['main.output'].sizes()
65
+ self.window.controller.ui.splitter_output_size_files = sizes
60
66
  else:
61
- self.window.controller.ui.splitter_output_size_input = self.window.ui.splitters['main.output'].sizes()
67
+ self.window.controller.ui.splitter_output_size_input = sizes
@@ -6,12 +6,14 @@
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.01.18 03:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
+ from functools import partial
13
+
12
14
  from PySide6.QtCore import Qt
13
15
  from PySide6.QtGui import QIcon
14
- from PySide6.QtWidgets import QVBoxLayout, QLabel, QHBoxLayout, QPushButton, QRadioButton, QCheckBox, QWidget, \
16
+ from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QPushButton, QRadioButton, QCheckBox, QWidget, \
15
17
  QGridLayout
16
18
 
17
19
  from pygpt_net.ui.layout.chat.attachments import Attachments
@@ -25,7 +27,6 @@ from pygpt_net.ui.widget.element.labels import HelpLabel
25
27
  from pygpt_net.ui.widget.tabs.Input import InputTabs
26
28
  from pygpt_net.ui.widget.textarea.input import ChatInput
27
29
  from pygpt_net.utils import trans
28
- import pygpt_net.icons_rc
29
30
 
30
31
 
31
32
  class Input:
@@ -58,30 +59,27 @@ class Input:
58
59
  files_uploaded = self.setup_attachments_uploaded()
59
60
  files_ctx = self.setup_attachments_ctx()
60
61
 
61
- # tabs
62
62
  self.window.ui.tabs['input'] = InputTabs(self.window)
63
- self.window.ui.tabs['input'].setMinimumHeight(self.min_height_input_tab)
64
- self.window.ui.tabs['input'].addTab(input, trans('input.tab'))
65
- self.window.ui.tabs['input'].addTab(files, trans('attachments.tab'))
66
- self.window.ui.tabs['input'].addTab(files_uploaded, trans('attachments_uploaded.tab'))
67
- self.window.ui.tabs['input'].addTab(files_ctx, trans('attachments_uploaded.tab'))
68
- self.window.ui.tabs['input'].currentChanged.connect(self.update_min_height) # update min height on tab change
69
-
70
- self.window.ui.tabs['input'].setTabIcon(0, QIcon(":/icons/input.svg"))
71
- self.window.ui.tabs['input'].setTabIcon(1, QIcon(":/icons/attachment.svg"))
72
- self.window.ui.tabs['input'].setTabIcon(2, QIcon(":/icons/upload.svg"))
73
- self.window.ui.tabs['input'].setTabIcon(3, QIcon(":/icons/upload.svg"))
74
-
75
- # layout
76
- layout = QVBoxLayout()
63
+ tabs = self.window.ui.tabs['input']
64
+ tabs.setMinimumHeight(self.min_height_input_tab)
65
+ tabs.addTab(input, trans('input.tab'))
66
+ tabs.addTab(files, trans('attachments.tab'))
67
+ tabs.addTab(files_uploaded, trans('attachments_uploaded.tab'))
68
+ tabs.addTab(files_ctx, trans('attachments_uploaded.tab'))
69
+ tabs.currentChanged.connect(self.update_min_height)
70
+
71
+ tabs.setTabIcon(0, QIcon(":/icons/input.svg"))
72
+ upload_icon = QIcon(":/icons/upload.svg")
73
+ tabs.setTabIcon(1, QIcon(":/icons/attachment.svg"))
74
+ tabs.setTabIcon(2, upload_icon)
75
+ tabs.setTabIcon(3, upload_icon)
76
+
77
+ widget = QWidget()
78
+ layout = QVBoxLayout(widget)
77
79
  layout.addLayout(self.setup_header())
78
- layout.addWidget(self.window.ui.tabs['input'])
80
+ layout.addWidget(tabs)
79
81
  layout.addLayout(self.setup_bottom())
80
82
  layout.setContentsMargins(0, 0, 0, 5)
81
-
82
- widget = QWidget()
83
- widget.setLayout(layout)
84
-
85
83
  return widget
86
84
 
87
85
  def setup_input(self) -> QWidget:
@@ -90,16 +88,13 @@ class Input:
90
88
 
91
89
  :return: QWidget
92
90
  """
93
- # input textarea
94
91
  self.window.ui.nodes['input'] = ChatInput(self.window)
95
92
  self.window.ui.nodes['input'].setMinimumHeight(self.min_height_input)
96
93
 
97
- layout = QVBoxLayout()
94
+ widget = QWidget()
95
+ layout = QVBoxLayout(widget)
98
96
  layout.addWidget(self.window.ui.nodes['input'])
99
97
  layout.setContentsMargins(0, 0, 0, 0)
100
-
101
- widget = QWidget()
102
- widget.setLayout(layout)
103
98
  return widget
104
99
 
105
100
  def setup_attachments(self) -> QWidget:
@@ -108,12 +103,10 @@ class Input:
108
103
 
109
104
  :return: QWidget
110
105
  """
111
- layout = QVBoxLayout()
106
+ widget = QWidget()
107
+ layout = QVBoxLayout(widget)
112
108
  layout.addLayout(self.attachments.setup())
113
109
  layout.setContentsMargins(0, 0, 0, 0)
114
-
115
- widget = QWidget()
116
- widget.setLayout(layout)
117
110
  widget.setMinimumHeight(self.min_height_files_tab)
118
111
  return widget
119
112
 
@@ -123,12 +116,10 @@ class Input:
123
116
 
124
117
  :return: QWidget
125
118
  """
126
- layout = QVBoxLayout()
119
+ widget = QWidget()
120
+ layout = QVBoxLayout(widget)
127
121
  layout.addLayout(self.attachments_uploaded.setup())
128
122
  layout.setContentsMargins(0, 0, 0, 0)
129
-
130
- widget = QWidget()
131
- widget.setLayout(layout)
132
123
  widget.setMinimumHeight(self.min_height_files_tab)
133
124
  return widget
134
125
 
@@ -138,12 +129,10 @@ class Input:
138
129
 
139
130
  :return: QWidget
140
131
  """
141
- layout = QVBoxLayout()
132
+ widget = QWidget()
133
+ layout = QVBoxLayout(widget)
142
134
  layout.addLayout(self.attachments_ctx.setup())
143
135
  layout.setContentsMargins(0, 0, 0, 0)
144
-
145
- widget = QWidget()
146
- widget.setLayout(layout)
147
136
  widget.setMinimumHeight(self.min_height_files_tab)
148
137
  return widget
149
138
 
@@ -184,7 +173,6 @@ class Input:
184
173
  layout.addWidget(self.window.ui.plugin_addon['audio.output.bar'], alignment=Qt.AlignCenter)
185
174
  layout.addLayout(self.setup_buttons())
186
175
  layout.setContentsMargins(2, 0, 2, 0)
187
-
188
176
  return layout
189
177
 
190
178
  def setup_buttons(self) -> QHBoxLayout:
@@ -193,87 +181,76 @@ class Input:
193
181
 
194
182
  :return: QHBoxLayout
195
183
  """
196
- # send with: enter
197
- self.window.ui.nodes['input.send_enter'] = QRadioButton(trans("input.radio.enter"))
198
- self.window.ui.nodes['input.send_enter'].clicked.connect(
199
- lambda: self.window.controller.chat.common.toggle_send_shift(
200
- 1))
201
-
202
- # send with: shift + enter
203
- self.window.ui.nodes['input.send_shift_enter'] = QRadioButton(trans("input.radio.enter_shift"))
204
- self.window.ui.nodes['input.send_shift_enter'].clicked.connect(
205
- lambda: self.window.controller.chat.common.toggle_send_shift(
206
- 2))
207
-
208
- # send with: none
209
- self.window.ui.nodes['input.send_none'] = QRadioButton(trans("input.radio.none"))
210
- self.window.ui.nodes['input.send_none'].clicked.connect(
211
- lambda: self.window.controller.chat.common.toggle_send_shift(
212
- 0))
213
-
214
- # send clear
215
- self.window.ui.nodes['input.send_clear'] = QCheckBox(trans('input.send_clear'))
216
- self.window.ui.nodes['input.send_clear'].stateChanged.connect(
217
- lambda: self.window.controller.chat.common.toggle_send_clear(
218
- self.window.ui.nodes['input.send_clear'].isChecked()))
219
-
220
- # stream
221
- self.window.ui.nodes['input.stream'] = QCheckBox(trans('input.stream'))
222
- self.window.ui.nodes['input.stream'].stateChanged.connect(
223
- lambda: self.window.controller.chat.common.toggle_stream(self.window.ui.nodes['input.stream'].isChecked()))
224
-
225
- # send button
226
- self.window.ui.nodes['input.send_btn'] = QPushButton(trans("input.btn.send"))
227
- self.window.ui.nodes['input.send_btn'].clicked.connect(
228
- lambda: self.window.controller.chat.input.send_input())
229
-
230
- # stop button
231
- self.window.ui.nodes['input.stop_btn'] = QPushButton(trans("input.btn.stop"))
232
- self.window.ui.nodes['input.stop_btn'].setVisible(False)
233
- self.window.ui.nodes['input.stop_btn'].clicked.connect(
234
- lambda: self.window.controller.kernel.stop())
235
-
236
- # update button
237
- self.window.ui.nodes['input.update_btn'] = QPushButton(trans("input.btn.update"))
238
- self.window.ui.nodes['input.update_btn'].setVisible(False)
239
- self.window.ui.nodes['input.update_btn'].clicked.connect(
240
- lambda: self.window.controller.ctx.extra.edit_submit())
241
-
242
- # cancel button
243
- self.window.ui.nodes['input.cancel_btn'] = QPushButton(trans("input.btn.cancel"))
244
- self.window.ui.nodes['input.cancel_btn'].setVisible(False)
245
- self.window.ui.nodes['input.cancel_btn'].clicked.connect(
246
- lambda: self.window.controller.ctx.extra.edit_cancel())
247
-
248
- # layout
249
- self.window.ui.nodes['ui.input.buttons'] = QHBoxLayout()
250
- self.window.ui.nodes['ui.input.buttons'].addWidget(self.window.ui.nodes['input.stream'])
251
- self.window.ui.nodes['ui.input.buttons'].addWidget(self.window.ui.nodes['input.send_clear'])
252
- self.window.ui.nodes['ui.input.buttons'].addWidget(self.window.ui.nodes['input.send_enter'])
253
- self.window.ui.nodes['ui.input.buttons'].addWidget(self.window.ui.nodes['input.send_shift_enter'])
254
- self.window.ui.nodes['ui.input.buttons'].addWidget(self.window.ui.nodes['input.send_none'])
255
- self.window.ui.nodes['ui.input.buttons'].addWidget(self.window.ui.nodes['input.send_btn'])
256
- self.window.ui.nodes['ui.input.buttons'].addWidget(self.window.ui.nodes['input.stop_btn'])
257
- self.window.ui.nodes['ui.input.buttons'].addWidget(self.window.ui.nodes['input.cancel_btn'])
258
- self.window.ui.nodes['ui.input.buttons'].addWidget(self.window.ui.nodes['input.update_btn'])
259
- self.window.ui.nodes['ui.input.buttons'].setAlignment(Qt.AlignRight)
260
-
261
- return self.window.ui.nodes['ui.input.buttons']
184
+ nodes = self.window.ui.nodes
185
+ controller = self.window.controller
186
+
187
+ nodes['input.send_enter'] = QRadioButton(trans("input.radio.enter"))
188
+ nodes['input.send_enter'].toggled.connect(partial(self._on_send_mode_toggled, 1))
189
+
190
+ nodes['input.send_shift_enter'] = QRadioButton(trans("input.radio.enter_shift"))
191
+ nodes['input.send_shift_enter'].toggled.connect(partial(self._on_send_mode_toggled, 2))
192
+
193
+ nodes['input.send_none'] = QRadioButton(trans("input.radio.none"))
194
+ nodes['input.send_none'].toggled.connect(partial(self._on_send_mode_toggled, 0))
195
+
196
+ nodes['input.send_clear'] = QCheckBox(trans('input.send_clear'))
197
+ nodes['input.send_clear'].toggled.connect(controller.chat.common.toggle_send_clear)
198
+
199
+ nodes['input.stream'] = QCheckBox(trans('input.stream'))
200
+ nodes['input.stream'].toggled.connect(controller.chat.common.toggle_stream)
201
+
202
+ nodes['input.send_btn'] = QPushButton(trans("input.btn.send"))
203
+ nodes['input.send_btn'].clicked.connect(controller.chat.input.send_input)
204
+
205
+ nodes['input.stop_btn'] = QPushButton(trans("input.btn.stop"))
206
+ nodes['input.stop_btn'].setVisible(False)
207
+ nodes['input.stop_btn'].clicked.connect(controller.kernel.stop)
208
+
209
+ nodes['input.update_btn'] = QPushButton(trans("input.btn.update"))
210
+ nodes['input.update_btn'].setVisible(False)
211
+ nodes['input.update_btn'].clicked.connect(controller.ctx.extra.edit_submit)
212
+
213
+ nodes['input.cancel_btn'] = QPushButton(trans("input.btn.cancel"))
214
+ nodes['input.cancel_btn'].setVisible(False)
215
+ nodes['input.cancel_btn'].clicked.connect(controller.ctx.extra.edit_cancel)
216
+
217
+ nodes['ui.input.buttons'] = QHBoxLayout()
218
+ nodes['ui.input.buttons'].addWidget(nodes['input.stream'])
219
+ nodes['ui.input.buttons'].addWidget(nodes['input.send_clear'])
220
+ nodes['ui.input.buttons'].addWidget(nodes['input.send_enter'])
221
+ nodes['ui.input.buttons'].addWidget(nodes['input.send_shift_enter'])
222
+ nodes['ui.input.buttons'].addWidget(nodes['input.send_none'])
223
+ nodes['ui.input.buttons'].addWidget(nodes['input.send_btn'])
224
+ nodes['ui.input.buttons'].addWidget(nodes['input.stop_btn'])
225
+ nodes['ui.input.buttons'].addWidget(nodes['input.cancel_btn'])
226
+ nodes['ui.input.buttons'].addWidget(nodes['input.update_btn'])
227
+ nodes['ui.input.buttons'].setAlignment(Qt.AlignRight)
228
+
229
+ return nodes['ui.input.buttons']
230
+
231
+ def _on_send_mode_toggled(self, mode: int, checked: bool):
232
+ if checked:
233
+ self.window.controller.chat.common.toggle_send_shift(mode)
262
234
 
263
235
  def update_min_height(self):
264
236
  """
265
237
  Update the minimum height of the input tab
266
238
  """
267
- idx = self.window.ui.tabs['input'].currentIndex()
239
+ tabs = self.window.ui.tabs['input']
240
+ nodes = self.window.ui.nodes
241
+ splitters = self.window.ui.splitters
242
+ controller_ui = self.window.controller.ui
243
+
244
+ idx = tabs.currentIndex()
268
245
  if idx == 0:
269
- self.window.ui.nodes['input'].setMinimumHeight(self.min_height_input)
270
- self.window.ui.tabs['input'].setMinimumHeight(self.min_height_input_tab)
271
- if (self.window.controller.ui.splitter_output_size_input
272
- and self.window.controller.ui.splitter_output_size_input != [0,0]):
273
- self.window.ui.splitters['main.output'].setSizes(self.window.controller.ui.splitter_output_size_input)
246
+ nodes['input'].setMinimumHeight(self.min_height_input)
247
+ tabs.setMinimumHeight(self.min_height_input_tab)
248
+ sizes = controller_ui.splitter_output_size_input
249
+ if sizes and sizes != [0, 0]:
250
+ splitters['main.output'].setSizes(sizes)
274
251
  else:
275
- if (self.window.controller.ui.splitter_output_size_files
276
- and self.window.controller.ui.splitter_output_size_input != [0,0]):
277
- self.window.ui.splitters['main.output'].setSizes(self.window.controller.ui.splitter_output_size_files)
278
- self.window.ui.nodes['input'].setMinimumHeight(self.min_height_files_tab)
279
- self.window.ui.tabs['input'].setMinimumHeight(self.min_height_files_tab + 90)
252
+ sizes = controller_ui.splitter_output_size_files
253
+ if sizes and controller_ui.splitter_output_size_input != [0, 0]:
254
+ splitters['main.output'].setSizes(sizes)
255
+ nodes['input'].setMinimumHeight(self.min_height_files_tab)
256
+ tabs.setMinimumHeight(self.min_height_files_tab + 90)