pygpt-net 2.6.1__py3-none-any.whl → 2.6.6__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 (131) hide show
  1. pygpt_net/CHANGELOG.txt +23 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +20 -1
  4. pygpt_net/config.py +55 -65
  5. pygpt_net/controller/__init__.py +5 -2
  6. pygpt_net/controller/calendar/note.py +101 -126
  7. pygpt_net/controller/chat/chat.py +38 -35
  8. pygpt_net/controller/chat/render.py +154 -214
  9. pygpt_net/controller/chat/response.py +5 -3
  10. pygpt_net/controller/chat/stream.py +92 -27
  11. pygpt_net/controller/config/config.py +39 -42
  12. pygpt_net/controller/config/field/checkbox.py +16 -12
  13. pygpt_net/controller/config/field/checkbox_list.py +36 -31
  14. pygpt_net/controller/config/field/cmd.py +51 -57
  15. pygpt_net/controller/config/field/combo.py +33 -16
  16. pygpt_net/controller/config/field/dictionary.py +48 -55
  17. pygpt_net/controller/config/field/input.py +50 -32
  18. pygpt_net/controller/config/field/slider.py +40 -45
  19. pygpt_net/controller/config/field/textarea.py +20 -6
  20. pygpt_net/controller/config/placeholder.py +110 -231
  21. pygpt_net/controller/ctx/common.py +48 -48
  22. pygpt_net/controller/ctx/ctx.py +91 -132
  23. pygpt_net/controller/lang/mapping.py +57 -95
  24. pygpt_net/controller/lang/plugins.py +64 -55
  25. pygpt_net/controller/lang/settings.py +39 -38
  26. pygpt_net/controller/layout/layout.py +176 -109
  27. pygpt_net/controller/mode/mode.py +88 -85
  28. pygpt_net/controller/model/model.py +73 -73
  29. pygpt_net/controller/plugins/plugins.py +209 -223
  30. pygpt_net/controller/plugins/presets.py +54 -55
  31. pygpt_net/controller/plugins/settings.py +54 -69
  32. pygpt_net/controller/presets/editor.py +33 -88
  33. pygpt_net/controller/presets/experts.py +20 -1
  34. pygpt_net/controller/presets/presets.py +293 -298
  35. pygpt_net/controller/settings/profile.py +16 -4
  36. pygpt_net/controller/theme/theme.py +72 -81
  37. pygpt_net/controller/ui/mode.py +118 -186
  38. pygpt_net/controller/ui/tabs.py +69 -90
  39. pygpt_net/controller/ui/ui.py +47 -56
  40. pygpt_net/controller/ui/vision.py +24 -23
  41. pygpt_net/core/agents/runner.py +15 -7
  42. pygpt_net/core/bridge/bridge.py +5 -5
  43. pygpt_net/core/command/command.py +149 -219
  44. pygpt_net/core/ctx/ctx.py +94 -146
  45. pygpt_net/core/debug/debug.py +48 -58
  46. pygpt_net/core/experts/experts.py +3 -3
  47. pygpt_net/core/models/models.py +74 -112
  48. pygpt_net/core/modes/modes.py +13 -21
  49. pygpt_net/core/plugins/plugins.py +154 -177
  50. pygpt_net/core/presets/presets.py +103 -176
  51. pygpt_net/core/render/web/body.py +217 -215
  52. pygpt_net/core/render/web/renderer.py +330 -474
  53. pygpt_net/core/text/utils.py +28 -44
  54. pygpt_net/core/tokens/tokens.py +104 -203
  55. pygpt_net/data/config/config.json +3 -3
  56. pygpt_net/data/config/models.json +3 -3
  57. pygpt_net/data/locale/locale.de.ini +2 -0
  58. pygpt_net/data/locale/locale.en.ini +2 -0
  59. pygpt_net/data/locale/locale.es.ini +2 -0
  60. pygpt_net/data/locale/locale.fr.ini +2 -0
  61. pygpt_net/data/locale/locale.it.ini +2 -0
  62. pygpt_net/data/locale/locale.pl.ini +3 -1
  63. pygpt_net/data/locale/locale.uk.ini +2 -0
  64. pygpt_net/data/locale/locale.zh.ini +2 -0
  65. pygpt_net/item/ctx.py +141 -139
  66. pygpt_net/plugin/agent/plugin.py +2 -1
  67. pygpt_net/plugin/audio_output/plugin.py +5 -2
  68. pygpt_net/plugin/base/plugin.py +101 -85
  69. pygpt_net/plugin/bitbucket/__init__.py +12 -0
  70. pygpt_net/plugin/bitbucket/config.py +267 -0
  71. pygpt_net/plugin/bitbucket/plugin.py +126 -0
  72. pygpt_net/plugin/bitbucket/worker.py +569 -0
  73. pygpt_net/plugin/cmd_code_interpreter/plugin.py +3 -2
  74. pygpt_net/plugin/cmd_custom/plugin.py +3 -2
  75. pygpt_net/plugin/cmd_files/plugin.py +3 -2
  76. pygpt_net/plugin/cmd_history/plugin.py +3 -2
  77. pygpt_net/plugin/cmd_mouse_control/plugin.py +5 -2
  78. pygpt_net/plugin/cmd_serial/plugin.py +3 -2
  79. pygpt_net/plugin/cmd_system/plugin.py +3 -6
  80. pygpt_net/plugin/cmd_web/plugin.py +3 -2
  81. pygpt_net/plugin/experts/plugin.py +2 -2
  82. pygpt_net/plugin/facebook/__init__.py +12 -0
  83. pygpt_net/plugin/facebook/config.py +359 -0
  84. pygpt_net/plugin/facebook/plugin.py +113 -0
  85. pygpt_net/plugin/facebook/worker.py +698 -0
  86. pygpt_net/plugin/github/__init__.py +12 -0
  87. pygpt_net/plugin/github/config.py +441 -0
  88. pygpt_net/plugin/github/plugin.py +126 -0
  89. pygpt_net/plugin/github/worker.py +674 -0
  90. pygpt_net/plugin/google/__init__.py +12 -0
  91. pygpt_net/plugin/google/config.py +367 -0
  92. pygpt_net/plugin/google/plugin.py +126 -0
  93. pygpt_net/plugin/google/worker.py +826 -0
  94. pygpt_net/plugin/idx_llama_index/plugin.py +3 -2
  95. pygpt_net/plugin/mailer/plugin.py +3 -5
  96. pygpt_net/plugin/openai_vision/plugin.py +3 -2
  97. pygpt_net/plugin/real_time/plugin.py +52 -60
  98. pygpt_net/plugin/slack/__init__.py +12 -0
  99. pygpt_net/plugin/slack/config.py +349 -0
  100. pygpt_net/plugin/slack/plugin.py +115 -0
  101. pygpt_net/plugin/slack/worker.py +639 -0
  102. pygpt_net/plugin/telegram/__init__.py +12 -0
  103. pygpt_net/plugin/telegram/config.py +308 -0
  104. pygpt_net/plugin/telegram/plugin.py +117 -0
  105. pygpt_net/plugin/telegram/worker.py +563 -0
  106. pygpt_net/plugin/twitter/__init__.py +12 -0
  107. pygpt_net/plugin/twitter/config.py +491 -0
  108. pygpt_net/plugin/twitter/plugin.py +125 -0
  109. pygpt_net/plugin/twitter/worker.py +837 -0
  110. pygpt_net/provider/agents/llama_index/legacy/openai_assistant.py +35 -3
  111. pygpt_net/tools/code_interpreter/tool.py +0 -1
  112. pygpt_net/tools/translator/tool.py +1 -1
  113. pygpt_net/ui/base/config_dialog.py +86 -100
  114. pygpt_net/ui/base/context_menu.py +48 -46
  115. pygpt_net/ui/dialog/preset.py +34 -77
  116. pygpt_net/ui/layout/ctx/ctx_list.py +10 -6
  117. pygpt_net/ui/layout/toolbox/presets.py +41 -41
  118. pygpt_net/ui/main.py +49 -31
  119. pygpt_net/ui/tray.py +61 -60
  120. pygpt_net/ui/widget/calendar/select.py +86 -70
  121. pygpt_net/ui/widget/lists/attachment.py +86 -44
  122. pygpt_net/ui/widget/lists/base_list_combo.py +85 -33
  123. pygpt_net/ui/widget/lists/context.py +135 -188
  124. pygpt_net/ui/widget/lists/preset.py +59 -61
  125. pygpt_net/ui/widget/textarea/web.py +161 -48
  126. pygpt_net/utils.py +8 -1
  127. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/METADATA +164 -2
  128. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/RECORD +131 -103
  129. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/LICENSE +0 -0
  130. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/WHEEL +0 -0
  131. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/entry_points.txt +0 -0
@@ -6,11 +6,9 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.12.09 03:00:00 #
9
+ # Updated Date: 2025.08.15 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
- import os
13
-
14
12
  from PySide6.QtWidgets import QApplication
15
13
  from PySide6.QtCore import QRect
16
14
 
@@ -26,7 +24,16 @@ class Layout:
26
24
  """
27
25
  self.window = window
28
26
  # self.splitters = ["main", "main.output", "toolbox", "toolbox.mode", "toolbox.presets"]
29
- self.splitters = ["main", "main.output", "toolbox", "toolbox.mode", "calendar", "interpreter", "interpreter.columns", "columns"]
27
+ self.splitters = [
28
+ "main",
29
+ "main.output",
30
+ "toolbox",
31
+ "toolbox.mode",
32
+ "calendar",
33
+ "interpreter",
34
+ "interpreter.columns",
35
+ "columns",
36
+ ]
30
37
  self.text_nodes = ["input"]
31
38
 
32
39
  def setup(self):
@@ -59,157 +66,202 @@ class Layout:
59
66
 
60
67
  def text_nodes_restore(self):
61
68
  """Restore nodes text"""
62
- if not self.window.core.config.has('layout.text_nodes'):
69
+ config = self.window.core.config
70
+ if not config.has('layout.text_nodes'):
63
71
  return
64
- data = self.window.core.config.get('layout.text_nodes')
65
- for id in self.window.ui.nodes:
66
- if id in data:
67
- try:
68
- self.window.ui.nodes[id].setText(data[id])
69
- except Exception as e:
70
- print("Error while restoring field state: " + str(e))
71
- self.window.core.debug.log(e)
72
+ data = config.get('layout.text_nodes')
73
+ nodes = self.window.ui.nodes
74
+ for node_id, value in data.items():
75
+ widget = nodes.get(node_id)
76
+ if widget is None:
77
+ continue
78
+ try:
79
+ widget.setText(value)
80
+ except Exception as e:
81
+ print("Error while restoring field state: " + str(e))
82
+ self.window.core.debug.log(e)
72
83
 
73
84
  def text_nodes_save(self):
74
85
  """Save nodes text"""
86
+ ui_nodes = self.window.ui.nodes
75
87
  data = {}
76
- for id in self.text_nodes:
77
- data[id] = self.window.ui.nodes[id].toPlainText()
88
+ for node_id in self.text_nodes:
89
+ widget = ui_nodes.get(node_id)
90
+ if widget is None:
91
+ continue
92
+ data[node_id] = widget.toPlainText()
78
93
  self.window.core.config.set('layout.text_nodes', data)
79
94
 
80
95
  def tabs_save(self):
81
96
  """Save tabs state"""
97
+ tabs = self.window.ui.tabs
82
98
  data = {}
83
- for tab in self.window.ui.tabs:
84
- if not isinstance(self.window.ui.tabs[tab], dict):
85
- data[tab] = self.window.ui.tabs[tab].currentIndex()
99
+ for name, widget in tabs.items():
100
+ if not isinstance(widget, dict):
101
+ try:
102
+ data[name] = widget.currentIndex()
103
+ except Exception:
104
+ pass
86
105
  self.window.core.config.set('layout.tabs', data)
87
106
 
88
107
  def groups_save(self):
89
108
  """Save groups state"""
109
+ groups = self.window.ui.groups
90
110
  data = {}
91
- for id in self.window.ui.groups:
92
- data[id] = self.window.ui.groups[id].box.isChecked()
111
+ for gid, group in groups.items():
112
+ data[gid] = group.box.isChecked()
93
113
  self.window.core.config.set('layout.groups', data)
94
114
 
95
115
  def tabs_restore(self):
96
116
  """Restore tabs state"""
97
- if not self.window.core.config.has('layout.tabs'):
117
+ config = self.window.core.config
118
+ if not config.has('layout.tabs'):
98
119
  return
99
- data = self.window.core.config.get('layout.tabs')
100
- for tab in self.window.ui.tabs:
101
- if tab in data:
102
- try:
103
- self.window.ui.tabs[tab].setCurrentIndex(data[tab])
104
- except Exception as e:
105
- print("Error while restoring tab state: " + str(e))
106
- self.window.core.debug.log(e)
120
+ data = config.get('layout.tabs')
121
+ tabs = self.window.ui.tabs
122
+ for name, index in data.items():
123
+ widget = tabs.get(name)
124
+ if widget is None:
125
+ continue
126
+ try:
127
+ if getattr(widget, "currentIndex", None) is not None and widget.currentIndex() != index:
128
+ widget.setCurrentIndex(index)
129
+ except Exception as e:
130
+ print("Error while restoring tab state: " + str(e))
131
+ self.window.core.debug.log(e)
107
132
 
108
133
  def splitters_save(self):
109
134
  """Save splitters state"""
110
135
  data = {}
136
+ ui_splitters = self.window.ui.splitters
111
137
  for splitter in self.splitters:
112
- # do not save main splitter state if notepad was not opened yet
113
138
  if splitter == "calendar" and not self.window.controller.notepad.opened_once:
114
139
  continue
140
+ splitter_widget = ui_splitters.get(splitter)
141
+ if splitter_widget is None:
142
+ continue
115
143
  try:
116
- if splitter in self.window.ui.splitters:
117
- data[splitter] = self.window.ui.splitters[splitter].sizes()
118
- except Exception as e:
144
+ data[splitter] = splitter_widget.sizes()
145
+ except Exception:
119
146
  pass
120
147
  self.window.core.config.set('layout.splitters', data)
121
148
 
122
149
  def splitters_restore(self):
123
150
  """Restore splitters state"""
124
- if not self.window.core.config.has('layout.splitters'):
151
+ config = self.window.core.config
152
+ if not config.has('layout.splitters'):
125
153
  return
126
- data = self.window.core.config.get('layout.splitters')
127
- for splitter in self.splitters:
128
- if splitter in data:
129
- try:
130
- if splitter in self.window.ui.splitters:
131
- self.window.ui.splitters[splitter].setSizes(data[splitter])
132
- except Exception as e:
133
- print("Error while restoring splitter state: " + str(e))
134
- self.window.core.debug.log(e)
154
+ data = config.get('layout.splitters')
155
+ ui_splitters = self.window.ui.splitters
156
+ for splitter, sizes in data.items():
157
+ splitter_widget = ui_splitters.get(splitter)
158
+ if splitter_widget is None:
159
+ continue
160
+ try:
161
+ current = splitter_widget.sizes()
162
+ if current != sizes:
163
+ splitter_widget.setSizes(sizes)
164
+ except Exception as e:
165
+ print("Error while restoring splitter state: " + str(e))
166
+ self.window.core.debug.log(e)
135
167
 
136
168
  def groups_restore(self):
137
169
  """Restore groups state"""
138
- if not self.window.core.config.has('layout.groups'):
170
+ config = self.window.core.config
171
+ if not config.has('layout.groups'):
139
172
  return
140
- data = self.window.core.config.get('layout.groups')
141
- for id in self.window.ui.groups:
142
- if id in data:
143
- try:
144
- self.window.ui.groups[id].collapse(data[id])
145
- except Exception as e:
146
- print("Error while restoring group state: " + str(e))
147
- self.window.core.debug.log(e)
173
+ data = config.get('layout.groups')
174
+ groups = self.window.ui.groups
175
+ for gid, state in data.items():
176
+ group = groups.get(gid)
177
+ if group is None:
178
+ continue
179
+ try:
180
+ if group.box.isChecked() != state:
181
+ group.collapse(state)
182
+ except Exception as e:
183
+ print("Error while restoring group state: " + str(e))
184
+ self.window.core.debug.log(e)
148
185
 
149
186
  def scroll_save(self):
150
187
  """Save scroll state"""
151
188
  data = {}
152
- if hasattr(self.window.ui.nodes['output'], 'verticalScrollBar'):
153
- data['output'] = self.window.ui.nodes['output'].verticalScrollBar().value()
189
+ nodes = self.window.ui.nodes
190
+ output = nodes.get('output')
191
+ if output is not None and hasattr(output, 'verticalScrollBar'):
192
+ data['output'] = output.verticalScrollBar().value()
154
193
 
155
- # notepads
156
- for id in self.window.ui.notepad:
194
+ for nid, notepad in self.window.ui.notepad.items():
157
195
  try:
158
- scroll_id = "notepad." + str(id)
159
- data[scroll_id] = self.window.ui.notepad[id].textarea.verticalScrollBar().value()
160
- except Exception as e:
196
+ scroll_id = f"notepad.{nid}"
197
+ data[scroll_id] = notepad.textarea.verticalScrollBar().value()
198
+ except Exception:
161
199
  pass
162
200
  self.window.core.config.set('layout.scroll', data)
163
201
 
164
202
  def scroll_restore(self):
165
203
  """Restore scroll state"""
166
- if not self.window.core.config.has('layout.scroll'):
204
+ config = self.window.core.config
205
+ if not config.has('layout.scroll'):
167
206
  return
168
- data = self.window.core.config.get('layout.scroll')
169
- for scroll_id in data:
170
- # notepads
207
+ data = config.get('layout.scroll')
208
+ notepads = self.window.ui.notepad
209
+ for scroll_id, value in data.items():
171
210
  if scroll_id.startswith("notepad."):
172
- id = int(scroll_id.replace("notepad.", ""))
173
- if id in self.window.ui.notepad:
174
- try:
175
- self.window.ui.notepad[id].textarea.verticalScrollBar().setValue(data[scroll_id])
176
- except Exception as e:
177
- print("Error while restoring scroll state: " + str(e))
178
- self.window.core.debug.log(e)
211
+ try:
212
+ nid = int(scroll_id.replace("notepad.", ""))
213
+ except Exception:
214
+ continue
215
+ npad = notepads.get(nid)
216
+ if npad is None:
217
+ continue
218
+ try:
219
+ sb = npad.textarea.verticalScrollBar()
220
+ if sb.value() != value:
221
+ sb.setValue(value)
222
+ except Exception as e:
223
+ print("Error while restoring scroll state: " + str(e))
224
+ self.window.core.debug.log(e)
179
225
  if 'output' in data:
180
226
  try:
181
- if hasattr(self.window.ui.nodes['output'], 'verticalScrollBar'):
182
- self.window.ui.nodes['output'].verticalScrollBar().setValue(data['output'])
227
+ output = self.window.ui.nodes.get('output')
228
+ if output is not None and hasattr(output, 'verticalScrollBar'):
229
+ sb = output.verticalScrollBar()
230
+ if sb.value() != data['output']:
231
+ sb.setValue(data['output'])
183
232
  except Exception as e:
184
233
  print("Error while restoring scroll state: " + str(e))
185
234
  self.window.core.debug.log(e)
186
235
 
187
236
  def state_restore(self):
188
237
  """Restore window state"""
189
- if not self.window.core.config.has('layout.window'):
238
+ config = self.window.core.config
239
+ if not config.has('layout.window'):
190
240
  return
191
- data = self.window.core.config.get('layout.window')
241
+ data = config.get('layout.window')
192
242
  try:
193
243
  screen = QApplication.primaryScreen()
244
+ if screen is None:
245
+ return
194
246
  available_geometry = screen.availableGeometry()
195
247
 
196
- if 'geometry' in data:
197
- geometry_data = data['geometry']
198
- x, y, width, height = (geometry_data['x'],
199
- geometry_data['y'],
200
- geometry_data['width'],
201
- geometry_data['height'])
248
+ geometry_data = data.get('geometry')
249
+ if geometry_data:
250
+ x = geometry_data.get('x', 0)
251
+ y = geometry_data.get('y', 0)
252
+ width = geometry_data.get('width', self.window.width())
253
+ height = geometry_data.get('height', self.window.height())
202
254
 
203
255
  window_rect = QRect(x, y, width, height)
204
256
  adjusted_rect = available_geometry.intersected(window_rect)
205
257
 
206
258
  if not available_geometry.contains(window_rect):
207
- adjusted_rect.adjust(0, 0, -20, -20) # Adjust to fit within the screen
259
+ adjusted_rect.adjust(0, 0, -20, -20)
208
260
 
209
261
  self.window.move(adjusted_rect.x(), adjusted_rect.y())
210
262
  self.window.resize(adjusted_rect.width(), adjusted_rect.height())
211
263
 
212
- if 'maximized' in data and data['maximized']:
264
+ if data.get('maximized'):
213
265
  self.window.showMaximized()
214
266
  except Exception as e:
215
267
  print("Error while restoring window state: " + str(e))
@@ -217,47 +269,62 @@ class Layout:
217
269
  def state_save(self):
218
270
  """Save window state"""
219
271
  data = {}
220
- geometry_data = {}
221
272
  geometry = self.window.geometry()
222
- geometry_data['x'] = geometry.x()
223
- geometry_data['y'] = geometry.y()
224
- geometry_data['width'] = geometry.width()
225
- geometry_data['height'] = geometry.height()
226
- data['geometry'] = geometry_data
273
+ data['geometry'] = {
274
+ 'x': geometry.x(),
275
+ 'y': geometry.y(),
276
+ 'width': geometry.width(),
277
+ 'height': geometry.height(),
278
+ }
227
279
  data['maximized'] = self.window.isMaximized()
228
280
  self.window.core.config.set('layout.window', data)
229
281
 
230
282
  # ------------------
231
283
 
232
284
  # output zoom save (Chromium, web engine only)
233
- if hasattr(self.window.ui.nodes['output'], 'get_zoom_value'):
234
- self.window.core.config.set('zoom', self.window.ui.nodes['output'].get_zoom_value())
285
+ output = self.window.ui.nodes.get('output')
286
+ if output is not None and hasattr(output, 'get_zoom_value'):
287
+ self.window.core.config.set('zoom', output.get_zoom_value())
235
288
 
236
289
  def restore_plugin_settings(self):
237
290
  """Restore groups state"""
238
- if not self.window.core.config.has('layout.groups'):
291
+ config = self.window.core.config
292
+ if not config.has('layout.groups'):
239
293
  return
240
- data = self.window.core.config.get('layout.groups')
241
- for id in self.window.ui.groups:
242
- if id in data and id.startswith('plugin.settings.'):
243
- try:
244
- self.window.ui.groups[id].collapse(data[id])
245
- except Exception as e:
246
- print("Error while restoring group state: " + str(e))
247
- self.window.core.debug.log(e)
294
+ data = config.get('layout.groups')
295
+ groups = self.window.ui.groups
296
+ for gid, state in data.items():
297
+ if not gid.startswith('plugin.settings.'):
298
+ continue
299
+ group = groups.get(gid)
300
+ if group is None:
301
+ continue
302
+ try:
303
+ if group.box.isChecked() != state:
304
+ group.collapse(state)
305
+ except Exception as e:
306
+ print("Error while restoring group state: " + str(e))
307
+ self.window.core.debug.log(e)
248
308
 
249
309
  def restore_settings(self):
250
310
  """Restore groups state"""
251
- if not self.window.core.config.has('layout.groups'):
311
+ config = self.window.core.config
312
+ if not config.has('layout.groups'):
252
313
  return
253
- data = self.window.core.config.get('layout.groups')
254
- for id in self.window.ui.groups:
255
- if id in data and id.startswith('settings.advanced.'):
256
- try:
257
- self.window.ui.groups[id].collapse(data[id])
258
- except Exception as e:
259
- print("Error while restoring group state: " + str(e))
260
- self.window.core.debug.log(e)
314
+ data = config.get('layout.groups')
315
+ groups = self.window.ui.groups
316
+ for gid, state in data.items():
317
+ if not gid.startswith('settings.advanced.'):
318
+ continue
319
+ group = groups.get(gid)
320
+ if group is None:
321
+ continue
322
+ try:
323
+ if group.box.isChecked() != state:
324
+ group.collapse(state)
325
+ except Exception as e:
326
+ print("Error while restoring group state: " + str(e))
327
+ self.window.core.debug.log(e)
261
328
 
262
329
  def restore_default_css(self, force: bool = False):
263
330
  """
@@ -293,4 +360,4 @@ class Layout:
293
360
 
294
361
  def reload(self):
295
362
  """Reload layout"""
296
- self.post_setup()
363
+ self.post_setup()