pygpt-net 2.7.7__py3-none-any.whl → 2.7.9__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 (98) hide show
  1. pygpt_net/CHANGELOG.txt +12 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +5 -1
  4. pygpt_net/controller/assistant/batch.py +2 -2
  5. pygpt_net/controller/assistant/files.py +7 -6
  6. pygpt_net/controller/assistant/threads.py +0 -0
  7. pygpt_net/controller/chat/command.py +0 -0
  8. pygpt_net/controller/dialogs/confirm.py +35 -58
  9. pygpt_net/controller/lang/mapping.py +9 -9
  10. pygpt_net/controller/realtime/realtime.py +13 -1
  11. pygpt_net/controller/remote_store/{google/batch.py → batch.py} +209 -252
  12. pygpt_net/controller/remote_store/remote_store.py +982 -13
  13. pygpt_net/core/command/command.py +0 -0
  14. pygpt_net/core/db/viewer.py +1 -1
  15. pygpt_net/core/realtime/worker.py +3 -1
  16. pygpt_net/{controller/remote_store/google → core/remote_store/anthropic}/__init__.py +0 -1
  17. pygpt_net/core/remote_store/anthropic/files.py +211 -0
  18. pygpt_net/core/remote_store/anthropic/store.py +208 -0
  19. pygpt_net/core/remote_store/openai/store.py +5 -4
  20. pygpt_net/core/remote_store/remote_store.py +5 -1
  21. pygpt_net/{controller/remote_store/openai → core/remote_store/xai}/__init__.py +0 -1
  22. pygpt_net/core/remote_store/xai/files.py +225 -0
  23. pygpt_net/core/remote_store/xai/store.py +219 -0
  24. pygpt_net/data/config/config.json +10 -6
  25. pygpt_net/data/config/models.json +38 -22
  26. pygpt_net/data/config/settings.json +54 -1
  27. pygpt_net/data/icons/folder_eye.svg +1 -0
  28. pygpt_net/data/icons/folder_eye_filled.svg +1 -0
  29. pygpt_net/data/icons/folder_open.svg +1 -0
  30. pygpt_net/data/icons/folder_open_filled.svg +1 -0
  31. pygpt_net/data/locale/locale.de.ini +4 -3
  32. pygpt_net/data/locale/locale.en.ini +14 -4
  33. pygpt_net/data/locale/locale.es.ini +4 -3
  34. pygpt_net/data/locale/locale.fr.ini +4 -3
  35. pygpt_net/data/locale/locale.it.ini +4 -3
  36. pygpt_net/data/locale/locale.pl.ini +5 -4
  37. pygpt_net/data/locale/locale.uk.ini +4 -3
  38. pygpt_net/data/locale/locale.zh.ini +4 -3
  39. pygpt_net/icons.qrc +4 -0
  40. pygpt_net/icons_rc.py +282 -138
  41. pygpt_net/provider/api/anthropic/__init__.py +2 -0
  42. pygpt_net/provider/api/anthropic/chat.py +84 -1
  43. pygpt_net/provider/api/anthropic/store.py +307 -0
  44. pygpt_net/provider/api/anthropic/stream.py +75 -0
  45. pygpt_net/provider/api/anthropic/worker/__init__.py +0 -0
  46. pygpt_net/provider/api/anthropic/worker/importer.py +278 -0
  47. pygpt_net/provider/api/google/chat.py +59 -2
  48. pygpt_net/provider/api/google/realtime/client.py +70 -24
  49. pygpt_net/provider/api/google/realtime/realtime.py +48 -12
  50. pygpt_net/provider/api/google/store.py +124 -3
  51. pygpt_net/provider/api/google/stream.py +91 -24
  52. pygpt_net/provider/api/google/worker/importer.py +16 -28
  53. pygpt_net/provider/api/openai/assistants.py +2 -2
  54. pygpt_net/provider/api/openai/realtime/realtime.py +26 -6
  55. pygpt_net/provider/api/openai/store.py +4 -1
  56. pygpt_net/provider/api/openai/worker/importer.py +19 -61
  57. pygpt_net/provider/api/openai/worker/importer_assistants.py +230 -0
  58. pygpt_net/provider/api/x_ai/__init__.py +27 -6
  59. pygpt_net/provider/api/x_ai/audio.py +43 -11
  60. pygpt_net/provider/api/x_ai/chat.py +92 -4
  61. pygpt_net/provider/api/x_ai/realtime/__init__.py +12 -0
  62. pygpt_net/provider/api/x_ai/realtime/client.py +1864 -0
  63. pygpt_net/provider/api/x_ai/realtime/realtime.py +213 -0
  64. pygpt_net/provider/api/x_ai/remote_tools.py +102 -1
  65. pygpt_net/provider/api/x_ai/store.py +610 -0
  66. pygpt_net/provider/api/x_ai/stream.py +30 -9
  67. pygpt_net/provider/api/x_ai/tools.py +51 -0
  68. pygpt_net/provider/api/x_ai/worker/importer.py +308 -0
  69. pygpt_net/provider/audio_input/xai_grok_voice.py +390 -0
  70. pygpt_net/provider/audio_output/xai_tts.py +325 -0
  71. pygpt_net/provider/core/config/patch.py +29 -3
  72. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +2 -2
  73. pygpt_net/provider/core/model/patch.py +49 -1
  74. pygpt_net/tools/image_viewer/tool.py +334 -34
  75. pygpt_net/tools/image_viewer/ui/dialogs.py +317 -21
  76. pygpt_net/ui/dialog/assistant.py +1 -1
  77. pygpt_net/ui/dialog/plugins.py +13 -5
  78. pygpt_net/ui/dialog/remote_store.py +552 -0
  79. pygpt_net/ui/dialogs.py +3 -5
  80. pygpt_net/ui/layout/ctx/ctx_list.py +58 -7
  81. pygpt_net/ui/menu/tools.py +6 -13
  82. pygpt_net/ui/widget/dialog/{remote_store_google.py → remote_store.py} +10 -10
  83. pygpt_net/ui/widget/element/button.py +4 -4
  84. pygpt_net/ui/widget/image/display.py +2 -2
  85. pygpt_net/ui/widget/lists/context.py +2 -2
  86. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.9.dist-info}/METADATA +14 -2
  87. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.9.dist-info}/RECORD +87 -75
  88. pygpt_net/controller/remote_store/google/store.py +0 -615
  89. pygpt_net/controller/remote_store/openai/batch.py +0 -524
  90. pygpt_net/controller/remote_store/openai/store.py +0 -699
  91. pygpt_net/ui/dialog/remote_store_google.py +0 -539
  92. pygpt_net/ui/dialog/remote_store_openai.py +0 -539
  93. pygpt_net/ui/widget/dialog/remote_store_openai.py +0 -56
  94. pygpt_net/ui/widget/lists/remote_store_google.py +0 -248
  95. pygpt_net/ui/widget/lists/remote_store_openai.py +0 -317
  96. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.9.dist-info}/LICENSE +0 -0
  97. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.9.dist-info}/WHEEL +0 -0
  98. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.9.dist-info}/entry_points.txt +0 -0
@@ -1,539 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # ================================================== #
4
- # This file is a part of PYGPT package #
5
- # Website: https://pygpt.net #
6
- # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
- # MIT License #
8
- # Created By : Marcin Szczygliński #
9
- # Updated Date: 2026.01.02 20:00:00 #
10
- # ================================================== #
11
-
12
- import copy
13
-
14
- from PySide6.QtCore import Qt, QPoint
15
- from PySide6.QtGui import QStandardItemModel, QAction, QIcon
16
- from PySide6.QtWidgets import QPushButton, QHBoxLayout, QLabel, QVBoxLayout, QScrollArea, QWidget, QTabWidget, QFrame, \
17
- QSplitter, QSizePolicy, QMenuBar, QCheckBox, QMenu, QListView
18
-
19
- from pygpt_net.ui.widget.dialog.remote_store_google import RemoteStoreGoogleDialog
20
- from pygpt_net.ui.widget.element.group import CollapsedGroup
21
- from pygpt_net.ui.widget.element.labels import UrlLabel
22
- from pygpt_net.ui.widget.lists.remote_store_google import RemoteStoreGoogleEditorList
23
- from pygpt_net.ui.widget.option.checkbox import OptionCheckbox
24
- from pygpt_net.ui.widget.option.checkbox_list import OptionCheckboxList
25
- from pygpt_net.ui.widget.option.combo import OptionCombo
26
- from pygpt_net.ui.widget.option.dictionary import OptionDict
27
- from pygpt_net.ui.widget.option.input import OptionInput, PasswordInput
28
- from pygpt_net.ui.widget.option.slider import OptionSlider
29
- from pygpt_net.ui.widget.option.textarea import OptionTextarea
30
- from pygpt_net.utils import trans
31
-
32
-
33
- class RemoteStoreGoogle:
34
- def __init__(self, window=None):
35
- """
36
- Google File Search store editor dialog
37
-
38
- :param window: Window instance
39
- """
40
- self.window = window
41
- self.dialog_id = "remote_store.google"
42
-
43
- def setup(self, idx=None):
44
- """
45
- Setup Google store editor dialog
46
-
47
- :param idx: current model tab index
48
- """
49
- ui = self.window.ui
50
- nodes = ui.nodes
51
- models = ui.models
52
- splitters = ui.splitters
53
- controller = self.window.controller
54
- core = self.window.core
55
-
56
- nodes['remote_store.google.btn.new'] = QPushButton(trans("dialog.remote_store.btn.new"))
57
- nodes['remote_store.google.btn.save'] = QPushButton(trans("dialog.remote_store.btn.save"))
58
- nodes['remote_store.google.btn.refresh_status'] = QPushButton(QIcon(":/icons/reload.svg"), "")
59
- nodes['remote_store.google.btn.close'] = QPushButton(trans("dialog.remote_store.btn.close"))
60
-
61
- nodes['remote_store.google.btn.refresh_status'].setToolTip(
62
- trans("dialog.remote_store.btn.refresh_status")
63
- )
64
- nodes['remote_store.google.btn.refresh_status'].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
65
-
66
- nodes['remote_store.google.btn.upload.files'] = QPushButton(trans("dialog.remote_store.btn.upload.files"))
67
- nodes['remote_store.google.btn.upload.dir'] = QPushButton(trans("dialog.remote_store.btn.upload.dir"))
68
-
69
- nodes['remote_store.google.hide_thread'] = QCheckBox(trans("remote_store.google.hide_threads"))
70
- nodes['remote_store.google.hide_thread'].setChecked(bool(core.config.get("remote_store.google.hide_threads")))
71
- nodes['remote_store.google.hide_thread'].toggled.connect(
72
- controller.remote_store.google.set_hide_thread
73
- )
74
-
75
- nodes['remote_store.google.btn.new'].clicked.connect(controller.remote_store.google.new)
76
- nodes['remote_store.google.btn.save'].clicked.connect(controller.remote_store.google.save_btn)
77
- nodes['remote_store.google.btn.close'].clicked.connect(controller.remote_store.google.close)
78
- nodes['remote_store.google.btn.refresh_status'].clicked.connect(controller.remote_store.google.refresh_status)
79
- nodes['remote_store.google.btn.upload.files'].clicked.connect(controller.remote_store.google.batch.open_upload_files)
80
- nodes['remote_store.google.btn.upload.dir'].clicked.connect(controller.remote_store.google.batch.open_upload_dir)
81
-
82
- nodes['remote_store.google.btn.new'].setAutoDefault(False)
83
- nodes['remote_store.google.btn.refresh_status'].setAutoDefault(False)
84
- nodes['remote_store.google.btn.save'].setAutoDefault(True)
85
-
86
- footer = QHBoxLayout()
87
- footer.addWidget(nodes['remote_store.google.btn.close'])
88
- footer.addWidget(nodes['remote_store.google.btn.save'])
89
-
90
- ui.tabs['remote_store.google'] = QTabWidget()
91
-
92
- parent_id = "remote_store.google"
93
-
94
- scroll = QScrollArea()
95
- scroll.setWidgetResizable(True)
96
- content = QVBoxLayout()
97
-
98
- if parent_id not in ui.config:
99
- ui.config[parent_id] = {}
100
-
101
- options = copy.deepcopy(controller.remote_store.google.get_options())
102
- widgets = self.build_widgets(options)
103
- advanced_keys = [k for k, v in options.items() if v.get('advanced')]
104
-
105
- for key, w in widgets.items():
106
- ui.config[parent_id][key] = w
107
-
108
- for key in widgets:
109
- if key in advanced_keys:
110
- continue
111
- content.addLayout(self.add_option(widgets[key], options[key]))
112
-
113
- if advanced_keys:
114
- group_id = 'remote_store.google.advanced'
115
- ui.groups[group_id] = CollapsedGroup(self.window, group_id, None, False, None)
116
- ui.groups[group_id].box.setText(trans('settings.advanced.collapse'))
117
- for key in widgets:
118
- if key not in advanced_keys:
119
- continue
120
- option = self.add_option(widgets[key], options[key])
121
- ui.groups[group_id].add_layout(option)
122
- content.addWidget(ui.groups[group_id])
123
-
124
- content.setContentsMargins(0, 0, 0, 0)
125
-
126
- scroll_widget = QWidget()
127
- scroll_widget.setLayout(content)
128
- scroll.setWidget(scroll_widget)
129
-
130
- area = QVBoxLayout()
131
- area.addWidget(scroll)
132
- area.setContentsMargins(0, 0, 0, 0)
133
-
134
- area_widget = QWidget()
135
- area_widget.setLayout(area)
136
-
137
- list_id = 'remote_store.google.list'
138
- nodes[list_id] = RemoteStoreGoogleEditorList(self.window, list_id)
139
- models[list_id] = self.create_model(self.window)
140
- nodes[list_id].setModel(models[list_id])
141
- nodes[list_id].setMinimumWidth(250)
142
-
143
- left_layout = QVBoxLayout()
144
- left_layout.addWidget(nodes['remote_store.google.btn.new'])
145
- left_layout.addWidget(nodes[list_id])
146
- left_layout.addWidget(nodes['remote_store.google.hide_thread'])
147
- left_layout.setContentsMargins(0, 0, 0, 0)
148
- left_widget = QWidget()
149
- left_widget.setLayout(left_layout)
150
-
151
- self.update_list(list_id, core.remote_store.google.items)
152
-
153
- # ==================== Files panel ====================
154
- files_panel = QVBoxLayout()
155
- files_label = QLabel("Files")
156
- files_label.setStyleSheet("font-weight: bold;")
157
- files_panel.addWidget(files_label)
158
-
159
- files_list_id = 'remote_store.google.files.list'
160
- nodes[files_list_id] = QListView()
161
- nodes[files_list_id].setEditTriggers(QListView.NoEditTriggers)
162
- nodes[files_list_id].setSelectionMode(QListView.SingleSelection)
163
- nodes[files_list_id].setContextMenuPolicy(Qt.CustomContextMenu)
164
- nodes[files_list_id].customContextMenuRequested.connect(self.on_files_context_menu)
165
-
166
- models[files_list_id] = self.create_model(self.window)
167
- nodes[files_list_id].setModel(models[files_list_id])
168
- nodes[files_list_id].setMinimumWidth(280)
169
-
170
- files_bottom = QHBoxLayout()
171
- files_bottom.setContentsMargins(0, 0, 0, 0)
172
- files_bottom.addWidget(nodes['remote_store.google.btn.upload.files'])
173
- files_bottom.addWidget(nodes['remote_store.google.btn.upload.dir'])
174
- files_bottom.addWidget(nodes['remote_store.google.btn.refresh_status'])
175
-
176
- files_panel.addWidget(nodes[files_list_id])
177
- files_panel.setContentsMargins(0, 0, 0, 5)
178
- files_panel.addLayout(files_bottom)
179
- files_widget = QWidget()
180
- files_widget.setLayout(files_panel)
181
- # ========================================================================
182
-
183
- splitters['dialog.remote_store.google'] = QSplitter(Qt.Horizontal)
184
- splitters['dialog.remote_store.google'].addWidget(left_widget)
185
-
186
- splitters['dialog.remote_store.google.right'] = QSplitter(Qt.Horizontal)
187
- splitters['dialog.remote_store.google.right'].addWidget(files_widget)
188
- splitters['dialog.remote_store.google.right'].addWidget(area_widget)
189
- splitters['dialog.remote_store.google.right'].setStretchFactor(1, 7)
190
- splitters['dialog.remote_store.google.right'].setStretchFactor(0, 3)
191
-
192
- splitters['dialog.remote_store.google'].addWidget(splitters['dialog.remote_store.google.right'])
193
- splitters['dialog.remote_store.google'].setStretchFactor(0, 2)
194
- splitters['dialog.remote_store.google'].setStretchFactor(1, 8)
195
- splitters['dialog.remote_store.google'].setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
196
-
197
- main_layout = QHBoxLayout()
198
- main_layout.addWidget(splitters['dialog.remote_store.google'])
199
- main_layout.setContentsMargins(0, 0, 0, 0)
200
-
201
- layout = QVBoxLayout()
202
- layout.addLayout(main_layout)
203
- layout.addLayout(footer)
204
-
205
- layout.setMenuBar(self.setup_menu())
206
-
207
- ui.dialog[self.dialog_id] = RemoteStoreGoogleDialog(self.window, self.dialog_id)
208
- ui.dialog[self.dialog_id].setLayout(layout)
209
- ui.dialog[self.dialog_id].setWindowTitle(trans('dialog.remote_store.google'))
210
-
211
- if idx is not None:
212
- try:
213
- controller.remote_store.google.set_by_tab(idx)
214
- except Exception:
215
- print('Failed restore Google store editor tab: {}'.format(idx))
216
- else:
217
- if controller.remote_store.google.current is None:
218
- controller.remote_store.google.set_by_tab(0)
219
-
220
- try:
221
- controller.remote_store.google.update_files_list()
222
- except Exception:
223
- pass
224
-
225
- def setup_menu(self) -> QMenuBar:
226
- """
227
- Setup menu bar
228
-
229
- :return: QMenuBar
230
- """
231
- self.actions = {}
232
- self.menu_bar = QMenuBar()
233
-
234
- self.menu = {}
235
- self.menu["current"] = self.menu_bar.addMenu(trans("dialog.remote_store.menu.current"))
236
- self.menu["all"] = self.menu_bar.addMenu(trans("dialog.remote_store.menu.all"))
237
-
238
- self.actions["current.import_files"] = QAction(QIcon(":/icons/download.svg"),
239
- trans("dialog.remote_store.menu.current.import_files"),
240
- self.menu_bar)
241
- self.actions["current.import_files"].triggered.connect(
242
- lambda: self.window.controller.remote_store.google.batch.import_store_files(
243
- self.window.controller.remote_store.google.current
244
- )
245
- )
246
-
247
- self.actions["current.refresh_store"] = QAction(QIcon(":/icons/reload.svg"),
248
- trans("dialog.remote_store.menu.current.refresh_store"),
249
- self.menu_bar)
250
- self.actions["current.refresh_store"].triggered.connect(
251
- self.window.controller.remote_store.google.refresh_status
252
- )
253
-
254
- self.actions["current.clear_files"] = QAction(QIcon(":/icons/close.svg"),
255
- trans("dialog.remote_store.menu.current.clear_files"),
256
- self.menu_bar)
257
- self.actions["current.clear_files"].triggered.connect(
258
- lambda: self.window.controller.remote_store.google.batch.clear_store_files(
259
- self.window.controller.remote_store.google.current
260
- )
261
- )
262
-
263
- self.actions["current.truncate_files"] = QAction(QIcon(":/icons/delete.svg"),
264
- trans("dialog.remote_store.menu.current.truncate_files"),
265
- self.menu_bar)
266
- self.actions["current.truncate_files"].triggered.connect(
267
- lambda: self.window.controller.remote_store.google.batch.truncate_store_files(
268
- self.window.controller.remote_store.google.current
269
- )
270
- )
271
-
272
- self.actions["current.delete"] = QAction(QIcon(":/icons/delete.svg"),
273
- trans("dialog.remote_store.menu.current.delete"),
274
- self.menu_bar)
275
- self.actions["current.delete"].triggered.connect(
276
- lambda: self.window.controller.remote_store.google.delete(
277
- self.window.controller.remote_store.google.current
278
- )
279
- )
280
-
281
- self.actions["all.import_all"] = QAction(QIcon(":/icons/download.svg"),
282
- trans("dialog.remote_store.menu.all.import_all"),
283
- self.menu_bar)
284
- self.actions["all.import_all"].triggered.connect(
285
- self.window.controller.remote_store.google.batch.import_stores
286
- )
287
-
288
- self.actions["all.import_files"] = QAction(QIcon(":/icons/download.svg"),
289
- trans("dialog.remote_store.menu.all.import_files"),
290
- self.menu_bar)
291
- self.actions["all.import_files"].triggered.connect(
292
- self.window.controller.remote_store.google.batch.import_files
293
- )
294
-
295
- self.actions["all.refresh_stores"] = QAction(QIcon(":/icons/reload.svg"),
296
- trans("dialog.remote_store.menu.all.refresh_store"),
297
- self.menu_bar)
298
- self.actions["all.refresh_stores"].triggered.connect(
299
- self.window.controller.remote_store.google.batch.refresh_stores
300
- )
301
-
302
- self.actions["all.clear_stores"] = QAction(QIcon(":/icons/close.svg"),
303
- trans("dialog.remote_store.menu.all.clear_store"),
304
- self.menu_bar)
305
- self.actions["all.clear_stores"].triggered.connect(
306
- self.window.controller.remote_store.google.batch.clear_stores
307
- )
308
-
309
- self.actions["all.clear_files"] = QAction(QIcon(":/icons/close.svg"),
310
- trans("dialog.remote_store.menu.all.clear_files"),
311
- self.menu_bar)
312
- self.actions["all.clear_files"].triggered.connect(
313
- self.window.controller.remote_store.google.batch.clear_files
314
- )
315
-
316
- self.actions["all.truncate_stores"] = QAction(QIcon(":/icons/delete.svg"),
317
- trans("dialog.remote_store.menu.all.truncate_store"),
318
- self.menu_bar)
319
- self.actions["all.truncate_stores"].triggered.connect(
320
- self.window.controller.remote_store.google.batch.truncate_stores
321
- )
322
-
323
- self.actions["all.truncate_files"] = QAction(QIcon(":/icons/delete.svg"),
324
- trans("dialog.remote_store.menu.all.truncate_files"),
325
- self.menu_bar)
326
- self.actions["all.truncate_files"].triggered.connect(
327
- self.window.controller.remote_store.google.batch.truncate_files
328
- )
329
-
330
- self.menu["current"].addAction(self.actions["current.import_files"])
331
- self.menu["current"].addAction(self.actions["current.refresh_store"])
332
- self.menu["current"].addAction(self.actions["current.clear_files"])
333
- self.menu["current"].addAction(self.actions["current.truncate_files"])
334
- self.menu["current"].addAction(self.actions["current.delete"])
335
-
336
- self.menu["all"].addAction(self.actions["all.import_all"])
337
- self.menu["all"].addAction(self.actions["all.import_files"])
338
- self.menu["all"].addAction(self.actions["all.refresh_stores"])
339
- self.menu["all"].addAction(self.actions["all.clear_stores"])
340
- self.menu["all"].addAction(self.actions["all.clear_files"])
341
- self.menu["all"].addAction(self.actions["all.truncate_stores"])
342
- self.menu["all"].addAction(self.actions["all.truncate_files"])
343
-
344
- return self.menu_bar
345
-
346
- def build_widgets(self, options: dict) -> dict:
347
- """
348
- Build settings options widgets
349
-
350
- :param options: model options
351
- :return: dict of widgets
352
- """
353
- parent = "model"
354
- widgets = {}
355
-
356
- for key in options:
357
- option = options[key]
358
- option['id'] = key
359
- if option['type'] == 'text' or option['type'] == 'int' or option['type'] == 'float':
360
- if 'slider' in option and option['slider'] \
361
- and (option['type'] == 'int' or option['type'] == 'float'):
362
- widgets[key] = OptionSlider(self.window, parent, key, option)
363
- else:
364
- if 'secret' in option and option['secret']:
365
- widgets[key] = PasswordInput(self.window, parent, key, option)
366
- else:
367
- widgets[key] = OptionInput(self.window, parent, key, option)
368
- elif option['type'] == 'textarea':
369
- widgets[key] = OptionTextarea(self.window, parent, key, option)
370
- elif option['type'] == 'bool':
371
- widgets[key] = OptionCheckbox(self.window, parent, key, option)
372
- elif option['type'] == 'bool_list':
373
- self.window.controller.config.placeholder.apply(option)
374
- widgets[key] = OptionCheckboxList(self.window, parent, key, option)
375
- elif option['type'] == 'dict':
376
- self.window.controller.config.placeholder.apply(option)
377
- widgets[key] = OptionDict(self.window, parent, key, option)
378
- widgets[key].setMinimumHeight(200)
379
- elif option['type'] == 'combo':
380
- self.window.controller.config.placeholder.apply(option)
381
- widgets[key] = OptionCombo(self.window, parent, key, option)
382
-
383
- return widgets
384
-
385
- def add_line(self) -> QFrame:
386
- """
387
- Make separator line
388
-
389
- :return: separator line
390
- """
391
- line = QFrame()
392
- line.setFrameShape(QFrame.HLine)
393
- line.setFrameShadow(QFrame.Sunken)
394
- return line
395
-
396
- def add_urls(self, urls: dict) -> QWidget:
397
- """
398
- Add clickable urls to list
399
-
400
- :param urls: urls dict
401
- :return: QWidget
402
- """
403
- layout = QVBoxLayout()
404
- for name in urls:
405
- url = urls[name]
406
- label = UrlLabel(name, url)
407
- layout.addWidget(label)
408
-
409
- widget = QWidget()
410
- widget.setLayout(layout)
411
- return widget
412
-
413
- def add_option(self, widget: QWidget, option: dict) -> QVBoxLayout:
414
- """
415
- Append option widget to layout
416
-
417
- :param widget: widget instance
418
- :param option: option dict
419
- :return: QVBoxLayout
420
- """
421
- one_column_types = ['textarea', 'dict', 'bool']
422
- key = option['id']
423
- label = trans(option['label'])
424
- label_key = 'model.' + key + '.label'
425
- desc = None
426
- desc_key = None
427
-
428
- if option['type'] != 'bool':
429
- self.window.ui.nodes[label_key] = QLabel(label)
430
- self.window.ui.nodes[label_key].setStyleSheet("font-weight: bold;")
431
-
432
- if "description" in option:
433
- desc = trans(option['description'])
434
- desc_key = 'model.' + key + '.desc'
435
-
436
- if option['type'] not in one_column_types:
437
- cols = QHBoxLayout()
438
- cols.addWidget(self.window.ui.nodes[label_key])
439
- cols.addWidget(widget)
440
-
441
- cols_widget = QWidget()
442
- cols_widget.setLayout(cols)
443
- cols_widget.setMaximumHeight(90)
444
-
445
- layout = QVBoxLayout()
446
- layout.addWidget(cols_widget)
447
- else:
448
- layout = QVBoxLayout()
449
- if option['type'] != 'bool':
450
- layout.addWidget(self.window.ui.nodes[label_key])
451
- else:
452
- widget.box.setText(label)
453
- layout.addWidget(widget)
454
-
455
- if desc:
456
- self.window.ui.nodes[desc_key] = QLabel(desc)
457
- self.window.ui.nodes[desc_key].setWordWrap(True)
458
- self.window.ui.nodes[desc_key].setMaximumHeight(40)
459
- self.window.ui.nodes[desc_key].setStyleSheet("font-size: 10px;")
460
- layout.addWidget(self.window.ui.nodes[desc_key])
461
-
462
- line = self.add_line()
463
- layout.addWidget(line)
464
-
465
- return layout
466
-
467
- def add_raw_option(self, option: dict) -> QHBoxLayout:
468
- """
469
- Add raw option row
470
-
471
- :param option: Option
472
- :return: QHBoxLayout
473
- """
474
- layout = QHBoxLayout()
475
- layout.addWidget(option)
476
- return layout
477
-
478
- def create_model(self, parent) -> QStandardItemModel:
479
- """
480
- Create list model
481
- :param parent: parent widget
482
- :return: QStandardItemModel
483
- """
484
- return QStandardItemModel(0, 1, parent)
485
-
486
- def update_list(self, id: str, data: dict):
487
- """
488
- Update list
489
-
490
- :param id: ID of the list
491
- :param data: Data to update
492
- """
493
- models = self.window.ui.models
494
- if id not in models:
495
- return
496
- model = models[id]
497
- hide_threads = bool(self.window.core.config.get("remote_store.google.hide_threads"))
498
- suffix = trans("remote_store.files.suffix")
499
-
500
- names = []
501
- for n, store in data.items():
502
- num_files = store.get_file_count()
503
- name = store.name
504
- extras = []
505
- if not name:
506
- if hide_threads:
507
- continue
508
- name = store.id
509
- if num_files > 0:
510
- extras.append(f"{num_files} {suffix}")
511
- if store.usage_bytes > 0:
512
- extras.append(self.window.core.filesystem.sizeof_fmt(store.usage_bytes))
513
- if extras:
514
- name = f"{name} ({' - '.join(extras)})"
515
- names.append(name)
516
-
517
- model.setRowCount(len(names))
518
- for i, name in enumerate(names):
519
- model.setData(model.index(i, 0), name)
520
-
521
- # ==================== files context menu handler ====================
522
- def on_files_context_menu(self, pos: QPoint):
523
- """
524
- Context menu for the files list: provides Delete action.
525
- """
526
- view_id = 'remote_store.google.files.list'
527
- if view_id not in self.window.ui.nodes:
528
- return
529
- view = self.window.ui.nodes[view_id]
530
- index = view.indexAt(pos)
531
- if not index.isValid():
532
- return
533
-
534
- row = index.row()
535
- menu = QMenu(view)
536
- act_delete = QAction(QIcon(":/icons/delete.svg"), trans("remote_store.menu.file.delete") if hasattr(self.window, 'tr') else "Delete", view)
537
- act_delete.triggered.connect(lambda r=row: self.window.controller.remote_store.google.delete_file_by_idx(r))
538
- menu.addAction(act_delete)
539
- menu.exec(view.mapToGlobal(pos))