pygpt-net 2.6.21__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 (144) hide show
  1. pygpt_net/CHANGELOG.txt +4 -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/assistant/batch.py +2 -3
  7. pygpt_net/controller/assistant/editor.py +2 -2
  8. pygpt_net/controller/assistant/files.py +2 -3
  9. pygpt_net/controller/assistant/store.py +2 -2
  10. pygpt_net/controller/audio/audio.py +2 -2
  11. pygpt_net/controller/ctx/ctx.py +2 -1
  12. pygpt_net/controller/idx/indexer.py +85 -76
  13. pygpt_net/controller/lang/lang.py +52 -34
  14. pygpt_net/controller/model/importer.py +2 -2
  15. pygpt_net/controller/notepad/notepad.py +86 -84
  16. pygpt_net/controller/plugins/settings.py +3 -4
  17. pygpt_net/controller/settings/profile.py +105 -124
  18. pygpt_net/controller/theme/menu.py +154 -57
  19. pygpt_net/controller/theme/nodes.py +51 -44
  20. pygpt_net/controller/theme/theme.py +33 -9
  21. pygpt_net/controller/tools/tools.py +2 -2
  22. pygpt_net/controller/ui/tabs.py +2 -3
  23. pygpt_net/core/ctx/container.py +13 -12
  24. pygpt_net/core/ctx/output.py +7 -4
  25. pygpt_net/core/debug/console/console.py +2 -2
  26. pygpt_net/core/filesystem/actions.py +1 -2
  27. pygpt_net/core/render/plain/helpers.py +2 -5
  28. pygpt_net/core/render/plain/renderer.py +26 -30
  29. pygpt_net/core/render/web/body.py +1 -1
  30. pygpt_net/core/settings/settings.py +43 -13
  31. pygpt_net/core/tabs/tabs.py +20 -13
  32. pygpt_net/data/config/config.json +4 -4
  33. pygpt_net/data/config/models.json +3 -3
  34. pygpt_net/data/locale/locale.de.ini +4 -1
  35. pygpt_net/data/locale/locale.en.ini +4 -1
  36. pygpt_net/data/locale/locale.es.ini +4 -1
  37. pygpt_net/data/locale/locale.fr.ini +4 -1
  38. pygpt_net/data/locale/locale.it.ini +4 -1
  39. pygpt_net/data/locale/locale.pl.ini +5 -4
  40. pygpt_net/data/locale/locale.uk.ini +4 -1
  41. pygpt_net/data/locale/locale.zh.ini +4 -1
  42. pygpt_net/plugin/twitter/plugin.py +2 -2
  43. pygpt_net/tools/audio_transcriber/ui/dialogs.py +44 -54
  44. pygpt_net/tools/code_interpreter/body.py +1 -2
  45. pygpt_net/tools/code_interpreter/tool.py +7 -4
  46. pygpt_net/tools/code_interpreter/ui/html.py +1 -3
  47. pygpt_net/tools/code_interpreter/ui/widgets.py +2 -3
  48. pygpt_net/tools/html_canvas/ui/widgets.py +1 -3
  49. pygpt_net/tools/image_viewer/ui/dialogs.py +40 -37
  50. pygpt_net/tools/indexer/ui/widgets.py +2 -4
  51. pygpt_net/tools/media_player/tool.py +2 -5
  52. pygpt_net/tools/media_player/ui/widgets.py +60 -36
  53. pygpt_net/tools/text_editor/ui/widgets.py +18 -19
  54. pygpt_net/tools/translator/ui/widgets.py +39 -35
  55. pygpt_net/ui/base/context_menu.py +9 -4
  56. pygpt_net/ui/dialog/db.py +1 -3
  57. pygpt_net/ui/dialog/models.py +1 -3
  58. pygpt_net/ui/dialog/models_importer.py +2 -4
  59. pygpt_net/ui/dialogs.py +34 -30
  60. pygpt_net/ui/layout/chat/attachments.py +72 -84
  61. pygpt_net/ui/layout/chat/attachments_ctx.py +40 -44
  62. pygpt_net/ui/layout/chat/attachments_uploaded.py +36 -39
  63. pygpt_net/ui/layout/chat/calendar.py +100 -70
  64. pygpt_net/ui/layout/chat/chat.py +23 -17
  65. pygpt_net/ui/layout/chat/input.py +95 -118
  66. pygpt_net/ui/layout/chat/output.py +100 -162
  67. pygpt_net/ui/layout/chat/painter.py +89 -61
  68. pygpt_net/ui/layout/ctx/ctx_list.py +43 -52
  69. pygpt_net/ui/layout/status.py +23 -14
  70. pygpt_net/ui/layout/toolbox/agent.py +27 -38
  71. pygpt_net/ui/layout/toolbox/agent_llama.py +42 -45
  72. pygpt_net/ui/layout/toolbox/assistants.py +42 -38
  73. pygpt_net/ui/layout/toolbox/computer_env.py +32 -23
  74. pygpt_net/ui/layout/toolbox/footer.py +13 -16
  75. pygpt_net/ui/layout/toolbox/image.py +18 -21
  76. pygpt_net/ui/layout/toolbox/indexes.py +46 -89
  77. pygpt_net/ui/layout/toolbox/mode.py +20 -7
  78. pygpt_net/ui/layout/toolbox/model.py +12 -10
  79. pygpt_net/ui/layout/toolbox/presets.py +68 -52
  80. pygpt_net/ui/layout/toolbox/prompt.py +31 -58
  81. pygpt_net/ui/layout/toolbox/toolbox.py +25 -21
  82. pygpt_net/ui/layout/toolbox/vision.py +20 -22
  83. pygpt_net/ui/main.py +2 -4
  84. pygpt_net/ui/menu/about.py +64 -84
  85. pygpt_net/ui/menu/audio.py +87 -63
  86. pygpt_net/ui/menu/config.py +121 -127
  87. pygpt_net/ui/menu/debug.py +69 -76
  88. pygpt_net/ui/menu/file.py +32 -35
  89. pygpt_net/ui/menu/menu.py +2 -3
  90. pygpt_net/ui/menu/plugins.py +69 -33
  91. pygpt_net/ui/menu/theme.py +45 -46
  92. pygpt_net/ui/menu/tools.py +56 -60
  93. pygpt_net/ui/menu/video.py +20 -25
  94. pygpt_net/ui/tray.py +1 -2
  95. pygpt_net/ui/widget/audio/bar.py +1 -3
  96. pygpt_net/ui/widget/audio/input_button.py +3 -4
  97. pygpt_net/ui/widget/calendar/select.py +1 -2
  98. pygpt_net/ui/widget/dialog/base.py +12 -9
  99. pygpt_net/ui/widget/dialog/editor_file.py +20 -23
  100. pygpt_net/ui/widget/dialog/find.py +25 -24
  101. pygpt_net/ui/widget/dialog/profile.py +57 -53
  102. pygpt_net/ui/widget/draw/painter.py +62 -93
  103. pygpt_net/ui/widget/element/button.py +42 -30
  104. pygpt_net/ui/widget/element/checkbox.py +23 -15
  105. pygpt_net/ui/widget/element/group.py +6 -5
  106. pygpt_net/ui/widget/element/labels.py +1 -2
  107. pygpt_net/ui/widget/filesystem/explorer.py +93 -102
  108. pygpt_net/ui/widget/image/display.py +1 -2
  109. pygpt_net/ui/widget/lists/assistant.py +1 -2
  110. pygpt_net/ui/widget/lists/attachment.py +1 -2
  111. pygpt_net/ui/widget/lists/attachment_ctx.py +1 -2
  112. pygpt_net/ui/widget/lists/context.py +2 -4
  113. pygpt_net/ui/widget/lists/index.py +1 -2
  114. pygpt_net/ui/widget/lists/model.py +1 -2
  115. pygpt_net/ui/widget/lists/model_editor.py +1 -2
  116. pygpt_net/ui/widget/lists/model_importer.py +1 -2
  117. pygpt_net/ui/widget/lists/preset.py +1 -2
  118. pygpt_net/ui/widget/lists/preset_plugins.py +1 -2
  119. pygpt_net/ui/widget/lists/profile.py +1 -2
  120. pygpt_net/ui/widget/lists/uploaded.py +1 -2
  121. pygpt_net/ui/widget/option/checkbox.py +2 -4
  122. pygpt_net/ui/widget/option/checkbox_list.py +1 -4
  123. pygpt_net/ui/widget/option/cmd.py +1 -4
  124. pygpt_net/ui/widget/option/dictionary.py +25 -28
  125. pygpt_net/ui/widget/option/input.py +1 -3
  126. pygpt_net/ui/widget/tabs/Input.py +16 -12
  127. pygpt_net/ui/widget/tabs/body.py +5 -3
  128. pygpt_net/ui/widget/tabs/layout.py +36 -25
  129. pygpt_net/ui/widget/tabs/output.py +96 -74
  130. pygpt_net/ui/widget/textarea/calendar_note.py +1 -2
  131. pygpt_net/ui/widget/textarea/editor.py +41 -73
  132. pygpt_net/ui/widget/textarea/find.py +11 -10
  133. pygpt_net/ui/widget/textarea/html.py +3 -6
  134. pygpt_net/ui/widget/textarea/input.py +63 -64
  135. pygpt_net/ui/widget/textarea/notepad.py +54 -38
  136. pygpt_net/ui/widget/textarea/output.py +65 -54
  137. pygpt_net/ui/widget/textarea/search_input.py +5 -4
  138. pygpt_net/ui/widget/textarea/web.py +2 -4
  139. pygpt_net/ui/widget/vision/camera.py +2 -31
  140. {pygpt_net-2.6.21.dist-info → pygpt_net-2.6.22.dist-info}/METADATA +15 -151
  141. {pygpt_net-2.6.21.dist-info → pygpt_net-2.6.22.dist-info}/RECORD +144 -144
  142. {pygpt_net-2.6.21.dist-info → pygpt_net-2.6.22.dist-info}/LICENSE +0 -0
  143. {pygpt_net-2.6.21.dist-info → pygpt_net-2.6.22.dist-info}/WHEEL +0 -0
  144. {pygpt_net-2.6.21.dist-info → pygpt_net-2.6.22.dist-info}/entry_points.txt +0 -0
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.08 21:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import json
@@ -25,8 +25,6 @@ from pygpt_net.core.text.web_finder import WebFinder
25
25
  from pygpt_net.tools.code_interpreter.body import Body
26
26
  from pygpt_net.utils import trans
27
27
 
28
- import pygpt_net.icons_rc
29
-
30
28
  class CodeBlock:
31
29
  def __init__(
32
30
  self,
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.05 21:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6 import QtCore
@@ -17,10 +17,9 @@ from PySide6.QtWidgets import QTextEdit, QApplication, QVBoxLayout, QLabel, QChe
17
17
 
18
18
  from pygpt_net.item.ctx import CtxItem
19
19
  from pygpt_net.ui.widget.textarea.editor import BaseCodeEditor
20
+ from pygpt_net.utils import trans
20
21
 
21
22
  from .html import HtmlOutput, CustomWebEnginePage
22
- from pygpt_net.utils import trans
23
- import pygpt_net.icons_rc
24
23
 
25
24
 
26
25
  class ToolWidget:
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.05 21:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt, Slot, QUrl, QObject, Signal
@@ -15,8 +15,6 @@ from PySide6.QtWidgets import QVBoxLayout, QCheckBox, QHBoxLayout
15
15
  from pygpt_net.ui.widget.element.labels import HelpLabel
16
16
  from pygpt_net.ui.widget.textarea.editor import BaseCodeEditor
17
17
  from pygpt_net.ui.widget.textarea.html import HtmlOutput, CustomWebEnginePage
18
-
19
- import pygpt_net.icons_rc
20
18
  from pygpt_net.utils import trans
21
19
 
22
20
 
@@ -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.11.26 02:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt
@@ -17,8 +17,6 @@ from pygpt_net.ui.widget.dialog.base import BaseDialog
17
17
  from pygpt_net.ui.widget.image.display import ImageLabel
18
18
  from pygpt_net.utils import trans
19
19
 
20
- import pygpt_net.icons_rc
21
-
22
20
  class DialogSpawner:
23
21
  def __init__(self, window=None):
24
22
  """
@@ -37,9 +35,13 @@ class DialogSpawner:
37
35
  :param id: dialog id
38
36
  :return: BaseDialog instance
39
37
  """
40
- source = ImageLabel(self.window, self.path)
38
+ dialog = ImageViewerDialog(self.window, self.id)
39
+ dialog.disable_geometry_store = True # disable geometry store
40
+ dialog.id = id
41
+
42
+ source = ImageLabel(dialog, self.path)
41
43
  source.setVisible(False)
42
- pixmap = ImageLabel(self.window, self.path)
44
+ pixmap = ImageLabel(dialog, self.path)
43
45
  pixmap.setSizePolicy(QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored))
44
46
 
45
47
  row = QHBoxLayout()
@@ -48,11 +50,7 @@ class DialogSpawner:
48
50
  layout = QVBoxLayout()
49
51
  layout.addLayout(row)
50
52
 
51
- dialog = ImageViewerDialog(self.window, self.id)
52
- dialog.disable_geometry_store = True # disable geometry store
53
- dialog.id = id
54
53
  dialog.append_layout(layout)
55
- dialog.setLayout(layout)
56
54
  dialog.source = source
57
55
  dialog.pixmap = pixmap
58
56
 
@@ -74,9 +72,14 @@ class ImageViewerDialog(BaseDialog):
74
72
  self.menu_bar = None
75
73
  self.file_menu = None
76
74
  self.actions = {}
77
- self.resizeEvent = self.onResizeEvent # resize event to adjust the pixmap
78
75
  self.source = None
79
76
  self.pixmap = None
77
+ self._last_src_key = 0
78
+ self._last_target_size = None
79
+ self._icon_add = QIcon(":/icons/add.svg")
80
+ self._icon_folder = QIcon(":/icons/folder.svg")
81
+ self._icon_save = QIcon(":/icons/save.svg")
82
+ self._icon_logout = QIcon(":/icons/logout.svg")
80
83
 
81
84
  def append_layout(self, layout):
82
85
  """
@@ -84,23 +87,29 @@ class ImageViewerDialog(BaseDialog):
84
87
 
85
88
  :param layout: layout
86
89
  """
87
- layout.setMenuBar(self.setup_menu()) # attach menu
90
+ layout.setMenuBar(self.setup_menu())
88
91
  self.setLayout(layout)
89
92
 
90
- def onResizeEvent(self, event):
93
+ def resizeEvent(self, event):
91
94
  """
92
95
  Resize event to adjust the pixmap on window resizing
93
96
 
94
97
  :param event: resize event
95
98
  """
96
- if self.source.pixmap() and not self.source.pixmap().isNull():
97
- self.pixmap.setPixmap(
98
- self.source.pixmap().scaled(
99
- self.pixmap.size(),
100
- Qt.KeepAspectRatio,
101
- Qt.SmoothTransformation
99
+ src = self.source.pixmap() if self.source is not None else None
100
+ if src and not src.isNull() and self.pixmap is not None:
101
+ target_size = self.pixmap.size()
102
+ key = src.cacheKey()
103
+ if key != self._last_src_key or target_size != self._last_target_size:
104
+ self.pixmap.setPixmap(
105
+ src.scaled(
106
+ target_size,
107
+ Qt.KeepAspectRatio,
108
+ Qt.SmoothTransformation
109
+ )
102
110
  )
103
- )
111
+ self._last_src_key = key
112
+ self._last_target_size = target_size
104
113
  super(ImageViewerDialog, self).resizeEvent(event)
105
114
 
106
115
  def setup_menu(self) -> QMenuBar:
@@ -109,44 +118,38 @@ class ImageViewerDialog(BaseDialog):
109
118
 
110
119
  :return: QMenuBar
111
120
  """
112
- self.menu_bar = QMenuBar()
121
+ self.menu_bar = QMenuBar(self)
113
122
  self.file_menu = self.menu_bar.addMenu(trans("menu.file"))
114
123
 
115
- # new
116
- self.actions["new"] = QAction(QIcon(":/icons/add.svg"), trans("action.new"))
124
+ self.actions["new"] = QAction(self._icon_add, trans("action.new"), self)
117
125
  self.actions["new"].triggered.connect(
118
- lambda: self.window.tools.get("viewer").new()
126
+ lambda checked=False: self.window.tools.get("viewer").new()
119
127
  )
120
128
 
121
- # open
122
- self.actions["open"] = QAction(QIcon(":/icons/folder.svg"), trans("action.open"))
129
+ self.actions["open"] = QAction(self._icon_folder, trans("action.open"), self)
123
130
  self.actions["open"].triggered.connect(
124
- lambda: self.window.tools.get("viewer").open_file(self.id, auto_close=True)
131
+ lambda checked=False: self.window.tools.get("viewer").open_file(self.id, auto_close=True)
125
132
  )
126
133
 
127
- # open (new window)
128
- self.actions["open_new"] = QAction(QIcon(":/icons/folder.svg"), trans("action.open_new_window"))
134
+ self.actions["open_new"] = QAction(self._icon_folder, trans("action.open_new_window"), self)
129
135
  self.actions["open_new"].triggered.connect(
130
- lambda: self.window.tools.get("viewer").open_file(self.id, auto_close=False)
136
+ lambda checked=False: self.window.tools.get("viewer").open_file(self.id, auto_close=False)
131
137
  )
132
138
 
133
- # save as
134
- self.actions["save_as"] = QAction(QIcon(":/icons/save.svg"), trans("action.save_as"))
139
+ self.actions["save_as"] = QAction(self._icon_save, trans("action.save_as"), self)
135
140
  self.actions["save_as"].triggered.connect(
136
- lambda: self.window.tools.get("viewer").save_by_id(self.id)
141
+ lambda checked=False: self.window.tools.get("viewer").save_by_id(self.id)
137
142
  )
138
143
 
139
- # exit
140
- self.actions["exit"] = QAction(QIcon(":/icons/logout.svg"), trans("menu.file.exit"))
144
+ self.actions["exit"] = QAction(self._icon_logout, trans("menu.file.exit"), self)
141
145
  self.actions["exit"].triggered.connect(
142
- lambda: self.window.tools.get("viewer").close_preview(self.id)
146
+ lambda checked=False: self.window.tools.get("viewer").close_preview(self.id)
143
147
  )
144
148
 
145
- # add actions
146
149
  self.file_menu.addAction(self.actions["new"])
147
150
  self.file_menu.addAction(self.actions["open"])
148
151
  self.file_menu.addAction(self.actions["open_new"])
149
152
  self.file_menu.addAction(self.actions["save_as"])
150
153
  self.file_menu.addAction(self.actions["exit"])
151
154
 
152
- return self.menu_bar
155
+ return self.menu_bar
@@ -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.17 01:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt
@@ -33,7 +33,6 @@ class IdxBrowseList(DatabaseList):
33
33
 
34
34
  :param parent: Parent
35
35
  """
36
-
37
36
  def copy_to_clipboard():
38
37
  index = parent.currentIndex()
39
38
  if index.isValid():
@@ -64,9 +63,8 @@ class IdxBrowser(DataBrowser):
64
63
  :return: Tables dictionary
65
64
  """
66
65
  data = self.window.tools.get("indexer").get_tables()
67
- tables = {k: v for k, v in data.items() if k in [
66
+ return {k: v for k, v in data.items() if k in [
68
67
  "idx_ctx", "idx_file", "idx_external"]}
69
- return tables
70
68
 
71
69
  @override
72
70
  def get_filters(self):
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.12 15:00:00 #
9
+ # Updated Date: 2025.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import datetime
@@ -15,15 +15,12 @@ import shutil
15
15
  from typing import Dict
16
16
 
17
17
  from PySide6.QtGui import QAction, QIcon
18
+ from PySide6.QtWidgets import QMessageBox, QFileDialog
18
19
 
19
20
  from pygpt_net.tools.base import BaseTool
20
21
  from pygpt_net.tools.media_player.ui.dialogs import VideoPlayer
21
- from PySide6.QtWidgets import QMessageBox, QFileDialog
22
-
23
22
  from pygpt_net.utils import trans
24
23
 
25
- import pygpt_net.icons_rc
26
-
27
24
 
28
25
  class MediaPlayer(BaseTool):
29
26
  def __init__(self, *args, **kwargs):
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.12 15:00:00 #
9
+ # Updated Date: 2025.08.25 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -19,8 +19,6 @@ from PySide6.QtWidgets import (QWidget, QPushButton, QHBoxLayout, QVBoxLayout,
19
19
  from pygpt_net.ui.widget.element.labels import HelpLabel
20
20
  from pygpt_net.utils import trans
21
21
 
22
- import pygpt_net.icons_rc
23
-
24
22
  class VideoPlayerWidget(QWidget):
25
23
  def __init__(self, window=None):
26
24
  super().__init__(window)
@@ -28,14 +26,18 @@ class VideoPlayerWidget(QWidget):
28
26
  self.player = None
29
27
  self.audio = None
30
28
  self.video = None
31
- self._QMediaPlayer = None
32
- self._QAudioOutput = None
33
- self._QVideoWidget = None
34
29
  self.loaded = False
35
30
  self.path = None
36
31
  self.stopped = False
37
32
  self.seeking = False
38
33
 
34
+ self._QMediaPlayer = None
35
+ self._QAudioOutput = None
36
+ self._QVideoWidget = None
37
+
38
+ self._waiting_position_set = False
39
+ self._last_position = -1
40
+
39
41
  self.update_timer = QTimer(self)
40
42
  self.update_timer.setInterval(200)
41
43
  self.update_timer.timeout.connect(self.update_ui)
@@ -48,11 +50,15 @@ class VideoPlayerWidget(QWidget):
48
50
  self._video_placeholder.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
49
51
  self._video_placeholder.setMinimumSize(640, 480)
50
52
 
53
+ self._icon_play = self.style().standardIcon(QStyle.SP_MediaPlay)
54
+ self._icon_pause = self.style().standardIcon(QStyle.SP_MediaPause)
55
+ self._icon_stop = self.style().standardIcon(QStyle.SP_MediaStop)
56
+
51
57
  self.btn_play_pause = QPushButton()
52
58
  self.btn_play_pause.setEnabled(False)
53
59
 
54
60
  self.btn_stop = QPushButton()
55
- self.btn_stop.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
61
+ self.btn_stop.setIcon(self._icon_stop)
56
62
  self.btn_stop.setEnabled(False)
57
63
 
58
64
  self.btn_mute = QPushButton()
@@ -149,6 +155,7 @@ class VideoPlayerWidget(QWidget):
149
155
  self.volume_slider.valueChanged.connect(self.adjust_volume)
150
156
  self.slider.sliderPressed.connect(self.on_slider_pressed)
151
157
  self.slider.sliderReleased.connect(self.on_slider_released)
158
+ self.slider.valueChanged.connect(self.on_slider_value_changed)
152
159
  self._video_placeholder.mousePressEvent = self.video_widget_clicked
153
160
  self.seeking = False
154
161
 
@@ -210,7 +217,7 @@ class VideoPlayerWidget(QWidget):
210
217
  self.stopped = True
211
218
  self.slider.setValue(0)
212
219
  self.label_time.setText(self.format_time(0))
213
- self.btn_play_pause.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
220
+ self.btn_play_pause.setIcon(self._icon_play)
214
221
  self.update_timer.stop()
215
222
 
216
223
  def toggle_play_pause(self):
@@ -226,11 +233,13 @@ class VideoPlayerWidget(QWidget):
226
233
  if self.player and self.player.source():
227
234
  self.player.stop()
228
235
  self.player.setSource(QUrl())
229
- self.btn_play_pause.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
236
+ self.btn_play_pause.setIcon(self._icon_play)
230
237
  self.update_timer.stop()
231
238
  self.slider.setValue(0)
232
239
  self.seeking = False
233
240
  self.loaded = False
241
+ self._waiting_position_set = False
242
+ self._last_position = -1
234
243
 
235
244
  def reset_player(self):
236
245
  """Reset player to default state"""
@@ -296,6 +305,7 @@ class VideoPlayerWidget(QWidget):
296
305
  def on_close(self):
297
306
  """Stop video"""
298
307
  self.reset()
308
+ self.autoplay_timer.stop()
299
309
 
300
310
  def toggle_fullscreen(self):
301
311
  """Toggle fullscreen"""
@@ -387,22 +397,28 @@ class VideoPlayerWidget(QWidget):
387
397
  return
388
398
  if self._QMediaPlayer and self.player.playbackState() == self._QMediaPlayer.PlayingState:
389
399
  self.player.pause()
390
- self.player.positionChanged.connect(self.on_position_set)
400
+ self._waiting_position_set = True
391
401
  self.player.setPosition(position)
392
402
 
393
403
  def on_slider_pressed(self):
394
404
  """Slider pressed"""
395
405
  self.seeking = True
406
+ self._waiting_position_set = False
396
407
  if self.player:
397
408
  self.player.pause()
398
409
 
410
+ def on_slider_value_changed(self, value):
411
+ """Slider value changed"""
412
+ if self.seeking:
413
+ self.label_time.setText(self.format_time(value))
414
+
399
415
  def on_slider_released(self):
400
416
  """Slider released"""
401
417
  if self.seeking:
402
418
  if self.player:
403
419
  self.player.setPosition(self.slider.value())
404
420
  self.update_audio()
405
- self.player.play()
421
+ QTimer.singleShot(0, self.player.play)
406
422
  self.seeking = False
407
423
 
408
424
  def position_changed(self, position):
@@ -411,25 +427,15 @@ class VideoPlayerWidget(QWidget):
411
427
 
412
428
  :param position: position
413
429
  """
430
+ if self._waiting_position_set and self.player:
431
+ self._waiting_position_set = False
432
+ if self._QMediaPlayer and self.player.playbackState() == self._QMediaPlayer.PausedState:
433
+ QTimer.singleShot(100, self.player.play)
414
434
  if not self.seeking:
435
+ self._last_position = position
415
436
  self.slider.setValue(position)
416
437
  self.label_time.setText(self.format_time(position))
417
438
 
418
- def on_position_set(self, position):
419
- """
420
- On position set
421
-
422
- :param position: position
423
- """
424
- if self.seeking and self.player:
425
- try:
426
- self.player.positionChanged.disconnect(self.on_position_set)
427
- except Exception:
428
- pass
429
- if self._QMediaPlayer and self.player.playbackState() == self._QMediaPlayer.PausedState:
430
- QTimer.singleShot(100, self.player.play)
431
- self.seeking = False
432
-
433
439
  def update_label_path(self):
434
440
  """Update label path"""
435
441
  if self.path:
@@ -440,32 +446,50 @@ class VideoPlayerWidget(QWidget):
440
446
  def update_play_pause_icon(self):
441
447
  """Update play/pause icon"""
442
448
  if self.player and self._QMediaPlayer and self.player.playbackState() == self._QMediaPlayer.PlayingState:
443
- self.btn_play_pause.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
444
- self.update_timer.start()
449
+ self.btn_play_pause.setIcon(self._icon_pause)
450
+ if not self.update_timer.isActive():
451
+ self.update_timer.start()
445
452
  else:
446
- self.btn_play_pause.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
447
- self.update_timer.stop()
453
+ self.btn_play_pause.setIcon(self._icon_play)
454
+ if self.update_timer.isActive():
455
+ self.update_timer.stop()
448
456
 
449
457
  def update_audio(self):
450
458
  """Re-assign audio"""
451
459
  if self.player and self.audio:
452
- self.player.setAudioOutput(None)
453
- self.player.setAudioOutput(self.audio)
460
+ try:
461
+ current = self.player.audioOutput()
462
+ except Exception:
463
+ current = None
464
+ if current is not self.audio:
465
+ self.player.setAudioOutput(self.audio)
454
466
 
455
467
  def update_mute_icon(self):
456
468
  """Update mute icon"""
457
469
  if self.audio:
458
- self.btn_mute.setChecked(self.audio.isMuted())
470
+ muted = self.audio.isMuted()
471
+ if self.btn_mute.isChecked() != muted:
472
+ block = self.btn_mute.blockSignals(True)
473
+ self.btn_mute.setChecked(muted)
474
+ self.btn_mute.blockSignals(block)
459
475
 
460
476
  def update_volume_slider(self):
461
477
  """Update volume slider"""
462
478
  if self.audio:
463
- self.volume_slider.setValue(int(self.audio.volume() * 100))
479
+ v = int(self.audio.volume() * 100)
480
+ if self.volume_slider.value() != v:
481
+ block = self.volume_slider.blockSignals(True)
482
+ self.volume_slider.setValue(v)
483
+ self.volume_slider.blockSignals(block)
464
484
 
465
485
  def update_ui(self):
466
486
  """Update UI"""
467
- if self.player:
468
- self.position_changed(self.player.position())
487
+ if self.player and not self.seeking:
488
+ pos = self.player.position()
489
+ if pos != self._last_position:
490
+ self._last_position = pos
491
+ self.slider.setValue(pos)
492
+ self.label_time.setText(self.format_time(pos))
469
493
 
470
494
  def format_time(self, ms):
471
495
  """
@@ -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.08.24 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtGui import QAction, QIcon, QKeySequence
@@ -14,8 +14,6 @@ from PySide6.QtGui import QAction, QIcon, QKeySequence
14
14
  from pygpt_net.ui.widget.textarea.editor import BaseCodeEditor
15
15
  from pygpt_net.utils import trans
16
16
 
17
- import pygpt_net.icons_rc
18
-
19
17
  class TextFileEditor(BaseCodeEditor):
20
18
  def __init__(self, window=None):
21
19
  """
@@ -23,7 +21,7 @@ class TextFileEditor(BaseCodeEditor):
23
21
 
24
22
  :param window: main window
25
23
  """
26
- super(TextFileEditor, self).__init__(window)
24
+ super().__init__(window)
27
25
  self.window = window
28
26
  self.setReadOnly(True)
29
27
  self.value = 12
@@ -33,6 +31,10 @@ class TextFileEditor(BaseCodeEditor):
33
31
  self.default_stylesheet = ""
34
32
  self.setStyleSheet(self.default_stylesheet)
35
33
 
34
+ self._icon_volume = QIcon(":/icons/volume.svg")
35
+ self._icon_save = QIcon(":/icons/save.svg")
36
+ self._icon_search = QIcon(":/icons/search.svg")
37
+
36
38
  def contextMenuEvent(self, event):
37
39
  """
38
40
  Context menu event
@@ -40,38 +42,35 @@ class TextFileEditor(BaseCodeEditor):
40
42
  :param event: Event
41
43
  """
42
44
  menu = self.createStandardContextMenu()
43
- selected_text = self.textCursor().selectedText()
45
+ cursor = self.textCursor()
44
46
 
45
- if selected_text:
46
- # plain text
47
- plain_text = self.textCursor().selection().toPlainText()
47
+ if cursor.hasSelection():
48
+ selected_text = cursor.selectedText()
49
+ plain_text = cursor.selection().toPlainText()
48
50
 
49
- # audio read
50
- action = QAction(QIcon(":/icons/volume.svg"), trans('text.context_menu.audio.read'), self)
51
+ action = QAction(self._icon_volume, trans('text.context_menu.audio.read'), menu)
51
52
  action.triggered.connect(self.audio_read_selection)
52
53
  menu.addAction(action)
53
54
 
54
- # copy to
55
- copy_to_menu = self.window.ui.context_menu.get_copy_to_menu(self, selected_text)
55
+ copy_to_menu = self.window.ui.context_menu.get_copy_to_menu(menu, selected_text)
56
56
  menu.addMenu(copy_to_menu)
57
57
 
58
- # save as (selected)
59
- action = QAction(QIcon(":/icons/save.svg"), trans('action.save_as'), self)
58
+ action = QAction(self._icon_save, trans('action.save_as'), menu)
60
59
  action.triggered.connect(
61
60
  lambda: self.window.controller.chat.common.save_text(plain_text)
62
61
  )
63
62
  menu.addAction(action)
64
63
  else:
65
- # save as (all)
66
- action = QAction(QIcon(":/icons/save.svg"), trans('action.save_as'), self)
64
+ action = QAction(self._icon_save, trans('action.save_as'), menu)
67
65
  action.triggered.connect(
68
66
  lambda: self.window.controller.chat.common.save_text(self.toPlainText())
69
67
  )
70
68
  menu.addAction(action)
71
69
 
72
- action = QAction(QIcon(":/icons/search.svg"), trans('text.context_menu.find'), self)
70
+ action = QAction(self._icon_search, trans('text.context_menu.find'), menu)
73
71
  action.triggered.connect(self.find_open)
74
- action.setShortcut(QKeySequence("Ctrl+F"))
72
+ action.setShortcut(QKeySequence.StandardKey.Find)
75
73
  menu.addAction(action)
76
74
 
77
- menu.exec_(event.globalPos())
75
+ menu.exec_(event.globalPos())
76
+ menu.deleteLater()