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
  #
@@ -31,15 +31,15 @@ pre-configured.
31
31
  import logging
32
32
 
33
33
  import cfclient
34
- from PyQt5.QtCore import pyqtSignal
35
- from PyQt5.QtWidgets import QMessageBox
36
- from cfclient.ui.tab import Tab
34
+ from PyQt6.QtCore import pyqtSignal
35
+ from PyQt6.QtWidgets import QMessageBox
36
+ from cfclient.ui.tab_toolbox import TabToolbox
37
37
  from cflib.crazyflie.log import LogConfig
38
- from PyQt5 import QtCore
39
- from PyQt5 import QtGui
40
- from PyQt5 import QtNetwork
41
- from PyQt5 import QtWebKit
42
- from PyQt5 import uic
38
+ from PyQt6 import QtCore
39
+ from PyQt6 import QtGui
40
+ from PyQt6 import QtNetwork
41
+ from PyQt6 import QtWebKit
42
+ from PyQt6 import uic
43
43
 
44
44
  __author__ = 'Bitcraze AB'
45
45
  __all__ = ['GpsTab']
@@ -50,7 +50,7 @@ gps_tab_class = uic.loadUiType(cfclient.module_path +
50
50
  "/ui/tabs/gpsTab.ui")[0]
51
51
 
52
52
 
53
- class GpsTab(Tab, gps_tab_class):
53
+ class GpsTab(TabToolbox, gps_tab_class):
54
54
  """Tab for plotting logging data"""
55
55
 
56
56
  _log_data_signal = pyqtSignal(int, object, object)
@@ -60,15 +60,9 @@ class GpsTab(Tab, gps_tab_class):
60
60
  _connected_signal = pyqtSignal(str)
61
61
  _console_signal = pyqtSignal(str)
62
62
 
63
- def __init__(self, tabWidget, helper, *args):
64
- super(GpsTab, self).__init__(*args)
63
+ def __init__(self, helper):
64
+ super(GpsTab, self).__init__(helper, 'GPS')
65
65
  self.setupUi(self)
66
-
67
- self.tabName = "GPS"
68
- self.menuName = "GPS"
69
-
70
- self.tabWidget = tabWidget
71
- self.helper = helper
72
66
  self._cf = helper.cf
73
67
 
74
68
  view = self.view = QtWebKit.QWebView()
@@ -95,9 +89,9 @@ class GpsTab(Tab, gps_tab_class):
95
89
  self._disconnected_signal.connect(self._disconnected)
96
90
 
97
91
  # Connect the callbacks from the Crazyflie API
98
- self.helper.cf.disconnected.add_callback(
92
+ self._helper.cf.disconnected.add_callback(
99
93
  self._disconnected_signal.emit)
100
- self.helper.cf.connected.add_callback(
94
+ self._helper.cf.connected.add_callback(
101
95
  self._connected_signal.emit)
102
96
 
103
97
  self._max_speed = 0.0
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ # || ____ _ __
5
+ # +------+ / __ )(_) /_______________ _____ ___
6
+ # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \
7
+ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
8
+ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
9
+ #
10
+ # Copyright (C) 2011-2025 Bitcraze AB
11
+ #
12
+ # Crazyflie Nano Quadcopter Client
13
+ #
14
+ # This program is free software; you can redistribute it and/or
15
+ # modify it under the terms of the GNU General Public License
16
+ # as published by the Free Software Foundation; either version 2
17
+ # of the License, or (at your option) any later version.
18
+ #
19
+ # This program is distributed in the hope that it will be useful,
20
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
+ # GNU General Public License for more details.
23
+
24
+ # You should have received a copy of the GNU General Public License
25
+ # along with this program; if not, write to the Free Software
26
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27
+ # 02110-1301, USA.
28
+
29
+ """
30
+ Basic tab to be able to set (and test) colors in the LED-ring.
31
+ """
32
+
33
+ import logging
34
+
35
+ from PyQt6 import QtGui, uic
36
+ from PyQt6.QtCore import pyqtSignal
37
+ from PyQt6 import QtWidgets
38
+
39
+ import cfclient
40
+ from cfclient.ui.tab_toolbox import TabToolbox
41
+
42
+ from cflib.crazyflie.mem import MemoryElement
43
+
44
+ __author__ = 'Bitcraze AB'
45
+ __all__ = ['LEDRingTab']
46
+
47
+ logger = logging.getLogger(__name__)
48
+
49
+ led_ring_tab_class = uic.loadUiType(cfclient.module_path + "/ui/tabs/ledRingTab.ui")[0]
50
+
51
+
52
+ class LEDRingTab(TabToolbox, led_ring_tab_class):
53
+ """Tab for plotting logging data"""
54
+
55
+ _connected_signal = pyqtSignal(str)
56
+ _disconnected_signal = pyqtSignal(str)
57
+
58
+ def __init__(self, helper):
59
+ super(LEDRingTab, self).__init__(helper, 'LED Ring')
60
+ self.setupUi(self)
61
+
62
+ # LED-ring effect dropdown and headlight checkbox
63
+ self._ledring_nbr_effects = 0
64
+
65
+ # Connect the headlight checkbox
66
+ self._led_ring_headlight.clicked.connect(
67
+ lambda enabled: self._helper.cf.param.set_value("ring.headlightEnable", int(enabled)))
68
+
69
+ # Update headlight when param changes
70
+ self._helper.cf.param.add_update_callback(
71
+ group="ring", name="headlightEnable",
72
+ cb=lambda name, checked: self._led_ring_headlight.setChecked(bool(int(checked))))
73
+
74
+ # Update LED-ring effect when param changes
75
+ self._helper.cf.param.add_update_callback(
76
+ group="ring", name="effect",
77
+ cb=self._ring_effect_updated)
78
+
79
+ # Populate dropdown when all params are updated
80
+ self._helper.cf.param.all_updated.add_callback(self._ring_populate_dropdown)
81
+
82
+ # Always wrap callbacks from Crazyflie API though QT Signal/Slots
83
+ # to avoid manipulating the UI when rendering it
84
+ self._connected_signal.connect(self._connected)
85
+ self._disconnected_signal.connect(self._disconnected)
86
+
87
+ # Connect the Crazyflie API callbacks to the signals
88
+ self._helper.cf.connected.add_callback(
89
+ self._connected_signal.emit)
90
+
91
+ self._helper.cf.disconnected.add_callback(
92
+ self._disconnected_signal.emit)
93
+
94
+ self._btns = [self._u1,
95
+ self._u2,
96
+ self._u3,
97
+ self._u4,
98
+ self._u5,
99
+ self._u6,
100
+ self._u7,
101
+ self._u8,
102
+ self._u9,
103
+ self._u10,
104
+ self._u11,
105
+ self._u12]
106
+
107
+ self._intensity = self._intensity_slider.value()
108
+
109
+ self._u1.clicked.connect(lambda: self._select(0))
110
+ self._u2.clicked.connect(lambda: self._select(1))
111
+ self._u3.clicked.connect(lambda: self._select(2))
112
+ self._u4.clicked.connect(lambda: self._select(3))
113
+ self._u5.clicked.connect(lambda: self._select(4))
114
+ self._u6.clicked.connect(lambda: self._select(5))
115
+ self._u7.clicked.connect(lambda: self._select(6))
116
+ self._u8.clicked.connect(lambda: self._select(7))
117
+ self._u9.clicked.connect(lambda: self._select(8))
118
+ self._u10.clicked.connect(lambda: self._select(9))
119
+ self._u11.clicked.connect(lambda: self._select(10))
120
+ self._u12.clicked.connect(lambda: self._select(11))
121
+
122
+ self._mem = None
123
+
124
+ self._intensity_slider.valueChanged.connect(self._intensity_change)
125
+ self._intensity_slider.valueChanged.connect(
126
+ self._intensity_spin.setValue)
127
+ self._intensity_spin.valueChanged.connect(
128
+ self._intensity_slider.setValue)
129
+
130
+ self._helper.inputDeviceReader.alt1_updated.add_callback(self.alt1_updated)
131
+ self._helper.inputDeviceReader.alt2_updated.add_callback(self.alt2_updated)
132
+
133
+ self._led_ring_effect.setEnabled(False)
134
+ self._led_ring_headlight.setEnabled(False)
135
+
136
+ def _select(self, nbr):
137
+ col = QtGui.QColor()
138
+
139
+ if self._mem:
140
+ led = self._mem.leds[nbr]
141
+ col = QtGui.QColor.fromRgb(led.r, led.g, led.b)
142
+
143
+ col = QtWidgets.QColorDialog.getColor(col)
144
+
145
+ if col.isValid() and self._mem:
146
+ r, g, b = col.red(), col.green(), col.blue()
147
+ self._mem.leds[nbr].set(r=r, g=g, b=b)
148
+
149
+ brightness = 0.299 * r + 0.587 * g + 0.114 * b
150
+ text_color = "white" if brightness < 128 else "black"
151
+
152
+ self.sender().setStyleSheet(
153
+ f"background-color: rgb({r}, {g}, {b}); color: {text_color};"
154
+ )
155
+
156
+ self._write_led_output()
157
+
158
+ def _intensity_change(self, value):
159
+ self._intensity = value
160
+ self._write_led_output()
161
+
162
+ def _write_led_output(self):
163
+ if self._mem:
164
+ for led in self._mem.leds:
165
+ led.intensity = self._intensity
166
+ self._mem.write_data(self._led_write_done)
167
+ else:
168
+ logger.info("No LED-ring memory found!")
169
+
170
+ def _led_write_done(self, mem, addr):
171
+ logger.info("LED write done callback")
172
+
173
+ def _connected(self, link_uri):
174
+ """Callback when the Crazyflie has been connected"""
175
+ mems = self._helper.cf.mem.get_mems(MemoryElement.TYPE_DRIVER_LED)
176
+ if len(mems) > 0:
177
+ self._mem = mems[0]
178
+ logger.info(self._mem)
179
+
180
+ if self._mem:
181
+ for btn in self._btns:
182
+ btn.setEnabled(True)
183
+ btn.setStyleSheet("background-color: black; color: white")
184
+ self._intensity_slider.setEnabled(True)
185
+ self._intensity_spin.setEnabled(True)
186
+
187
+ self._led_ring_effect.setEnabled(True)
188
+ self._led_ring_headlight.setEnabled(True)
189
+
190
+ def _disconnected(self, link_uri):
191
+ """Callback for when the Crazyflie has been disconnected"""
192
+ for btn in self._btns:
193
+ btn.setEnabled(False)
194
+ btn.setStyleSheet("background-color: none")
195
+ self._intensity_slider.setEnabled(False)
196
+ self._intensity_spin.setEnabled(False)
197
+ self._intensity_slider.setValue(100)
198
+
199
+ self._led_ring_effect.setEnabled(False)
200
+ self._led_ring_headlight.setEnabled(False)
201
+
202
+ def _ring_populate_dropdown(self):
203
+ try:
204
+ nbr = int(self._helper.cf.param.values["ring"]["neffect"])
205
+ current = int(self._helper.cf.param.values["ring"]["effect"])
206
+ except KeyError:
207
+ return
208
+
209
+ self._ring_effect = current
210
+ self._ledring_nbr_effects = nbr
211
+
212
+ hardcoded_names = {
213
+ 0: "Off",
214
+ 1: "White spinner",
215
+ 2: "Color spinner",
216
+ 3: "Tilt effect",
217
+ 4: "Brightness effect",
218
+ 5: "Color spinner 2",
219
+ 6: "Double spinner",
220
+ 7: "Solid color effect",
221
+ 8: "Factory test",
222
+ 9: "Battery status",
223
+ 10: "Boat lights",
224
+ 11: "Alert",
225
+ 12: "Gravity",
226
+ 13: "LED tab",
227
+ 14: "Color fader",
228
+ 15: "Link quality",
229
+ 16: "Location server status",
230
+ 17: "Sequencer",
231
+ 18: "Lighthouse quality",
232
+ }
233
+
234
+ self._led_ring_effect.clear()
235
+ for i in range(nbr + 1):
236
+ name = "{}: ".format(i)
237
+ name += hardcoded_names.get(i, "N/A")
238
+ self._led_ring_effect.addItem(name, i)
239
+
240
+ self._led_ring_effect.currentIndexChanged.connect(self._ring_effect_changed)
241
+ self._led_ring_effect.setCurrentIndex(current)
242
+ self._led_ring_effect.setEnabled(int(self._helper.cf.param.values["deck"]["bcLedRing"]) == 1)
243
+ self._led_ring_headlight.setEnabled(int(self._helper.cf.param.values["deck"]["bcLedRing"]) == 1)
244
+
245
+ try:
246
+ self._helper.cf.param.set_value("ring.effect", "13")
247
+ self._led_ring_effect.setCurrentIndex(13)
248
+ self._ring_effect = 13
249
+ logger.info("Initialized LED ring to 'LED tab' mode (effect 13).")
250
+ except Exception as e:
251
+ logger.warning(f"Could not set LED tab effect on connect: {e}")
252
+
253
+ def _ring_effect_changed(self, index):
254
+ self._ring_effect = index
255
+ if index > -1:
256
+ i = self._led_ring_effect.itemData(index)
257
+ if i != int(self._helper.cf.param.values["ring"]["effect"]):
258
+ self._helper.cf.param.set_value("ring.effect", str(i))
259
+
260
+ if i == 13:
261
+ self._intensity_slider.setEnabled(True)
262
+ self._intensity_spin.setEnabled(True)
263
+ else:
264
+ self._intensity_slider.setEnabled(False)
265
+ self._intensity_spin.setEnabled(False)
266
+
267
+ def _ring_effect_updated(self, name, value):
268
+ if self._helper.cf.param.is_updated:
269
+ self._led_ring_effect.setCurrentIndex(int(value))
270
+
271
+ def alt1_updated(self, state):
272
+ if state:
273
+ new_index = (self._ring_effect+1) % (self._ledring_nbr_effects+1)
274
+ self._helper.cf.param.set_value("ring.effect", str(new_index))
275
+
276
+ def alt2_updated(self, state):
277
+ self._helper.cf.param.set_value("ring.headlightEnable", str(state))
@@ -7,7 +7,7 @@
7
7
  # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
8
8
  # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
9
9
  #
10
- # Copyright (C) 2013 Bitcraze AB
10
+ # Copyright (C) 2013-2023 Bitcraze AB
11
11
  #
12
12
  # Crazyflie Nano Quadcopter Client
13
13
  #
@@ -30,20 +30,19 @@ Shows all the parameters available in the Crazyflie and also gives the ability
30
30
  to edit them.
31
31
  """
32
32
 
33
- from PyQt5 import QtCore, QtWidgets, uic
34
- from PyQt5.QtCore import Qt, pyqtSignal
33
+ from PyQt6 import QtWidgets, uic
34
+ from PyQt6.QtCore import Qt, pyqtSignal
35
35
 
36
36
  import cfclient
37
- from cfclient.ui.tab import Tab
37
+ from cfclient.ui.tab_toolbox import TabToolbox
38
38
 
39
39
  __author__ = 'Bitcraze AB'
40
40
  __all__ = ['LogBlockDebugTab']
41
41
 
42
- logblock_tab_class = uic.loadUiType(cfclient.module_path +
43
- "/ui/tabs/logBlockDebugTab.ui")[0]
42
+ logblock_tab_class = uic.loadUiType(cfclient.module_path + "/ui/tabs/logBlockDebugTab.ui")[0]
44
43
 
45
44
 
46
- class LogBlockDebugTab(Tab, logblock_tab_class):
45
+ class LogBlockDebugTab(TabToolbox, logblock_tab_class):
47
46
  """
48
47
  Used to show debug-information about log status.
49
48
  """
@@ -51,16 +50,10 @@ class LogBlockDebugTab(Tab, logblock_tab_class):
51
50
  _blocks_updated_signal = pyqtSignal(object, bool)
52
51
  _disconnected_signal = pyqtSignal(str)
53
52
 
54
- def __init__(self, tabWidget, helper, *args):
55
- super(LogBlockDebugTab, self).__init__(*args)
53
+ def __init__(self, helper):
54
+ super(LogBlockDebugTab, self).__init__(helper, 'Log Blocks Debugging')
56
55
  self.setupUi(self)
57
56
 
58
- self.tabName = "Log Blocks Debugging"
59
- self.menuName = "Log Blocks Debugging"
60
-
61
- self._helper = helper
62
- self.tabWidget = tabWidget
63
-
64
57
  self._helper.cf.log.block_added_cb.add_callback(self._block_added)
65
58
  self._disconnected_signal.connect(self._disconnected)
66
59
  self._helper.cf.disconnected.add_callback(
@@ -70,7 +63,7 @@ class LogBlockDebugTab(Tab, logblock_tab_class):
70
63
  self._block_tree.setHeaderLabels(
71
64
  ['Id', 'Name', 'Period (ms)', 'Added', 'Started', 'Error',
72
65
  'Contents'])
73
- self._block_tree.sortItems(0, QtCore.Qt.AscendingOrder)
66
+ self._block_tree.sortItems(0, Qt.SortOrder.AscendingOrder)
74
67
 
75
68
  def _block_added(self, block):
76
69
  """Callback when a new logblock has been created"""
@@ -82,19 +75,19 @@ class LogBlockDebugTab(Tab, logblock_tab_class):
82
75
  self._block_tree.clear()
83
76
  for block in self._helper.cf.log.log_blocks:
84
77
  item = QtWidgets.QTreeWidgetItem()
85
- item.setFlags(Qt.ItemIsEnabled |
86
- Qt.ItemIsSelectable)
87
- item.setData(0, Qt.DisplayRole, block.id)
88
- item.setData(1, Qt.EditRole, block.name)
89
- item.setData(2, Qt.DisplayRole, (block.period_in_ms))
90
- item.setData(3, Qt.DisplayRole, block.added)
91
- item.setData(4, Qt.EditRole, block.started)
92
- item.setData(5, Qt.EditRole, block.err_no)
78
+ item.setFlags(Qt.ItemFlag.ItemIsEnabled |
79
+ Qt.ItemFlag.ItemIsSelectable)
80
+ item.setData(0, Qt.ItemDataRole.DisplayRole, block.id)
81
+ item.setData(1, Qt.ItemDataRole.EditRole, block.name)
82
+ item.setData(2, Qt.ItemDataRole.DisplayRole, (block.period_in_ms))
83
+ item.setData(3, Qt.ItemDataRole.DisplayRole, block.added)
84
+ item.setData(4, Qt.ItemDataRole.EditRole, block.started)
85
+ item.setData(5, Qt.ItemDataRole.EditRole, block.err_no)
93
86
  for var in block.variables:
94
87
  subItem = QtWidgets.QTreeWidgetItem()
95
- subItem.setFlags(Qt.ItemIsEnabled |
96
- Qt.ItemIsSelectable)
97
- subItem.setData(6, Qt.EditRole, var.name)
88
+ subItem.setFlags(Qt.ItemFlag.ItemIsEnabled |
89
+ Qt.ItemFlag.ItemIsSelectable)
90
+ subItem.setData(6, Qt.ItemDataRole.EditRole, var.name)
98
91
  item.addChild(subItem)
99
92
 
100
93
  self._block_tree.addTopLevelItem(item)
@@ -7,7 +7,7 @@
7
7
  # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
8
8
  # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
9
9
  #
10
- # Copyright (C) 2013 Bitcraze AB
10
+ # Copyright (C) 2013-2023 Bitcraze AB
11
11
  #
12
12
  # Crazyflie Nano Quadcopter Client
13
13
  #
@@ -31,25 +31,24 @@ This tab shows all log blocks that are registered and can be used to start the
31
31
  logging and also to write the logging data to file.
32
32
  """
33
33
 
34
- from PyQt5 import uic
35
- from PyQt5.QtCore import Qt, pyqtSignal
34
+ from PyQt6 import uic
35
+ from PyQt6.QtCore import Qt, pyqtSignal
36
36
 
37
37
  import cfclient
38
- from cfclient.ui.tab import Tab
38
+ from cfclient.ui.tab_toolbox import TabToolbox
39
39
 
40
40
  import logging
41
41
 
42
- from PyQt5.QtWidgets import QApplication, QStyledItemDelegate
43
- from PyQt5.QtWidgets import QAbstractItemView, QStyleOptionButton, QStyle
44
- from PyQt5.QtCore import QAbstractItemModel, QModelIndex
42
+ from PyQt6.QtWidgets import QApplication, QStyledItemDelegate
43
+ from PyQt6.QtWidgets import QAbstractItemView, QStyleOptionButton, QStyle
44
+ from PyQt6.QtCore import QAbstractItemModel, QModelIndex
45
45
 
46
46
  from cfclient.utils.logdatawriter import LogWriter
47
47
 
48
48
  __author__ = 'Bitcraze AB'
49
49
  __all__ = ['LogBlockTab']
50
50
 
51
- logblock_tab_class = uic.loadUiType(
52
- cfclient.module_path + "/ui/tabs/logBlockTab.ui")[0]
51
+ logblock_tab_class = uic.loadUiType(cfclient.module_path + "/ui/tabs/logBlockTab.ui")[0]
53
52
 
54
53
  logger = logging.getLogger(__name__)
55
54
 
@@ -176,6 +175,7 @@ class LogBlockModel(QAbstractItemModel):
176
175
  def add_block(self, block, connected_ts):
177
176
  self._nodes.append(LogBlockItem(block, self, connected_ts))
178
177
  self.layoutChanged.emit()
178
+ self._nodes.sort(key=lambda conf: conf.name.lower())
179
179
 
180
180
  def refresh(self):
181
181
  """Force a refresh of the view though the model"""
@@ -220,7 +220,7 @@ class LogBlockModel(QAbstractItemModel):
220
220
 
221
221
  def headerData(self, section, orientation, role):
222
222
  """Re-implemented method to get the headers"""
223
- if role == Qt.DisplayRole:
223
+ if role == Qt.ItemDataRole.DisplayRole:
224
224
  return self._column_headers[section]
225
225
 
226
226
  def rowCount(self, parent):
@@ -250,20 +250,20 @@ class LogBlockModel(QAbstractItemModel):
250
250
  node = index.internalPointer()
251
251
  parent = node.parent
252
252
  if parent:
253
- if role == Qt.DisplayRole and index.column() == 5:
253
+ if role == Qt.ItemDataRole.DisplayRole and index.column() == 5:
254
254
  return node.name
255
- elif not parent and role == Qt.DisplayRole and index.column() == 5:
255
+ elif not parent and role == Qt.ItemDataRole.DisplayRole and index.column() == 5:
256
256
  return node.var_list()
257
- elif not parent and role == Qt.DisplayRole:
257
+ elif not parent and role == Qt.ItemDataRole.DisplayRole:
258
258
  if index.column() == 0:
259
259
  return node.id
260
260
  if index.column() == 1:
261
261
  return node.name
262
262
  if index.column() == 2:
263
263
  return str(node.period)
264
- if role == Qt.TextAlignmentRole and \
264
+ if role == Qt.ItemDataRole.TextAlignmentRole and \
265
265
  (index.column() == 4 or index.column() == 3):
266
- return Qt.AlignHCenter | Qt.AlignVCenter
266
+ return Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter
267
267
 
268
268
  return None
269
269
 
@@ -286,31 +286,37 @@ class CheckboxDelegate(QStyledItemDelegate):
286
286
  col = index.column()
287
287
  if not item.parent and (col == 3 or col == 4):
288
288
  s = QStyleOptionButton()
289
- checkbox_rect = QApplication.style(). \
290
- subElementRect(QStyle.SE_CheckBoxIndicator, option)
289
+ checkbox_rect = QApplication.style().subElementRect(QStyle.SubElement.SE_CheckBoxIndicator, option)
291
290
  s.rect = option.rect
292
- center_offset = s.rect.width() / 2 - checkbox_rect.width() / 2
291
+ center_offset = int(s.rect.width() / 2 - checkbox_rect.width() / 2)
293
292
  s.rect.adjust(center_offset, 0, 0, 0)
294
293
 
295
294
  if col == 3:
296
295
  if not item.doing_transaction():
297
- s.state = QStyle.State_Enabled
296
+ s.state = QStyle.StateFlag.State_Enabled
298
297
  if item.logging_started():
299
- s.state |= QStyle.State_On
298
+ s.state |= QStyle.StateFlag.State_On
300
299
 
301
300
  if col == 4:
302
- s.state = QStyle.State_Enabled
301
+ s.state = QStyle.StateFlag.State_Enabled
303
302
  if item.writing_to_file():
304
- s.state |= QStyle.State_On
305
-
306
- QApplication.style().drawControl(
307
- QStyle.CE_CheckBox, s, painter)
303
+ s.state |= QStyle.StateFlag.State_On
308
304
 
305
+ self._paint_checkbox(s, painter)
309
306
  else:
310
307
  super(CheckboxDelegate, self).paint(painter, option, index)
311
308
 
309
+ def _paint_checkbox(self, style, painter):
310
+ QApplication.style().drawControl(QStyle.ControlElement.CE_CheckBox, style, painter)
311
+
312
+ def _paint_checkbox_osx_workaround(self, style, painter):
313
+ painter.save()
314
+ painter.translate(style.rect.topLeft())
315
+ QApplication.style().drawControl(QStyle.ControlElement.CE_CheckBox, style, painter)
316
+ painter.restore()
317
+
312
318
 
313
- class LogBlockTab(Tab, logblock_tab_class):
319
+ class LogBlockTab(TabToolbox, logblock_tab_class):
314
320
  """
315
321
  Used to show debug-information about logblock status.
316
322
  """
@@ -318,17 +324,11 @@ class LogBlockTab(Tab, logblock_tab_class):
318
324
  _blocks_updated_signal = pyqtSignal(bool)
319
325
  _disconnected_signal = pyqtSignal(str)
320
326
 
321
- def __init__(self, tabWidget, helper, *args):
327
+ def __init__(self, helper):
322
328
  """Initialize the tab"""
323
- super(LogBlockTab, self).__init__(*args)
329
+ super(LogBlockTab, self).__init__(helper, 'Log Blocks')
324
330
  self.setupUi(self)
325
331
 
326
- self.tabName = "Log Blocks"
327
- self.menuName = "Log Blocks"
328
-
329
- self._helper = helper
330
- self.tabWidget = tabWidget
331
-
332
332
  self._helper.cf.log.block_added_cb.add_callback(self._block_added)
333
333
  self._disconnected_signal.connect(self._disconnected)
334
334
  self._helper.cf.disconnected.add_callback(
@@ -338,7 +338,7 @@ class LogBlockTab(Tab, logblock_tab_class):
338
338
  self._block_tree.setModel(self._model)
339
339
  self._block_tree.clicked.connect(self._model.clicked)
340
340
  self._block_tree.setItemDelegate(CheckboxDelegate())
341
- self._block_tree.setSelectionMode(QAbstractItemView.NoSelection)
341
+ self._block_tree.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
342
342
 
343
343
  def _block_added(self, block):
344
344
  """Callback from logging layer when a new block is added"""
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ # || ____ _ __
5
+ # +------+ / __ )(_) /_______________ _____ ___
6
+ # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \
7
+ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
8
+ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
9
+ #
10
+ # Copyright (C) 2011-2023 Bitcraze AB
11
+ #
12
+ # Crazyflie Nano Quadcopter Client
13
+ #
14
+ # This program is free software; you can redistribute it and/or
15
+ # modify it under the terms of the GNU General Public License
16
+ # as published by the Free Software Foundation; either version 2
17
+ # of the License, or (at your option) any later version.
18
+ #
19
+ # This program is distributed in the hope that it will be useful,
20
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
+ # GNU General Public License for more details.
23
+
24
+ # You should have received a copy of the GNU General Public License along with
25
+ # this program; if not, write to the Free Software Foundation, Inc.,
26
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27
+
28
+ """
29
+ Shows information from the Python logging framework
30
+ """
31
+
32
+ import logging
33
+
34
+ from PyQt6 import uic
35
+ from PyQt6.QtCore import pyqtSignal
36
+
37
+ import cfclient
38
+ from cfclient.ui.tab_toolbox import TabToolbox
39
+
40
+ __author__ = 'Bitcraze AB'
41
+ __all__ = ['LogClientTab']
42
+
43
+ logger = logging.getLogger(__name__)
44
+
45
+ log_client_tab_class = uic.loadUiType(cfclient.module_path + "/ui/tabs/logClientTab.ui")[0]
46
+
47
+
48
+ class LogHandler(logging.StreamHandler):
49
+ def __init__(self, signal):
50
+ logging.StreamHandler.__init__(self)
51
+ self._signal = signal
52
+
53
+ def emit(self, record):
54
+ fmt = '%(levelname)s:%(name)s:%(message)s'
55
+ formatter = logging.Formatter(fmt)
56
+ #
57
+ # Calling .emit() on the signal will make the callback
58
+ # we registered in LogClientTab run.
59
+ #
60
+ self._signal.emit(formatter.format(record))
61
+
62
+
63
+ class LogClientTab(TabToolbox, log_client_tab_class):
64
+ """
65
+ A tab for showing client logging information, such
66
+ as USB Gamepad connections or scan feedback.
67
+ """
68
+ _update = pyqtSignal(str)
69
+
70
+ def __init__(self, helper):
71
+ super(LogClientTab, self).__init__(helper, 'Log Client')
72
+ self.setupUi(self)
73
+
74
+ self._update.connect(self.printText)
75
+ self._clearButton.clicked.connect(self.clear)
76
+
77
+ cflogger = logging.getLogger(None)
78
+ cflogger.addHandler(LogHandler(self._update))
79
+
80
+ def printText(self, text):
81
+ logger.debug("[%s]", text)
82
+ self.syslog.insertPlainText(text + '\n')
83
+
84
+ def clear(self):
85
+ self.syslog.clear()