cfclient 2017.4__py3-none-any.whl → 2025.12.1__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 (140) hide show
  1. cfclient/__init__.py +16 -11
  2. cfclient/configs/config.json +4 -3
  3. cfclient/configs/input/Generic_OS_X.json +1 -0
  4. cfclient/configs/input/Joystick.json +1 -0
  5. cfclient/configs/input/PS3_Mode_1.json +1 -0
  6. cfclient/configs/input/PS3_Mode_2.json +1 -0
  7. cfclient/configs/input/PS3_Mode_3.json +1 -0
  8. cfclient/configs/input/PS4_Mode_1.json +1 -0
  9. cfclient/configs/input/PS4_Mode_2.json +1 -0
  10. cfclient/configs/input/PS4_shoulder_btns_yaw.json +1 -0
  11. cfclient/configs/input/xbox360_mode1.json +1 -0
  12. cfclient/configs/log/PID_tuning/Attitude.json +46 -0
  13. cfclient/configs/log/PID_tuning/Attitude_rate.json +46 -0
  14. cfclient/configs/log/PID_tuning/Position.json +46 -0
  15. cfclient/configs/log/PID_tuning/Velocity.json +46 -0
  16. cfclient/configs/log/PID_tuning_components/Pitch.json +22 -0
  17. cfclient/configs/log/PID_tuning_components/Pitch_rate.json +22 -0
  18. cfclient/configs/log/PID_tuning_components/Position_x.json +22 -0
  19. cfclient/configs/log/PID_tuning_components/Position_y.json +22 -0
  20. cfclient/configs/log/PID_tuning_components/Position_z.json +22 -0
  21. cfclient/configs/log/PID_tuning_components/Roll.json +22 -0
  22. cfclient/configs/log/PID_tuning_components/Roll_rate.json +22 -0
  23. cfclient/configs/log/PID_tuning_components/Velocity_x.json +22 -0
  24. cfclient/configs/log/PID_tuning_components/Velocity_y.json +22 -0
  25. cfclient/configs/log/PID_tuning_components/Velocity_z.json +22 -0
  26. cfclient/configs/log/PID_tuning_components/Yaw.json +22 -0
  27. cfclient/configs/log/PID_tuning_components/Yaw_rate.json +22 -0
  28. cfclient/gui.py +44 -9
  29. cfclient/headless.py +3 -12
  30. cfclient/resources/log_param_doc.json +1 -0
  31. cfclient/ui/connectivity_manager.py +198 -0
  32. cfclient/ui/dialogs/about.py +53 -36
  33. cfclient/ui/dialogs/about.ui +23 -3
  34. cfclient/ui/dialogs/anchor_position_dialog.py +252 -0
  35. cfclient/ui/dialogs/anchor_position_dialog.ui +138 -0
  36. cfclient/ui/dialogs/basestation_mode_dialog.py +185 -0
  37. cfclient/ui/dialogs/basestation_mode_dialog.ui +186 -0
  38. cfclient/ui/dialogs/bootloader.py +448 -85
  39. cfclient/ui/dialogs/bootloader.ui +387 -134
  40. cfclient/ui/dialogs/cf2config.py +4 -4
  41. cfclient/ui/dialogs/cf2config.ui +3 -4
  42. cfclient/ui/dialogs/inputconfigdialogue.py +24 -19
  43. cfclient/ui/dialogs/inputconfigdialogue.ui +53 -30
  44. cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py +220 -0
  45. cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.ui +110 -0
  46. cfclient/ui/dialogs/lighthouse_system_type_dialog.py +93 -0
  47. cfclient/ui/dialogs/lighthouse_system_type_dialog.ui +121 -0
  48. cfclient/ui/dialogs/logconfigdialogue.py +401 -101
  49. cfclient/ui/dialogs/logconfigdialogue.ui +117 -72
  50. cfclient/ui/icons/bl.webp +0 -0
  51. cfclient/ui/icons/bolt.webp +0 -0
  52. cfclient/ui/icons/cf21.webp +0 -0
  53. cfclient/ui/icons/checkmark_black.png +0 -0
  54. cfclient/ui/icons/checkmark_white.png +0 -0
  55. cfclient/ui/icons/create.png +0 -0
  56. cfclient/ui/icons/delete.png +0 -0
  57. cfclient/ui/icons/flapper.webp +0 -0
  58. cfclient/ui/icons/tag.webp +0 -0
  59. cfclient/ui/main.py +328 -258
  60. cfclient/ui/main.ui +184 -80
  61. cfclient/ui/pluginhelper.py +7 -1
  62. cfclient/ui/pose_logger.py +116 -0
  63. cfclient/ui/tab_toolbox.py +208 -0
  64. cfclient/ui/tabs/ColorLEDTab.py +752 -0
  65. cfclient/ui/tabs/ConsoleTab.py +48 -13
  66. cfclient/ui/{toolboxes → tabs}/CrtpSharkToolbox.py +19 -34
  67. cfclient/ui/tabs/ExampleTab.py +9 -16
  68. cfclient/ui/tabs/FlightTab.py +437 -325
  69. cfclient/ui/tabs/GpsTab.py +14 -20
  70. cfclient/ui/tabs/LEDRingTab.py +277 -0
  71. cfclient/ui/tabs/LogBlockDebugTab.py +20 -27
  72. cfclient/ui/tabs/LogBlockTab.py +35 -35
  73. cfclient/ui/tabs/LogClientTab.py +85 -0
  74. cfclient/ui/tabs/LogTab.py +50 -27
  75. cfclient/ui/tabs/ParamTab.py +443 -57
  76. cfclient/ui/tabs/PlotTab.py +23 -25
  77. cfclient/ui/tabs/TuningTab.py +292 -0
  78. cfclient/ui/tabs/__init__.py +12 -2
  79. cfclient/ui/tabs/colorLEDTab.ui +624 -0
  80. cfclient/ui/tabs/consoleTab.ui +46 -0
  81. cfclient/ui/tabs/flightActionContainer.ui +103 -0
  82. cfclient/ui/tabs/flightTab.ui +724 -237
  83. cfclient/ui/tabs/{ledTab.ui → ledRingTab.ui} +63 -46
  84. cfclient/ui/tabs/lighthouse_tab.py +714 -0
  85. cfclient/ui/tabs/lighthouse_tab.ui +430 -0
  86. cfclient/ui/tabs/locopositioning_tab.py +606 -389
  87. cfclient/ui/tabs/locopositioning_tab.ui +370 -253
  88. cfclient/ui/tabs/logClientTab.ui +52 -0
  89. cfclient/ui/tabs/logTab.ui +1 -1
  90. cfclient/ui/tabs/paramTab.ui +204 -3
  91. cfclient/ui/tabs/tuningTab.ui +773 -0
  92. cfclient/ui/widgets/ai.py +37 -39
  93. cfclient/ui/widgets/hexspinbox.py +16 -10
  94. cfclient/ui/widgets/plotter.ui +39 -47
  95. cfclient/ui/widgets/plotwidget.py +57 -22
  96. cfclient/ui/widgets/super_slider.py +112 -0
  97. cfclient/ui/wizards/__init__.py +0 -0
  98. cfclient/ui/wizards/bslh_1.png +0 -0
  99. cfclient/ui/wizards/bslh_2.png +0 -0
  100. cfclient/ui/wizards/bslh_3.png +0 -0
  101. cfclient/ui/wizards/bslh_4.png +0 -0
  102. cfclient/ui/wizards/bslh_5.png +0 -0
  103. cfclient/ui/wizards/lighthouse_geo_bs_estimation_wizard.py +465 -0
  104. cfclient/utils/config_manager.py +5 -4
  105. cfclient/utils/input/__init__.py +77 -19
  106. cfclient/utils/input/inputinterfaces/wiimote.py +2 -2
  107. cfclient/utils/input/inputreaderinterface.py +17 -7
  108. cfclient/utils/input/inputreaders/__init__.py +17 -0
  109. cfclient/utils/logconfigreader.py +245 -25
  110. cfclient/utils/logdatawriter.py +3 -1
  111. cfclient/utils/periodictimer.py +1 -1
  112. cfclient/utils/ui.py +336 -0
  113. cfclient/utils/zmq_led_driver.py +5 -0
  114. cfclient/utils/zmq_param.py +6 -0
  115. cfclient/version.py +34 -1
  116. cfclient-2025.12.1.dist-info/METADATA +70 -0
  117. cfclient-2025.12.1.dist-info/RECORD +152 -0
  118. {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/WHEEL +1 -1
  119. {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/entry_points.txt +0 -1
  120. cfclient-2025.12.1.dist-info/licenses/LICENSE.txt +350 -0
  121. {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/top_level.txt +1 -0
  122. cfconfig/Makefile +51 -0
  123. cfconfig/configblock.py +111 -0
  124. cfloader/__init__.py +41 -55
  125. cfzmq/__init__.py +22 -14
  126. cfclient/ui/dialogs/cf1config.py +0 -265
  127. cfclient/ui/dialogs/cf1config.ui +0 -260
  128. cfclient/ui/tab.py +0 -96
  129. cfclient/ui/tabs/LEDTab.py +0 -169
  130. cfclient/ui/toolboxes/ConsoleToolbox.py +0 -69
  131. cfclient/ui/toolboxes/DebugDriverToolbox.py +0 -107
  132. cfclient/ui/toolboxes/__init__.py +0 -45
  133. cfclient/ui/toolboxes/consoleToolbox.ui +0 -62
  134. cfclient/ui/toolboxes/debugDriverToolbox.ui +0 -86
  135. cfclient-2017.4.dist-info/DESCRIPTION.rst +0 -3
  136. cfclient-2017.4.dist-info/METADATA +0 -22
  137. cfclient-2017.4.dist-info/RECORD +0 -104
  138. cfclient-2017.4.dist-info/metadata.json +0 -1
  139. /cfclient/{icon-256.png → ui/icons/icon-256.png} +0 -0
  140. /cfclient/ui/{toolboxes → tabs}/crtpSharkToolbox.ui +0 -0
@@ -7,7 +7,7 @@
7
7
  # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
8
8
  # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
9
9
  #
10
- # Copyright (C) 2011-2013 Bitcraze AB
10
+ # Copyright (C) 2011-2023 Bitcraze AB
11
11
  #
12
12
  # Crazyflie Nano Quadcopter Client
13
13
  #
@@ -32,12 +32,13 @@ views in the UI.
32
32
  """
33
33
 
34
34
  import logging
35
+ import struct
35
36
 
36
37
  import cfclient
37
- from PyQt5 import Qt, QtWidgets, uic
38
- from PyQt5.QtCore import * # noqa
39
- from PyQt5.QtWidgets import * # noqa
40
- from PyQt5.Qt import * # noqa
38
+ from cfclient.utils.ui import UiUtils
39
+ from PyQt6 import QtWidgets, uic
40
+ from PyQt6.QtCore import Qt, QTimer
41
+ from PyQt6.QtGui import QShortcut, QKeySequence
41
42
 
42
43
  from cflib.crazyflie.log import LogConfig
43
44
 
@@ -51,8 +52,9 @@ logger = logging.getLogger(__name__)
51
52
 
52
53
  NAME_FIELD = 0
53
54
  ID_FIELD = 1
54
- PTYPE_FIELD = 2
55
- CTYPE_FIELD = 3
55
+ TYPE_FIELD = 2
56
+ SIZE_FIELD = 3
57
+ MAX_LOG_SIZE = 26
56
58
 
57
59
 
58
60
  class LogConfigDialogue(QtWidgets.QWidget, logconfig_widget_class):
@@ -62,47 +64,320 @@ class LogConfigDialogue(QtWidgets.QWidget, logconfig_widget_class):
62
64
  self.setupUi(self)
63
65
  self.helper = helper
64
66
 
65
- self.logTree.setHeaderLabels(['Name', 'ID', 'Unpack', 'Storage'])
66
- self.varTree.setHeaderLabels(['Name', 'ID', 'Unpack', 'Storage'])
67
+ self.logTree.setHeaderLabels(['Name', 'ID', 'Type', 'Size'])
68
+ self.varTree.setHeaderLabels(['Name', 'ID', 'Type', 'Size'])
69
+ self.categoryTree.setHeaderLabels(['Categories'])
67
70
 
71
+ self.logTree.setSortingEnabled(True)
72
+ self.varTree.setSortingEnabled(True)
73
+
74
+ # Item-click callbacks.
68
75
  self.addButton.clicked.connect(lambda: self.moveNode(self.logTree,
69
76
  self.varTree))
70
77
  self.removeButton.clicked.connect(lambda: self.moveNode(self.varTree,
71
78
  self.logTree))
72
- self.cancelButton.clicked.connect(self.close)
73
- self.loadButton.clicked.connect(self.loadConfig)
74
79
  self.saveButton.clicked.connect(self.saveConfig)
75
80
 
81
+ self.categoryTree.itemSelectionChanged.connect(self._item_selected)
82
+ self.categoryTree.itemPressed.connect(self._on_item_press)
83
+ self.categoryTree.itemChanged.connect(self._config_changed)
84
+
85
+ # Add/remove item on doubleclick.
86
+ self.logTree.itemDoubleClicked.connect(self.itemDoubleClicked)
87
+ self.varTree.itemDoubleClicked.connect(lambda: self.moveNode(
88
+ self.varTree, self.logTree))
76
89
  self.loggingPeriod.textChanged.connect(self.periodChanged)
77
90
 
78
- self.packetSize.setMaximum(26)
79
91
  self.currentSize = 0
92
+ self.packetSize.setMaximum(100)
80
93
  self.packetSize.setValue(0)
81
94
  self.period = 0
82
95
 
83
- def decodeSize(self, s):
84
- size = 0
85
- if ("16" in s):
86
- size = 2
87
- if ("float" in s):
88
- size = 4
89
- if ("8" in s):
90
- size = 1
91
- if ("FP16" in s):
92
- size = 2
93
- if ("32" in s):
94
- size = 4
95
- return size
96
+ # Used when renaming a config/category
97
+ self._last_pressed_item = None
98
+
99
+ # set icons
100
+ save_icon, delete_icon = self.helper.logConfigReader.get_icons()
101
+ self.createCategoryBtn.setIcon(save_icon)
102
+ self.createConfigBtn.setIcon(save_icon)
103
+ self.deleteBtn.setIcon(delete_icon)
104
+
105
+ # bind buttons
106
+ self.createCategoryBtn.clicked.connect(self._create_category)
107
+ self.createConfigBtn.clicked.connect(self._create_config)
108
+ self.deleteBtn.clicked.connect(self._delete_config)
109
+
110
+ # set tooltips
111
+ self.createCategoryBtn.setToolTip('Create a new category')
112
+ self.createConfigBtn.setToolTip('Create a new log-config')
113
+ self.deleteBtn.setToolTip('Delete category')
114
+
115
+ # enable right-click context-menu
116
+ self.categoryTree.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
117
+ self.categoryTree.customContextMenuRequested.connect(
118
+ self.menuContextTree)
119
+
120
+ # keyboard shortcuts
121
+ shortcut_delete = QShortcut(QKeySequence("Delete"), self)
122
+ shortcut_delete.activated.connect(self._delete_config)
123
+
124
+ shortcut_f2 = QShortcut(QKeySequence("F2"), self)
125
+ shortcut_f2.activated.connect(self._edit_name)
126
+
127
+ self._config_saved_timer = QTimer()
128
+ self._config_saved_timer.timeout.connect(self._config_saved_status)
129
+
130
+ self.closeOnSave.setChecked(True)
131
+
132
+ def itemDoubleClicked(self):
133
+ if self.categoryTree.selectedItems():
134
+ self.moveNode(self.logTree, self.varTree)
135
+
136
+ def _config_saved_status(self):
137
+ self.statusText.setText('')
138
+ self._config_saved_timer.stop()
139
+
140
+ def _on_item_press(self, item):
141
+ self._last_pressed_item = item, item.text(0)
142
+
143
+ def _create_config(self):
144
+ """ Creates a new log-configuration in the chosen
145
+ category. If no category is selected, the
146
+ configuration is stored in the 'Default' category.
147
+ """
148
+ items = self.categoryTree.selectedItems()
149
+
150
+ if items:
151
+ config = items[0]
152
+ parent = config.parent()
153
+ if parent:
154
+ category = parent.text(0)
155
+ else:
156
+ category = config.text(0)
157
+
158
+ conf_name = self.helper.logConfigReader.create_empty_log_conf(
159
+ category)
160
+ self._reload()
161
+ # Load the newly created log-config.
162
+ self._select_item(conf_name, category)
163
+ self._edit_name()
164
+
165
+ def _create_category(self):
166
+ """ Creates a new category and enables editing the name. """
167
+ category_name = self.helper.logConfigReader.create_category()
168
+ self._load_saved_configs()
169
+ self.sortTrees()
170
+ self._select_category(category_name)
171
+ self._edit_name()
172
+
173
+ def _delete_config(self):
174
+
175
+ """ Deletes a category or a configuration
176
+ depending on if the item has a parent or not.
177
+ """
178
+
179
+ items = self.categoryTree.selectedItems()
180
+ if items:
181
+ config = items[0]
182
+ parent = config.parent()
183
+
184
+ if parent:
185
+ # Delete a configuration in the given category.
186
+ category = parent.text(0)
187
+ self.helper.logConfigReader.delete_config(config.text(0),
188
+ category)
189
+ self._reload()
190
+ else:
191
+ # Delete a category and all its log-configurations
192
+ category = config.text(0)
193
+ if category != 'Default':
194
+ self.helper.logConfigReader.delete_category(category)
195
+ self._reload()
196
+
197
+ def _config_changed(self, config):
198
+ """ Changes the name for a log-configuration or a category.
199
+ This is a callback function that gets called when an item
200
+ is changed.
201
+ """
202
+ item, old_name = self._last_pressed_item
203
+
204
+ parent = config.parent()
205
+ if parent:
206
+ # Change name for a log-config, inside of the category.
207
+ new_conf_name = item.text(0)
208
+ category = parent.text(0)
209
+ self.helper.logConfigReader.change_name_config(old_name,
210
+ new_conf_name,
211
+ category)
212
+ else:
213
+ # Change name for the category.
214
+ category = config.text(0)
215
+ self.helper.logConfigReader.change_name_category(old_name,
216
+ category)
217
+
218
+ def _edit_name(self):
219
+ """ Enables editing the clicked item.
220
+ When the edit is saved, a callback is fired.
221
+ """
222
+ items = self.categoryTree.selectedItems()
223
+ if items:
224
+ item_clicked = items[0]
225
+ self.categoryTree.editItem(item_clicked, 0)
226
+
227
+ def _reload(self):
228
+ self.resetTrees()
229
+ self._load_saved_configs()
230
+ self.sortTrees()
231
+
232
+ def menuContextTree(self, point):
233
+
234
+ menu = QtWidgets.QMenu()
235
+
236
+ createConfig = None
237
+ createCategory = None
238
+ delete = None
239
+ edit = None
240
+
241
+ item = self.categoryTree.itemAt(point)
242
+ if item:
243
+ createConfig = menu.addAction('Create new log configuration')
244
+ edit = menu.addAction('Edit name')
245
+
246
+ if item.parent():
247
+ delete = menu.addAction('Delete config')
248
+ else:
249
+ delete = menu.addAction('Delete category')
250
+ else:
251
+ createCategory = menu.addAction('Create new Category')
252
+
253
+ action = menu.exec_(self.categoryTree.mapToGlobal(point))
254
+
255
+ if action == createConfig:
256
+ self._create_config()
257
+ elif createCategory:
258
+ self._create_category()
259
+ elif action == delete:
260
+ self._delete_config()
261
+ elif action == edit:
262
+ self._edit_name()
263
+
264
+ def _select_category(self, category):
265
+ items = self.categoryTree.findItems(category, Qt.MatchFlag.MatchFixedString | Qt.MatchFlag.MatchRecursive)
266
+ if items:
267
+ category = items[0]
268
+ self.categoryTree.setCurrentItem(category)
269
+ self._last_pressed_item = category, category.text(0)
270
+
271
+ def _select_item(self, conf_name, category):
272
+ """ loads the given config in the correct category """
273
+ items = self.categoryTree.findItems(conf_name, Qt.MatchFlag.MatchFixedString | Qt.MatchFlag.MatchRecursive)
274
+ for item in items:
275
+ if item.parent().text(0) == category:
276
+ self._last_pressed_item = item, conf_name
277
+ self._loadConfig(category, conf_name)
278
+ self.categoryTree.setCurrentItem(item)
279
+
280
+ def _item_selected(self):
281
+ """ Opens the log configuration of the pressed
282
+ item in the category-tree. """
283
+ items = self.categoryTree.selectedItems()
284
+
285
+ if items:
286
+ config = items[0]
287
+ category = config.parent()
288
+ if category:
289
+ self._loadConfig(category.text(NAME_FIELD),
290
+ config.text(NAME_FIELD))
291
+ else:
292
+ # if category is None, it's the category that's clicked
293
+ self._clear_trees_and_progressbar()
294
+
295
+ def _clear_trees_and_progressbar(self):
296
+ self.varTree.clear()
297
+ self.logTree.clear()
298
+ self.currentSize = 0
299
+ self.loggingPeriod.setText('')
300
+ self.updatePacketSizeBar()
301
+
302
+ def _load_saved_configs(self):
303
+ """ Read saved log-configs and display them on
304
+ the left-side category-tree. """
305
+
306
+ config = None
307
+ config = self.helper.logConfigReader._getLogConfigs()
308
+
309
+ if (config is None):
310
+ logger.warning("Could not load config")
311
+ else:
312
+ self.categoryTree.clear()
313
+ # Create category-tree.
314
+ for conf_category in config:
315
+ category = QtWidgets.QTreeWidgetItem()
316
+ category.setData(NAME_FIELD, Qt.ItemDataRole.DisplayRole, conf_category)
317
+ category.setFlags(category.flags() | Qt.ItemFlag.ItemIsEditable)
318
+
319
+ # Copulate category-tree with log configurations.
320
+ for conf in config[conf_category]:
321
+ item = QtWidgets.QTreeWidgetItem()
322
+
323
+ # Check if name contains category/config-name.
324
+ # This is only true is a new config has been added
325
+ # during a session, and the window re-opened.
326
+ if '/' in conf.name:
327
+ conf_name = conf.name.split('/')[1]
328
+ else:
329
+ conf_name = conf.name
330
+
331
+ item.setData(NAME_FIELD, Qt.ItemDataRole.DisplayRole, conf_name)
332
+ category.addChild(item)
333
+
334
+ # Enable item-editing.
335
+ item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEditable)
336
+
337
+ self.categoryTree.addTopLevelItem(category)
338
+ self.categoryTree.expandItem(category)
339
+
340
+ self.sortTrees()
341
+
342
+ def _loadConfig(self, category, config_name):
343
+ configs = self.helper.logConfigReader._getLogConfigs()[category]
344
+
345
+ if (configs is None):
346
+ logger.warning("Could not load config")
347
+
348
+ else:
349
+ for config in configs:
350
+ name = self._parse_configname(config)
351
+ if name == config_name:
352
+ self.resetTrees()
353
+ self.loggingPeriod.setText("%d" % config.period_in_ms)
354
+ self.period = config.period_in_ms
355
+ for v in config.variables:
356
+ if (v.is_toc_variable()):
357
+ parts = v.name.split(".")
358
+ varParent = parts[0]
359
+ varName = parts[1]
360
+ if self.moveNodeByName(
361
+ self.logTree, self.varTree, varParent,
362
+ varName) is False:
363
+ logger.warning("Could not find node %s.%s!!",
364
+ varParent, varName)
365
+ else:
366
+ logger.warning("Error: Mem vars not supported!")
367
+
368
+ self.sortTrees()
369
+
370
+ def resetTrees(self):
371
+ self.varTree.clear()
372
+ self.logTree.clear()
373
+ self.updateToc()
96
374
 
97
375
  def sortTrees(self):
98
- self.varTree.invisibleRootItem().sortChildren(NAME_FIELD,
99
- Qt.AscendingOrder)
100
- for node in self.getNodeChildren(self.varTree.invisibleRootItem()):
101
- node.sortChildren(NAME_FIELD, Qt.AscendingOrder)
102
- self.logTree.invisibleRootItem().sortChildren(NAME_FIELD,
103
- Qt.AscendingOrder)
104
- for node in self.getNodeChildren(self.logTree.invisibleRootItem()):
105
- node.sortChildren(NAME_FIELD, Qt.AscendingOrder)
376
+ """ Sorts all trees by their name. """
377
+ for tree in [self.logTree, self.varTree, self.categoryTree]:
378
+ tree.sortItems(NAME_FIELD, Qt.SortOrder.AscendingOrder)
379
+ for node in self.getNodeChildren(tree.invisibleRootItem()):
380
+ node.sortChildren(NAME_FIELD, Qt.SortOrder.AscendingOrder)
106
381
 
107
382
  def getNodeChildren(self, treeNode):
108
383
  children = []
@@ -115,25 +390,33 @@ class LogConfigDialogue(QtWidgets.QWidget, logconfig_widget_class):
115
390
  for node in self.getNodeChildren(self.varTree.invisibleRootItem()):
116
391
  for leaf in self.getNodeChildren(node):
117
392
  self.currentSize = (self.currentSize +
118
- self.decodeSize(leaf.text(CTYPE_FIELD)))
119
- if self.currentSize > 26:
120
- self.packetSize.setMaximum(self.currentSize / 26.0 * 100.0)
393
+ int(leaf.text(SIZE_FIELD)))
394
+
395
+ self.packetSizeText.setText('%s/%s bytes' % (self.currentSize,
396
+ MAX_LOG_SIZE))
397
+
398
+ if self.currentSize > MAX_LOG_SIZE:
399
+ self.packetSize.setMaximum(self.currentSize / MAX_LOG_SIZE * 100)
121
400
  self.packetSize.setFormat("%v%")
122
- self.packetSize.setValue(self.currentSize / 26.0 * 100.0)
401
+ self.packetSize.setValue(int(self.currentSize / MAX_LOG_SIZE * 100))
402
+ self.packetSize.setStyleSheet(
403
+ UiUtils.progressbar_stylesheet('red'))
123
404
  else:
124
- self.packetSize.setMaximum(26)
405
+ self.packetSize.setMaximum(MAX_LOG_SIZE)
125
406
  self.packetSize.setFormat("%p%")
126
- self.packetSize.setValue(self.currentSize)
407
+ self.packetSize.setValue(int(self.currentSize))
408
+ self.packetSize.setStyleSheet(
409
+ UiUtils.progressbar_stylesheet(UiUtils.COLOR_GREEN))
127
410
 
128
411
  def addNewVar(self, logTreeItem, target):
129
412
  parentName = logTreeItem.parent().text(NAME_FIELD)
130
- varParent = target.findItems(parentName, Qt.MatchExactly, NAME_FIELD)
413
+ varParent = target.findItems(parentName, Qt.MatchFlag.MatchExactly, NAME_FIELD)
131
414
 
132
415
  item = logTreeItem.clone()
133
416
 
134
417
  if (len(varParent) == 0):
135
418
  newParent = QtWidgets.QTreeWidgetItem()
136
- newParent.setData(0, Qt.DisplayRole, parentName)
419
+ newParent.setData(0, Qt.ItemDataRole.DisplayRole, parentName)
137
420
  newParent.addChild(item)
138
421
  target.addTopLevelItem(newParent)
139
422
  target.expandItem(newParent)
@@ -168,7 +451,7 @@ class LogConfigDialogue(QtWidgets.QWidget, logconfig_widget_class):
168
451
  self.moveNodeItem(source, target, source.currentItem())
169
452
 
170
453
  def moveNodeByName(self, source, target, parentName, itemName):
171
- parents = source.findItems(parentName, Qt.MatchExactly, NAME_FIELD)
454
+ parents = source.findItems(parentName, Qt.MatchFlag.MatchExactly, NAME_FIELD)
172
455
  node = None
173
456
  if (len(parents) > 0):
174
457
  parent = parents[0]
@@ -182,55 +465,43 @@ class LogConfigDialogue(QtWidgets.QWidget, logconfig_widget_class):
182
465
  return False
183
466
 
184
467
  def showEvent(self, event):
185
- self.updateToc()
186
- self.populateDropDown()
187
- toc = self.helper.cf.log.toc
188
- if (len(list(toc.toc.keys())) > 0):
189
- self.configNameCombo.setEnabled(True)
190
- else:
191
- self.configNameCombo.setEnabled(False)
192
-
193
- def resetTrees(self):
194
- self.varTree.clear()
195
- self.updateToc()
468
+ self._clear_trees_and_progressbar()
469
+ self._load_saved_configs()
196
470
 
197
471
  def periodChanged(self, value):
198
472
  try:
199
473
  self.period = int(value)
200
474
  self.checkAndEnableSaveButton()
201
- except:
475
+ except Exception:
202
476
  self.period = 0
203
477
 
204
478
  def showErrorPopup(self, caption, message):
205
- self.box = QMessageBox() # noqa
479
+ self.box = QtWidgets.QMessageBox() # noqa
206
480
  self.box.setWindowTitle(caption)
207
481
  self.box.setText(message)
208
482
  # self.box.setButtonText(1, "Ok")
209
- self.box.setWindowFlags(Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)
483
+ self.box.setWindowFlags(Qt.WindowType.Dialog | Qt.WindowType.MSWindowsFixedSizeDialogHint)
210
484
  self.box.show()
211
485
 
212
486
  def updateToc(self):
213
487
  self.logTree.clear()
214
-
215
488
  toc = self.helper.cf.log.toc
216
489
 
217
490
  for group in list(toc.toc.keys()):
218
491
  groupItem = QtWidgets.QTreeWidgetItem()
219
- groupItem.setData(NAME_FIELD, Qt.DisplayRole, group)
492
+ groupItem.setData(NAME_FIELD, Qt.ItemDataRole.DisplayRole, group)
220
493
  for param in list(toc.toc[group].keys()):
221
494
  item = QtWidgets.QTreeWidgetItem()
222
- item.setData(NAME_FIELD, Qt.DisplayRole, param)
223
- item.setData(ID_FIELD, Qt.DisplayRole,
495
+ item.setData(NAME_FIELD, Qt.ItemDataRole.DisplayRole, param)
496
+ item.setData(ID_FIELD, Qt.ItemDataRole.DisplayRole,
224
497
  toc.toc[group][param].ident)
225
- item.setData(PTYPE_FIELD, Qt.DisplayRole,
226
- toc.toc[group][param].pytype)
227
- item.setData(CTYPE_FIELD, Qt.DisplayRole,
498
+ item.setData(TYPE_FIELD, Qt.ItemDataRole.DisplayRole,
228
499
  toc.toc[group][param].ctype)
500
+ item.setData(SIZE_FIELD, Qt.ItemDataRole.DisplayRole,
501
+ struct.calcsize(toc.toc[group][param].pytype))
229
502
  groupItem.addChild(item)
230
503
 
231
504
  self.logTree.addTopLevelItem(groupItem)
232
- self.logTree.expandItem(groupItem)
233
- self.sortTrees()
234
505
 
235
506
  def populateDropDown(self):
236
507
  self.configNameCombo.clear()
@@ -240,48 +511,77 @@ class LogConfigDialogue(QtWidgets.QWidget, logconfig_widget_class):
240
511
  if (len(toc) > 0):
241
512
  self.loadButton.setEnabled(True)
242
513
 
243
- def loadConfig(self):
244
- cText = self.configNameCombo.currentText()
245
- config = None
246
- for d in self.helper.logConfigReader.getLogConfigs():
247
- if (d.name == cText):
248
- config = d
249
- if (config is None):
250
- logger.warning("Could not load config")
251
- else:
252
- self.resetTrees()
253
- self.loggingPeriod.setText("%d" % config.period_in_ms)
254
- self.period = config.period_in_ms
255
- for v in config.variables:
256
- if (v.is_toc_variable()):
257
- parts = v.name.split(".")
258
- varParent = parts[0]
259
- varName = parts[1]
260
- if self.moveNodeByName(
261
- self.logTree, self.varTree, varParent,
262
- varName) is False:
263
- logger.warning("Could not find node %s.%s!!",
264
- varParent, varName)
265
- else:
266
- logger.warning("Error: Mem vars not supported!")
267
-
268
514
  def saveConfig(self):
269
- updatedConfig = self.createConfigFromSelection()
270
- try:
271
- self.helper.logConfigReader.saveLogConfigFile(updatedConfig)
272
- self.close()
273
- except Exception as e:
274
- self.showErrorPopup("Error when saving file", "Error: %s" % e)
515
+ items = self.categoryTree.selectedItems()
516
+
517
+ if items:
518
+ config = items[0]
519
+ parent = config.parent()
520
+
521
+ if parent:
522
+
523
+ # If we're just editing an existing config, we'll delete
524
+ # the old one first.
525
+ self._delete_from_plottab(self._last_pressed_item[1])
526
+
527
+ category = parent.text(NAME_FIELD)
528
+ config_name = config.text(NAME_FIELD)
529
+ updatedConfig = self.createConfigFromSelection(config_name)
530
+
531
+ if category != 'Default':
532
+ plot_tab_name = '%s/%s' % (category, config_name)
533
+ else:
534
+ plot_tab_name = config_name
535
+
536
+ try:
537
+ self.helper.logConfigReader.saveLogConfigFile(
538
+ category,
539
+ updatedConfig)
540
+ self.statusText.setText('Log config succesfully saved!')
541
+ self._config_saved_timer.start(4000)
542
+ if self.closeOnSave.isChecked():
543
+ self.close()
544
+
545
+ except Exception as e:
546
+ self.showErrorPopup("Error when saving file",
547
+ "Error: %s" % e)
548
+
549
+ # The name of the config is changed due to displaying
550
+ # it as category/config-name in the plotter-tab.
551
+ # The config is however saved with only the config-name.
552
+ updatedConfig.name = plot_tab_name
275
553
  self.helper.cf.log.add_config(updatedConfig)
276
554
 
277
- def createConfigFromSelection(self):
278
- logconfig = LogConfig(str(self.configNameCombo.currentText()),
279
- self.period)
280
- for node in self.getNodeChildren(self.varTree.invisibleRootItem()):
555
+ def _parse_configname(self, config):
556
+ """ If the configs are placed in a category,
557
+ they are named as Category/confname.
558
+ """
559
+ parts = config.name.split('/')
560
+ return parts[1] if len(parts) > 1 else parts[0]
561
+
562
+ def _delete_from_plottab(self, conf_name):
563
+ """ Removes a config from the plot-tab. """
564
+ for logconfig in self.helper.cf.log.log_blocks:
565
+ config_to_delete = self._parse_configname(logconfig)
566
+ if config_to_delete == conf_name:
567
+ self.helper.plotTab.remove_config(logconfig)
568
+ self.helper.cf.log.log_blocks.remove(logconfig)
569
+ logconfig.delete()
570
+
571
+ def _get_node_children(self):
572
+ root_item = self.varTree.invisibleRootItem()
573
+ return [root_item.child(i) for i in range(root_item.childCount())]
574
+
575
+ def createConfigFromSelection(self, config):
576
+ logconfig = LogConfig(config, self.period)
577
+
578
+ for node in self._get_node_children():
281
579
  parentName = node.text(NAME_FIELD)
580
+
282
581
  for leaf in self.getNodeChildren(node):
283
582
  varName = leaf.text(NAME_FIELD)
284
- varType = str(leaf.text(CTYPE_FIELD))
583
+ varType = str(leaf.text(TYPE_FIELD))
285
584
  completeName = "%s.%s" % (parentName, varName)
286
585
  logconfig.add_variable(completeName, varType)
586
+
287
587
  return logconfig