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.
- cfclient/__init__.py +16 -11
- cfclient/configs/config.json +4 -3
- cfclient/configs/input/Generic_OS_X.json +1 -0
- cfclient/configs/input/Joystick.json +1 -0
- cfclient/configs/input/PS3_Mode_1.json +1 -0
- cfclient/configs/input/PS3_Mode_2.json +1 -0
- cfclient/configs/input/PS3_Mode_3.json +1 -0
- cfclient/configs/input/PS4_Mode_1.json +1 -0
- cfclient/configs/input/PS4_Mode_2.json +1 -0
- cfclient/configs/input/PS4_shoulder_btns_yaw.json +1 -0
- cfclient/configs/input/xbox360_mode1.json +1 -0
- cfclient/configs/log/PID_tuning/Attitude.json +46 -0
- cfclient/configs/log/PID_tuning/Attitude_rate.json +46 -0
- cfclient/configs/log/PID_tuning/Position.json +46 -0
- cfclient/configs/log/PID_tuning/Velocity.json +46 -0
- cfclient/configs/log/PID_tuning_components/Pitch.json +22 -0
- cfclient/configs/log/PID_tuning_components/Pitch_rate.json +22 -0
- cfclient/configs/log/PID_tuning_components/Position_x.json +22 -0
- cfclient/configs/log/PID_tuning_components/Position_y.json +22 -0
- cfclient/configs/log/PID_tuning_components/Position_z.json +22 -0
- cfclient/configs/log/PID_tuning_components/Roll.json +22 -0
- cfclient/configs/log/PID_tuning_components/Roll_rate.json +22 -0
- cfclient/configs/log/PID_tuning_components/Velocity_x.json +22 -0
- cfclient/configs/log/PID_tuning_components/Velocity_y.json +22 -0
- cfclient/configs/log/PID_tuning_components/Velocity_z.json +22 -0
- cfclient/configs/log/PID_tuning_components/Yaw.json +22 -0
- cfclient/configs/log/PID_tuning_components/Yaw_rate.json +22 -0
- cfclient/gui.py +44 -9
- cfclient/headless.py +3 -12
- cfclient/resources/log_param_doc.json +1 -0
- cfclient/ui/connectivity_manager.py +198 -0
- cfclient/ui/dialogs/about.py +53 -36
- cfclient/ui/dialogs/about.ui +23 -3
- cfclient/ui/dialogs/anchor_position_dialog.py +252 -0
- cfclient/ui/dialogs/anchor_position_dialog.ui +138 -0
- cfclient/ui/dialogs/basestation_mode_dialog.py +185 -0
- cfclient/ui/dialogs/basestation_mode_dialog.ui +186 -0
- cfclient/ui/dialogs/bootloader.py +448 -85
- cfclient/ui/dialogs/bootloader.ui +387 -134
- cfclient/ui/dialogs/cf2config.py +4 -4
- cfclient/ui/dialogs/cf2config.ui +3 -4
- cfclient/ui/dialogs/inputconfigdialogue.py +24 -19
- cfclient/ui/dialogs/inputconfigdialogue.ui +53 -30
- cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py +220 -0
- cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.ui +110 -0
- cfclient/ui/dialogs/lighthouse_system_type_dialog.py +93 -0
- cfclient/ui/dialogs/lighthouse_system_type_dialog.ui +121 -0
- cfclient/ui/dialogs/logconfigdialogue.py +401 -101
- cfclient/ui/dialogs/logconfigdialogue.ui +117 -72
- cfclient/ui/icons/bl.webp +0 -0
- cfclient/ui/icons/bolt.webp +0 -0
- cfclient/ui/icons/cf21.webp +0 -0
- cfclient/ui/icons/checkmark_black.png +0 -0
- cfclient/ui/icons/checkmark_white.png +0 -0
- cfclient/ui/icons/create.png +0 -0
- cfclient/ui/icons/delete.png +0 -0
- cfclient/ui/icons/flapper.webp +0 -0
- cfclient/ui/icons/tag.webp +0 -0
- cfclient/ui/main.py +328 -258
- cfclient/ui/main.ui +184 -80
- cfclient/ui/pluginhelper.py +7 -1
- cfclient/ui/pose_logger.py +116 -0
- cfclient/ui/tab_toolbox.py +208 -0
- cfclient/ui/tabs/ColorLEDTab.py +752 -0
- cfclient/ui/tabs/ConsoleTab.py +48 -13
- cfclient/ui/{toolboxes → tabs}/CrtpSharkToolbox.py +19 -34
- cfclient/ui/tabs/ExampleTab.py +9 -16
- cfclient/ui/tabs/FlightTab.py +437 -325
- cfclient/ui/tabs/GpsTab.py +14 -20
- cfclient/ui/tabs/LEDRingTab.py +277 -0
- cfclient/ui/tabs/LogBlockDebugTab.py +20 -27
- cfclient/ui/tabs/LogBlockTab.py +35 -35
- cfclient/ui/tabs/LogClientTab.py +85 -0
- cfclient/ui/tabs/LogTab.py +50 -27
- cfclient/ui/tabs/ParamTab.py +443 -57
- cfclient/ui/tabs/PlotTab.py +23 -25
- cfclient/ui/tabs/TuningTab.py +292 -0
- cfclient/ui/tabs/__init__.py +12 -2
- cfclient/ui/tabs/colorLEDTab.ui +624 -0
- cfclient/ui/tabs/consoleTab.ui +46 -0
- cfclient/ui/tabs/flightActionContainer.ui +103 -0
- cfclient/ui/tabs/flightTab.ui +724 -237
- cfclient/ui/tabs/{ledTab.ui → ledRingTab.ui} +63 -46
- cfclient/ui/tabs/lighthouse_tab.py +714 -0
- cfclient/ui/tabs/lighthouse_tab.ui +430 -0
- cfclient/ui/tabs/locopositioning_tab.py +606 -389
- cfclient/ui/tabs/locopositioning_tab.ui +370 -253
- cfclient/ui/tabs/logClientTab.ui +52 -0
- cfclient/ui/tabs/logTab.ui +1 -1
- cfclient/ui/tabs/paramTab.ui +204 -3
- cfclient/ui/tabs/tuningTab.ui +773 -0
- cfclient/ui/widgets/ai.py +37 -39
- cfclient/ui/widgets/hexspinbox.py +16 -10
- cfclient/ui/widgets/plotter.ui +39 -47
- cfclient/ui/widgets/plotwidget.py +57 -22
- cfclient/ui/widgets/super_slider.py +112 -0
- cfclient/ui/wizards/__init__.py +0 -0
- cfclient/ui/wizards/bslh_1.png +0 -0
- cfclient/ui/wizards/bslh_2.png +0 -0
- cfclient/ui/wizards/bslh_3.png +0 -0
- cfclient/ui/wizards/bslh_4.png +0 -0
- cfclient/ui/wizards/bslh_5.png +0 -0
- cfclient/ui/wizards/lighthouse_geo_bs_estimation_wizard.py +465 -0
- cfclient/utils/config_manager.py +5 -4
- cfclient/utils/input/__init__.py +77 -19
- cfclient/utils/input/inputinterfaces/wiimote.py +2 -2
- cfclient/utils/input/inputreaderinterface.py +17 -7
- cfclient/utils/input/inputreaders/__init__.py +17 -0
- cfclient/utils/logconfigreader.py +245 -25
- cfclient/utils/logdatawriter.py +3 -1
- cfclient/utils/periodictimer.py +1 -1
- cfclient/utils/ui.py +336 -0
- cfclient/utils/zmq_led_driver.py +5 -0
- cfclient/utils/zmq_param.py +6 -0
- cfclient/version.py +34 -1
- cfclient-2025.12.1.dist-info/METADATA +70 -0
- cfclient-2025.12.1.dist-info/RECORD +152 -0
- {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/WHEEL +1 -1
- {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/entry_points.txt +0 -1
- cfclient-2025.12.1.dist-info/licenses/LICENSE.txt +350 -0
- {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/top_level.txt +1 -0
- cfconfig/Makefile +51 -0
- cfconfig/configblock.py +111 -0
- cfloader/__init__.py +41 -55
- cfzmq/__init__.py +22 -14
- cfclient/ui/dialogs/cf1config.py +0 -265
- cfclient/ui/dialogs/cf1config.ui +0 -260
- cfclient/ui/tab.py +0 -96
- cfclient/ui/tabs/LEDTab.py +0 -169
- cfclient/ui/toolboxes/ConsoleToolbox.py +0 -69
- cfclient/ui/toolboxes/DebugDriverToolbox.py +0 -107
- cfclient/ui/toolboxes/__init__.py +0 -45
- cfclient/ui/toolboxes/consoleToolbox.ui +0 -62
- cfclient/ui/toolboxes/debugDriverToolbox.ui +0 -86
- cfclient-2017.4.dist-info/DESCRIPTION.rst +0 -3
- cfclient-2017.4.dist-info/METADATA +0 -22
- cfclient-2017.4.dist-info/RECORD +0 -104
- cfclient-2017.4.dist-info/metadata.json +0 -1
- /cfclient/{icon-256.png → ui/icons/icon-256.png} +0 -0
- /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-
|
|
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
|
|
35
|
-
from
|
|
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(
|
|
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(
|
|
218
|
-
sld.setFocusPolicy(
|
|
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(
|
|
232
|
-
sldPitch.setFocusPolicy(
|
|
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(
|
|
239
|
-
sldHeight.setFocusPolicy(
|
|
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(
|
|
245
|
-
sldT.setFocusPolicy(
|
|
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.
|
|
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-
|
|
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
|
|
34
|
-
from
|
|
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
|
-
|
|
44
|
-
regexp =
|
|
45
|
-
self.validator =
|
|
46
|
-
self.
|
|
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
|
-
|
|
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())
|
cfclient/ui/widgets/plotter.ui
CHANGED
|
@@ -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="
|
|
45
|
+
<widget class="QDoubleSpinBox" name="_range_x_min">
|
|
36
46
|
<property name="enabled">
|
|
37
47
|
<bool>false</bool>
|
|
38
48
|
</property>
|
|
39
|
-
<property name="
|
|
40
|
-
<
|
|
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="
|
|
45
|
-
<widget class="
|
|
46
|
-
<property name="
|
|
47
|
-
<
|
|
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>
|
|
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-
|
|
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
|
|
35
|
+
from PyQt6 import QtWidgets, uic
|
|
36
36
|
|
|
37
37
|
from time import time
|
|
38
38
|
|
|
39
39
|
import logging
|
|
40
40
|
|
|
41
|
-
from
|
|
42
|
-
from
|
|
43
|
-
from
|
|
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
|
-
|
|
197
|
-
self.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
self.
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
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
|