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
cfclient/ui/widgets/ai.py CHANGED
@@ -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,8 +31,9 @@ Attitude indicator widget.
31
31
 
32
32
  import sys
33
33
 
34
- from PyQt5 import QtGui
35
- from PyQt5 import QtWidgets, QtCore
34
+ from PyQt6 import QtGui
35
+ from PyQt6 import QtWidgets
36
+ from PyQt6.QtCore import Qt
36
37
 
37
38
  __author__ = 'Bitcraze AB'
38
39
  __all__ = ['AttitudeIndicator']
@@ -95,81 +96,78 @@ class AttitudeIndicator(QtWidgets.QWidget):
95
96
  qp.rotate(self.roll)
96
97
  qp.translate(0, (self.pitch * h) / 50)
97
98
  qp.translate(-w / 2, -h / 2)
98
- qp.setRenderHint(qp.Antialiasing)
99
+ qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing)
99
100
 
100
- font = QtGui.QFont('Serif', 7, QtGui.QFont.Light)
101
+ font = QtGui.QFont('Serif', 7, QtGui.QFont.Weight.Light)
101
102
  qp.setFont(font)
102
103
 
103
104
  # Draw the blue
104
105
  qp.setPen(QtGui.QColor(0, 61, 144))
105
106
  qp.setBrush(QtGui.QColor(0, 61, 144))
106
- qp.drawRect(-w, h / 2, 3 * w, -3 * h)
107
+ qp.drawRect(-w, int(h / 2), 3 * w, -3 * h)
107
108
 
108
109
  # Draw the marron
109
110
  qp.setPen(QtGui.QColor(59, 41, 39))
110
111
  qp.setBrush(QtGui.QColor(59, 41, 39))
111
- qp.drawRect(-w, h / 2, 3 * w, 3 * h)
112
+ qp.drawRect(-w, int(h / 2), 3 * w, 3 * h)
112
113
 
113
- pen = QtGui.QPen(QtGui.QColor(255, 255, 255), 1.5,
114
- QtCore.Qt.SolidLine)
114
+ pen = QtGui.QPen(QtGui.QColor(255, 255, 255), 1.5, Qt.PenStyle.SolidLine)
115
115
  qp.setPen(pen)
116
- qp.drawLine(-w, h / 2, 3 * w, h / 2)
116
+ qp.drawLine(-w, int(h / 2), 3 * w, int(h / 2))
117
117
 
118
118
  # Drawing pitch lines
119
119
  for ofset in [-180, 0, 180]:
120
120
  for i in range(-900, 900, 25):
121
- pos = (((i / 10.0) + 25 + ofset) * h / 50.0)
121
+ pos = int((((i / 10.0) + 25 + ofset) * h / 50.0))
122
122
  if i % 100 == 0:
123
123
  length = 0.35 * w
124
124
  if i != 0:
125
125
  if ofset == 0:
126
- qp.drawText((w / 2) + (length / 2) + (w * 0.06),
126
+ qp.drawText(int((w / 2) + (length / 2) + (w * 0.06)),
127
127
  pos, "{}".format(-i / 10))
128
- qp.drawText((w / 2) - (length / 2) - (w * 0.08),
128
+ qp.drawText(int((w / 2) - (length / 2) - (w * 0.08)),
129
129
  pos, "{}".format(-i / 10))
130
130
  else:
131
- qp.drawText((w / 2) + (length / 2) + (w * 0.06),
131
+ qp.drawText(int((w / 2) + (length / 2) + (w * 0.06)),
132
132
  pos, "{}".format(i / 10))
133
- qp.drawText((w / 2) - (length / 2) - (w * 0.08),
133
+ qp.drawText(int((w / 2) - (length / 2) - (w * 0.08)),
134
134
  pos, "{}".format(i / 10))
135
135
  elif i % 50 == 0:
136
136
  length = 0.2 * w
137
137
  else:
138
138
  length = 0.1 * w
139
139
 
140
- qp.drawLine((w / 2) - (length / 2), pos,
141
- (w / 2) + (length / 2), pos)
140
+ qp.drawLine(int((w / 2) - (length / 2)), pos,
141
+ int((w / 2) + (length / 2)), pos)
142
142
 
143
143
  qp.setWorldMatrixEnabled(False)
144
144
 
145
- pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2,
146
- QtCore.Qt.SolidLine)
145
+ pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2, Qt.PenStyle.SolidLine)
147
146
  qp.setBrush(QtGui.QColor(0, 0, 0))
148
147
  qp.setPen(pen)
149
- qp.drawLine(0, h / 2, w, h / 2)
148
+ qp.drawLine(0, int(h / 2), w, int(h / 2))
150
149
 
151
150
  # Draw Hover vs Target
152
151
 
153
152
  qp.setWorldMatrixEnabled(False)
154
153
 
155
- pen = QtGui.QPen(QtGui.QColor(255, 255, 255), 2,
156
- QtCore.Qt.SolidLine)
154
+ pen = QtGui.QPen(QtGui.QColor(255, 255, 255), 2, Qt.PenStyle.SolidLine)
157
155
  qp.setBrush(QtGui.QColor(255, 255, 255))
158
156
  qp.setPen(pen)
159
- fh = max(7, h / 50)
160
- font = QtGui.QFont('Sans', fh, QtGui.QFont.Light)
157
+ fh = int(max(7, h / 50))
158
+ font = QtGui.QFont('Sans', fh, QtGui.QFont.Weight.Light)
161
159
  qp.setFont(font)
162
160
  qp.resetTransform()
163
161
 
164
162
  qp.translate(0, h / 2)
165
163
  if not self.hover:
166
164
  # height
167
- qp.drawText(w - fh * 10, fh / 2, str(round(self.hoverHeight, 2)))
165
+ qp.drawText(w - fh * 10, int(fh / 2), str(round(self.hoverHeight, 2)))
168
166
 
169
167
  if self.hover:
170
168
  # target height (center)
171
169
  qp.drawText(
172
- w - fh * 10, fh / 2, str(round(self.hoverTargetHeight, 2)))
170
+ w - fh * 10, int(fh / 2), str(round(self.hoverTargetHeight, 2)))
173
171
  diff = round(self.hoverHeight - self.hoverTargetHeight, 2)
174
172
  pos_y = -h / 6 * diff
175
173
 
@@ -182,13 +180,13 @@ class AttitudeIndicator(QtWidgets.QWidget):
182
180
  pos_y = -h / 6 * diff
183
181
 
184
182
  # difference from target (moves up and down +- 2.8m)
185
- qp.drawText(w - fh * 3.8, pos_y + fh / 2, str(diff))
183
+ qp.drawText(int(w - fh * 3.8), int(pos_y + fh / 2), str(diff))
186
184
  # vertical line
187
- qp.drawLine(w - fh * 4.5, 0, w - fh * 4.5, pos_y)
185
+ qp.drawLine(int(w - fh * 4.5), 0, int(w - fh * 4.5), int(pos_y))
188
186
  # left horizontal line
189
- qp.drawLine(w - fh * 4.7, 0, w - fh * 4.5, 0)
187
+ qp.drawLine(int(w - fh * 4.7), 0, int(w - fh * 4.5), 0)
190
188
  # right horizontal line
191
- qp.drawLine(w - fh * 4.2, pos_y, w - fh * 4.5, pos_y)
189
+ qp.drawLine(int(w - fh * 4.2), int(pos_y), int(w - fh * 4.5), int(pos_y))
192
190
 
193
191
 
194
192
  if __name__ == "__main__":
@@ -214,8 +212,8 @@ if __name__ == "__main__":
214
212
  def initUI(self):
215
213
  vbox = QtWidgets.QVBoxLayout()
216
214
 
217
- sld = QtWidgets.QSlider(QtCore.Qt.Horizontal, self)
218
- sld.setFocusPolicy(QtCore.Qt.NoFocus)
215
+ sld = QtWidgets.QSlider(Qt.Orientation.Horizontal, self)
216
+ sld.setFocusPolicy(Qt.FocusPolicy.NoFocus)
219
217
  sld.setRange(0, 3600)
220
218
  sld.setValue(1800)
221
219
  vbox.addWidget(sld)
@@ -228,21 +226,21 @@ if __name__ == "__main__":
228
226
  hbox = QtWidgets.QHBoxLayout()
229
227
  hbox.addLayout(vbox)
230
228
 
231
- sldPitch = QtWidgets.QSlider(QtCore.Qt.Vertical, self)
232
- sldPitch.setFocusPolicy(QtCore.Qt.NoFocus)
229
+ sldPitch = QtWidgets.QSlider(Qt.Orientation.Vertical, self)
230
+ sldPitch.setFocusPolicy(Qt.FocusPolicy.NoFocus)
233
231
  sldPitch.setRange(0, 180)
234
232
  sldPitch.setValue(90)
235
233
  sldPitch.valueChanged[int].connect(self.updatePitch)
236
234
  hbox.addWidget(sldPitch)
237
235
 
238
- sldHeight = QtWidgets.QSlider(QtCore.Qt.Vertical, self)
239
- sldHeight.setFocusPolicy(QtCore.Qt.NoFocus)
236
+ sldHeight = QtWidgets.QSlider(Qt.Orientation.Vertical, self)
237
+ sldHeight.setFocusPolicy(Qt.FocusPolicy.NoFocus)
240
238
  sldHeight.setRange(-200, 200)
241
239
  sldHeight.setValue(0)
242
240
  sldHeight.valueChanged[int].connect(self.updateBaro)
243
241
 
244
- sldT = QtWidgets.QSlider(QtCore.Qt.Vertical, self)
245
- sldT.setFocusPolicy(QtCore.Qt.NoFocus)
242
+ sldT = QtWidgets.QSlider(Qt.Orientation.Vertical, self)
243
+ sldT.setFocusPolicy(Qt.FocusPolicy.NoFocus)
246
244
  sldT.setRange(-200, 200)
247
245
  sldT.setValue(0)
248
246
  sldT.valueChanged[int].connect(self.updateTarget)
@@ -263,7 +261,7 @@ if __name__ == "__main__":
263
261
  def main():
264
262
  app = QtWidgets.QApplication(sys.argv)
265
263
  Example()
266
- sys.exit(app.exec_())
264
+ sys.exit(app.exec())
267
265
 
268
266
  if __name__ == '__main__':
269
267
  main()
@@ -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
  #
@@ -30,20 +30,23 @@ This class provides a spin box with hexadecimal numbers and arbitrarily length
30
30
  (i.e. not limited by 32 bit).
31
31
  """
32
32
 
33
- from PyQt5 import QtGui, QtCore
34
- from PyQt5.QtWidgets import QAbstractSpinBox
33
+ from PyQt6.QtGui import QRegularExpressionValidator
34
+ from PyQt6.QtCore import QRegularExpression
35
+ from PyQt6.QtCore import pyqtSignal
36
+ from PyQt6.QtWidgets import QAbstractSpinBox
35
37
 
36
38
  __author__ = 'Bitcraze AB'
37
39
  __all__ = ['HexSpinBox']
38
40
 
39
41
 
40
42
  class HexSpinBox(QAbstractSpinBox):
43
+ valueChanged = pyqtSignal(object)
41
44
 
42
- def __init__(self, *args):
43
- QAbstractSpinBox.__init__(self, *args)
44
- regexp = QtCore.QRegExp('^0x[0-9A-Fa-f]{1,10}$')
45
- self.validator = QtGui.QRegExpValidator(regexp)
46
- self._value = 0
45
+ def __init__(self, *args, **kwargs):
46
+ super().__init__(*args, **kwargs)
47
+ regexp = QRegularExpression('^0x[0-9A-Fa-f]{1,10}$')
48
+ self.validator = QRegularExpressionValidator(regexp)
49
+ self.setValue(0)
47
50
 
48
51
  def validate(self, text, pos):
49
52
  return self.validator.validate(text, pos)
@@ -57,6 +60,7 @@ class HexSpinBox(QAbstractSpinBox):
57
60
  def setValue(self, value):
58
61
  self._value = value
59
62
  self.lineEdit().setText(self.textFromValue(value))
63
+ self.valueChanged.emit(self._value)
60
64
 
61
65
  def value(self):
62
66
  self._value = self.valueFromText(self.lineEdit().text())
@@ -66,5 +70,7 @@ class HexSpinBox(QAbstractSpinBox):
66
70
  self.setValue(self._value + steps)
67
71
 
68
72
  def stepEnabled(self):
69
- return (QAbstractSpinBox.StepUpEnabled |
70
- QAbstractSpinBox.StepDownEnabled)
73
+ return (QAbstractSpinBox.StepEnabledFlag.StepUpEnabled | QAbstractSpinBox.StepEnabledFlag.StepDownEnabled)
74
+
75
+ def is_text_different_from_value(self):
76
+ return self._value != self.valueFromText(self.lineEdit().text())
@@ -31,20 +31,51 @@
31
31
  <layout class="QGridLayout" name="gridLayout">
32
32
  <item row="1" column="0">
33
33
  <layout class="QGridLayout" name="gridLayout_5">
34
+ <item row="0" column="0">
35
+ <widget class="QRadioButton" name="_enable_range_x">
36
+ <property name="text">
37
+ <string>Range (s)</string>
38
+ </property>
39
+ <property name="autoExclusive">
40
+ <bool>false</bool>
41
+ </property>
42
+ </widget>
43
+ </item>
34
44
  <item row="0" column="1">
35
- <widget class="QLineEdit" name="_range_x_min">
45
+ <widget class="QDoubleSpinBox" name="_range_x_min">
36
46
  <property name="enabled">
37
47
  <bool>false</bool>
38
48
  </property>
39
- <property name="text">
40
- <string>0</string>
49
+ <property name="minimum">
50
+ <double>0</double>
51
+ </property>
52
+ <property name="maximum">
53
+ <double>86400</double>
54
+ </property>
55
+ <property name="singleStep">
56
+ <double>1</double>
57
+ </property>
58
+ <property name="value">
59
+ <double>0</double>
41
60
  </property>
42
61
  </widget>
43
62
  </item>
44
- <item row="0" column="2">
45
- <widget class="QLabel" name="label_2">
46
- <property name="text">
47
- <string>-</string>
63
+ <item row="0" column="4">
64
+ <widget class="QDoubleSpinBox" name="_range_x_max">
65
+ <property name="enabled">
66
+ <bool>false</bool>
67
+ </property>
68
+ <property name="minimum">
69
+ <double>0</double>
70
+ </property>
71
+ <property name="maximum">
72
+ <double>86400</double>
73
+ </property>
74
+ <property name="singleStep">
75
+ <double>1</double>
76
+ </property>
77
+ <property name="value">
78
+ <double>60</double>
48
79
  </property>
49
80
  </widget>
50
81
  </item>
@@ -70,7 +101,7 @@
70
101
  <string/>
71
102
  </property>
72
103
  <property name="maximum">
73
- <number>2000</number>
104
+ <number>3000</number>
74
105
  </property>
75
106
  <property name="singleStep">
76
107
  <number>100</number>
@@ -80,47 +111,8 @@
80
111
  </property>
81
112
  </widget>
82
113
  </item>
83
- <item row="0" column="3">
84
- <widget class="QLineEdit" name="_range_x_max">
85
- <property name="enabled">
86
- <bool>false</bool>
87
- </property>
88
- <property name="text">
89
- <string>1000</string>
90
- </property>
91
- </widget>
92
- </item>
93
- <item row="0" column="0">
94
- <widget class="QRadioButton" name="_enable_range_x">
95
- <property name="enabled">
96
- <bool>false</bool>
97
- </property>
98
- <property name="text">
99
- <string>Range</string>
100
- </property>
101
- <property name="autoExclusive">
102
- <bool>false</bool>
103
- </property>
104
- </widget>
105
- </item>
106
- <item row="3" column="0">
107
- <widget class="QRadioButton" name="_enable_manual_x">
108
- <property name="enabled">
109
- <bool>false</bool>
110
- </property>
111
- <property name="text">
112
- <string>Manual</string>
113
- </property>
114
- <property name="autoExclusive">
115
- <bool>false</bool>
116
- </property>
117
- </widget>
118
- </item>
119
114
  <item row="2" column="0">
120
115
  <widget class="QRadioButton" name="_enable_seconds_x">
121
- <property name="enabled">
122
- <bool>false</bool>
123
- </property>
124
116
  <property name="text">
125
117
  <string>Seconds</string>
126
118
  </property>
@@ -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,16 +32,15 @@ and manipulating the plot.
32
32
  For more advanced plotting save the data and use an external application.
33
33
  """
34
34
 
35
- from PyQt5 import QtWidgets, uic
35
+ from PyQt6 import QtWidgets, uic
36
36
 
37
37
  from time import time
38
38
 
39
39
  import logging
40
40
 
41
- from PyQt5.QtWidgets import QButtonGroup
42
- from PyQt5.QtCore import * # noqa
43
- from PyQt5.QtWidgets import * # noqa
44
- from PyQt5.Qt import * # noqa
41
+ from PyQt6.QtWidgets import QButtonGroup
42
+ from PyQt6.QtCore import * # noqa
43
+ from PyQt6.QtWidgets import * # noqa
45
44
 
46
45
  import cfclient
47
46
 
@@ -130,8 +129,8 @@ class PlotWidget(QtWidgets.QWidget, plot_widget_class):
130
129
  self._last_item = 0
131
130
 
132
131
  self.setSizePolicy(QtWidgets.QSizePolicy(
133
- QtWidgets.QSizePolicy.MinimumExpanding,
134
- QtWidgets.QSizePolicy.MinimumExpanding))
132
+ QtWidgets.QSizePolicy.Policy.MinimumExpanding,
133
+ QtWidgets.QSizePolicy.Policy.MinimumExpanding))
135
134
 
136
135
  self.setMinimumSize(self.minimumSizeHint())
137
136
  self.parent = parent
@@ -157,12 +156,18 @@ class PlotWidget(QtWidgets.QWidget, plot_widget_class):
157
156
  self._enable_samples_x.setChecked(True)
158
157
  self._last_ts = None
159
158
  self._dtime = None
159
+ self._first_ts = None
160
160
 
161
161
  self._x_range = (
162
- float(self._range_x_min.text()), float(self._range_x_max.text()))
162
+ float(self._range_x_min.text().replace(',', '.')), float(self._range_x_max.text().replace(',', '.'))
163
+ )
163
164
  self._nbr_samples = int(self._nbr_of_samples_x.text())
164
-
165
165
  self._nbr_of_samples_x.valueChanged.connect(self._nbr_samples_changed)
166
+ self._nbr_seconds = int(self._nbr_of_seconds_x.text())
167
+ self._nbr_of_seconds_x.valueChanged.connect(self._nbr_of_seconds_changed)
168
+ self._range_x_min.valueChanged.connect(self._x_range_changed)
169
+ self._range_x_max.valueChanged.connect(self._x_range_changed)
170
+
166
171
  self._range_y_min.valueChanged.connect(self._y_range_changed)
167
172
  self._range_y_max.valueChanged.connect(self._y_range_changed)
168
173
 
@@ -176,7 +181,6 @@ class PlotWidget(QtWidgets.QWidget, plot_widget_class):
176
181
  self._x_btn_group.addButton(self._enable_range_x)
177
182
  self._x_btn_group.addButton(self._enable_samples_x)
178
183
  self._x_btn_group.addButton(self._enable_seconds_x)
179
- self._x_btn_group.addButton(self._enable_manual_x)
180
184
  self._x_btn_group.setExclusive(True)
181
185
  self._x_btn_group.buttonClicked.connect(self._x_mode_change)
182
186
 
@@ -192,14 +196,17 @@ class PlotWidget(QtWidgets.QWidget, plot_widget_class):
192
196
 
193
197
  def _x_mode_change(self, box):
194
198
  """Callback when user changes the X-axis mode"""
199
+ self._nbr_of_samples_x.setEnabled(False)
200
+ self._nbr_of_seconds_x.setEnabled(False)
201
+ self._range_x_min.setEnabled(False)
202
+ self._range_x_max.setEnabled(False)
195
203
  if box == self._enable_range_x:
196
- logger.info("Enable range x")
197
- self._x_range = (
198
- float(self._range_x_min.text()),
199
- float(self._range_x_max.text()))
200
- else:
201
- self._range_x_min.setEnabled(False)
202
- self._range_x_max.setEnabled(False)
204
+ self._range_x_min.setEnabled(True)
205
+ self._range_x_max.setEnabled(True)
206
+ elif box == self._enable_samples_x:
207
+ self._nbr_of_samples_x.setEnabled(True)
208
+ elif box == self._enable_seconds_x:
209
+ self._nbr_of_seconds_x.setEnabled(True)
203
210
 
204
211
  def _y_mode_change(self, box):
205
212
  """Callback when user changes the Y-axis mode"""
@@ -241,6 +248,14 @@ class PlotWidget(QtWidgets.QWidget, plot_widget_class):
241
248
  """Callback when user changes the number of samples to be shown"""
242
249
  self._nbr_samples = val
243
250
 
251
+ def _nbr_of_seconds_changed(self, val):
252
+ """Callback when user changes the number of seconds to be shown"""
253
+ self._nbr_seconds = val
254
+
255
+ def _x_range_changed(self, val):
256
+ self._range_x_min.setMaximum(self._range_x_max.value()-1)
257
+ self._range_x_max.setMinimum(self._range_x_min.value()+1)
258
+
244
259
  def set_title(self, title):
245
260
  """
246
261
  Set the title of the plot.
@@ -267,11 +282,15 @@ class PlotWidget(QtWidgets.QWidget, plot_widget_class):
267
282
  pairs
268
283
  ts - timestamp of the data in ms
269
284
  """
270
- if not self._last_ts:
271
- self._last_ts = ts
272
- elif not self._last_ts:
285
+
286
+ if self._first_ts is None:
287
+ self._first_ts = ts
288
+
289
+ if self._last_ts is None:
290
+ self._dtime = 1e12
291
+ else:
273
292
  self._dtime = ts - self._last_ts
274
- self._last_ts = ts
293
+ self._last_ts = ts
275
294
 
276
295
  x_min_limit = 0
277
296
  x_max_limit = 0
@@ -279,6 +298,16 @@ class PlotWidget(QtWidgets.QWidget, plot_widget_class):
279
298
  if self._enable_samples_x.isChecked():
280
299
  x_min_limit = max(0, self._last_item - self._nbr_samples)
281
300
  x_max_limit = max(self._last_item, self._nbr_samples)
301
+ self._range_x_min.setValue(int(self._first_ts + x_min_limit * self._dtime)/1000.)
302
+ self._range_x_max.setValue(int(self._first_ts + x_max_limit * self._dtime)/1000.)
303
+ elif self._enable_seconds_x.isChecked():
304
+ x_min_limit = max(0, int(((self._last_ts - self._first_ts) - self._nbr_seconds * 1000.) / self._dtime))
305
+ x_max_limit = max(0, self._last_item)
306
+ self._range_x_min.setValue((self._first_ts + x_min_limit * self._dtime)/1000.)
307
+ self._range_x_max.setValue((self._first_ts + x_min_limit * self._dtime)/1000. + self._nbr_seconds)
308
+ elif self._enable_range_x.isChecked():
309
+ x_min_limit = max(0, int((self._range_x_min.value() * 1000. - self._first_ts) / self._dtime))
310
+ x_max_limit = max(0, int((self._range_x_max.value() * 1000. - self._first_ts) / self._dtime))
282
311
 
283
312
  for name in self._items:
284
313
  self._items[name].add_point(data[name], ts)
@@ -290,6 +319,11 @@ class PlotWidget(QtWidgets.QWidget, plot_widget_class):
290
319
  if (self._enable_samples_x.isChecked() and self._dtime and
291
320
  self._last_item < self._nbr_samples):
292
321
  self._x_max = self._x_min + self._nbr_samples * self._dtime
322
+ elif (self._enable_seconds_x.isChecked() and self._dtime and
323
+ self._last_item < int(self._nbr_seconds * 1000. / self._dtime)):
324
+ self._x_max = self._x_min + self._nbr_seconds * 1000.
325
+ elif (self._enable_range_x.isChecked() and self._dtime) and self._last_item < x_max_limit:
326
+ self._x_max = self._x_min + (x_max_limit - x_min_limit) * self._dtime
293
327
 
294
328
  self._last_item = self._last_item + 1
295
329
  self._plot_widget.getViewBox().setRange(
@@ -305,6 +339,7 @@ class PlotWidget(QtWidgets.QWidget, plot_widget_class):
305
339
  self._items = {}
306
340
  self._last_item = 0
307
341
  self._last_ts = None
342
+ self._first_ts = None
308
343
  self._dtime = None
309
344
  self._plot_widget.clear()
310
345
 
@@ -0,0 +1,112 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # ,---------, ____ _ __
4
+ # | ,-^-, | / __ )(_) /_______________ _____ ___
5
+ # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \
6
+ # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
7
+ # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/
8
+ #
9
+ # Copyright (C) 2022-2023 Bitcraze AB
10
+ #
11
+ # This program is free software: you can redistribute it and/or modify
12
+ # it under the terms of the GNU General Public License as published by
13
+ # the Free Software Foundation, in version 3.
14
+ #
15
+ # This program is distributed in the hope that it will be useful,
16
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ # GNU General Public License for more details.
19
+ #
20
+ # You should have received a copy of the GNU General Public License
21
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
22
+
23
+ """
24
+ Slider widget with advanced features
25
+ """
26
+
27
+ from PyQt6 import QtWidgets, QtCore
28
+ from cflib.utils.callbacks import Caller
29
+
30
+
31
+ __author__ = 'Bitcraze AB'
32
+ __all__ = ['SuperSlider']
33
+
34
+
35
+ class SuperSlider(QtWidgets.QWidget):
36
+
37
+ def __init__(self, min: float, max: float, value: float):
38
+ super(SuperSlider, self).__init__()
39
+
40
+ self.slider_scaling: int = 100
41
+
42
+ self.min = min
43
+ self.max = max
44
+ self.value = value
45
+
46
+ self.value_changed_cb = Caller()
47
+ self._initUI()
48
+
49
+ def set_value(self, value: float):
50
+ self.input.blockSignals(True)
51
+ self.slider.blockSignals(True)
52
+
53
+ self.value = value
54
+ self.input.setValue(self.value)
55
+ self.slider.setValue(int(self.value * self.slider_scaling))
56
+
57
+ self.input.blockSignals(False)
58
+ self.slider.blockSignals(False)
59
+
60
+ def _initUI(self):
61
+ # Create controls
62
+ self.slider = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal, self)
63
+ self.slider.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
64
+ self.slider.setRange(int(self.min * self.slider_scaling), int(self.max * self.slider_scaling))
65
+ self.slider.setValue(int(self.value * self.slider_scaling))
66
+ self.slider.setTickInterval(self.slider_scaling)
67
+ self.slider.setTickPosition(QtWidgets.QSlider.TickPosition.TicksBelow)
68
+
69
+ self.min_label = QtWidgets.QLabel(str(self.min), self)
70
+ self.min_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
71
+ self._small_font(self.min_label)
72
+
73
+ self.input = QtWidgets.QDoubleSpinBox(self)
74
+ self.input.setMinimum(self.min)
75
+ self.input.setMaximum(self.max)
76
+ self.input.setSingleStep(0.1)
77
+ self.input.setValue(self.value)
78
+
79
+ self.max_label = QtWidgets.QLabel(str(self.max), self)
80
+ self.max_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
81
+ self._small_font(self.max_label)
82
+
83
+ # Connect functionality
84
+ self.slider.valueChanged.connect(self._slider_moved)
85
+ self.input.valueChanged.connect(self._input_changed)
86
+
87
+ # Layout
88
+ layout = QtWidgets.QGridLayout()
89
+ layout.addWidget(self.slider, 0, 0, 1, 3)
90
+ layout.addWidget(self.min_label, 1, 0)
91
+ layout.addWidget(self.input, 1, 1)
92
+ layout.addWidget(self.max_label, 1, 2)
93
+ self.setLayout(layout)
94
+
95
+ def _small_font(self, label):
96
+ font = label.font()
97
+ font.setPointSize(int(font.pointSize() * 0.8))
98
+ label.setFont(font)
99
+
100
+ def _slider_moved(self, value):
101
+ self.input.blockSignals(True)
102
+ self.value = value / self.slider_scaling
103
+ self.input.setValue(self.value)
104
+ self.input.blockSignals(False)
105
+ self.value_changed_cb.call(self.value)
106
+
107
+ def _input_changed(self, value):
108
+ self.slider.blockSignals(True)
109
+ self.value = value
110
+ self.slider.setValue(int(self.value * self.slider_scaling))
111
+ self.slider.blockSignals(False)
112
+ self.value_changed_cb.call(self.value)
File without changes
Binary file
Binary file
Binary file
Binary file
Binary file