diff --git a/docs/development/dev_info_client.md b/docs/development/dev_info_client.md index d4a3bf9444..616f9641e8 100644 --- a/docs/development/dev_info_client.md +++ b/docs/development/dev_info_client.md @@ -10,7 +10,7 @@ same kind of information is available here for the Crazyflie Python API. Here\'s a quick overview: -- The GUI is made in QT5 (using QTDesigner 4 and loading the .ui files +- The GUI is made in QT6 (using QTDesigner and loading the .ui files at runtime) - It uses the SDL2 to read input devices on Windows/Mac OSX and raw jsdevs on Linux. It also supports custom input from diff --git a/setup.py b/setup.py index ff323f8bdc..a09a787be4 100644 --- a/setup.py +++ b/setup.py @@ -95,13 +95,11 @@ def relative(lst, base=''): 'pyzmq~=25.0', 'pyqtgraph~=0.11', 'PyYAML~=6.0.1', - 'qasync~=0.23.0', - 'qtm~=2.1.1', 'numpy>=1.20,<1.25', 'vispy~=0.13.0', 'pyserial~=3.5', - 'pyqt5~=5.15.0', - 'PyQt5-sip>=12.9.0', + 'pyqt6~=6.5', + 'PyQt6-sip~=13.5', 'pysdl2~=0.9.14 ; platform_system=="Windows" or platform_system=="Darwin"', 'pysdl2-dll==2.24.0 ; platform_system=="Windows" or platform_system=="Darwin"'], diff --git a/src/cfclient/gui.py b/src/cfclient/gui.py index 2a42a4e6f2..db72075aec 100644 --- a/src/cfclient/gui.py +++ b/src/cfclient/gui.py @@ -6,7 +6,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2013 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -29,14 +29,12 @@ import platform import sys import os -import asyncio import argparse import datetime import signal import logging -from qasync import QEventLoop import cfclient __author__ = 'Bitcraze AB' @@ -69,8 +67,8 @@ def main(): os.environ['DYLD_FALLBACK_LIBRARY_PATH'] = os.path.dirname( sys.executable) - # Set ERROR level for PyQt5 logger - qtlogger = logging.getLogger('PyQt5') + # Set ERROR level for PyQt logger + qtlogger = logging.getLogger('PyQt6') qtlogger.setLevel(logging.ERROR) parser = argparse.ArgumentParser( @@ -125,9 +123,9 @@ def main(): sys.exit(1) try: - import PyQt5 # noqa + import PyQt6 # noqa except ImportError: - logger.critical("No PyQT5 installation found, exiting!") + logger.critical("No PyQT6 installation found, exiting!") sys.exit(1) # Disable printouts from STL @@ -162,8 +160,8 @@ def main(): # Start up the main user-interface from .ui.main import MainUI - from PyQt5.QtWidgets import QApplication - from PyQt5.QtGui import QIcon + from PyQt6.QtWidgets import QApplication + from PyQt6.QtGui import QIcon if os.name == 'posix': logger.info('If startup fails because of "xcb", install dependency with `sudo apt install libxcb-xinerama0`.') @@ -172,10 +170,6 @@ def main(): app.setStyle("Fusion") from cfclient.utils.ui import UiUtils - # Create and set an event loop that combines qt and asyncio - loop = QEventLoop(app) - asyncio.set_event_loop(loop) - app.setWindowIcon(QIcon(cfclient.module_path + "/ui/icons/icon-256.png")) app.setApplicationName("Crazyflie client") # Make sure the right icon is set in Windows 7+ taskbar @@ -193,7 +187,7 @@ def main(): app.setFont(UiUtils.FONT) main_window.show() main_window.set_default_theme() - sys.exit(app.exec_()) + sys.exit(app.exec()) if __name__ == "__main__": diff --git a/src/cfclient/ui/connectivity_manager.py b/src/cfclient/ui/connectivity_manager.py index 4bd305e94c..e536eb0961 100644 --- a/src/cfclient/ui/connectivity_manager.py +++ b/src/cfclient/ui/connectivity_manager.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2021 Bitcraze AB +# Copyright (C) 2021-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -26,7 +26,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. from collections import namedtuple -from PyQt5.QtCore import pyqtSignal, QObject +from PyQt6.QtCore import pyqtSignal, QObject __author__ = 'Bitcraze AB' __all__ = ['ConnectivityManager'] @@ -66,8 +66,7 @@ def register_ui_elements(self, ui_elements): ui_elements.address_spinner.valueChanged.connect(self._address_changed_handler) ui_elements.address_spinner.editingFinished.connect(self._address_edited_handler) - ui_elements.interface_combo.currentIndexChanged['QString'].connect( - self._interface_combo_current_index_changed_handler) + ui_elements.interface_combo.currentIndexChanged.connect(self._interface_combo_current_index_changed_handler) def set_state(self, state): if self._state != state: @@ -149,11 +148,12 @@ def _address_edited_handler(self): ui_elements.address_spinner.setValue(value) def _interface_combo_current_index_changed_handler(self, interface): + interface_s = str(interface) can_connect = interface != self.INTERFACE_PROMPT_TEXT for ui_elements in self._ui_elements: combo = ui_elements.interface_combo - if combo.currentText != interface: - combo.setCurrentText(interface) + if combo.currentText != interface_s: + combo.setCurrentText(interface_s) ui_elements.connect_button.setEnabled(can_connect) def _update_ui(self): diff --git a/src/cfclient/ui/dialogs/about.py b/src/cfclient/ui/dialogs/about.py index f5db845086..08323efd2c 100644 --- a/src/cfclient/ui/dialogs/about.py +++ b/src/cfclient/ui/dialogs/about.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2013 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -31,11 +31,11 @@ import cfclient import cflib.crtp -from PyQt5.QtCore import QT_VERSION_STR -from PyQt5.QtCore import PYQT_VERSION_STR -from PyQt5 import QtWidgets -from PyQt5 import uic -from PyQt5.QtCore import pyqtSignal +from PyQt6.QtCore import QT_VERSION_STR +from PyQt6.QtCore import PYQT_VERSION_STR +from PyQt6 import QtWidgets +from PyQt6 import uic +from PyQt6.QtCore import pyqtSignal from cflib.crazyflie.mem import MemoryElement __author__ = 'Bitcraze AB' diff --git a/src/cfclient/ui/dialogs/anchor_position_dialog.py b/src/cfclient/ui/dialogs/anchor_position_dialog.py index ce31e9be35..2c43631576 100644 --- a/src/cfclient/ui/dialogs/anchor_position_dialog.py +++ b/src/cfclient/ui/dialogs/anchor_position_dialog.py @@ -6,7 +6,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2018 Bitcraze AB +# Copyright (C) 2018-2023 Bitcraze AB # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -29,11 +29,11 @@ import cfclient from cfclient.utils.logconfigreader import FILE_REGEX_YAML -from PyQt5 import QtWidgets -from PyQt5 import uic -from PyQt5.QtCore import QAbstractTableModel, QVariant, Qt -from PyQt5.QtGui import QBrush, QColor -from PyQt5.QtWidgets import QInputDialog, QFileDialog +from PyQt6 import QtWidgets +from PyQt6 import uic +from PyQt6.QtCore import QAbstractTableModel, QVariant, Qt +from PyQt6.QtGui import QBrush, QColor +from PyQt6.QtWidgets import QInputDialog, QFileDialog import yaml import os @@ -68,22 +68,22 @@ def data(self, index, role=None): value = self._anchor_positions[index.row()][index.column()] if index.isValid(): if index.column() == 0: - if role == Qt.CheckStateRole: + if role == Qt.ItemDataRole.CheckStateRole: return QVariant(value) elif index.column() == 1: - if role == Qt.DisplayRole: + if role == Qt.ItemDataRole.DisplayRole: return QVariant(value) else: - if role == Qt.DisplayRole: + if role == Qt.ItemDataRole.DisplayRole: return QVariant('%.2f' % (value)) - elif role == Qt.EditRole: + elif role == Qt.ItemDataRole.EditRole: return QVariant(value) - elif role == Qt.BackgroundRole: + elif role == Qt.ItemDataRole.BackgroundRole: return self._get_background(index.row(), index.column()) return QVariant() - def setData(self, index, value, role=Qt.EditRole): + def setData(self, index, value, role=Qt.ItemDataRole.EditRole): if not index.isValid(): return False @@ -91,7 +91,7 @@ def setData(self, index, value, role=Qt.EditRole): return True def headerData(self, col, orientation, role=None): - if orientation == Qt.Horizontal and role == Qt.DisplayRole: + if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole: return QVariant(self._headers[col]) return QVariant() @@ -100,11 +100,11 @@ def flags(self, index): return None if index.column() == 0: - return Qt.ItemIsEnabled | Qt.ItemIsUserCheckable + return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsUserCheckable elif index.column() == 1: - return Qt.ItemIsEnabled + return Qt.ItemFlag.ItemIsEnabled else: - return Qt.ItemIsEnabled | Qt.ItemIsEditable + return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsEditable def add_anchor(self, anchor_id, x=0.0, y=0.0, z=0.0): if not self._id_exist(anchor_id): @@ -176,11 +176,11 @@ def __init__(self, lps_tab, helper): self._table_view.verticalHeader().setVisible(False) header = self._table_view.horizontalHeader() - header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) - header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents) - header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch) - header.setSectionResizeMode(3, QtWidgets.QHeaderView.Stretch) - header.setSectionResizeMode(4, QtWidgets.QHeaderView.Stretch) + header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeMode.ResizeToContents) + header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeMode.ResizeToContents) + header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeMode.Stretch) + header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeMode.Stretch) + header.setSectionResizeMode(4, QtWidgets.QHeaderView.ResizeMode.Stretch) self._add_anchor_button.clicked.connect( self._add_anchor_button_clicked) diff --git a/src/cfclient/ui/dialogs/basestation_mode_dialog.py b/src/cfclient/ui/dialogs/basestation_mode_dialog.py index 6ba2eadc05..3e0ed53dd6 100644 --- a/src/cfclient/ui/dialogs/basestation_mode_dialog.py +++ b/src/cfclient/ui/dialogs/basestation_mode_dialog.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2013 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -27,9 +27,9 @@ Toolbox used to interact with the Basestation to set it in a certain channel """ -from PyQt5 import QtWidgets -from PyQt5 import uic -from PyQt5.QtCore import Qt +from PyQt6 import QtWidgets +from PyQt6 import uic +from PyQt6.QtCore import Qt import io import serial @@ -154,7 +154,7 @@ def disable(self): return def preferedDockArea(self): - return Qt.RightDockWidgetArea + return Qt.DockWidgetArea.RightDockWidgetArea def _find_basestation(self): ports = comports() diff --git a/src/cfclient/ui/dialogs/bootloader.py b/src/cfclient/ui/dialogs/bootloader.py index e12789ac42..ccd773bf53 100644 --- a/src/cfclient/ui/dialogs/bootloader.py +++ b/src/cfclient/ui/dialogs/bootloader.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2013 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -44,8 +44,8 @@ from urllib.error import URLError import zipfile -from PyQt5 import QtWidgets, uic -from PyQt5.QtCore import pyqtSlot, pyqtSignal, QThread +from PyQt6 import QtWidgets, uic +from PyQt6.QtCore import pyqtSlot, pyqtSignal, QThread import cfclient import cflib.crazyflie diff --git a/src/cfclient/ui/dialogs/cf2config.py b/src/cfclient/ui/dialogs/cf2config.py index 7b8f855f62..291b4de62a 100644 --- a/src/cfclient/ui/dialogs/cf2config.py +++ b/src/cfclient/ui/dialogs/cf2config.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2013 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -32,9 +32,9 @@ import cfclient from cflib.crazyflie.mem import MemoryElement -from PyQt5 import QtWidgets -from PyQt5 import uic -from PyQt5.QtCore import pyqtSignal +from PyQt6 import QtWidgets +from PyQt6 import uic +from PyQt6.QtCore import pyqtSignal __author__ = 'Bitcraze AB' __all__ = ['Cf2ConfigDialog'] diff --git a/src/cfclient/ui/dialogs/inputconfigdialogue.py b/src/cfclient/ui/dialogs/inputconfigdialogue.py index 00ad739113..7ece4cc68a 100644 --- a/src/cfclient/ui/dialogs/inputconfigdialogue.py +++ b/src/cfclient/ui/dialogs/inputconfigdialogue.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2017 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -30,15 +30,14 @@ import logging import cfclient -from PyQt5.QtCore import QThread -from PyQt5.QtCore import QTimer -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtWidgets import QMessageBox +from PyQt6.QtCore import Qt +from PyQt6.QtCore import QThread +from PyQt6.QtCore import QTimer +from PyQt6.QtCore import pyqtSignal +from PyQt6.QtWidgets import QMessageBox from cfclient.utils.config_manager import ConfigManager -from PyQt5 import Qt -from PyQt5 import QtWidgets -from PyQt5 import uic -from PyQt5.Qt import * # noqa +from PyQt6 import QtWidgets +from PyQt6 import uic __author__ = 'Bitcraze AB' __all__ = ['InputConfigDialogue'] @@ -213,7 +212,7 @@ def _show_config_popup(self, caption, message, directions=[]): self.cancelButton = QtWidgets.QPushButton('Cancel') self._popup.addButton(self.cancelButton, QMessageBox.DestructiveRole) self._popup.setWindowTitle(caption) - self._popup.setWindowFlags(Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint) + self._popup.setWindowFlags(Qt.WindowType.Dialog | Qt.WindowType.MSWindowsFixedSizeDialogHint) if len(directions) > 1: self._popup.originalMessage = message message = self._popup.originalMessage % directions[0] diff --git a/src/cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py b/src/cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py index 8c874834fd..f8658bdc78 100644 --- a/src/cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py +++ b/src/cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py @@ -6,7 +6,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2021 Bitcraze AB +# Copyright (C) 2021-2023 Bitcraze AB # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -28,9 +28,9 @@ import logging import cfclient -from PyQt5 import QtWidgets -from PyQt5 import uic -from PyQt5.QtCore import QVariant, Qt, QAbstractTableModel, pyqtSignal +from PyQt6 import QtWidgets +from PyQt6 import uic +from PyQt6.QtCore import QVariant, Qt, QAbstractTableModel, pyqtSignal from cflib.localization import LighthouseBsGeoEstimator from cflib.localization import LighthouseSweepAngleAverageReader from cflib.crazyflie.mem import LighthouseBsGeometry @@ -64,13 +64,13 @@ def columnCount(self, parent=None, *args, **kwargs): def data(self, index, role=None): if index.isValid(): value = self._table_values[index.row()][index.column()] - if role == Qt.DisplayRole: + if role == Qt.ItemDataRole.DisplayRole: return QVariant(value) return QVariant() def headerData(self, col, orientation, role=None): - if orientation == Qt.Horizontal and role == Qt.DisplayRole: + if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole: return QVariant(self._headers[col]) return QVariant() @@ -169,10 +169,10 @@ def __init__(self, lighthouse_tab, *args): self._table_view.verticalHeader().setVisible(False) header = self._table_view.horizontalHeader() - header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) - header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch) - header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch) - header.setSectionResizeMode(3, QtWidgets.QHeaderView.Stretch) + header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeMode.ResizeToContents) + header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeMode.Stretch) + header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeMode.Stretch) + header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeMode.Stretch) self._update_ui() diff --git a/src/cfclient/ui/dialogs/lighthouse_system_type_dialog.py b/src/cfclient/ui/dialogs/lighthouse_system_type_dialog.py index 2e3be0bee7..1b5572429d 100644 --- a/src/cfclient/ui/dialogs/lighthouse_system_type_dialog.py +++ b/src/cfclient/ui/dialogs/lighthouse_system_type_dialog.py @@ -6,7 +6,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2021 Bitcraze AB +# Copyright (C) 2021-2023 Bitcraze AB # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -28,8 +28,8 @@ import logging import cfclient -from PyQt5 import QtWidgets -from PyQt5 import uic +from PyQt6 import QtWidgets +from PyQt6 import uic __author__ = 'Bitcraze AB' __all__ = ['LighthouseSystemTypeDialog'] diff --git a/src/cfclient/ui/dialogs/logconfigdialogue.py b/src/cfclient/ui/dialogs/logconfigdialogue.py index 7808ee042a..8919499e3b 100644 --- a/src/cfclient/ui/dialogs/logconfigdialogue.py +++ b/src/cfclient/ui/dialogs/logconfigdialogue.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2013 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -36,8 +36,9 @@ import cfclient from cfclient.utils.ui import UiUtils -from PyQt5 import QtWidgets, uic, QtGui -from PyQt5.QtCore import Qt, QTimer +from PyQt6 import QtWidgets, uic +from PyQt6.QtCore import Qt, QTimer +from PyQt6.QtGui import QShortcut, QKeySequence from cflib.crazyflie.log import LogConfig @@ -112,16 +113,15 @@ def __init__(self, helper, *args): self.deleteBtn.setToolTip('Delete category') # enable right-click context-menu - self.categoryTree.setContextMenuPolicy(Qt.CustomContextMenu) + self.categoryTree.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.categoryTree.customContextMenuRequested.connect( self.menuContextTree) # keyboard shortcuts - shortcut_delete = QtWidgets.QShortcut(QtGui.QKeySequence("Delete"), - self) + shortcut_delete = QShortcut(QKeySequence("Delete"), self) shortcut_delete.activated.connect(self._delete_config) - shortcut_f2 = QtWidgets.QShortcut(QtGui.QKeySequence("F2"), self) + shortcut_f2 = QShortcut(QKeySequence("F2"), self) shortcut_f2.activated.connect(self._edit_name) self._config_saved_timer = QTimer() @@ -262,9 +262,7 @@ def menuContextTree(self, point): self._edit_name() def _select_category(self, category): - items = self.categoryTree.findItems(category, - Qt.MatchFixedString - | Qt.MatchRecursive) + items = self.categoryTree.findItems(category, Qt.MatchFlag.MatchFixedString | Qt.MatchFlag.MatchRecursive) if items: category = items[0] self.categoryTree.setCurrentItem(category) @@ -272,9 +270,7 @@ def _select_category(self, category): def _select_item(self, conf_name, category): """ loads the given config in the correct category """ - items = self.categoryTree.findItems(conf_name, - Qt.MatchFixedString - | Qt.MatchRecursive) + items = self.categoryTree.findItems(conf_name, Qt.MatchFlag.MatchFixedString | Qt.MatchFlag.MatchRecursive) for item in items: if item.parent().text(0) == category: self._last_pressed_item = item, conf_name @@ -317,8 +313,8 @@ def _load_saved_configs(self): # Create category-tree. for conf_category in config: category = QtWidgets.QTreeWidgetItem() - category.setData(NAME_FIELD, Qt.DisplayRole, conf_category) - category.setFlags(category.flags() | Qt.ItemIsEditable) + category.setData(NAME_FIELD, Qt.ItemDataRole.DisplayRole, conf_category) + category.setFlags(category.flags() | Qt.ItemFlag.ItemIsEditable) # Copulate category-tree with log configurations. for conf in config[conf_category]: @@ -332,11 +328,11 @@ def _load_saved_configs(self): else: conf_name = conf.name - item.setData(NAME_FIELD, Qt.DisplayRole, conf_name) + item.setData(NAME_FIELD, Qt.ItemDataRole.DisplayRole, conf_name) category.addChild(item) # Enable item-editing. - item.setFlags(item.flags() | Qt.ItemIsEditable) + item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEditable) self.categoryTree.addTopLevelItem(category) self.categoryTree.expandItem(category) @@ -379,9 +375,9 @@ def resetTrees(self): def sortTrees(self): """ Sorts all trees by their name. """ for tree in [self.logTree, self.varTree, self.categoryTree]: - tree.sortItems(NAME_FIELD, Qt.AscendingOrder) + tree.sortItems(NAME_FIELD, Qt.SortOrder.AscendingOrder) for node in self.getNodeChildren(tree.invisibleRootItem()): - node.sortChildren(NAME_FIELD, Qt.AscendingOrder) + node.sortChildren(NAME_FIELD, Qt.SortOrder.AscendingOrder) def getNodeChildren(self, treeNode): children = [] @@ -414,13 +410,13 @@ def updatePacketSizeBar(self): def addNewVar(self, logTreeItem, target): parentName = logTreeItem.parent().text(NAME_FIELD) - varParent = target.findItems(parentName, Qt.MatchExactly, NAME_FIELD) + varParent = target.findItems(parentName, Qt.MatchFlag.MatchExactly, NAME_FIELD) item = logTreeItem.clone() if (len(varParent) == 0): newParent = QtWidgets.QTreeWidgetItem() - newParent.setData(0, Qt.DisplayRole, parentName) + newParent.setData(0, Qt.ItemDataRole.DisplayRole, parentName) newParent.addChild(item) target.addTopLevelItem(newParent) target.expandItem(newParent) @@ -455,7 +451,7 @@ def moveNode(self, source, target): self.moveNodeItem(source, target, source.currentItem()) def moveNodeByName(self, source, target, parentName, itemName): - parents = source.findItems(parentName, Qt.MatchExactly, NAME_FIELD) + parents = source.findItems(parentName, Qt.MatchFlag.MatchExactly, NAME_FIELD) node = None if (len(parents) > 0): parent = parents[0] @@ -484,7 +480,7 @@ def showErrorPopup(self, caption, message): self.box.setWindowTitle(caption) self.box.setText(message) # self.box.setButtonText(1, "Ok") - self.box.setWindowFlags(Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint) + self.box.setWindowFlags(Qt.WindowType.Dialog | Qt.WindowType.MSWindowsFixedSizeDialogHint) self.box.show() def updateToc(self): @@ -493,15 +489,15 @@ def updateToc(self): for group in list(toc.toc.keys()): groupItem = QtWidgets.QTreeWidgetItem() - groupItem.setData(NAME_FIELD, Qt.DisplayRole, group) + groupItem.setData(NAME_FIELD, Qt.ItemDataRole.DisplayRole, group) for param in list(toc.toc[group].keys()): item = QtWidgets.QTreeWidgetItem() - item.setData(NAME_FIELD, Qt.DisplayRole, param) - item.setData(ID_FIELD, Qt.DisplayRole, + item.setData(NAME_FIELD, Qt.ItemDataRole.DisplayRole, param) + item.setData(ID_FIELD, Qt.ItemDataRole.DisplayRole, toc.toc[group][param].ident) - item.setData(TYPE_FIELD, Qt.DisplayRole, + item.setData(TYPE_FIELD, Qt.ItemDataRole.DisplayRole, toc.toc[group][param].ctype) - item.setData(SIZE_FIELD, Qt.DisplayRole, + item.setData(SIZE_FIELD, Qt.ItemDataRole.DisplayRole, struct.calcsize(toc.toc[group][param].pytype)) groupItem.addChild(item) diff --git a/src/cfclient/ui/main.py b/src/cfclient/ui/main.py index 63f71e8c68..b3a3b971c0 100644 --- a/src/cfclient/ui/main.py +++ b/src/cfclient/ui/main.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2021 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -48,22 +48,22 @@ from cflib.crazyflie import Crazyflie from cflib.crazyflie.log import LogConfig from cflib.crazyflie.mem import MemoryElement -from PyQt5 import QtWidgets -from PyQt5 import uic -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtCore import pyqtSlot -from PyQt5.QtCore import QDir -from PyQt5.QtCore import QThread -from PyQt5.QtCore import QUrl -from PyQt5.QtCore import Qt -from PyQt5.QtWidgets import QAction -from PyQt5.QtWidgets import QActionGroup -from PyQt5.QtWidgets import QShortcut -from PyQt5.QtGui import QDesktopServices -from PyQt5.QtGui import QPalette -from PyQt5.QtWidgets import QLabel -from PyQt5.QtWidgets import QMenu -from PyQt5.QtWidgets import QMessageBox +from PyQt6 import QtWidgets +from PyQt6 import uic +from PyQt6.QtCore import pyqtSignal +from PyQt6.QtCore import pyqtSlot +from PyQt6.QtCore import QDir +from PyQt6.QtCore import QThread +from PyQt6.QtCore import QUrl +from PyQt6.QtCore import Qt +from PyQt6.QtGui import QAction +from PyQt6.QtGui import QActionGroup +from PyQt6.QtGui import QShortcut +from PyQt6.QtGui import QDesktopServices +from PyQt6.QtGui import QPalette +from PyQt6.QtWidgets import QLabel +from PyQt6.QtWidgets import QMenu +from PyQt6.QtWidgets import QMessageBox from .dialogs.cf2config import Cf2ConfigDialog from .dialogs.inputconfigdialogue import InputConfigDialogue @@ -140,8 +140,8 @@ def __init__(self, *args): # figure out what bgcolor to set from that. We always use the current # palette forgreound. # - self.textColor = self._statusbar_label.palette().color(QPalette.WindowText) - self.bgColor = self._statusbar_label.palette().color(QPalette.Background) + self.textColor = self._statusbar_label.palette().color(QPalette.ColorRole.WindowText) + self.bgColor = self._statusbar_label.palette().color(QPalette.ColorRole.Window) self.isDark = self.textColor.value() > self.bgColor.value() self.joystickReader = JoystickReader() @@ -332,7 +332,7 @@ def create_tab_toolboxes(self, tabs_menu_item, toolboxes_menu_item, tab_widget): cfclient.ui.pluginhelper.plotTab = tab_toolbox # Add to tabs menu - tab_action_item = QtWidgets.QAction(tab_toolbox.get_tab_toolbox_name()) + tab_action_item = QAction(tab_toolbox.get_tab_toolbox_name()) tab_action_item.setCheckable(True) tab_action_item.triggered.connect(self.toggle_tab_visibility) tab_action_item.tab_toolbox = tab_toolbox @@ -341,7 +341,7 @@ def create_tab_toolboxes(self, tabs_menu_item, toolboxes_menu_item, tab_widget): tabs_menu_item.addAction(tab_action_item) # Add to toolbox menu - toolbox_action_item = QtWidgets.QAction(tab_toolbox.get_tab_toolbox_name()) + toolbox_action_item = QAction(tab_toolbox.get_tab_toolbox_name()) toolbox_action_item.setCheckable(True) toolbox_action_item.triggered.connect(self.toggle_toolbox_visibility) toolbox_action_item.tab_toolbox = tab_toolbox diff --git a/src/cfclient/ui/tab_toolbox.py b/src/cfclient/ui/tab_toolbox.py index 428df72ebe..debd4dbc06 100644 --- a/src/cfclient/ui/tab_toolbox.py +++ b/src/cfclient/ui/tab_toolbox.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2013 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -31,10 +31,10 @@ import logging -from PyQt5 import QtWidgets -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtCore import Qt -from PyQt5.QtGui import QCloseEvent +from PyQt6 import QtWidgets +from PyQt6.QtCore import pyqtSignal +from PyQt6.QtCore import Qt +from PyQt6.QtGui import QCloseEvent from cfclient.utils.config import Config @@ -90,7 +90,7 @@ def set_display_state(self, new_display_state): self.enable() def preferred_dock_area(self): - return self._dock_area + return Qt.DockWidgetArea(self._dock_area) def set_preferred_dock_area(self, area): self._dock_area = area @@ -154,7 +154,7 @@ def _store_open_config(self, key, config): Config().set(key, value) def _get_toolbox_area_config(self): - result = Qt.RightDockWidgetArea + result = Qt.DockWidgetArea.RightDockWidgetArea config = self._read_toolbox_area_config() @@ -165,7 +165,7 @@ def _get_toolbox_area_config(self): def _store_toolbox_area_config(self, area): config = self._read_toolbox_area_config() - config[self.tab_toolbox_name] = area + config[self.tab_toolbox_name] = area.value self._write_toolbox_area_config(config) def _read_toolbox_area_config(self): @@ -183,7 +183,7 @@ def _read_toolbox_area_config(self): try: parts = composite.split(':') config[parts[0]] = int(parts[1]) - except KeyError: + except (KeyError, ValueError): logger.info(f'Can not understand config {composite}') return config diff --git a/src/cfclient/ui/tabs/ConsoleTab.py b/src/cfclient/ui/tabs/ConsoleTab.py index c49df310a6..7a894045e8 100644 --- a/src/cfclient/ui/tabs/ConsoleTab.py +++ b/src/cfclient/ui/tabs/ConsoleTab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2022 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -31,9 +31,9 @@ import logging -from PyQt5 import uic -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtGui import QTextCursor +from PyQt6 import uic +from PyQt6.QtCore import pyqtSignal +from PyQt6.QtGui import QTextCursor import cfclient from cfclient.ui.tab_toolbox import TabToolbox @@ -97,7 +97,7 @@ def printText(self, text): prev_cursor = self.console.textCursor() was_maximum = prev_scroll == scrollbar.maximum() - self.console.moveCursor(QTextCursor.End) + self.console.moveCursor(QTextCursor.MoveOperation.End) self.console.insertPlainText(text) self.console.setTextCursor(prev_cursor) diff --git a/src/cfclient/ui/tabs/CrtpSharkToolbox.py b/src/cfclient/ui/tabs/CrtpSharkToolbox.py index 1a8696c62c..9a04b1d1c5 100644 --- a/src/cfclient/ui/tabs/CrtpSharkToolbox.py +++ b/src/cfclient/ui/tabs/CrtpSharkToolbox.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2022 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -31,11 +31,11 @@ from time import time from binascii import hexlify -from PyQt5 import QtWidgets -from PyQt5 import uic -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtCore import pyqtSlot -from PyQt5.QtCore import Qt +from PyQt6 import QtWidgets +from PyQt6 import uic +from PyQt6.QtCore import pyqtSignal +from PyQt6.QtCore import pyqtSlot +from PyQt6.QtCore import Qt import cfclient from cfclient.ui.tab_toolbox import TabToolbox @@ -75,11 +75,11 @@ def _packet(self, dir, pk): line = QtWidgets.QTreeWidgetItem() ms_diff = int(round(time() * 1000)) - self._ms_offset - line.setData(0, Qt.DisplayRole, "%d" % ms_diff) - line.setData(1, Qt.DisplayRole, "%s" % dir) - line.setData(2, Qt.DisplayRole, "%d/%d" % (pk.port, pk.channel)) + line.setData(0, Qt.ItemDataRole.DisplayRole, "%d" % ms_diff) + line.setData(1, Qt.ItemDataRole.DisplayRole, "%s" % dir) + line.setData(2, Qt.ItemDataRole.DisplayRole, "%d/%d" % (pk.port, pk.channel)) - line.setData(3, Qt.DisplayRole, hexlify(pk.data).decode('utf8')) + line.setData(3, Qt.ItemDataRole.DisplayRole, hexlify(pk.data).decode('utf8')) s = "%d, %s, %d/%d, %s" % (ms_diff, dir, pk.port, pk.channel, hexlify(pk.data).decode('utf8')) diff --git a/src/cfclient/ui/tabs/ExampleTab.py b/src/cfclient/ui/tabs/ExampleTab.py index e147d15f4b..f10043c0bf 100644 --- a/src/cfclient/ui/tabs/ExampleTab.py +++ b/src/cfclient/ui/tabs/ExampleTab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2022 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -34,9 +34,9 @@ import logging -from PyQt5 import uic -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtWidgets import QMessageBox +from PyQt6 import uic +from PyQt6.QtCore import pyqtSignal +from PyQt6.QtWidgets import QMessageBox import cfclient from cfclient.ui.tab_toolbox import TabToolbox diff --git a/src/cfclient/ui/tabs/FlightTab.py b/src/cfclient/ui/tabs/FlightTab.py index 0f7c400c71..5b39b84220 100644 --- a/src/cfclient/ui/tabs/FlightTab.py +++ b/src/cfclient/ui/tabs/FlightTab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2022 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -32,9 +32,9 @@ import logging from enum import Enum -from PyQt5 import uic -from PyQt5.QtCore import Qt, pyqtSignal -from PyQt5.QtWidgets import QMessageBox +from PyQt6 import uic +from PyQt6.QtCore import Qt, pyqtSignal +from PyQt6.QtWidgets import QMessageBox import cfclient from cfclient.ui.widgets.ai import AttitudeIndicator @@ -247,7 +247,7 @@ def thrustToPercentage(self, thrust): return ((thrust / MAX_THRUST) * 100.0) def uiSetupReady(self): - flightComboIndex = self.flightModeCombo.findText(Config().get("flightmode"), Qt.MatchFixedString) + flightComboIndex = self.flightModeCombo.findText(Config().get("flightmode"), Qt.MatchFlag.MatchFixedString) if (flightComboIndex < 0): self.flightModeCombo.setCurrentIndex(0) self.flightModeCombo.currentIndexChanged.emit(0) @@ -755,14 +755,10 @@ def _populate_assisted_mode_dropdown(self): self._assist_mode_combo.addItem("Hover", 3) # Add the tooltips to the assist-mode items. - self._assist_mode_combo.setItemData(0, TOOLTIP_ALTITUDE_HOLD, - Qt.ToolTipRole) - self._assist_mode_combo.setItemData(1, TOOLTIP_POSITION_HOLD, - Qt.ToolTipRole) - self._assist_mode_combo.setItemData(2, TOOLTIP_HEIGHT_HOLD, - Qt.ToolTipRole) - self._assist_mode_combo.setItemData(3, TOOLTIP_HOVER, - Qt.ToolTipRole) + self._assist_mode_combo.setItemData(0, TOOLTIP_ALTITUDE_HOLD, Qt.ItemDataRole.ToolTipRole) + self._assist_mode_combo.setItemData(1, TOOLTIP_POSITION_HOLD, Qt.ItemDataRole.ToolTipRole) + self._assist_mode_combo.setItemData(2, TOOLTIP_HEIGHT_HOLD, Qt.ItemDataRole.ToolTipRole) + self._assist_mode_combo.setItemData(3, TOOLTIP_HOVER, Qt.ItemDataRole.ToolTipRole) heightHoldPossible = False hoverPossible = False diff --git a/src/cfclient/ui/tabs/GpsTab.py b/src/cfclient/ui/tabs/GpsTab.py index 11425f2467..fc71c528f8 100644 --- a/src/cfclient/ui/tabs/GpsTab.py +++ b/src/cfclient/ui/tabs/GpsTab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2022 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -31,15 +31,15 @@ import logging import cfclient -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtWidgets import QMessageBox +from PyQt6.QtCore import pyqtSignal +from PyQt6.QtWidgets import QMessageBox from cfclient.ui.tab_toolbox import TabToolbox from cflib.crazyflie.log import LogConfig -from PyQt5 import QtCore -from PyQt5 import QtGui -from PyQt5 import QtNetwork -from PyQt5 import QtWebKit -from PyQt5 import uic +from PyQt6 import QtCore +from PyQt6 import QtGui +from PyQt6 import QtNetwork +from PyQt6 import QtWebKit +from PyQt6 import uic __author__ = 'Bitcraze AB' __all__ = ['GpsTab'] diff --git a/src/cfclient/ui/tabs/LEDTab.py b/src/cfclient/ui/tabs/LEDTab.py index 5b85a9452b..bb99035ef1 100644 --- a/src/cfclient/ui/tabs/LEDTab.py +++ b/src/cfclient/ui/tabs/LEDTab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2022 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -32,9 +32,9 @@ import logging -from PyQt5 import QtGui, uic -from PyQt5.QtCore import pyqtSignal -from PyQt5 import QtWidgets +from PyQt6 import QtGui, uic +from PyQt6.QtCore import pyqtSignal +from PyQt6 import QtWidgets import cfclient from cfclient.ui.tab_toolbox import TabToolbox diff --git a/src/cfclient/ui/tabs/LogBlockDebugTab.py b/src/cfclient/ui/tabs/LogBlockDebugTab.py index dd944c08fb..42551373f6 100644 --- a/src/cfclient/ui/tabs/LogBlockDebugTab.py +++ b/src/cfclient/ui/tabs/LogBlockDebugTab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2013-2022 Bitcraze AB +# Copyright (C) 2013-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -30,8 +30,8 @@ to edit them. """ -from PyQt5 import QtCore, QtWidgets, uic -from PyQt5.QtCore import Qt, pyqtSignal +from PyQt6 import QtWidgets, uic +from PyQt6.QtCore import Qt, pyqtSignal import cfclient from cfclient.ui.tab_toolbox import TabToolbox @@ -63,7 +63,7 @@ def __init__(self, helper): self._block_tree.setHeaderLabels( ['Id', 'Name', 'Period (ms)', 'Added', 'Started', 'Error', 'Contents']) - self._block_tree.sortItems(0, QtCore.Qt.AscendingOrder) + self._block_tree.sortItems(0, Qt.SortOrder.AscendingOrder) def _block_added(self, block): """Callback when a new logblock has been created""" @@ -75,19 +75,19 @@ def _update_tree(self, conf, value): self._block_tree.clear() for block in self._helper.cf.log.log_blocks: item = QtWidgets.QTreeWidgetItem() - item.setFlags(Qt.ItemIsEnabled | - Qt.ItemIsSelectable) - item.setData(0, Qt.DisplayRole, block.id) - item.setData(1, Qt.EditRole, block.name) - item.setData(2, Qt.DisplayRole, (block.period_in_ms)) - item.setData(3, Qt.DisplayRole, block.added) - item.setData(4, Qt.EditRole, block.started) - item.setData(5, Qt.EditRole, block.err_no) + item.setFlags(Qt.ItemFlag.ItemIsEnabled | + Qt.ItemFlag.ItemIsSelectable) + item.setData(0, Qt.ItemDataRole.DisplayRole, block.id) + item.setData(1, Qt.ItemDataRole.EditRole, block.name) + item.setData(2, Qt.ItemDataRole.DisplayRole, (block.period_in_ms)) + item.setData(3, Qt.ItemDataRole.DisplayRole, block.added) + item.setData(4, Qt.ItemDataRole.EditRole, block.started) + item.setData(5, Qt.ItemDataRole.EditRole, block.err_no) for var in block.variables: subItem = QtWidgets.QTreeWidgetItem() - subItem.setFlags(Qt.ItemIsEnabled | - Qt.ItemIsSelectable) - subItem.setData(6, Qt.EditRole, var.name) + subItem.setFlags(Qt.ItemFlag.ItemIsEnabled | + Qt.ItemFlag.ItemIsSelectable) + subItem.setData(6, Qt.ItemDataRole.EditRole, var.name) item.addChild(subItem) self._block_tree.addTopLevelItem(item) diff --git a/src/cfclient/ui/tabs/LogBlockTab.py b/src/cfclient/ui/tabs/LogBlockTab.py index 8ce86d0232..2342f2bb1e 100644 --- a/src/cfclient/ui/tabs/LogBlockTab.py +++ b/src/cfclient/ui/tabs/LogBlockTab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2013-2022 Bitcraze AB +# Copyright (C) 2013-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -31,17 +31,17 @@ logging and also to write the logging data to file. """ -from PyQt5 import uic -from PyQt5.QtCore import Qt, pyqtSignal +from PyQt6 import uic +from PyQt6.QtCore import Qt, pyqtSignal import cfclient from cfclient.ui.tab_toolbox import TabToolbox import logging -from PyQt5.QtWidgets import QApplication, QStyledItemDelegate -from PyQt5.QtWidgets import QAbstractItemView, QStyleOptionButton, QStyle -from PyQt5.QtCore import QAbstractItemModel, QModelIndex +from PyQt6.QtWidgets import QApplication, QStyledItemDelegate +from PyQt6.QtWidgets import QAbstractItemView, QStyleOptionButton, QStyle +from PyQt6.QtCore import QAbstractItemModel, QModelIndex from cfclient.utils.logdatawriter import LogWriter @@ -220,7 +220,7 @@ def columnCount(self, parent): def headerData(self, section, orientation, role): """Re-implemented method to get the headers""" - if role == Qt.DisplayRole: + if role == Qt.ItemDataRole.DisplayRole: return self._column_headers[section] def rowCount(self, parent): @@ -250,20 +250,20 @@ def data(self, index, role): node = index.internalPointer() parent = node.parent if parent: - if role == Qt.DisplayRole and index.column() == 5: + if role == Qt.ItemDataRole.DisplayRole and index.column() == 5: return node.name - elif not parent and role == Qt.DisplayRole and index.column() == 5: + elif not parent and role == Qt.ItemDataRole.DisplayRole and index.column() == 5: return node.var_list() - elif not parent and role == Qt.DisplayRole: + elif not parent and role == Qt.ItemDataRole.DisplayRole: if index.column() == 0: return node.id if index.column() == 1: return node.name if index.column() == 2: return str(node.period) - if role == Qt.TextAlignmentRole and \ + if role == Qt.ItemDataRole.TextAlignmentRole and \ (index.column() == 4 or index.column() == 3): - return Qt.AlignHCenter | Qt.AlignVCenter + return Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter return None @@ -286,35 +286,33 @@ def paint(self, painter, option, index): col = index.column() if not item.parent and (col == 3 or col == 4): s = QStyleOptionButton() - checkbox_rect = QApplication.style(). \ - subElementRect(QStyle.SE_CheckBoxIndicator, option) + checkbox_rect = QApplication.style().subElementRect(QStyle.SubElement.SE_CheckBoxIndicator, option) s.rect = option.rect center_offset = int(s.rect.width() / 2 - checkbox_rect.width() / 2) s.rect.adjust(center_offset, 0, 0, 0) if col == 3: if not item.doing_transaction(): - s.state = QStyle.State_Enabled + s.state = QStyle.StateFlag.State_Enabled if item.logging_started(): - s.state |= QStyle.State_On + s.state |= QStyle.StateFlag.State_On if col == 4: - s.state = QStyle.State_Enabled + s.state = QStyle.StateFlag.State_Enabled if item.writing_to_file(): - s.state |= QStyle.State_On + s.state |= QStyle.StateFlag.State_On self._paint_checkbox(s, painter) else: super(CheckboxDelegate, self).paint(painter, option, index) def _paint_checkbox(self, style, painter): - QApplication.style().drawControl( - QStyle.CE_CheckBox, style, painter) + QApplication.style().drawControl(QStyle.ControlElement.CE_CheckBox, style, painter) def _paint_checkbox_osx_workaround(self, style, painter): painter.save() painter.translate(style.rect.topLeft()) - QApplication.style().drawControl(QStyle.CE_CheckBox, style, painter) + QApplication.style().drawControl(QStyle.ControlElement.CE_CheckBox, style, painter) painter.restore() @@ -340,7 +338,7 @@ def __init__(self, helper): self._block_tree.setModel(self._model) self._block_tree.clicked.connect(self._model.clicked) self._block_tree.setItemDelegate(CheckboxDelegate()) - self._block_tree.setSelectionMode(QAbstractItemView.NoSelection) + self._block_tree.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection) def _block_added(self, block): """Callback from logging layer when a new block is added""" diff --git a/src/cfclient/ui/tabs/LogClientTab.py b/src/cfclient/ui/tabs/LogClientTab.py index fbd56c2816..085edf694c 100644 --- a/src/cfclient/ui/tabs/LogClientTab.py +++ b/src/cfclient/ui/tabs/LogClientTab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2022 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -31,8 +31,8 @@ import logging -from PyQt5 import uic -from PyQt5.QtCore import pyqtSignal +from PyQt6 import uic +from PyQt6.QtCore import pyqtSignal import cfclient from cfclient.ui.tab_toolbox import TabToolbox diff --git a/src/cfclient/ui/tabs/LogTab.py b/src/cfclient/ui/tabs/LogTab.py index da6e92915a..43cf01fe6f 100644 --- a/src/cfclient/ui/tabs/LogTab.py +++ b/src/cfclient/ui/tabs/LogTab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2012 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -29,11 +29,11 @@ import cfclient from cfclient.ui.tab_toolbox import TabToolbox -from PyQt5 import QtWidgets -from PyQt5 import uic -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtCore import pyqtSlot -from PyQt5.QtCore import Qt +from PyQt6 import QtWidgets +from PyQt6 import uic +from PyQt6.QtCore import pyqtSignal +from PyQt6.QtCore import pyqtSlot +from PyQt6.QtCore import Qt __author__ = 'Bitcraze AB' __all__ = ['LogTab'] @@ -61,7 +61,7 @@ def __init__(self, helper): self.logTree.setHeaderLabels(['Name', 'ID', 'Unpack', 'Storage', 'Description']) self.logTree.header().resizeSection(0, 150) self.logTree.setSortingEnabled(True) - self.logTree.sortItems(0, Qt.AscendingOrder) + self.logTree.sortItems(0, Qt.SortOrder.AscendingOrder) self.cf.connected.add_callback(self.connectedSignal.emit) self.connectedSignal.connect(self.connected) @@ -75,7 +75,7 @@ def disconnected(self, linkname): root = self.logTree.invisibleRootItem() for i in range(root.childCount()): item = root.child(i) - item.setFlags(Qt.NoItemFlags) + item.setFlags(Qt.ItemFlag.NoItemFlags) @pyqtSlot(str) def connected(self, linkURI): @@ -85,20 +85,20 @@ def connected(self, linkURI): for row_idx, group in enumerate(list(toc.toc.keys())): groupItem = QtWidgets.QTreeWidgetItem() - groupItem.setData(0, Qt.DisplayRole, group) + groupItem.setData(0, Qt.ItemDataRole.DisplayRole, group) for param in list(toc.toc[group].keys()): item = QtWidgets.QTreeWidgetItem() - item.setData(0, Qt.DisplayRole, param) - item.setData(1, Qt.DisplayRole, toc.toc[group][param].ident) - item.setData(2, Qt.DisplayRole, toc.toc[group][param].pytype) - item.setData(3, Qt.DisplayRole, toc.toc[group][param].ctype) + item.setData(0, Qt.ItemDataRole.DisplayRole, param) + item.setData(1, Qt.ItemDataRole.DisplayRole, toc.toc[group][param].ident) + item.setData(2, Qt.ItemDataRole.DisplayRole, toc.toc[group][param].pytype) + item.setData(3, Qt.ItemDataRole.DisplayRole, toc.toc[group][param].ctype) if cfclient.log_param_doc is not None: try: log_groups = cfclient.log_param_doc['logs'][group] log_variable = log_groups['variables'][param] - item.setData(4, Qt.DisplayRole, log_variable['short_desc']) + item.setData(4, Qt.ItemDataRole.DisplayRole, log_variable['short_desc']) except: # noqa pass diff --git a/src/cfclient/ui/tabs/ParamTab.py b/src/cfclient/ui/tabs/ParamTab.py index 48d2c33e4d..cd159924d7 100644 --- a/src/cfclient/ui/tabs/ParamTab.py +++ b/src/cfclient/ui/tabs/ParamTab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2022 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -32,11 +32,11 @@ import logging -from PyQt5 import uic, QtCore -from PyQt5.QtCore import QSortFilterProxyModel, Qt, pyqtSignal -from PyQt5.QtCore import QAbstractItemModel, QModelIndex, QVariant -from PyQt5.QtGui import QBrush, QColor -from PyQt5.QtWidgets import QHeaderView +from PyQt6 import uic, QtCore +from PyQt6.QtCore import QSortFilterProxyModel, Qt, pyqtSignal +from PyQt6.QtCore import QAbstractItemModel, QModelIndex, QVariant +from PyQt6.QtGui import QBrush, QColor +from PyQt6.QtWidgets import QHeaderView from cflib.crazyflie.param import PersistentParamState @@ -167,7 +167,7 @@ def columnCount(self, parent): def headerData(self, section, orientation, role): """Re-implemented method to get the headers""" - if role == Qt.DisplayRole: + if role == Qt.ItemDataRole.DisplayRole: return self._column_headers[section] def rowCount(self, parent): @@ -195,7 +195,7 @@ def index(self, row, column, parent): def data(self, index, role): """Re-implemented method to get the data for a given index and role""" - if role == Qt.BackgroundColorRole: + if role == Qt.ItemDataRole.BackgroundRole: if index.row() % 2 == 0: return QVariant(self._mainUI.bgColor) else: @@ -211,9 +211,9 @@ def data(self, index, role): node = index.internalPointer() parent = node.parent if not parent: - if role == Qt.DisplayRole and index.column() == 0: + if role == Qt.ItemDataRole.DisplayRole and index.column() == 0: return node.name - elif role == Qt.DisplayRole: + elif role == Qt.ItemDataRole.DisplayRole: if index.column() == 0: return node.name if index.column() == 1: @@ -224,7 +224,7 @@ def data(self, index, role): return 'Yes' if node.persistent else 'No' if index.column() == 4: return node.value - elif (role == Qt.BackgroundRole and index.column() == 4 and + elif (role == Qt.ItemDataRole.BackgroundRole and index.column() == 4 and node.is_updating): return self._red_brush @@ -235,7 +235,7 @@ def flags(self, index): flag = super(ParamBlockModel, self).flags(index) if not self._enabled: - return Qt.NoItemFlags + return Qt.ItemFlag.NoItemFlags return flag @@ -313,7 +313,7 @@ def onFilterChanged(text): self.filterBox.textChanged.connect(onFilterChanged) self.paramTree.setModel(self.proxyModel) - self.paramTree.header().setSectionResizeMode(QHeaderView.ResizeToContents) + self.paramTree.header().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) self.paramTree.selectionModel().selectionChanged.connect(self._paramChanged) def _param_default_cb(self, default_value): diff --git a/src/cfclient/ui/tabs/PlotTab.py b/src/cfclient/ui/tabs/PlotTab.py index 4bfdca3ce1..52e867a424 100644 --- a/src/cfclient/ui/tabs/PlotTab.py +++ b/src/cfclient/ui/tabs/PlotTab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2022 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -32,12 +32,12 @@ from cfclient.ui.tab_toolbox import TabToolbox from cfclient.ui.widgets.plotwidget import PlotWidget -from PyQt5 import uic -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtCore import QAbstractItemModel -from PyQt5.QtCore import QModelIndex -from PyQt5.QtCore import Qt -from PyQt5.QtWidgets import QMessageBox +from PyQt6 import uic +from PyQt6.QtCore import pyqtSignal +from PyQt6.QtCore import QAbstractItemModel +from PyQt6.QtCore import QModelIndex +from PyQt6.QtCore import Qt +from PyQt6.QtWidgets import QMessageBox import cfclient @@ -99,7 +99,7 @@ def data(self, index, role): node = index.internalPointer() # noqa if not index.isValid() or not 0 <= index.row() < len(self._nodes): return None - if role == Qt.DisplayRole: + if role == Qt.ItemDataRole.DisplayRole: return self._nodes[index.row()].name return None diff --git a/src/cfclient/ui/tabs/QualisysTab.py b/src/cfclient/ui/tabs/QualisysTab.py deleted file mode 100644 index 94baa92f52..0000000000 --- a/src/cfclient/ui/tabs/QualisysTab.py +++ /dev/null @@ -1,1424 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2022 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -# 02110-1301, USA. -""" -Tab for controlling the Crazyflie using Qualisys Motion Capturing system -""" - -import logging -import time -import datetime -import math -from enum import Enum - -from PyQt5 import uic -from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, pyqtProperty -from PyQt5.QtCore import QStateMachine, QState, QEvent, QTimer -from PyQt5.QtCore import QAbstractTransition -from PyQt5.QtWidgets import QMessageBox -from PyQt5.QtGui import QStandardItemModel, QStandardItem - -import cfclient -from cfclient.ui.tab_toolbox import TabToolbox -from cfclient.utils.config import Config -from cflib.crazyflie.log import LogConfig -from cflib.crazyflie.syncLogger import SyncLogger - -import xml.etree.cElementTree as ET -import threading - -import qtm -import asyncio - -__author__ = 'Bitcraze AB' -__all__ = ['QualisysTab'] - -logger = logging.getLogger(__name__) - -qualisys_tab_class, _ = uic.loadUiType(cfclient.module_path + "/ui/tabs/qualisysTab.ui") - - -class FlightModeEvent(QEvent): - - def __init__(self, mode, parent=None): - super(FlightModeEvent, self).__init__(QEvent.Type(QEvent.User + 1)) - self.mode = mode - - -class FlightModeTransition(QAbstractTransition): - - def __init__(self, value, parent=None): - super(FlightModeTransition, self).__init__(parent) - self.value = value - - def eventTest(self, event): - if event.type() != QEvent.Type(QEvent.User + 1): - return False - - return event.mode == self.value - - def onTransition(self, event): - pass - - -class FlightModeStates(Enum): - LAND = 0 - LIFT = 1 - FOLLOW = 2 - PATH = 3 - HOVERING = 4 - GROUNDED = 5 - DISCONNECTED = 6 - CIRCLE = 7 - RECORD = 8 - - -def start_async_task(task): - return asyncio.ensure_future(task) - - -class QDiscovery(QObject): - discoveringChanged = pyqtSignal(bool) - discoveredQTM = pyqtSignal(str, str) - - def __init__(self, *args): - super().__init__(*args) - self._discovering = False - self._found_qtms = {} - - @pyqtProperty(bool, notify=discoveringChanged) - def discovering(self): - return self._discovering - - @discovering.setter - def discovering(self, value): - if value != self._discovering: - self._discovering = value - self.discoveringChanged.emit(value) - - def discover(self, *, interface='0.0.0.0'): - self.discovering = True - start_async_task(self._discover_qtm(interface)) - - async def _discover_qtm(self, interface): - try: - async for qtm_instance in qtm.Discover(interface): - info = qtm_instance.info.decode("utf-8").split(",")[0] - self.discoveredQTM.emit(info, qtm_instance.host) - - except Exception as e: - logger.info("Exception during qtm discovery: %s", e) - - self.discovering = False - - -class QualisysTab(TabToolbox, qualisys_tab_class): - """ - Tab for controlling the crazyflie using - Qualisys Motion Capturing system - """ - - _connected_signal = pyqtSignal(str) - _disconnected_signal = pyqtSignal(str) - _log_data_signal = pyqtSignal(int, object, object) - _log_error_signal = pyqtSignal(object, str) - _param_updated_signal = pyqtSignal(str, str) - _imu_data_signal = pyqtSignal(int, object, object) - - _flight_path_select_row = pyqtSignal(int) - _flight_path_set_model = pyqtSignal(object) - _path_selector_add_item = pyqtSignal(str) - _path_selector_set_index = pyqtSignal(int) - - statusChanged = pyqtSignal(str) - cfStatusChanged = pyqtSignal(str) - qtmStatusChanged = pyqtSignal(str) - - def __init__(self, helper): - super(QualisysTab, self).__init__(helper, 'Qualisys') - - # Setting self._qtm_status should not be required here, but for some - # reason python 3.7.5 crashes without it. - self._qtm_status = None - - self.setupUi(self) - - self._machine = QStateMachine() - self._setup_states() - self._event = threading.Event() - - self.qtm_6DoF_labels = None - self._qtm_connection = None - self._cf = None - self.model = QStandardItemModel(10, 4) - - self._cf_status = self.cfStatusLabel.text() - self._status = self.statusLabel.text() - self._qtm_status = self.qtmStatusLabel.text() - - self.flying_enabled = False - self.switch_flight_mode(FlightModeStates.DISCONNECTED) - self.path_pos_threshold = 0.2 - self.circle_pos_threshold = 0.1 - self.circle_radius = 1.5 - self.circle_resolution = 15.0 - self.position_hold_timelimit = 0.1 - self.length_from_wand = 2.0 - self.circle_height = 1.2 - self.new_path = [] - self.recording = False - self.land_for_recording = False - self.default_flight_paths = [ - [ - "Path 1: Sandbox", - [0.0, -1.0, 1.0, 0.0], - [0.0, 1.0, 1.0, 0.0]], - [ - "Path 2: Height Test", - [0.0, 0.0, 0.5, 0.0], - [0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 1.5, 0.0], - [0.0, 0.0, 2.0, 0.0], - [0.0, 0.0, 2.3, 0.0], - [0.0, 0.0, 1.8, 0.0], - [0.0, 0.0, 0.5, 0.0], - [0.0, 0.0, 0.3, 0.0], - [0.0, 0.0, 0.15, 0.0]], - [ - "Path 3: 'Spiral'", - [0.0, 0.0, 1.0, 0.0], - [0.5, 0.5, 1.0, 0.0], - [0.0, 1.0, 1.0, 0.0], - [-0.5, 0.5, 1.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - [0.5, 0.5, 1.2, 0.0], - [0.0, 1.0, 1.4, 0.0], - [-0.5, 0.5, 1.6, 0.0], - [0.0, 0.0, 1.8, 0.0], - [0.5, 0.5, 1.5, 0.0], - [0.0, 1.0, 1.0, 0.0], - [-0.5, 0.5, 0.5, 0.0], - [0.0, 0.0, 0.25, 0.0]]] - - # The position and rotation of the cf and wand obtained by the - # camera tracking, if it cant be tracked the position becomes Nan - self.cf_pos = Position(0, 0, 0) - self.wand_pos = Position(0, 0, 0) - - # The regular cf_pos can a times due to lost tracing become Nan, - # this the latest known valid cf position - self.valid_cf_pos = Position(0, 0, 0) - - try: - self.flight_paths = Config().get("flight_paths") - except Exception: - logger.debug("No flight config") - self.flight_paths = self.default_flight_paths - - if self.flight_paths == []: - self.flight_paths = self.default_flight_paths - - # Always wrap callbacks from Crazyflie API though QT Signal/Slots - # to avoid manipulating the UI when rendering it - self._connected_signal.connect(self._connected) - self._disconnected_signal.connect(self._disconnected) - self._log_data_signal.connect(self._log_data_received) - self._param_updated_signal.connect(self._param_updated) - - self._flight_path_select_row.connect(self._select_flight_path_row) - self._flight_path_set_model.connect(self._set_flight_path_model) - self._path_selector_add_item.connect(self._add_path_selector_item) - self._path_selector_set_index.connect(self._set_path_selector_index) - - self.statusChanged.connect(self._update_status) - self.cfStatusChanged.connect(self._update_cf_status) - self.qtmStatusChanged.connect(self._update_qtm_status) - - # Connect the Crazyflie API callbacks to the signals - self._helper.cf.connected.add_callback(self._connected_signal.emit) - - self._helper.cf.disconnected.add_callback( - self._disconnected_signal.emit) - - # Connect the UI elements - self.connectQtmButton.clicked.connect(self.establish_qtm_connection) - self.landButton.clicked.connect(self.set_land_mode) - self.liftButton.clicked.connect(self.set_lift_mode) - self.followButton.clicked.connect(self.set_follow_mode) - self.emergencyButton.clicked.connect(self.set_kill_engine) - self.pathButton.clicked.connect(self.set_path_mode) - self.circleButton.clicked.connect(self.set_circle_mode) - self.recordButton.clicked.connect(self.set_record_mode) - self.removePathButton.clicked.connect(self.remove_current_path) - - for i in range(len(self.flight_paths)): - self.pathSelector.addItem(self.flight_paths[i][0]) - - self.pathSelector.currentIndexChanged.connect(self.path_changed) - - self.quadBox.currentIndexChanged[str].connect(self.quad_changed) - self.stickBox.currentIndexChanged[str].connect(self.stick_changed) - self.stickName = 'qstick' - self.quadName = 'crazyflie' - - # Populate UI elements - self.posHoldPathBox.setText(str(self.position_hold_timelimit)) - self.radiusBox.setText(str(self.circle_radius)) - self.posHoldCircleBox.setText(str(self.position_hold_timelimit)) - self.resolutionBox.setText(str(self.circle_resolution)) - self.path_changed() - - self._discovery = QDiscovery() - self._discovery.discoveringChanged.connect(self._is_discovering) - self._discovery.discoveredQTM.connect(self._qtm_discovered) - - self.discoverQTM.clicked.connect(self._discovery.discover) - self._discovery.discover() - - self._ui_update_timer = QTimer(self) - self._ui_update_timer.timeout.connect(self._update_ui) - - def _setup_states(self): - parent_state = QState() - - # DISCONNECTED - disconnected = QState(parent_state) - disconnected.assignProperty(self, "status", "Disabled") - disconnected.assignProperty(self.pathButton, "text", "Path Mode") - disconnected.assignProperty(self.followButton, "text", "Follow Mode") - disconnected.assignProperty(self.circleButton, "text", "Circle Mode") - disconnected.assignProperty(self.recordButton, "text", "Record Mode") - disconnected.assignProperty(self.pathButton, "enabled", False) - disconnected.assignProperty(self.emergencyButton, "enabled", False) - disconnected.assignProperty(self.landButton, "enabled", False) - disconnected.assignProperty(self.followButton, "enabled", False) - disconnected.assignProperty(self.liftButton, "enabled", False) - disconnected.assignProperty(self.circleButton, "enabled", False) - disconnected.assignProperty(self.recordButton, "enabled", False) - disconnected.entered.connect(self._flight_mode_disconnected_entered) - - # HOVERING - hovering = QState(parent_state) - hovering.assignProperty(self, "status", "Hovering...") - hovering.assignProperty(self.pathButton, "text", "Path Mode") - hovering.assignProperty(self.followButton, "text", "Follow Mode") - hovering.assignProperty(self.circleButton, "text", "Circle Mode") - hovering.assignProperty(self.recordButton, "text", "Record Mode") - hovering.assignProperty(self.pathButton, "enabled", True) - hovering.assignProperty(self.emergencyButton, "enabled", True) - hovering.assignProperty(self.landButton, "enabled", True) - hovering.assignProperty(self.followButton, "enabled", True) - hovering.assignProperty(self.liftButton, "enabled", False) - hovering.assignProperty(self.circleButton, "enabled", True) - hovering.assignProperty(self.recordButton, "enabled", True) - hovering.entered.connect(self._flight_mode_hovering_entered) - - # GROUNDED - grounded = QState(parent_state) - grounded.assignProperty(self, "status", "Landed") - grounded.assignProperty(self.pathButton, "text", "Path Mode") - grounded.assignProperty(self.followButton, "text", "Follow Mode") - grounded.assignProperty(self.circleButton, "text", "Circle Mode") - grounded.assignProperty(self.recordButton, "text", "Record Mode") - grounded.assignProperty(self.pathButton, "enabled", True) - grounded.assignProperty(self.emergencyButton, "enabled", True) - grounded.assignProperty(self.landButton, "enabled", False) - grounded.assignProperty(self.followButton, "enabled", False) - grounded.assignProperty(self.liftButton, "enabled", True) - grounded.assignProperty(self.circleButton, "enabled", True) - grounded.assignProperty(self.recordButton, "enabled", True) - grounded.entered.connect(self._flight_mode_grounded_entered) - - # PATH - path = QState(parent_state) - path.assignProperty(self, "status", "Path Mode") - path.assignProperty(self.pathButton, "text", "Stop") - path.assignProperty(self.followButton, "text", "Follow Mode") - path.assignProperty(self.circleButton, "text", "Circle Mode") - path.assignProperty(self.recordButton, "text", "Record Mode") - path.assignProperty(self.pathButton, "enabled", True) - path.assignProperty(self.emergencyButton, "enabled", True) - path.assignProperty(self.landButton, "enabled", True) - path.assignProperty(self.followButton, "enabled", False) - path.assignProperty(self.liftButton, "enabled", False) - path.assignProperty(self.circleButton, "enabled", False) - path.assignProperty(self.recordButton, "enabled", False) - path.entered.connect(self._flight_mode_path_entered) - - # FOLLOW - follow = QState(parent_state) - follow.assignProperty(self, "status", "Follow Mode") - follow.assignProperty(self.pathButton, "text", "Path Mode") - follow.assignProperty(self.followButton, "text", "Stop") - follow.assignProperty(self.circleButton, "text", "Circle Mode") - follow.assignProperty(self.recordButton, "text", "Record Mode") - follow.assignProperty(self.pathButton, "enabled", False) - follow.assignProperty(self.emergencyButton, "enabled", True) - follow.assignProperty(self.landButton, "enabled", True) - follow.assignProperty(self.followButton, "enabled", False) - follow.assignProperty(self.liftButton, "enabled", False) - follow.assignProperty(self.circleButton, "enabled", False) - follow.assignProperty(self.recordButton, "enabled", False) - follow.entered.connect(self._flight_mode_follow_entered) - - # LIFT - lift = QState(parent_state) - lift.assignProperty(self, "status", "Lifting...") - lift.assignProperty(self.pathButton, "enabled", False) - lift.assignProperty(self.emergencyButton, "enabled", True) - lift.assignProperty(self.landButton, "enabled", True) - lift.assignProperty(self.followButton, "enabled", False) - lift.assignProperty(self.liftButton, "enabled", False) - lift.assignProperty(self.circleButton, "enabled", False) - lift.assignProperty(self.recordButton, "enabled", False) - lift.entered.connect(self._flight_mode_lift_entered) - - # LAND - land = QState(parent_state) - land.assignProperty(self, "status", "Landing...") - land.assignProperty(self.pathButton, "enabled", False) - land.assignProperty(self.emergencyButton, "enabled", True) - land.assignProperty(self.landButton, "enabled", False) - land.assignProperty(self.followButton, "enabled", False) - land.assignProperty(self.liftButton, "enabled", False) - land.assignProperty(self.circleButton, "enabled", False) - land.assignProperty(self.recordButton, "enabled", False) - land.entered.connect(self._flight_mode_land_entered) - - # CIRCLE - circle = QState(parent_state) - circle.assignProperty(self, "status", "Circle Mode") - circle.assignProperty(self.pathButton, "text", "Path Mode") - circle.assignProperty(self.followButton, "text", "Follow Mode") - circle.assignProperty(self.circleButton, "text", "Stop") - circle.assignProperty(self.recordButton, "text", "Record Mode") - circle.assignProperty(self.pathButton, "enabled", False) - circle.assignProperty(self.emergencyButton, "enabled", True) - circle.assignProperty(self.landButton, "enabled", True) - circle.assignProperty(self.followButton, "enabled", False) - circle.assignProperty(self.liftButton, "enabled", False) - circle.assignProperty(self.circleButton, "enabled", True) - circle.assignProperty(self.recordButton, "enabled", False) - circle.entered.connect(self._flight_mode_circle_entered) - - # RECORD - record = QState(parent_state) - record.assignProperty(self, "status", "Record Mode") - record.assignProperty(self.pathButton, "text", "Path Mode") - record.assignProperty(self.followButton, "text", "Follow Mode") - record.assignProperty(self.circleButton, "text", "Circle Mode") - record.assignProperty(self.recordButton, "text", "Stop") - record.assignProperty(self.pathButton, "enabled", False) - record.assignProperty(self.emergencyButton, "enabled", True) - record.assignProperty(self.landButton, "enabled", False) - record.assignProperty(self.followButton, "enabled", False) - record.assignProperty(self.liftButton, "enabled", False) - record.assignProperty(self.circleButton, "enabled", False) - record.assignProperty(self.recordButton, "enabled", True) - record.entered.connect(self._flight_mode_record_entered) - - def add_transition(mode, child_state, parent): - transition = FlightModeTransition(mode) - transition.setTargetState(child_state) - parent.addTransition(transition) - - add_transition(FlightModeStates.LAND, land, parent_state) - add_transition(FlightModeStates.LIFT, lift, parent_state) - add_transition(FlightModeStates.FOLLOW, follow, parent_state) - add_transition(FlightModeStates.PATH, path, parent_state) - add_transition(FlightModeStates.HOVERING, hovering, parent_state) - add_transition(FlightModeStates.GROUNDED, grounded, parent_state) - add_transition(FlightModeStates.DISCONNECTED, disconnected, - parent_state) - add_transition(FlightModeStates.CIRCLE, circle, parent_state) - add_transition(FlightModeStates.RECORD, record, parent_state) - - parent_state.setInitialState(disconnected) - self._machine.addState(parent_state) - self._machine.setInitialState(parent_state) - self._machine.start() - - def _update_flight_status(self): - prev_flying_enabled = self.flying_enabled - self.flying_enabled = self._cf is not None and \ - self._qtm_connection is not None - - if not prev_flying_enabled and self.flying_enabled: - self.switch_flight_mode(FlightModeStates.GROUNDED) - t = threading.Thread(target=self.flight_controller) - t.start() - - if prev_flying_enabled and not self.flying_enabled: - self.switch_flight_mode(FlightModeStates.DISCONNECTED) - - def _is_discovering(self, discovering): - if discovering: - self.qtmIpBox.clear() - self.discoverQTM.setEnabled(not discovering) - - def _qtm_discovered(self, info, ip): - self.qtmIpBox.addItem("{} {}".format(ip, info)) - - @pyqtSlot(str) - def _update_status(self, status): - self.statusLabel.setText("Status: {}".format(status)) - - @pyqtSlot(str) - def _update_cf_status(self, status): - self.cfStatusLabel.setText(status) - - @pyqtSlot(str) - def _update_qtm_status(self, status): - self.qtmStatusLabel.setText(status) - - @pyqtSlot(str) - def quad_changed(self, quad): - self.quadName = quad - - @pyqtSlot(str) - def stick_changed(self, stick): - self.stickName = stick - - # Properties - - @pyqtProperty(str, notify=statusChanged) - def status(self): - return self._status - - @status.setter - def status(self, value): - if value != self._status: - self._status = value - self.statusChanged.emit(value) - - @pyqtProperty(str, notify=qtmStatusChanged) - def qtmStatus(self): - return self._qtm_status - - @qtmStatus.setter - def qtmStatus(self, value): - if value != self._qtm_status: - self._qtm_status = value - self.qtmStatusChanged.emit(value) - - @pyqtProperty(str, notify=cfStatusChanged) - def cfStatus(self): - return self._qtm_status - - @cfStatus.setter - def cfStatus(self, value): - if value != self._cf_status: - self._cf_status = value - self.cfStatusChanged.emit(value) - - def _select_flight_path_row(self, row): - self.flightPathDataTable.selectRow(row) - - def _set_flight_path_model(self, model): - self.flightPathDataTable.setModel(model) - - def _add_path_selector_item(self, item): - self.pathSelector.addItem(item) - - def _set_path_selector_index(self, index): - self.pathSelector.setCurrentIndex(index) - - def path_changed(self): - - if self.flight_mode == FlightModeStates.PATH: - self.switch_flight_mode(FlightModeStates.HOVERING) - time.sleep(0.1) - - # Flight path ui table setup - self.model = QStandardItemModel(10, 4) - self.model.setHorizontalHeaderItem(0, QStandardItem('X (m)')) - self.model.setHorizontalHeaderItem(1, QStandardItem('Y (m)')) - self.model.setHorizontalHeaderItem(2, QStandardItem('Z (m)')) - self.model.setHorizontalHeaderItem(3, QStandardItem('Yaw (deg)')) - - # Populate the table with data - if (len(self.flight_paths) == 0): - return - current = self.flight_paths[self.pathSelector.currentIndex()] - for i in range(1, len(current)): - for j in range(0, 4): - self.model.setItem(i - 1, j, - QStandardItem(str(current[i][j]))) - self._flight_path_set_model.emit(self.model) - Config().set("flight_paths", self.flight_paths) - - def remove_current_path(self): - - if self.flight_mode == FlightModeStates.PATH: - self.switch_flight_mode(FlightModeStates.HOVERING) - time.sleep(0.1) - if len(self.flight_paths) == 0: - return - - current_index = self.pathSelector.currentIndex() - answer = QMessageBox.question( - self, "CFClient: Qualisystab", "Delete the flightpath: {}?".format( - self.flight_paths[current_index][0]), - QMessageBox.Yes | QMessageBox.No) - - if answer == QMessageBox.Yes: - self.flight_paths.pop(current_index) - self.pathSelector.clear() - - for j in range(len(self.flight_paths)): - self.pathSelector.addItem(self.flight_paths[j][0]) - - if current_index == 0: - self.pathSelector.setCurrentIndex(0) - else: - self.pathSelector.setCurrentIndex(current_index - 1) - - self.path_changed() - - def set_lift_mode(self): - self.switch_flight_mode(FlightModeStates.LIFT) - - def set_land_mode(self): - self.switch_flight_mode(FlightModeStates.LAND) - - def set_circle_mode(self): - - # Toggle circle mode on and off - - if self.flight_mode == FlightModeStates.CIRCLE: - self.switch_flight_mode(FlightModeStates.HOVERING) - - else: - try: - self.position_hold_timelimit = float( - self.posHoldCircleBox.text()) - self.circle_radius = float(self.radiusBox.text()) - self.circle_resolution = float(self.resolutionBox.text()) - self.circle_pos_threshold = (2 * self.circle_radius * round( - math.sin(math.radians( - (self.circle_resolution / 2))), 4)) * 2 - logger.info(self.circle_pos_threshold) - except ValueError as err: - self.status = ("illegal character used in circle" - " settings: {}").format(str(err)) - logger.info(self.status) - return - - self.switch_flight_mode(FlightModeStates.CIRCLE) - - def set_record_mode(self): - # Toggle record mode on and off - - if self.flight_mode == FlightModeStates.RECORD: - # Cancel the recording - self.recording = False - self.switch_flight_mode(FlightModeStates.GROUNDED) - self.land_for_recording = False - elif self.flight_mode != FlightModeStates.GROUNDED: - # If the cf is flying, start by landing - self.land_for_recording = True - self.switch_flight_mode(FlightModeStates.LAND) - else: - self.switch_flight_mode(FlightModeStates.RECORD) - - def set_follow_mode(self): - # Toggle follow mode on and off - - if self.flight_mode == FlightModeStates.FOLLOW: - self.switch_flight_mode(FlightModeStates.HOVERING) - else: - self.switch_flight_mode(FlightModeStates.FOLLOW) - - def set_path_mode(self): - logger.info(self.model.item(0, 0)) - # Toggle path mode on and off - - # Path mode on, return to hovering - if self.flight_mode == FlightModeStates.PATH: - self.switch_flight_mode(FlightModeStates.HOVERING) - - elif self.model.item(0, 0) is None: - self.status = "missing Flight Plan" - return - # Path mode off, read data from UI table and start path mode - else: - - try: - self.position_hold_timelimit = float( - self.posHoldPathBox.text()) - except ValueError as err: - self.status = ("illegal character used in path" - " settings: {}").format(str(err)) - logger.info(self.status) - return - - # Get the flightpath from the GUI table - x, y = 0, 0 - temp = self.model.item(x, y) - reading_data = True - list = '' - while reading_data: - try: - element = str(temp.text()) - - if element != "": - list += temp.text() - # a "," gets added after the last element, - # remove that later for neatness - list += ',' - try: - float(element) - except ValueError: - self._flight_path_select_row.emit(y) - self.status = ("Value at cell x:{} y:{} " - "must be a number").format(x, y) - logger.info(self.status) - break - - x += 1 - if x % 4 == 0: - x = 0 - y += 1 - # list += temp_position - # temp_position = [] - temp = self.model.item(y, x) - - except Exception: - reading_data = False - # remove the last "," element - list = list[:(len(list) - 1)] - list = list.split(',') - list = [float(i) for i in list] - if (len(list) % 4) != 0: - self.status = ("Missing value to create a valid" - " flight path") - logger.info(self.status) - break - list = [list[i:i + 4] for i in range(0, len(list), 4)] - list.insert( - 0, - self.flight_paths[self.pathSelector.currentIndex()][0]) - self.flight_paths[self.pathSelector.currentIndex()] = list - Config().set("flight_paths", self.flight_paths) - self.switch_flight_mode(FlightModeStates.PATH) - - def set_kill_engine(self): - self.send_setpoint(Position(0, 0, 0)) - self.switch_flight_mode(FlightModeStates.GROUNDED) - logger.info('Stop button pressed, kill engines') - - def establish_qtm_connection(self): - if self.qtmIpBox.count() == 0 and self.qtmIpBox.currentText() == "": - return - - if self._qtm_connection is None: - try: - ip = self.qtmIpBox.currentText().split(" ")[0] - except Exception as e: - logger.error("Incorrect entry: %s", e) - return - - self.connectQtmButton.setEnabled(False) - start_async_task(self.qtm_connect(ip)) - - else: - self._qtm_connection.disconnect() - self._qtm_connection = None - - async def qtm_connect(self, ip): - - connection = await qtm.connect( - ip, - on_event=self.on_qtm_event, - on_disconnect=lambda reason: start_async_task( - self.on_qtm_disconnect(reason))) - - if connection is None: - start_async_task(self.on_qtm_disconnect("Failed to connect")) - return - - self._qtm_connection = connection - await self.setup_qtm_connection() - - def setup_6dof_comboboxes(self): - quadName = self.quadName - stickName = self.stickName - - self.quadBox.clear() - self.stickBox.clear() - for label in self.qtm_6DoF_labels: - self.quadBox.addItem(label) - self.stickBox.addItem(label) - - if quadName in self.qtm_6DoF_labels: - self.quadBox.setCurrentIndex( - self.qtm_6DoF_labels.index(quadName)) - - if stickName in self.qtm_6DoF_labels: - self.stickBox.setCurrentIndex( - self.qtm_6DoF_labels.index(stickName)) - - async def setup_qtm_connection(self): - self.connectQtmButton.setEnabled(True) - self.connectQtmButton.setText('Disconnect QTM') - self.qtmStatus = ': connected : Waiting QTM to start sending data' - - try: - result = await self._qtm_connection.get_parameters( - parameters=['6d']) - - # Parse the returned xml - xml = ET.fromstring(result) - self.qtm_6DoF_labels = [label.text.strip() for index, label in enumerate(xml.findall('*/Body/Name'))] - - # Make all names lowercase - self.qtm_6DoF_labels = [x.lower() for x in self.qtm_6DoF_labels] - logger.info('6Dof bodies active in qtm: {}'.format( - self.qtm_6DoF_labels)) - - self.setup_6dof_comboboxes() - - # Gui - self.qtmStatus = ': connected' - self.qtmCfPositionBox.setEnabled(True) - self.qtmWandPositionBox.setEnabled(True) - self.discoverQTM.setEnabled(False) - self.qtmIpBox.setEnabled(False) - - self._update_flight_status() - - self._ui_update_timer.start(200) - - # Make sure this is the last thing done with the qtm_connection - # (due to qtmRTProtocol structure) - await self._qtm_connection.stream_frames( - components=['6deuler', '3d'], on_packet=self.on_packet) - - except Exception as err: - logger.info(err) - - async def on_qtm_disconnect(self, reason): - """Callback when QTM has been disconnected""" - - self._ui_update_timer.stop() - self._update_flight_status() - - self._qtm_connection = None - logger.info(reason) - - # Gui - self.qtmCfPositionBox.setEnabled(False) - self.qtmWandPositionBox.setEnabled(False) - self.discoverQTM.setEnabled(True) - self.qtmIpBox.setEnabled(True) - self.connectQtmButton.setEnabled(True) - self.connectQtmButton.setText('Connect QTM') - self.qtmStatus = ': not connected : {}'.format( - reason if reason is not None else '') - - def on_qtm_event(self, event): - logger.info(event) - if event == qtm.QRTEvent.EventRTfromFileStarted: - self.qtmStatus = ': connected' - self.qtmCfPositionBox.setEnabled(True) - self.qtmWandPositionBox.setEnabled(True) - - elif event == qtm.QRTEvent.EventRTfromFileStopped: - self.qtmStatus = ': connected : Waiting QTM to start sending data' - self.qtmCfPositionBox.setEnabled(False) - self.qtmWandPositionBox.setEnabled(False) - - def on_packet(self, packet): - # Callback when QTM sends a 'packet' of the requested data, - # one every tracked frame. - # The speed depends on QTM settings - header, bodies = packet.get_6d_euler() - - # Cf not created yet or no packet received due to various reasons... - # Wait for the two asynchronous calls in 'setup connection' - # to return with data - if bodies is None or self.qtm_6DoF_labels is None: - return - - try: - temp_cf_pos = bodies[self.qtm_6DoF_labels.index(self.quadName)] - # QTM returns in mm in the order x, y, z, the Crazyflie api need - # data in meters, divide by thousand - # QTM returns euler rotations in deg in the order - # yaw, pitch, roll, not Qualisys Standard! - self.cf_pos = Position( - temp_cf_pos[0][0] / 1000, - temp_cf_pos[0][1] / 1000, - temp_cf_pos[0][2] / 1000, - roll=temp_cf_pos[1][2], - pitch=temp_cf_pos[1][1], - yaw=temp_cf_pos[1][0]) - - except ValueError: - self.qtmStatus = ' : connected : No 6DoF body found' - - try: - temp_wand_pos = bodies[self.qtm_6DoF_labels.index(self.stickName)] - self.wand_pos = Position( - temp_wand_pos[0][0] / 1000, - temp_wand_pos[0][1] / 1000, - temp_wand_pos[0][2] / 1000, - roll=temp_wand_pos[1][2], - pitch=temp_wand_pos[1][1], - yaw=temp_wand_pos[1][0]) - - except ValueError: - self.qtmStatus = ' : connected : No 6DoF body found' - - if self._cf is not None and self.cf_pos.is_valid(): - # If a cf exists and the position is valid - # Feed the current position of the cf back to the cf to - # allow for self correction - self._cf.extpos.send_extpos(self.cf_pos.x, self.cf_pos.y, - self.cf_pos.z) - - def _update_ui(self): - # Update the data in the GUI - self.qualisysX.setText(("%0.4f" % self.cf_pos.x)) - self.qualisysY.setText(("%0.4f" % self.cf_pos.y)) - self.qualisysZ.setText(("%0.4f" % self.cf_pos.z)) - - self.qualisysRoll.setText(("%0.2f" % self.cf_pos.roll)) - self.qualisysPitch.setText(("%0.2f" % self.cf_pos.pitch)) - self.qualisysYaw.setText(("%0.2f" % self.cf_pos.yaw)) - - self.qualisysWandX.setText(("%0.4f" % self.wand_pos.x)) - self.qualisysWandY.setText(("%0.4f" % self.wand_pos.y)) - self.qualisysWandZ.setText(("%0.4f" % self.wand_pos.z)) - - self.qualisysWandRoll.setText(("%0.2f" % self.wand_pos.roll)) - self.qualisysWandPitch.setText(("%0.2f" % self.wand_pos.pitch)) - self.qualisysWandYaw.setText(("%0.2f" % self.wand_pos.yaw)) - - def _flight_mode_land_entered(self): - self.current_goal_pos = self.valid_cf_pos - logger.info('Trying to land at: x: {} y: {}'.format( - self.current_goal_pos.x, self.current_goal_pos.y)) - self.land_rate = 1 - self._event.set() - - def _flight_mode_path_entered(self): - self.path_index = 1 - - current = self.flight_paths[self.pathSelector.currentIndex()] - self.current_goal_pos = Position( - current[self.path_index][0], - current[self.path_index][1], - current[self.path_index][2], - yaw=current[self.path_index][3]) - logger.info('Setting position {}'.format( - self.current_goal_pos)) - self._flight_path_select_row.emit(self.path_index - 1) - self._event.set() - - def _flight_mode_circle_entered(self): - self.current_goal_pos = Position( - round(math.cos(math.radians(self.circle_angle)), - 8) * self.circle_radius, - round(math.sin(math.radians(self.circle_angle)), 8) - * self.circle_radius, - self.circle_height, - yaw=self.circle_angle) - - logger.info('Setting position {}'.format( - self.current_goal_pos)) - self._event.set() - - def _flight_mode_follow_entered(self): - self.last_valid_wand_pos = Position(0, 0, 1) - self._event.set() - - def _flight_mode_record_entered(self): - self.new_path = [] - self._event.set() - - def _flight_mode_lift_entered(self): - self.current_goal_pos = self.valid_cf_pos - logger.info('Trying to lift at: {}'.format( - self.current_goal_pos)) - self._event.set() - - def _flight_mode_hovering_entered(self): - self.current_goal_pos = self.valid_cf_pos - logger.info('Hovering at: {}'.format( - self.current_goal_pos)) - self._event.set() - - def _flight_mode_grounded_entered(self): - self._event.set() - - def _flight_mode_disconnected_entered(self): - self._event.set() - - def flight_controller(self): - try: - logger.info('Starting flight controller thread') - self._cf.param.set_value('stabilizer.estimator', '2') - self.reset_estimator(self._cf) - - self._cf.param.set_value('flightmode.posSet', '1') - - time.sleep(0.1) - - # The threshold for how many frames without tracking - # is allowed before the cf's motors are stopped - lost_tracking_threshold = 100 - frames_without_tracking = 0 - position_hold_timer = 0 - self.circle_angle = 0.0 - - # The main flight control loop, the behaviour - # is controlled by the state of "FlightMode" - while self.flying_enabled: - - # Check that the position is valid and store it - if self.cf_pos.is_valid(): - self.valid_cf_pos = self.cf_pos - frames_without_tracking = 0 - else: - # if it isn't, count number of frames - frames_without_tracking += 1 - - if frames_without_tracking > lost_tracking_threshold: - self.switch_flight_mode(FlightModeStates.GROUNDED) - self.status = "Tracking lost, turning off motors" - logger.info(self.status) - - # If the cf is upside down, kill the motors - if self.flight_mode != FlightModeStates.GROUNDED and ( - self.valid_cf_pos.roll > 120 - or self.valid_cf_pos.roll < -120): - self.switch_flight_mode(FlightModeStates.GROUNDED) - self.status = "Status: Upside down, turning off motors" - logger.info(self.status) - - # Switch on the FlightModeState and take actions accordingly - # Wait so that any on state change actions are completed - self._event.wait() - - if self.flight_mode == FlightModeStates.LAND: - - self.send_setpoint( - Position( - self.current_goal_pos.x, - self.current_goal_pos.y, - (self.current_goal_pos.z / self.land_rate), - yaw=0)) - # Check if the cf has reached the position, - # if it has set a new position - - if self.valid_cf_pos.distance_to( - Position(self.current_goal_pos.x, - self.current_goal_pos.y, - self.current_goal_pos.z / self.land_rate - )) < self.path_pos_threshold: - self.land_rate *= 1.1 - - if self.land_rate > 1000: - self.send_setpoint(Position(0, 0, 0)) - if self.land_for_recording: - # Return the control to the recording mode - # after landing - mode = FlightModeStates.RECORD - self.land_for_recording = False - else: - # Regular landing - mode = FlightModeStates.GROUNDED - self.switch_flight_mode(mode) - - elif self.flight_mode == FlightModeStates.PATH: - - self.send_setpoint(self.current_goal_pos) - # Check if the cf has reached the goal position, - # if it has set a new goal position - if self.valid_cf_pos.distance_to( - self.current_goal_pos) < self.path_pos_threshold: - - if position_hold_timer > self.position_hold_timelimit: - - current = self.flight_paths[ - self.pathSelector.currentIndex()] - - self.path_index += 1 - if self.path_index == len(current): - self.path_index = 1 - position_hold_timer = 0 - - self.current_goal_pos = Position( - current[self.path_index][0], - current[self.path_index][1], - current[self.path_index][2], - yaw=current[self.path_index][3]) - - logger.info('Setting position {}'.format( - self.current_goal_pos)) - self._flight_path_select_row.emit( - self.path_index - 1) - elif position_hold_timer == 0: - - time_of_pos_reach = time.time() - # Add som time just to get going, - # it will be overwritten in the next step. - # Setting it higher than the limit - # will break the code. - position_hold_timer = 0.0001 - else: - position_hold_timer = time.time( - ) - time_of_pos_reach - - elif self.flight_mode == FlightModeStates.CIRCLE: - self.send_setpoint(self.current_goal_pos) - - # Check if the cf has reached the goal position, - # if it has set a new goal position - if self.valid_cf_pos.distance_to( - self.current_goal_pos) < self.circle_pos_threshold: - - if position_hold_timer >= self.position_hold_timelimit: - - position_hold_timer = 0 - - # increment the angle - self.circle_angle = ((self.circle_angle + - self.circle_resolution) - % 360) - - # Calculate the next position in - # the circle to fly to - self.current_goal_pos = Position( - round( - math.cos(math.radians(self.circle_angle)), - 4) * self.circle_radius, - round( - math.sin(math.radians(self.circle_angle)), - 4) * self.circle_radius, - self.circle_height, - yaw=self.circle_angle) - - logger.info('Setting position {}'.format( - self.current_goal_pos)) - - elif position_hold_timer == 0: - - time_of_pos_reach = time.time() - # Add som time just to get going, it will be - # overwritten in the next step. - # Setting it higher than the imit will - # break the code. - position_hold_timer = 0.0001 - else: - position_hold_timer = time.time( - ) - time_of_pos_reach - - elif self.flight_mode == FlightModeStates.FOLLOW: - - if self.wand_pos.is_valid(): - self.last_valid_wand_pos = self.wand_pos - - # Fit the angle of the wand in the interval 0-4 - self.length_from_wand = (2 * ( - (self.wand_pos.roll + 90) / 180) - 1) + 2 - self.send_setpoint( - Position( - self.wand_pos.x + round( - math.cos(math.radians(self.wand_pos.yaw)), - 4) * self.length_from_wand, - self.wand_pos.y + round( - math.sin(math.radians(self.wand_pos.yaw)), - 4) * self.length_from_wand, - ((self.wand_pos.z + round( - math.sin( - math.radians(self.wand_pos.pitch)), 4) - * self.length_from_wand) if - ((self.wand_pos.z + round( - math.sin( - math.radians(self.wand_pos.pitch)), 4) - * self.length_from_wand) > 0) else 0))) - else: - self.length_from_wand = (2 * ( - (self.last_valid_wand_pos.roll + 90) / 180) - - 1) + 2 - self.send_setpoint( - Position( - self.last_valid_wand_pos.x + round( - math.cos( - math.radians( - self.last_valid_wand_pos.yaw)), - 4) * self.length_from_wand, - self.last_valid_wand_pos.y + round( - math.sin( - math.radians( - self.last_valid_wand_pos.yaw)), - 4) * self.length_from_wand, - int(self.last_valid_wand_pos.z + round( - math.sin( - math.radians(self.last_valid_wand_pos. - pitch)), 4) * - self.length_from_wand))) - - elif self.flight_mode == FlightModeStates.LIFT: - - self.send_setpoint( - Position(self.current_goal_pos.x, - self.current_goal_pos.y, 1)) - - if self.valid_cf_pos.distance_to( - Position(self.current_goal_pos.x, - self.current_goal_pos.y, 1)) < 0.05: - # Wait for hte crazyflie to reach the goal - self.switch_flight_mode(FlightModeStates.HOVERING) - - elif self.flight_mode == FlightModeStates.HOVERING: - self.send_setpoint(self.current_goal_pos) - - elif self.flight_mode == FlightModeStates.RECORD: - - if self.valid_cf_pos.z > 1.0 and not self.recording: - # Start recording when the cf is lifted - self.recording = True - # Start the timer thread - self.save_current_position() - # Gui - self.status = "Recording Flightpath" - logger.info(self.status) - - elif self.valid_cf_pos.z < 0.03 and self.recording: - # Stop the recording when the cf is put on - # the ground again - logger.info("Recording stopped") - self.recording = False - - # Remove the last bit (1s) of the recording, - # containing setting the cf down - for self.path_index in range(20): - self.new_path.pop() - - # Add the new path to list and Gui - now = datetime.datetime.fromtimestamp(time.time()) - - new_name = ("Recording {}/{}/{} {}:{}".format( - now.year - 2000, now.month - if now.month > 9 else "0{}".format(now.month), - now.day if now.day > 9 else "0{}".format(now.day), - now.hour if now.hour > 9 else "0{}".format( - now.hour), now.minute - if now.minute > 9 else "0{}".format(now.minute))) - - self.new_path.insert(0, new_name) - self.flight_paths.append(self.new_path) - self._path_selector_add_item.emit(new_name) - - # Select the new path - self._path_selector_set_index.emit( - len(self.flight_paths) - 1) - self.path_changed() - Config().set("flight_paths", self.flight_paths) - - # Wait while the operator moves away - self.status = "Replay in 3s" - time.sleep(1) - self.status = "Replay in 2s" - time.sleep(1) - self.status = "Replay in 1s" - time.sleep(1) - # Switch to path mode and replay the recording - self.switch_flight_mode(FlightModeStates.PATH) - - elif self.flight_mode == FlightModeStates.GROUNDED: - pass # If grounded, the control is switched back to gamepad - - time.sleep(0.001) - - except Exception as err: - logger.error(err) - self.cfStatus = str(err) - - logger.info('Terminating flight controller thread') - - def save_current_position(self): - if self.recording: - # Restart the timer - threading.Timer(0.05, self.save_current_position).start() - # Save the current position - self.new_path.append([ - self.valid_cf_pos.x, self.valid_cf_pos.y, - self.valid_cf_pos.z, self.valid_cf_pos.yaw - ]) - - def _connected(self, link_uri): - """Callback when the Crazyflie has been connected""" - - self._cf = self._helper.cf - self._update_flight_status() - - logger.debug("Crazyflie connected to {}".format(link_uri)) - - # Gui - self.cfStatus = ': connected' - - def _disconnected(self, link_uri): - """Callback for when the Crazyflie has been disconnected""" - - logger.info("Crazyflie disconnected from {}".format(link_uri)) - self.cfStatus = ': not connected' - self._cf = None - self._update_flight_status() - - def _param_updated(self, name, value): - """Callback when the registered parameter get's updated""" - - logger.debug("Updated {0} to {1}".format(name, value)) - - def _log_data_received(self, timestamp, data, log_conf): - """Callback when the log layer receives new data""" - - logger.debug("{0}:{1}:{2}".format(timestamp, log_conf.name, data)) - - def _logging_error(self, log_conf, msg): - """Callback from the log layer when an error occurs""" - - QMessageBox.about( - self, "Example error", "Error when using log config" - " [{0}]: {1}".format(log_conf.name, msg)) - - def wait_for_position_estimator(self, cf): - logger.info('Waiting for estimator to find stable position...') - - self.cfStatus = ( - 'Waiting for estimator to find stable position... ' - '(QTM needs to be connected and providing data)' - ) - - log_config = LogConfig(name='Kalman Variance', period_in_ms=500) - log_config.add_variable('kalman.varPX', 'float') - log_config.add_variable('kalman.varPY', 'float') - log_config.add_variable('kalman.varPZ', 'float') - - var_y_history = [1000] * 10 - var_x_history = [1000] * 10 - var_z_history = [1000] * 10 - - threshold = 0.001 - - with SyncLogger(cf, log_config) as log: - for log_entry in log: - data = log_entry[1] - - var_x_history.append(data['kalman.varPX']) - var_x_history.pop(0) - var_y_history.append(data['kalman.varPY']) - var_y_history.pop(0) - var_z_history.append(data['kalman.varPZ']) - var_z_history.pop(0) - - min_x = min(var_x_history) - max_x = max(var_x_history) - min_y = min(var_y_history) - max_y = max(var_y_history) - min_z = min(var_z_history) - max_z = max(var_z_history) - - # print("{} {} {}". - # format(max_x - min_x, max_y - min_y, max_z - min_z)) - - if (max_x - min_x) < threshold and ( - max_y - min_y) < threshold and ( - max_z - min_z) < threshold: - logger.info("Position found with error in, x: {}, y: {}, " - "z: {}".format(max_x - min_x, - max_y - min_y, - max_z - min_z)) - - self.cfStatus = ": connected" - - self.switch_flight_mode(FlightModeStates.GROUNDED) - - break - - def reset_estimator(self, cf): - # Reset the kalman filter - - cf.param.set_value('kalman.resetEstimation', '1') - time.sleep(0.1) - cf.param.set_value('kalman.resetEstimation', '0') - - self.wait_for_position_estimator(cf) - - def switch_flight_mode(self, mode): - # Handles the behaviour of switching between flight modes - self.flight_mode = mode - - # Handle client input control. - # Disable gamepad input if we are not grounded - if self.flight_mode in [ - FlightModeStates.GROUNDED, - FlightModeStates.DISCONNECTED, - FlightModeStates.RECORD - ]: - self._helper.mainUI.disable_input(False) - else: - self._helper.mainUI.disable_input(True) - - self._event.clear() - # Threadsafe call - self._machine.postEvent(FlightModeEvent(mode)) - - logger.debug('Switching Flight Mode to: %s', mode) - - def send_setpoint(self, pos): - # Wraps the send command to the crazyflie - if self._cf is not None: - self._cf.commander.send_position_setpoint(pos.x, pos.y, pos.z, 0.0) - - -class Position: - def __init__(self, x, y, z, roll=0.0, pitch=0.0, yaw=0.0): - self.x = x - self.y = y - self.z = z - self.roll = roll - self.pitch = pitch - self.yaw = yaw - - def distance_to(self, other_point): - return math.sqrt( - math.pow(self.x - other_point.x, 2) + - math.pow(self.y - other_point.y, 2) + - math.pow(self.z - other_point.z, 2)) - - def is_valid(self): - # Checking if the respective values are nan - return self.x == self.x and self.y == self.y and self.z == self.z - - def __str__(self): - return "x: {} y: {} z: {} Roll: {} Pitch: {} Yaw: {}".format( - self.x, self.y, self.z, self.roll, self.pitch, self.yaw) diff --git a/src/cfclient/ui/tabs/TuningTab.py b/src/cfclient/ui/tabs/TuningTab.py index 64e383c6ce..04f4d0bf53 100644 --- a/src/cfclient/ui/tabs/TuningTab.py +++ b/src/cfclient/ui/tabs/TuningTab.py @@ -6,7 +6,7 @@ # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2022 Bitcraze AB +# Copyright (C) 2022-2023 Bitcraze AB # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,9 +26,9 @@ import logging -from PyQt5 import QtWidgets -from PyQt5 import uic -from PyQt5.QtCore import pyqtSignal, Qt +from PyQt6 import QtWidgets +from PyQt6 import uic +from PyQt6.QtCore import pyqtSignal, Qt import time import cfclient diff --git a/src/cfclient/ui/tabs/__init__.py b/src/cfclient/ui/tabs/__init__.py index 1ae8f3fa16..b5af0a0123 100644 --- a/src/cfclient/ui/tabs/__init__.py +++ b/src/cfclient/ui/tabs/__init__.py @@ -40,7 +40,6 @@ from .ParamTab import ParamTab from .PlotTab import PlotTab from .locopositioning_tab import LocoPositioningTab -from .QualisysTab import QualisysTab from .LogClientTab import LogClientTab from .lighthouse_tab import LighthouseTab from .TuningTab import TuningTab @@ -60,7 +59,6 @@ PlotTab, LocoPositioningTab, LighthouseTab, - QualisysTab, LogClientTab, TuningTab, CrtpSharkToolbox, diff --git a/src/cfclient/ui/tabs/lighthouse_tab.py b/src/cfclient/ui/tabs/lighthouse_tab.py index 987f1e6727..8d29699428 100644 --- a/src/cfclient/ui/tabs/lighthouse_tab.py +++ b/src/cfclient/ui/tabs/lighthouse_tab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2022 Bitcraze AB +# Copyright (C) 2022-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -32,11 +32,11 @@ import logging -from PyQt5 import uic -from PyQt5.QtCore import Qt, pyqtSignal, QTimer -from PyQt5.QtWidgets import QMessageBox -from PyQt5.QtWidgets import QFileDialog -from PyQt5.QtWidgets import QLabel +from PyQt6 import uic +from PyQt6.QtCore import Qt, pyqtSignal, QTimer +from PyQt6.QtWidgets import QMessageBox +from PyQt6.QtWidgets import QFileDialog +from PyQt6.QtWidgets import QLabel import cfclient from cfclient.ui.tab_toolbox import TabToolbox @@ -626,7 +626,7 @@ def _mask_status_matrix(self, bs_available_mask): def _create_label(self, text=None): label = QLabel() label.setMinimumSize(30, 0) - label.setAlignment(Qt.AlignCenter) + label.setAlignment(Qt.AlignmentFlag.AlignCenter) if text: label.setText(str(text)) diff --git a/src/cfclient/ui/tabs/locopositioning_tab.py b/src/cfclient/ui/tabs/locopositioning_tab.py index b3e94affae..4a027880cf 100644 --- a/src/cfclient/ui/tabs/locopositioning_tab.py +++ b/src/cfclient/ui/tabs/locopositioning_tab.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2022 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -35,10 +35,10 @@ from collections import namedtuple import time -from PyQt5 import uic -from PyQt5.QtCore import Qt, pyqtSignal, QTimer -from PyQt5.QtWidgets import QMessageBox -from PyQt5.QtWidgets import QLabel +from PyQt6 import uic +from PyQt6.QtCore import Qt, pyqtSignal, QTimer +from PyQt6.QtWidgets import QMessageBox +from PyQt6.QtWidgets import QLabel import cfclient from cfclient.ui.tab_toolbox import TabToolbox @@ -690,7 +690,7 @@ def _update_ranging_status_indicators(self): label = QLabel() label.setMinimumSize(30, 0) label.setProperty('frameShape', 'QFrame::Box') - label.setAlignment(Qt.AlignCenter) + label.setAlignment(Qt.AlignmentFlag.AlignCenter) container.addWidget(label, row, col) label.setText(str(id)) diff --git a/src/cfclient/ui/tabs/qualisysTab.ui b/src/cfclient/ui/tabs/qualisysTab.ui deleted file mode 100644 index 7e32982e9d..0000000000 --- a/src/cfclient/ui/tabs/qualisysTab.ui +++ /dev/null @@ -1,1018 +0,0 @@ - - - Layout - - - - 0 - 0 - 745 - 512 - - - - Plot - - - - - - 0 - - - 0 - - - - - 0 - - - - - QTM - - - - - - - - 173 - 0 - - - - true - - - - - - - Connect to QTM - - - - - - - Scan - - - - - - - : not connected - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - 10 - - - - - Crazyflie status - - - - - - - : not connected - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - font: 11pt "MS Shell Dlg 2"; - - - - Status: disabled - - - true - - - Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft - - - 0 - - - false - - - - - - - Qt::Horizontal - - - - - - - 0 - - - 0 - - - - - QLayout::SetDefaultConstraint - - - 0 - - - - - true - - - - 0 - 0 - - - - - 120 - 0 - - - - - 16777215 - 220 - - - - Crazyflie controls - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - 9 - - - 9 - - - - - false - - - - 0 - 0 - - - - Lift - - - - - - - false - - - - 0 - 0 - - - - Fly along path - - - false - - - - - - - false - - - - 0 - 0 - - - - Fly in cicrcle - - - - - - - false - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Qt::LeftToRight - - - Follow Mode - - - - - - - false - - - - 0 - 0 - - - - Record Mode - - - - - - - false - - - - 0 - 0 - - - - - 40 - 0 - - - - - 16777215 - 16777215 - - - - Land - - - - - - - false - - - - 0 - 0 - - - - - 0 - 20 - - - - - 16777215 - 16777215 - - - - Kill engines - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - 0 - - - - - - 305 - 194 - - - - Qt::LeftToRight - - - Path Settings - - - - 0 - - - 9 - - - - - - 285 - 140 - - - - - 16777215 - 120 - - - - 60 - - - 20 - - - - - - - 6 - - - 9 - - - 0 - - - 9 - - - - - - 45 - 16777215 - - - - 0.5 - - - - - - - - 0 - 15 - - - - Position hold (s) - - - - - - - Qt::Horizontal - - - - 40 - 15 - - - - - - - - - 145 - 0 - - - - - 145 - 16777215 - - - - - - - - - 20 - 22 - - - - X - - - - - - - - - - - - - 0 - 105 - - - - Circle Settings - - - - 9 - - - - - - 45 - 16777215 - - - - 1 - - - - - - - Radius (m) - - - - - - - - 45 - 16777215 - - - - 0.5 - - - - - - - Position hold (s) - - - - - - - - 45 - 16777215 - - - - 10 - - - - - - - Resolution (deg) - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - 6 - - - 0 - - - 0 - - - - - false - - - - 0 - 0 - - - - - 210 - 170 - - - - - 210 - 170 - - - - Crazyflie - - - - - - y: - - - - - - - - 50 - 16777215 - - - - - - - - - 50 - 16777215 - - - - - - - - - 50 - 16777215 - - - - - - - - - 20 - 16777215 - - - - Roll: - - - - - - - Pitch: - - - - - - - - 0 - 0 - - - - - 50 - 16777215 - - - - - - - - z: - - - - - - - - 50 - 16777215 - - - - - - - - - 10 - 16777215 - - - - x: - - - - - - - - 50 - 16777215 - - - - - - - - Yaw: - - - - - - - - - 6DOF - - - - - - - - 0 - 0 - - - - - - - - - - - - - false - - - - 0 - 0 - - - - - 210 - 170 - - - - - 210 - 170 - - - - QStick - - - - - - - 10 - 16777215 - - - - x: - - - - - - - - 50 - 16777215 - - - - - - - - - 20 - 16777215 - - - - Roll: - - - - - - - - 50 - 16777215 - - - - - - - - y: - - - - - - - - 50 - 16777215 - - - - - - - - Pitch: - - - - - - - - 50 - 16777215 - - - - - - - - z: - - - - - - - - 50 - 16777215 - - - - - - - - Yaw: - - - - - - - - 50 - 16777215 - - - - - - - - - - 6DOF - - - - - - - - 0 - 0 - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - qtmIpBox - connectQtmButton - liftButton - pathButton - circleButton - followButton - recordButton - landButton - emergencyButton - flightPathDataTable - posHoldPathBox - pathSelector - removePathButton - radiusBox - posHoldCircleBox - resolutionBox - qualisysX - qualisysY - qualisysZ - qualisysRoll - qualisysPitch - qualisysYaw - qualisysWandX - qualisysWandY - qualisysWandZ - qualisysWandRoll - qualisysWandPitch - qualisysWandYaw - - - - diff --git a/src/cfclient/ui/widgets/ai.py b/src/cfclient/ui/widgets/ai.py index ae8cc3b47c..97fed4667a 100644 --- a/src/cfclient/ui/widgets/ai.py +++ b/src/cfclient/ui/widgets/ai.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2013 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -31,8 +31,9 @@ import sys -from PyQt5 import QtGui -from PyQt5 import QtWidgets, QtCore +from PyQt6 import QtGui +from PyQt6 import QtWidgets +from PyQt6.QtCore import Qt __author__ = 'Bitcraze AB' __all__ = ['AttitudeIndicator'] @@ -95,9 +96,9 @@ def drawWidget(self, qp): qp.rotate(self.roll) qp.translate(0, (self.pitch * h) / 50) qp.translate(-w / 2, -h / 2) - qp.setRenderHint(qp.Antialiasing) + qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing) - font = QtGui.QFont('Serif', 7, QtGui.QFont.Light) + font = QtGui.QFont('Serif', 7, QtGui.QFont.Weight.Light) qp.setFont(font) # Draw the blue @@ -110,8 +111,7 @@ def drawWidget(self, qp): qp.setBrush(QtGui.QColor(59, 41, 39)) qp.drawRect(-w, int(h / 2), 3 * w, 3 * h) - pen = QtGui.QPen(QtGui.QColor(255, 255, 255), 1.5, - QtCore.Qt.SolidLine) + pen = QtGui.QPen(QtGui.QColor(255, 255, 255), 1.5, Qt.PenStyle.SolidLine) qp.setPen(pen) qp.drawLine(-w, int(h / 2), 3 * w, int(h / 2)) @@ -142,8 +142,7 @@ def drawWidget(self, qp): qp.setWorldMatrixEnabled(False) - pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2, - QtCore.Qt.SolidLine) + pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2, Qt.PenStyle.SolidLine) qp.setBrush(QtGui.QColor(0, 0, 0)) qp.setPen(pen) qp.drawLine(0, int(h / 2), w, int(h / 2)) @@ -152,12 +151,11 @@ def drawWidget(self, qp): qp.setWorldMatrixEnabled(False) - pen = QtGui.QPen(QtGui.QColor(255, 255, 255), 2, - QtCore.Qt.SolidLine) + pen = QtGui.QPen(QtGui.QColor(255, 255, 255), 2, Qt.PenStyle.SolidLine) qp.setBrush(QtGui.QColor(255, 255, 255)) qp.setPen(pen) fh = int(max(7, h / 50)) - font = QtGui.QFont('Sans', fh, QtGui.QFont.Light) + font = QtGui.QFont('Sans', fh, QtGui.QFont.Weight.Light) qp.setFont(font) qp.resetTransform() @@ -214,8 +212,8 @@ def updateBaro(self, height): def initUI(self): vbox = QtWidgets.QVBoxLayout() - sld = QtWidgets.QSlider(QtCore.Qt.Horizontal, self) - sld.setFocusPolicy(QtCore.Qt.NoFocus) + sld = QtWidgets.QSlider(Qt.Orientation.Horizontal, self) + sld.setFocusPolicy(Qt.FocusPolicy.NoFocus) sld.setRange(0, 3600) sld.setValue(1800) vbox.addWidget(sld) @@ -228,21 +226,21 @@ def initUI(self): hbox = QtWidgets.QHBoxLayout() hbox.addLayout(vbox) - sldPitch = QtWidgets.QSlider(QtCore.Qt.Vertical, self) - sldPitch.setFocusPolicy(QtCore.Qt.NoFocus) + sldPitch = QtWidgets.QSlider(Qt.Orientation.Vertical, self) + sldPitch.setFocusPolicy(Qt.FocusPolicy.NoFocus) sldPitch.setRange(0, 180) sldPitch.setValue(90) sldPitch.valueChanged[int].connect(self.updatePitch) hbox.addWidget(sldPitch) - sldHeight = QtWidgets.QSlider(QtCore.Qt.Vertical, self) - sldHeight.setFocusPolicy(QtCore.Qt.NoFocus) + sldHeight = QtWidgets.QSlider(Qt.Orientation.Vertical, self) + sldHeight.setFocusPolicy(Qt.FocusPolicy.NoFocus) sldHeight.setRange(-200, 200) sldHeight.setValue(0) sldHeight.valueChanged[int].connect(self.updateBaro) - sldT = QtWidgets.QSlider(QtCore.Qt.Vertical, self) - sldT.setFocusPolicy(QtCore.Qt.NoFocus) + sldT = QtWidgets.QSlider(Qt.Orientation.Vertical, self) + sldT.setFocusPolicy(Qt.FocusPolicy.NoFocus) sldT.setRange(-200, 200) sldT.setValue(0) sldT.valueChanged[int].connect(self.updateTarget) @@ -263,7 +261,7 @@ def changeValue(self, value): def main(): app = QtWidgets.QApplication(sys.argv) Example() - sys.exit(app.exec_()) + sys.exit(app.exec()) if __name__ == '__main__': main() diff --git a/src/cfclient/ui/widgets/hexspinbox.py b/src/cfclient/ui/widgets/hexspinbox.py index f93b038944..33c318947a 100644 --- a/src/cfclient/ui/widgets/hexspinbox.py +++ b/src/cfclient/ui/widgets/hexspinbox.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2013 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -30,9 +30,10 @@ (i.e. not limited by 32 bit). """ -from PyQt5 import QtGui, QtCore -from PyQt5.QtCore import pyqtSignal -from PyQt5.QtWidgets import QAbstractSpinBox +from PyQt6.QtGui import QRegularExpressionValidator +from PyQt6.QtCore import QRegularExpression +from PyQt6.QtCore import pyqtSignal +from PyQt6.QtWidgets import QAbstractSpinBox __author__ = 'Bitcraze AB' __all__ = ['HexSpinBox'] @@ -41,10 +42,10 @@ class HexSpinBox(QAbstractSpinBox): valueChanged = pyqtSignal(object) - def __init__(self, *args): - QAbstractSpinBox.__init__(self, *args) - regexp = QtCore.QRegExp('^0x[0-9A-Fa-f]{1,10}$') - self.validator = QtGui.QRegExpValidator(regexp) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + regexp = QRegularExpression('^0x[0-9A-Fa-f]{1,10}$') + self.validator = QRegularExpressionValidator(regexp) self.setValue(0) def validate(self, text, pos): @@ -69,8 +70,7 @@ def stepBy(self, steps): self.setValue(self._value + steps) def stepEnabled(self): - return (QAbstractSpinBox.StepUpEnabled | - QAbstractSpinBox.StepDownEnabled) + return (QAbstractSpinBox.StepEnabledFlag.StepUpEnabled | QAbstractSpinBox.StepEnabledFlag.StepDownEnabled) def is_text_different_from_value(self): return self._value != self.valueFromText(self.lineEdit().text()) diff --git a/src/cfclient/ui/widgets/plotwidget.py b/src/cfclient/ui/widgets/plotwidget.py index 53f6891e83..38982aa07e 100644 --- a/src/cfclient/ui/widgets/plotwidget.py +++ b/src/cfclient/ui/widgets/plotwidget.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2013 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -32,16 +32,15 @@ For more advanced plotting save the data and use an external application. """ -from PyQt5 import QtWidgets, uic +from PyQt6 import QtWidgets, uic from time import time import logging -from PyQt5.QtWidgets import QButtonGroup -from PyQt5.QtCore import * # noqa -from PyQt5.QtWidgets import * # noqa -from PyQt5.Qt import * # noqa +from PyQt6.QtWidgets import QButtonGroup +from PyQt6.QtCore import * # noqa +from PyQt6.QtWidgets import * # noqa import cfclient @@ -130,8 +129,8 @@ def __init__(self, parent=None, fps=100, title="", *args): self._last_item = 0 self.setSizePolicy(QtWidgets.QSizePolicy( - QtWidgets.QSizePolicy.MinimumExpanding, - QtWidgets.QSizePolicy.MinimumExpanding)) + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QtWidgets.QSizePolicy.Policy.MinimumExpanding)) self.setMinimumSize(self.minimumSizeHint()) self.parent = parent diff --git a/src/cfclient/ui/widgets/super_slider.py b/src/cfclient/ui/widgets/super_slider.py index 3ec696d2b1..51f4e3924d 100644 --- a/src/cfclient/ui/widgets/super_slider.py +++ b/src/cfclient/ui/widgets/super_slider.py @@ -6,7 +6,7 @@ # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2022 Bitcraze AB +# Copyright (C) 2022-2023 Bitcraze AB # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ Slider widget with advanced features """ -from PyQt5 import QtWidgets, QtCore +from PyQt6 import QtWidgets, QtCore from cflib.utils.callbacks import Caller @@ -59,8 +59,8 @@ def set_value(self, value: float): def _initUI(self): # Create controls - self.slider = QtWidgets.QSlider(QtCore.Qt.Horizontal, self) - self.slider.setFocusPolicy(QtCore.Qt.NoFocus) + self.slider = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal, self) + self.slider.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) self.slider.setRange(int(self.min * self.slider_scaling), int(self.max * self.slider_scaling)) self.slider.setValue(int(self.value * self.slider_scaling)) self.slider.setTickInterval(self.slider_scaling) diff --git a/src/cfclient/ui/wizards/lighthouse_geo_bs_estimation_wizard.py b/src/cfclient/ui/wizards/lighthouse_geo_bs_estimation_wizard.py index 3cbb1f9508..63ef456d1c 100644 --- a/src/cfclient/ui/wizards/lighthouse_geo_bs_estimation_wizard.py +++ b/src/cfclient/ui/wizards/lighthouse_geo_bs_estimation_wizard.py @@ -6,7 +6,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2022 Bitcraze AB +# Copyright (C) 2022-2023 Bitcraze AB # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -43,7 +43,7 @@ from cflib.localization.lighthouse_system_scaler import LighthouseSystemScaler from cflib.localization.lighthouse_types import Pose, LhDeck4SensorPositions, LhMeasurement, LhCfPoseSample -from PyQt5 import QtCore, QtWidgets, QtGui +from PyQt6 import QtCore, QtWidgets, QtGui import time @@ -66,14 +66,14 @@ def __init__(self, cf, ready_cb, parent=None, *args): self.wizard_opened_first_time = True self.reset() - self.button(QtWidgets.QWizard.FinishButton).clicked.connect(self._finish_button_clicked_callback) + self.button(QtWidgets.QWizard.WizardButton.FinishButton).clicked.connect(self._finish_button_clicked_callback) def _finish_button_clicked_callback(self): self.ready_cb(self.get_geometry_page.get_geometry()) def reset(self): - self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint) - self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowCloseButtonHint) + self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowType.WindowContextHelpButtonHint) + self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowType.WindowCloseButtonHint) if not self.wizard_opened_first_time: self.removePage(0) @@ -112,12 +112,12 @@ def __init__(self, cf: Crazyflie, show_add_measurements=False, parent=None): self.layout = QtWidgets.QVBoxLayout() self.explanation_picture = QtWidgets.QLabel() - self.explanation_picture.setAlignment(QtCore.Qt.AlignCenter) + self.explanation_picture.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.layout.addWidget(self.explanation_picture) self.explanation_text = QtWidgets.QLabel() self.explanation_text.setText(' ') - self.explanation_text.setAlignment(QtCore.Qt.AlignCenter) + self.explanation_text.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.layout.addWidget(self.explanation_text) self.layout.addStretch() @@ -127,7 +127,7 @@ def __init__(self, cf: Crazyflie, show_add_measurements=False, parent=None): self.status_text = QtWidgets.QLabel() self.status_text.setFont(QtGui.QFont('Courier New', 10)) self.status_text.setText(self.str_pad('')) - self.status_text.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Plain) + self.status_text.setFrameStyle(QtWidgets.QFrame.Shape.Panel | QtWidgets.QFrame.Shadow.Plain) self.layout.addWidget(self.status_text) self.start_action_button = QtWidgets.QPushButton("Start Measurement") @@ -462,4 +462,4 @@ def get_geometry(self): app = QtWidgets.QApplication(sys.argv) wizard = LighthouseBasestationGeometryWizard() wizard.show() - sys.exit(app.exec_()) + sys.exit(app.exec()) diff --git a/src/cfclient/utils/logconfigreader.py b/src/cfclient/utils/logconfigreader.py index 8c0cd8c390..1e2aafa792 100644 --- a/src/cfclient/utils/logconfigreader.py +++ b/src/cfclient/utils/logconfigreader.py @@ -7,7 +7,7 @@ # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2011-2013 Bitcraze AB +# Copyright (C) 2011-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -45,7 +45,7 @@ import cfclient from cflib.crazyflie.log import LogVariable, LogConfig -from PyQt5 import QtGui +from PyQt6 import QtGui __author__ = 'Bitcraze AB' __all__ = ['LogVariable', 'LogConfigReader'] diff --git a/src/cfclient/utils/ui.py b/src/cfclient/utils/ui.py index 9d11d76cca..62efc4c419 100644 --- a/src/cfclient/utils/ui.py +++ b/src/cfclient/utils/ui.py @@ -6,7 +6,7 @@ # | / ,--ยด | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ # -# Copyright (C) 2020 Bitcraze AB +# Copyright (C) 2020-2023 Bitcraze AB # # Crazyflie Nano Quadcopter Client # @@ -26,7 +26,7 @@ # MA 02110-1301, USA. import os -from PyQt5.QtGui import QFont +from PyQt6.QtGui import QFont import cfclient