pygpt-net 2.4.39__py3-none-any.whl → 2.4.40__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 (85) hide show
  1. CHANGELOG.md +13 -0
  2. README.md +19 -2
  3. pygpt_net/CHANGELOG.txt +13 -0
  4. pygpt_net/__init__.py +3 -3
  5. pygpt_net/controller/__init__.py +5 -3
  6. pygpt_net/controller/audio/__init__.py +9 -1
  7. pygpt_net/controller/chat/input.py +2 -1
  8. pygpt_net/controller/chat/render.py +2 -2
  9. pygpt_net/controller/ctx/__init__.py +2 -2
  10. pygpt_net/controller/debug/__init__.py +13 -2
  11. pygpt_net/controller/kernel/__init__.py +2 -1
  12. pygpt_net/controller/notepad.py +7 -6
  13. pygpt_net/controller/theme/nodes.py +2 -5
  14. pygpt_net/controller/tools/__init__.py +37 -1
  15. pygpt_net/controller/ui/__init__.py +1 -5
  16. pygpt_net/controller/ui/tabs.py +104 -12
  17. pygpt_net/core/command.py +3 -1
  18. pygpt_net/core/ctx/__init__.py +6 -2
  19. pygpt_net/core/ctx/container.py +5 -5
  20. pygpt_net/core/debug/tabs.py +3 -1
  21. pygpt_net/core/render/base.py +2 -2
  22. pygpt_net/core/render/web/body.py +1 -1
  23. pygpt_net/core/render/web/renderer.py +208 -38
  24. pygpt_net/core/tabs/__init__.py +104 -43
  25. pygpt_net/core/tabs/tab.py +4 -1
  26. pygpt_net/core/web.py +127 -1
  27. pygpt_net/data/config/config.json +4 -3
  28. pygpt_net/data/config/models.json +3 -3
  29. pygpt_net/data/config/modes.json +3 -3
  30. pygpt_net/data/css/web-blocks.css +18 -0
  31. pygpt_net/data/css/web-blocks.light.css +7 -0
  32. pygpt_net/data/css/web-chatgpt.css +8 -0
  33. pygpt_net/data/css/web-chatgpt_wide.css +8 -0
  34. pygpt_net/data/icons/split_screen.svg +1 -0
  35. pygpt_net/data/locale/locale.de.ini +1 -1
  36. pygpt_net/data/locale/locale.en.ini +4 -2
  37. pygpt_net/data/locale/locale.es.ini +1 -1
  38. pygpt_net/data/locale/locale.fr.ini +1 -1
  39. pygpt_net/data/locale/locale.it.ini +1 -1
  40. pygpt_net/data/locale/locale.pl.ini +2 -2
  41. pygpt_net/data/locale/locale.uk.ini +1 -1
  42. pygpt_net/data/locale/locale.zh.ini +1 -1
  43. pygpt_net/data/locale/plugin.cmd_web.de.ini +2 -0
  44. pygpt_net/data/locale/plugin.cmd_web.en.ini +20 -10
  45. pygpt_net/data/locale/plugin.cmd_web.es.ini +2 -0
  46. pygpt_net/data/locale/plugin.cmd_web.fr.ini +2 -0
  47. pygpt_net/data/locale/plugin.cmd_web.it.ini +2 -0
  48. pygpt_net/data/locale/plugin.cmd_web.pl.ini +2 -0
  49. pygpt_net/data/locale/plugin.cmd_web.uk.ini +2 -0
  50. pygpt_net/data/locale/plugin.cmd_web.zh.ini +2 -0
  51. pygpt_net/icons.qrc +1 -0
  52. pygpt_net/icons_rc.py +165 -136
  53. pygpt_net/item/ctx.py +46 -24
  54. pygpt_net/plugin/audio_output/__init__.py +4 -1
  55. pygpt_net/plugin/base/plugin.py +18 -4
  56. pygpt_net/plugin/cmd_code_interpreter/__init__.py +39 -37
  57. pygpt_net/plugin/cmd_code_interpreter/runner.py +25 -12
  58. pygpt_net/plugin/cmd_web/__init__.py +46 -6
  59. pygpt_net/plugin/cmd_web/config.py +74 -48
  60. pygpt_net/plugin/cmd_web/websearch.py +61 -28
  61. pygpt_net/plugin/cmd_web/worker.py +79 -13
  62. pygpt_net/provider/core/config/patch.py +22 -1
  63. pygpt_net/tools/__init__.py +9 -1
  64. pygpt_net/tools/base.py +15 -1
  65. pygpt_net/tools/code_interpreter/__init__.py +174 -75
  66. pygpt_net/tools/code_interpreter/ui/dialogs.py +21 -103
  67. pygpt_net/tools/code_interpreter/ui/widgets.py +284 -9
  68. pygpt_net/tools/html_canvas/__init__.py +78 -23
  69. pygpt_net/tools/html_canvas/ui/dialogs.py +46 -62
  70. pygpt_net/tools/html_canvas/ui/widgets.py +96 -3
  71. pygpt_net/ui/base/context_menu.py +2 -2
  72. pygpt_net/ui/layout/ctx/ctx_list.py +13 -4
  73. pygpt_net/ui/layout/toolbox/footer.py +1 -1
  74. pygpt_net/ui/main.py +2 -2
  75. pygpt_net/ui/menu/debug.py +11 -1
  76. pygpt_net/ui/widget/filesystem/explorer.py +2 -2
  77. pygpt_net/ui/widget/lists/context.py +26 -5
  78. pygpt_net/ui/widget/tabs/Input.py +2 -2
  79. pygpt_net/ui/widget/tabs/body.py +2 -1
  80. pygpt_net/ui/widget/tabs/output.py +126 -61
  81. {pygpt_net-2.4.39.dist-info → pygpt_net-2.4.40.dist-info}/METADATA +20 -3
  82. {pygpt_net-2.4.39.dist-info → pygpt_net-2.4.40.dist-info}/RECORD +85 -84
  83. {pygpt_net-2.4.39.dist-info → pygpt_net-2.4.40.dist-info}/LICENSE +0 -0
  84. {pygpt_net-2.4.39.dist-info → pygpt_net-2.4.40.dist-info}/WHEEL +0 -0
  85. {pygpt_net-2.4.39.dist-info → pygpt_net-2.4.40.dist-info}/entry_points.txt +0 -0
@@ -6,20 +6,24 @@
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.20 03:00:00 #
9
+ # Updated Date: 2024.12.12 04:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
13
13
 
14
14
  from PySide6.QtCore import QTimer
15
15
  from PySide6.QtGui import QTextCursor, QAction, QIcon
16
+ from PySide6.QtWidgets import QWidget
16
17
 
18
+ from pygpt_net.core.tabs.tab import Tab
17
19
  from pygpt_net.tools.base import BaseTool
18
- from pygpt_net.tools.code_interpreter.ui.dialogs import Interpreter
20
+ from pygpt_net.tools.code_interpreter.ui.dialogs import Tool
19
21
  from pygpt_net.core.events import Event
20
22
  from pygpt_net.item.ctx import CtxItem
21
23
  from pygpt_net.utils import trans
22
24
 
25
+ from .ui.widgets import PythonInput, PythonOutput, ToolWidget, ToolSignals
26
+
23
27
 
24
28
  class CodeInterpreter(BaseTool):
25
29
  def __init__(self, *args, **kwargs):
@@ -30,11 +34,15 @@ class CodeInterpreter(BaseTool):
30
34
  """
31
35
  super(CodeInterpreter, self).__init__(*args, **kwargs)
32
36
  self.id = "interpreter"
37
+ self.has_tab = True
38
+ self.tab_title = "menu.tools.interpreter"
39
+ self.tab_icon = ":/icons/code.svg"
33
40
  self.opened = False
34
41
  self.is_edit = False
35
42
  self.auto_clear = False
36
43
  self.dialog = None
37
44
  self.ipython = True
45
+ self.signals = ToolSignals()
38
46
 
39
47
  # interpreter data files in /data directory
40
48
  self.file_current = ".interpreter.current.py"
@@ -49,15 +57,15 @@ class CodeInterpreter(BaseTool):
49
57
 
50
58
  # restore
51
59
  if self.window.core.config.has("interpreter.input"):
52
- self.window.ui.nodes['interpreter.input'].setPlainText(self.window.core.config.get("interpreter.input"))
60
+ self.signals.update_input.emit(self.window.core.config.get("interpreter.input"))
53
61
  if self.window.core.config.has("interpreter.execute_all"):
54
- self.window.ui.nodes['interpreter.all'].setChecked(self.window.core.config.get("interpreter.execute_all"))
62
+ self.signals.set_checkbox_all.emit(self.window.core.config.get("interpreter.execute_all"))
55
63
  if self.window.core.config.has("interpreter.auto_clear"):
56
- self.window.ui.nodes['interpreter.auto_clear'].setChecked(self.window.core.config.get("interpreter.auto_clear"))
64
+ self.signals.set_checkbox_auto_clear.emit(self.window.core.config.get("interpreter.auto_clear"))
57
65
  if self.window.core.config.has("interpreter.ipython"):
58
- self.window.ui.nodes['interpreter.ipython'].setChecked(self.window.core.config.get("interpreter.ipython"))
66
+ self.signals.set_checkbox_ipython.emit(self.window.core.config.get("interpreter.ipython"))
59
67
  if self.ipython:
60
- self.window.ui.nodes['interpreter.all'].setVisible(False)
68
+ self.signals.toggle_all_visible.emit(False)
61
69
 
62
70
  # set initial size
63
71
  if not self.window.core.config.has("interpreter.dialog.initialized"):
@@ -119,28 +127,62 @@ class CodeInterpreter(BaseTool):
119
127
  :param type: Output type
120
128
  :param kwargs: Additional parameters
121
129
  """
122
- area = self.window.interpreter
123
- if type == "stdin":
124
- data = ">> " + str(output)
125
- else:
126
- data = str(output)
127
- cur = area.textCursor()
128
- cur.movePosition(QTextCursor.End)
129
- s = data + "\n"
130
- while s:
131
- head, sep, s = s.partition("\n")
132
- cur.insertText(head)
133
- if sep: # New line if LF
134
- cur.insertText("\n")
135
- area.setTextCursor(cur)
130
+ self.signals.update.emit(output, type)
136
131
  self.save_output()
137
132
  self.load_history()
138
133
 
134
+ def get_path_input(self) -> str:
135
+ """
136
+ Get input path
137
+
138
+ :return: Input path
139
+ """
140
+ return os.path.join(self.window.core.config.get_user_dir("data"), self.file_input)
141
+
142
+ def get_path_output(self) -> str:
143
+ """
144
+ Get output path
145
+
146
+ :return: Output path
147
+ """
148
+ return os.path.join(self.window.core.config.get_user_dir("data"), self.file_output)
149
+
150
+ def get_widget(self) -> ToolWidget:
151
+ """
152
+ Get tool widget
153
+
154
+ :return: ToolWidget instance
155
+ """
156
+ return self.dialog.widget
157
+
158
+ def get_widget_history(self) -> PythonOutput:
159
+ """
160
+ Get history widget
161
+
162
+ :return: PythonOutput widget
163
+ """
164
+ return self.dialog.widget.history
165
+
166
+ def get_widget_output(self) -> PythonOutput:
167
+ """
168
+ Get output widget
169
+
170
+ :return: PythonOutput widget
171
+ """
172
+ return self.dialog.widget.output
173
+
174
+ def get_widget_input(self) -> PythonInput:
175
+ """
176
+ Get input widget
177
+
178
+ :return: PythonInput widget
179
+ """
180
+ return self.dialog.widget.input
181
+
139
182
  def load_history(self):
140
183
  """Load history data from file"""
141
184
  data = self.get_history()
142
- self.window.ui.nodes['interpreter.code'].setPlainText(data)
143
- self.cursor_to_end()
185
+ self.signals.update_history.emit(data)
144
186
 
145
187
  def get_output(self) -> str:
146
188
  """
@@ -149,7 +191,7 @@ class CodeInterpreter(BaseTool):
149
191
  :return: Output data
150
192
  """
151
193
  data = ""
152
- path = os.path.join(self.window.core.config.get_user_dir("data"), self.file_output)
194
+ path = self.get_path_output()
153
195
  if os.path.exists(path):
154
196
  with open(path, "r", encoding="utf-8") as f:
155
197
  try:
@@ -161,13 +203,13 @@ class CodeInterpreter(BaseTool):
161
203
  def load_output(self):
162
204
  """Load output data from file"""
163
205
  data = self.get_output()
164
- self.window.interpreter.setPlainText(data)
165
- self.window.ui.nodes['interpreter.input'].setFocus()
206
+ self.signals.update.emit(data, "stdout")
207
+ self.signals.focus_input.emit()
166
208
 
167
209
  def save_output(self):
168
210
  """Save output data to file"""
169
- path = os.path.join(self.window.core.config.get_user_dir("data"), self.file_output)
170
- data = self.window.interpreter.toPlainText()
211
+ path = self.get_path_output()
212
+ data = self.get_widget_output().toPlainText()
171
213
  with open(path, "w", encoding="utf-8") as f:
172
214
  f.write(data)
173
215
 
@@ -178,7 +220,7 @@ class CodeInterpreter(BaseTool):
178
220
  :return: History data
179
221
  """
180
222
  data = ""
181
- path = os.path.join(self.window.core.config.get_user_dir("data"), self.file_input)
223
+ path = self.get_path_input()
182
224
  if os.path.exists(path):
183
225
  with open(path, "r", encoding="utf-8") as f:
184
226
  try:
@@ -193,23 +235,23 @@ class CodeInterpreter(BaseTool):
193
235
 
194
236
  :param input: Input data
195
237
  """
196
- path = os.path.join(self.window.core.config.get_user_dir("data"), self.file_input)
238
+ path = self.get_path_input()
197
239
  with open(path , "w", encoding="utf-8") as f:
198
240
  f.write(input)
199
241
 
200
242
  def clear_history(self):
201
243
  """Clear input"""
202
- path = os.path.join(self.window.core.config.get_user_dir("data"), self.file_input)
244
+ path = self.get_path_input()
203
245
  if os.path.exists(path):
204
246
  os.remove(path)
205
- self.window.ui.nodes['interpreter.code'].clear()
247
+ self.signals.clear_history.emit()
206
248
 
207
249
  def clear_output(self):
208
250
  """Clear output"""
209
- path = os.path.join(self.window.core.config.get_user_dir("data"), self.file_output)
251
+ path = self.get_path_output()
210
252
  if os.path.exists(path):
211
253
  os.remove(path)
212
- self.window.interpreter.clear()
254
+ self.signals.clear_output.emit()
213
255
 
214
256
  def clear(self, force: bool = False):
215
257
  """
@@ -248,15 +290,32 @@ class CodeInterpreter(BaseTool):
248
290
  })
249
291
  event.ctx = CtxItem() # tmp
250
292
  self.window.controller.command.dispatch_only(event)
251
- self.window.ui.nodes['interpreter.input'].setFocus()
293
+ self.signals.focus_input.emit()
294
+
295
+ def send_input(self, widget: ToolWidget):
296
+ """
297
+ Send input to interpreter
252
298
 
253
- def send_input(self):
254
- """Send input to interpreter"""
255
- self.store_history()
299
+ :param widget: PythonInput widget
300
+ """
301
+ self.store_history(widget)
256
302
  if self.auto_clear:
257
303
  self.clear_output()
258
304
 
259
- input = str(self.window.ui.nodes['interpreter.input'].toPlainText())
305
+ input_textarea = widget.input
306
+ input = str(input_textarea.toPlainText()).strip()
307
+
308
+ if input == "/restart":
309
+ self.restart_kernel()
310
+ input_textarea.clear()
311
+ input_textarea.setFocus()
312
+ return
313
+ elif input == "/clear":
314
+ self.clear(force=True)
315
+ input_textarea.clear()
316
+ input_textarea.setFocus()
317
+ return
318
+
260
319
  if self.is_all():
261
320
  cmd = "code_execute_all"
262
321
  else:
@@ -284,12 +343,12 @@ class CodeInterpreter(BaseTool):
284
343
  })
285
344
  event.ctx = CtxItem() # tmp
286
345
  self.window.controller.command.dispatch_only(event)
287
- self.window.ui.nodes['interpreter.input'].clear()
288
- self.window.ui.nodes['interpreter.input'].setFocus()
346
+ input_textarea.clear()
347
+ input_textarea.setFocus()
289
348
 
290
349
  def update_input(self):
291
350
  """Update input data"""
292
- data = self.window.ui.nodes['interpreter.input'].toPlainText()
351
+ data = self.get_widget_input().toPlainText()
293
352
  self.window.core.config.set("interpreter.input", data)
294
353
 
295
354
  def append_to_input(self, data: str):
@@ -298,11 +357,7 @@ class CodeInterpreter(BaseTool):
298
357
 
299
358
  :param data: Data
300
359
  """
301
- current = self.window.ui.nodes['interpreter.input'].toPlainText()
302
- if current:
303
- current += "\n"
304
- current += data
305
- self.window.ui.nodes['interpreter.input'].setPlainText(current)
360
+ self.signals.append_input.emit(data)
306
361
 
307
362
  def append_to_edit(self, data: str):
308
363
  """
@@ -317,9 +372,15 @@ class CodeInterpreter(BaseTool):
317
372
  self.save_history(prev)
318
373
  self.load_history()
319
374
 
320
- def store_history(self):
321
- """Save history data to file"""
322
- data = self.window.ui.nodes['interpreter.code'].toPlainText()
375
+ def store_history(self, widget: ToolWidget):
376
+ """
377
+ Save history data to file
378
+
379
+ :param widget: ToolWidget
380
+ """
381
+ if not widget.history:
382
+ return
383
+ data = widget.history.toPlainText()
323
384
  self.save_history(data)
324
385
 
325
386
  def open(self):
@@ -328,7 +389,7 @@ class CodeInterpreter(BaseTool):
328
389
  self.load_history()
329
390
  self.load_output()
330
391
  self.window.ui.dialogs.open('interpreter', width=800, height=600)
331
- self.window.ui.nodes['interpreter.input'].setFocus()
392
+ self.get_widget_input().setFocus()
332
393
  self.cursor_to_end()
333
394
  self.scroll_to_bottom()
334
395
  self.update()
@@ -341,11 +402,11 @@ class CodeInterpreter(BaseTool):
341
402
 
342
403
  def scroll_to_bottom(self):
343
404
  """Scroll down"""
344
- self.window.ui.nodes['interpreter.code'].verticalScrollBar().setValue(
345
- self.window.ui.nodes['interpreter.code'].verticalScrollBar().maximum()
405
+ self.get_widget_history().verticalScrollBar().setValue(
406
+ self.get_widget_history().verticalScrollBar().maximum()
346
407
  )
347
- self.window.interpreter.verticalScrollBar().setValue(
348
- self.window.interpreter.verticalScrollBar().maximum()
408
+ self.get_widget_output().verticalScrollBar().setValue(
409
+ self.get_widget_output().verticalScrollBar().maximum()
349
410
  )
350
411
 
351
412
  def toggle(self):
@@ -366,31 +427,55 @@ class CodeInterpreter(BaseTool):
366
427
  else:
367
428
  self.close()
368
429
 
430
+ def get_toolbar_icon(self) -> QWidget:
431
+ """
432
+ Get toolbar icon
433
+
434
+ :return: QWidget
435
+ """
436
+ return self.window.ui.nodes['icon.interpreter']
437
+
369
438
  def toggle_icon(self, state: bool):
370
439
  """
371
440
  Toggle interpreter icon
372
441
 
373
442
  :param state: State
374
443
  """
375
- self.window.ui.nodes['icon.interpreter'].setVisible(state)
444
+ self.get_toolbar_icon().setVisible(state)
376
445
 
377
- def toggle_auto_clear(self):
378
- """Toggle auto clear"""
379
- self.auto_clear = self.window.ui.nodes['interpreter.auto_clear'].isChecked()
446
+ def toggle_auto_clear(self, widget: ToolWidget):
447
+ """
448
+ Toggle auto clear
449
+
450
+ :param widget: ToolWidget instance
451
+ """
452
+ self.auto_clear = widget.checkbox_auto_clear.isChecked()
380
453
  self.window.core.config.set("interpreter.auto_clear", self.auto_clear)
454
+ self.signals.set_checkbox_auto_clear.emit(self.auto_clear)
455
+
456
+ def toggle_ipython(self, widget: ToolWidget):
457
+ """
458
+ Toggle ipython
381
459
 
382
- def toggle_ipython(self):
383
- """Toggle ipython"""
384
- self.ipython = self.window.ui.nodes['interpreter.ipython'].isChecked()
460
+ :param widget: ToolWidget instance
461
+ """
462
+ self.ipython = widget.checkbox_ipython.isChecked()
385
463
  self.window.core.config.set("interpreter.ipython", self.ipython)
464
+ self.signals.set_checkbox_ipython.emit(self.ipython)
386
465
  if self.ipython:
387
- self.window.ui.nodes['interpreter.all'].setVisible(False)
466
+ self.signals.toggle_all_visible.emit(False)
388
467
  else:
389
- self.window.ui.nodes['interpreter.all'].setVisible(True)
468
+ self.signals.toggle_all_visible.emit(True)
469
+
470
+ def toggle_all(self, widget: ToolWidget):
471
+ """
472
+ Toggle execute all
390
473
 
391
- def toggle_all(self):
392
- """Toggle execute all"""
393
- self.window.core.config.set("interpreter.execute_all", self.window.ui.nodes['interpreter.all'].isChecked())
474
+ :param widget: ToolWidget instance
475
+ """
476
+ state = widget.checkbox_all.isChecked()
477
+ self.window.core.config.set("interpreter.execute_all", state)
478
+ self.signals.set_checkbox_all.emit(state)
394
479
 
395
480
  def get_current_output(self) -> str:
396
481
  """
@@ -398,7 +483,7 @@ class CodeInterpreter(BaseTool):
398
483
 
399
484
  :return: Output data
400
485
  """
401
- return self.window.interpreter.toPlainText()
486
+ return self.get_output()
402
487
 
403
488
  def get_current_history(self) -> str:
404
489
  """
@@ -406,7 +491,7 @@ class CodeInterpreter(BaseTool):
406
491
 
407
492
  :return: Edit code
408
493
  """
409
- return self.window.ui.nodes['interpreter.code'].toPlainText()
494
+ return self.get_history()
410
495
 
411
496
  def is_all(self) -> bool:
412
497
  """
@@ -414,13 +499,13 @@ class CodeInterpreter(BaseTool):
414
499
 
415
500
  :return: True if execute all is enabled
416
501
  """
417
- return self.window.ui.nodes['interpreter.all'].isChecked()
502
+ return self.get_widget().checkbox_all.isChecked()
418
503
 
419
504
  def cursor_to_end(self):
420
505
  """Move cursor to end"""
421
- cur = self.window.ui.nodes['interpreter.code'].textCursor()
506
+ cur = self.get_widget_history().textCursor()
422
507
  cur.movePosition(QTextCursor.End)
423
- self.window.ui.nodes['interpreter.code'].setTextCursor(cur)
508
+ self.get_widget_history().setTextCursor(cur)
424
509
 
425
510
  def setup_menu(self) -> dict:
426
511
  """
@@ -440,15 +525,29 @@ class CodeInterpreter(BaseTool):
440
525
  )
441
526
  return actions
442
527
 
528
+ def as_tab(self, tab: Tab) -> QWidget:
529
+ """
530
+ Spawn and return tab instance
531
+
532
+ :param tab: Parent Tab instance
533
+ :return: Tab widget instance
534
+ """
535
+ tool = Tool(window=self.window, tool=self)
536
+ layout = tool.widget.setup(all=False)
537
+ widget = QWidget()
538
+ widget.setLayout(layout)
539
+ self.load_history()
540
+ self.load_output()
541
+ return widget
542
+
443
543
  def setup_dialogs(self):
444
544
  """Setup dialogs (static)"""
445
- self.dialog = Interpreter(self.window)
545
+ self.dialog = Tool(self.window, self)
446
546
  self.dialog.setup()
447
547
 
448
548
  def setup_theme(self):
449
549
  """Setup theme"""
450
- size = self.window.core.config.get('font_size')
451
- self.window.interpreter.value = size
550
+ self.get_widget_output().value = self.window.core.config.get('font_size')
452
551
 
453
552
  def get_lang_mappings(self) -> dict:
454
553
  """
@@ -6,25 +6,29 @@
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 17:00:00 #
9
+ # Updated Date: 2024.12.12 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt
13
13
  from PySide6.QtGui import QAction, QIcon
14
- from PySide6.QtWidgets import QPushButton, QHBoxLayout, QVBoxLayout, QSplitter, QCheckBox, QLabel, QWidget, QMenuBar
14
+ from PySide6.QtWidgets import QMenuBar
15
15
 
16
- from pygpt_net.tools.code_interpreter.ui.widgets import PythonInput, PythonOutput
16
+ from pygpt_net.tools.code_interpreter.ui.widgets import ToolWidget
17
17
  from pygpt_net.ui.widget.dialog.base import BaseDialog
18
18
  from pygpt_net.utils import trans
19
19
 
20
- class Interpreter:
21
- def __init__(self, window=None):
20
+ class Tool:
21
+ def __init__(self, window=None, tool=None):
22
22
  """
23
23
  Python interpreter dialog
24
24
 
25
25
  :param window: Window instance
26
+ :param tool: Tool instance
26
27
  """
27
28
  self.window = window
29
+ self.tool = tool # tool instance
30
+ self.layout = None
31
+ self.widget = ToolWidget(window, tool)
28
32
  self.menu_bar = None
29
33
  self.menu = {}
30
34
  self.actions = {} # menu actions
@@ -43,23 +47,23 @@ class Interpreter:
43
47
  self.actions["file.clear_output"] = QAction(QIcon(":/icons/close.svg"),
44
48
  trans("interpreter.menu.file.clear_output"))
45
49
  self.actions["file.clear_output"].triggered.connect(
46
- lambda: self.window.tools.get("interpreter").clear_output()
50
+ lambda: self.tool.clear_output()
47
51
  )
48
52
  self.actions["file.clear_history"] = QAction(QIcon(":/icons/close.svg"),
49
53
  trans("interpreter.menu.file.clear_history"))
50
54
  self.actions["file.clear_history"].triggered.connect(
51
- lambda: self.window.tools.get("interpreter").clear_history()
55
+ lambda: self.tool.clear_history()
52
56
  )
53
57
  self.actions["file.clear_all"] = QAction(QIcon(":/icons/close.svg"),
54
58
  trans("interpreter.menu.file.clear_all"))
55
59
  self.actions["file.clear_all"].triggered.connect(
56
- lambda: self.window.tools.get("interpreter").clear_all()
60
+ lambda: self.tool.clear_all()
57
61
  )
58
62
 
59
63
  self.actions["kernel.restart"] = QAction(QIcon(":/icons/reload.svg"),
60
64
  trans("interpreter.menu.kernel.restart"))
61
65
  self.actions["kernel.restart"].triggered.connect(
62
- lambda: self.window.tools.get("interpreter").restart_kernel()
66
+ lambda: self.tool.restart_kernel()
63
67
  )
64
68
 
65
69
  # add actions
@@ -71,102 +75,16 @@ class Interpreter:
71
75
 
72
76
  def setup(self):
73
77
  """Setup interpreter dialog"""
74
- self.window.interpreter = PythonOutput(self.window)
75
- self.window.interpreter.setReadOnly(True)
78
+ self.layout = self.widget.setup(all=True)
79
+ self.layout.setMenuBar(self.setup_menu()) # add menu bar
76
80
 
77
- self.window.ui.nodes['interpreter.code'] = PythonOutput(self.window)
78
- self.window.ui.nodes['interpreter.code'].textChanged.connect(
79
- lambda: self.window.tools.get("interpreter").store_history()
80
- )
81
- self.window.ui.nodes['interpreter.code'].setReadOnly(False)
82
- self.window.ui.nodes['interpreter.code'].excluded_copy_to = ["interpreter_edit"]
83
-
84
- self.window.ui.nodes['interpreter.output_label'] = QLabel(trans("interpreter.edit_label.output"))
85
- self.window.ui.nodes['interpreter.edit_label'] = QLabel(trans("interpreter.edit_label.edit"))
86
-
87
- self.window.ui.nodes['interpreter.all'] = QCheckBox(trans("interpreter.all"))
88
- self.window.ui.nodes['interpreter.all'].setChecked(True)
89
- self.window.ui.nodes['interpreter.all'].clicked.connect(
90
- lambda: self.window.tools.get("interpreter").toggle_all()
91
- )
92
-
93
- self.window.ui.nodes['interpreter.auto_clear'] = QCheckBox(trans("interpreter.auto_clear"))
94
- self.window.ui.nodes['interpreter.auto_clear'].setChecked(False)
95
- self.window.ui.nodes['interpreter.auto_clear'].clicked.connect(
96
- lambda: self.window.tools.get("interpreter").toggle_auto_clear()
97
- )
98
-
99
- self.window.ui.nodes['interpreter.ipython'] = QCheckBox("IPython")
100
- self.window.ui.nodes['interpreter.ipython'].setChecked(True)
101
- self.window.ui.nodes['interpreter.ipython'].clicked.connect(
102
- lambda: self.window.tools.get("interpreter").toggle_ipython()
103
- )
104
-
105
- self.window.ui.nodes['interpreter.btn.clear'] = QPushButton(trans("interpreter.btn.clear"))
106
- self.window.ui.nodes['interpreter.btn.clear'].clicked.connect(
107
- lambda: self.window.tools.get("interpreter").clear())
108
-
109
- self.window.ui.nodes['interpreter.btn.send'] = QPushButton(trans("interpreter.btn.send"))
110
- self.window.ui.nodes['interpreter.btn.send'].clicked.connect(
111
- lambda: self.window.tools.get("interpreter").send_input()
112
- )
113
-
114
- self.window.ui.nodes['interpreter.input'] = PythonInput(self.window)
115
- self.window.ui.nodes['interpreter.input'].setPlaceholderText(trans("interpreter.input.placeholder"))
116
- self.window.ui.nodes['interpreter.input'].excluded_copy_to = ["interpreter_input"]
117
-
118
- left_layout = QVBoxLayout()
119
- left_layout.addWidget(self.window.ui.nodes['interpreter.output_label'])
120
- left_layout.addWidget(self.window.interpreter)
121
- left_layout.setContentsMargins(0, 0, 0, 0)
122
- left_widget = QWidget()
123
- left_widget.setLayout(left_layout)
124
-
125
- right_layout = QVBoxLayout()
126
- right_layout.addWidget(self.window.ui.nodes['interpreter.edit_label'])
127
- right_layout.addWidget(self.window.ui.nodes['interpreter.code'])
128
- right_layout.setContentsMargins(0, 0, 0, 0)
129
- right_widget = QWidget()
130
- right_widget.setLayout(right_layout)
131
- right_widget.setMinimumWidth(300)
132
-
133
- self.window.ui.splitters['interpreter.columns'] = QSplitter(Qt.Horizontal)
134
- self.window.ui.splitters['interpreter.columns'].addWidget(left_widget)
135
- self.window.ui.splitters['interpreter.columns'].addWidget(right_widget)
136
-
137
- bottom_layout = QHBoxLayout()
138
- bottom_layout.addWidget(self.window.ui.nodes['interpreter.btn.clear'])
139
- bottom_layout.addWidget(self.window.ui.nodes['interpreter.ipython'])
140
- bottom_layout.addWidget(self.window.ui.nodes['interpreter.auto_clear'])
141
- bottom_layout.addStretch()
142
- bottom_layout.addWidget(self.window.ui.nodes['interpreter.all'])
143
- bottom_layout.addWidget(self.window.ui.nodes['interpreter.btn.send'])
144
-
145
- edit_layout = QVBoxLayout()
146
- edit_layout.addWidget(self.window.ui.splitters['interpreter.columns'])
147
- edit_layout.setContentsMargins(0, 0, 0, 0)
148
-
149
- edit_widget = QWidget()
150
- edit_widget.setLayout(edit_layout)
151
-
152
- self.window.ui.splitters['interpreter'] = QSplitter(Qt.Vertical)
153
- self.window.ui.splitters['interpreter'].addWidget(edit_widget)
154
- self.window.ui.splitters['interpreter'].addWidget(self.window.ui.nodes['interpreter.input'])
155
- self.window.ui.splitters['interpreter'].setStretchFactor(0, 4)
156
- self.window.ui.splitters['interpreter'].setStretchFactor(1, 1)
157
-
158
- layout = QVBoxLayout()
159
- layout.setMenuBar(self.setup_menu()) # add menu bar
160
- layout.addWidget(self.window.ui.splitters['interpreter'])
161
- layout.addLayout(bottom_layout)
162
-
163
- self.window.ui.dialog['interpreter'] = InterpreterDialog(self.window)
164
- self.window.ui.dialog['interpreter'].setLayout(layout)
81
+ self.window.ui.dialog['interpreter'] = ToolDialog(self.window)
82
+ self.window.ui.dialog['interpreter'].setLayout(self.layout)
165
83
  self.window.ui.dialog['interpreter'].setWindowTitle(trans("dialog.interpreter.title"))
166
84
  self.window.ui.dialog['interpreter'].resize(800, 500)
167
85
 
168
86
 
169
- class InterpreterDialog(BaseDialog):
87
+ class ToolDialog(BaseDialog):
170
88
  def __init__(self, window=None, id="interpreter"):
171
89
  """
172
90
  Interpreter dialog
@@ -174,7 +92,7 @@ class InterpreterDialog(BaseDialog):
174
92
  :param window: main window
175
93
  :param id: logger id
176
94
  """
177
- super(InterpreterDialog, self).__init__(window, id)
95
+ super(ToolDialog, self).__init__(window, id)
178
96
  self.window = window
179
97
 
180
98
  def closeEvent(self, event):
@@ -184,7 +102,7 @@ class InterpreterDialog(BaseDialog):
184
102
  :param event: close event
185
103
  """
186
104
  self.cleanup()
187
- super(InterpreterDialog, self).closeEvent(event)
105
+ super(ToolDialog, self).closeEvent(event)
188
106
 
189
107
  def keyPressEvent(self, event):
190
108
  """
@@ -196,7 +114,7 @@ class InterpreterDialog(BaseDialog):
196
114
  self.cleanup()
197
115
  self.close() # close dialog when the Esc key is pressed.
198
116
  else:
199
- super(InterpreterDialog, self).keyPressEvent(event)
117
+ super(ToolDialog, self).keyPressEvent(event)
200
118
 
201
119
  def cleanup(self):
202
120
  """