Swarm supervision (#3671)
Doxygen / build (push) Canceled after 0s

* [supervision] tiny refector

* [supervision] Add multi selection mode

* [supervision] remove mono selection mode

* [supervision] remove outdated code

---------

Co-authored-by: Fabien-B <Fabien-B@github.com>
This commit is contained in:
Fabien-B
2026-07-20 19:22:05 +02:00
committed by GitHub
co-authored by Fabien-B
parent e595b7b38d
commit f3a8d3317e
15 changed files with 1902 additions and 1335 deletions
File diff suppressed because it is too large Load Diff
+38 -59
View File
@@ -6,8 +6,8 @@ from generated.ui_build import Ui_Build
import lxml.etree as ET
import os
import utils
from conf import Aircraft, Conf
from typing import List, Dict
from conf import Aircraft
from typing import Dict, List, Optional, Set
from dataclasses import dataclass, field
import re
@@ -27,23 +27,17 @@ class FlashMode:
class BuildWidget(Ui_Build, QWidget):
spawn_program = QtCore.pyqtSignal(str, list, str, object)
refresh_ac = QtCore.pyqtSignal(object)
multi_action_requested = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
QWidget.__init__(self, parent=parent)
self.setupUi(self)
# uic.loadUi("ui/build.ui", self)
self.ac: Aircraft = None
self.conf: Conf = None # to save conf before building
self.flash_modes: List[FlashMode] = self.parse_flash_modes()
self.build_button.clicked.connect(self.build)
self.clean_button.clicked.connect(self.clean)
self.flash_button.clicked.connect(self.flash)
self.target_combo.currentTextChanged.connect(self.update_flash_mode)
def set_conf(self, conf: Conf):
self.conf = conf
self.target_combo.currentTextChanged.connect(lambda _: self.multi_action_requested.emit("RefreshFlashModes"))
@staticmethod
def parse_flash_modes() -> List[FlashMode]:
@@ -68,68 +62,53 @@ class BuildWidget(Ui_Build, QWidget):
mode_names = [mode.name for mode in modes]
return mode_names
def update_targets(self, ac: Aircraft):
self.ac = ac
def update_targets_for_aircrafts(self, aircrafts: List[Aircraft]):
selected_target = self.target_combo.currentText()
self.target_combo.clear()
for target in ac.boards.keys():
self.target_combo.addItem(target)
last_target = utils.get_settings().value("ui/last_target", None, str)
if last_target is not None and last_target in ac.boards.keys():
self.target_combo.setCurrentText(last_target)
def update_flash_mode(self, target):
self.device_combo.clear()
if target != "":
if not aircrafts:
return
common_targets = set(aircrafts[0].boards.keys())
for ac in aircrafts[1:]:
common_targets.intersection_update(ac.boards.keys())
for target in sorted(common_targets):
self.target_combo.addItem(target)
if selected_target in common_targets:
self.target_combo.setCurrentText(selected_target)
self.update_flash_modes_for_aircrafts(aircrafts)
def update_flash_modes_for_aircrafts(self, aircrafts: List[Aircraft]):
selected_flash_mode = self.device_combo.currentText()
self.device_combo.clear()
self.device_combo.addItem("Default")
board = self.ac.boards[target]
flash_modes = self.get_flash_modes(board)
self.device_combo.addItems(flash_modes)
last_flash_mode = utils.get_settings().value("ui/last_flash_mode", None, str)
if last_flash_mode in flash_modes:
self.device_combo.setCurrentText(last_flash_mode)
target = self.target_combo.currentText()
if not aircrafts or not target:
return
common_modes: Optional[Set[str]] = None
for ac in aircrafts:
board = ac.boards.get(target)
modes = set(self.get_flash_modes(board)) if board is not None else set()
common_modes = modes if common_modes is None else common_modes.intersection(modes)
if common_modes:
self.device_combo.addItems(sorted(common_modes))
if selected_flash_mode and self.device_combo.findText(selected_flash_mode) >= 0:
self.device_combo.setCurrentText(selected_flash_mode)
def get_current_target(self) -> str:
return self.target_combo.currentText()
def build(self):
target = self.target_combo.currentText()
cmd = ["make", "-C", utils.PAPARAZZI_HOME, "-f", "Makefile.ac",
"AIRCRAFT={}".format(self.ac.name), "{}.compile".format(target)]
if self.print_config_checkbox.isChecked():
cmd.append("PRINT_CONFIG=1")
shortname = "Build {}".format(self.ac.name)
self.refresh_ac.emit(self.ac)
self.conf.save(False)
self.target_combo.setCurrentText(target)
self.enable_buttons(False)
utils.get_settings().setValue("ui/last_target", target)
self.spawn_program.emit(shortname, cmd, None, lambda: self.enable_buttons(True))
self.multi_action_requested.emit("Build")
def clean(self):
cmd = ["make", "-C", utils.PAPARAZZI_HOME, "-f", "Makefile.ac",
"AIRCRAFT={}".format(self.ac.name), "clean_ac"]
shortname = "Clean {}".format(self.ac.name)
self.enable_buttons(False)
self.spawn_program.emit(shortname, cmd, None, lambda: self.enable_buttons(True))
self.multi_action_requested.emit("Clean")
def flash(self):
target = self.target_combo.currentText()
vars = []
flash_mode = self.device_combo.currentText()
if flash_mode != "Default":
for mode in self.flash_modes:
if mode.name == flash_mode:
vars = ["{}={}".format(var_name, var_value) for (var_name, var_value) in mode.vars.items()]
break
else:
raise Exception("Flash mode {} not found!".format(flash_mode))
cmd = ["make", "-C", utils.PAPARAZZI_HOME, "-f", "Makefile.ac",
"AIRCRAFT={}".format(self.ac.name)] + vars + ["{}.upload".format(target)]
shortname = "Flash {}".format(self.ac.name)
utils.get_settings().setValue("ui/last_flash_mode", flash_mode)
self.spawn_program.emit(shortname, cmd, None, None)
self.multi_action_requested.emit("Flash")
def enable_buttons(self, enable: bool):
self.build_button.setEnabled(enable)
self.clean_button.setEnabled(enable)
self.flash_button.setEnabled(enable)
+69 -2
View File
@@ -9,6 +9,7 @@ import utils
MOD_DEP = os.path.join(utils.PAPARAZZI_SRC, "sw", "tools", "generators", "dump_modules_list.out")
CONF = os.path.join(utils.PAPARAZZI_HOME, "conf", "conf.xml")
PLACEHOLDER = "<...>"
class ConfError(Exception):
@@ -27,6 +28,26 @@ class Setting:
return f"[{self.name}]"
@dataclass
class AircraftConfig:
airframe: str = ""
radio: str = ""
telemetry: str = ""
flight_plan: str = ""
settings: List[Setting] = field(default_factory=list)
settings_modules: List[Setting] = field(default_factory=list)
def copy(self) -> "AircraftConfig":
return AircraftConfig(
self.airframe,
self.radio,
self.telemetry,
self.flight_plan,
list(self.settings),
list(self.settings_modules),
)
@dataclass
class Aircraft:
name: str = ""
@@ -60,6 +81,24 @@ class Aircraft:
else:
self.gui_color = color
def get_config(self) -> AircraftConfig:
return AircraftConfig(
self.airframe,
self.radio,
self.telemetry,
self.flight_plan,
list(self.settings),
list(self.settings_modules),
)
def set_config(self, config: AircraftConfig):
self.airframe = config.airframe
self.radio = config.radio
self.telemetry = config.telemetry
self.flight_plan = config.flight_plan
self.settings = list(config.settings)
self.settings_modules = list(config.settings_modules)
def update(self):
self.update_targets()
self.update_settings()
@@ -136,6 +175,36 @@ class Aircraft:
raise ConfError("XMLSyntaxError, file {} is illformed !".format(self.airframe))
def common_aircraft_config(aircrafts: List[Aircraft]) -> AircraftConfig:
if not aircrafts:
return AircraftConfig()
configs = [ac.get_config() for ac in aircrafts]
def common_value(field: str) -> str:
first = getattr(configs[0], field)
if all(getattr(config, field) == first for config in configs[1:]):
return first
return PLACEHOLDER
def common_settings(field: str) -> List[Setting]:
first = getattr(configs[0], field)
settings_lists = [getattr(config, field) for config in configs]
common = [setting for setting in first if all(setting in settings for settings in settings_lists[1:])]
if any(settings != first for settings in settings_lists[1:]):
common.append(Setting(PLACEHOLDER, False))
return common
return AircraftConfig(
common_value("airframe"),
common_value("radio"),
common_value("telemetry"),
common_value("flight_plan"),
common_settings("settings"),
common_settings("settings_modules"),
)
class Conf:
def __init__(self, file: str):
self.file = file # type: str
@@ -239,5 +308,3 @@ class Conf:
+42 -10
View File
@@ -3,6 +3,7 @@ from PyQt5 import QtCore
import conf
import utils
from conf import AircraftConfig, PLACEHOLDER, Setting
from conf_file_widget import ConfFileWidget
from conf_settings_widget import ConfSettingsWidget
@@ -10,23 +11,24 @@ from conf_settings_widget import ConfSettingsWidget
class ConfWidget(QWidget):
conf_changed = QtCore.pyqtSignal()
file_changed = QtCore.pyqtSignal(str, str)
setting_changed = QtCore.pyqtSignal()
def __init__(self, parent=None):
QWidget.__init__(self, parent=parent)
lay = QVBoxLayout(self)
self.airframe = ConfFileWidget("Airframe", "airframes", self)
self.airframe.file_changed.connect(self.conf_changed)
self.airframe.file_changed.connect(lambda: self.handle_file_changed("airframe", self.airframe.path))
self.flight_plan = ConfFileWidget("Flight Plan", "flight_plans", self)
self.flight_plan.file_changed.connect(self.conf_changed)
self.flight_plan.file_changed.connect(lambda: self.handle_file_changed("flight_plan", self.flight_plan.path))
self.flight_plan.edit_alt_button.show()
self.flight_plan.edit_alt_button.setText("Edit GCS")
self.settings = ConfSettingsWidget(self)
self.settings.settings_changed.connect(self.setting_changed)
self.radio = ConfFileWidget("Radio", "radios", self)
self.radio.file_changed.connect(self.conf_changed)
self.radio.file_changed.connect(lambda: self.handle_file_changed("radio", self.radio.path))
self.telemetry = ConfFileWidget("Telemetry", "telemetry", self)
self.telemetry.file_changed.connect(self.conf_changed)
self.telemetry.file_changed.connect(lambda: self.handle_file_changed("telemetry", self.telemetry.path))
self.settings.settings.itemDoubleClicked.connect(self.edit_setting)
self.settings.settings.itemChanged.connect(self.setting_changed)
@@ -39,17 +41,48 @@ class ConfWidget(QWidget):
lay.addWidget(self.radio)
lay.addWidget(self.telemetry)
def handle_file_changed(self, field: str, path: str):
self.file_changed.emit(field, path)
self.conf_changed.emit()
def set_ac(self, ac: conf.Aircraft):
self.airframe.set_path(ac.airframe)
self.telemetry.set_path(ac.telemetry)
self.radio.set_path(ac.radio)
self.flight_plan.set_path(ac.flight_plan)
self.set_config(ac.get_config())
def set_config(self, config: AircraftConfig):
self.airframe.set_path(config.airframe)
self.telemetry.set_path(config.telemetry)
self.radio.set_path(config.radio)
self.flight_plan.set_path(config.flight_plan)
with QtCore.QSignalBlocker(self.settings.settings):
self.settings.settings.clear()
for setting in ac.settings + ac.settings_modules:
for setting in config.settings + config.settings_modules:
item = QListWidgetItem(setting.name)
if setting.name != PLACEHOLDER:
item.setCheckState(QtCore.Qt.Checked if setting.enabled else QtCore.Qt.Unchecked)
self.settings.settings.addItem(item)
def get_config(self) -> AircraftConfig:
settings, modules = self.get_settings()
return AircraftConfig(
self.airframe.path,
self.radio.path,
self.telemetry.path,
self.flight_plan.path,
settings,
modules,
)
def get_settings(self):
modules, settings = [], []
for i in range(self.settings.settings.count()):
item = self.settings.settings.item(i)
setting = Setting(item.text(), item.checkState() == QtCore.Qt.Checked)
if item.text().startswith("module"):
modules.append(setting)
else:
settings.append(setting)
return settings, modules
def edit_setting(self, item: QListWidgetItem):
utils.edit_file(item.text())
@@ -59,4 +92,3 @@ class ConfWidget(QWidget):
self.telemetry.set_path("")
self.radio.set_path("")
self.settings.settings.clear()
+24 -24
View File
@@ -14,6 +14,7 @@ class ConfigurationPanel(QWidget, Ui_ConfigurationPanel):
clear_error = QtCore.pyqtSignal()
ac_edited = QtCore.pyqtSignal(Aircraft)
config_file_changed = QtCore.pyqtSignal(str, str)
program_state_changed = QtCore.pyqtSignal(TabProgramsState)
def __init__(self, parent=None, *args, **kwargs):
@@ -23,10 +24,10 @@ class ConfigurationPanel(QWidget, Ui_ConfigurationPanel):
self.currentAC = None # type: Aircraft
self.flight_plan_editor = None
self.programs_state: TabProgramsState = TabProgramsState.IDLE
self.conf_widget.file_changed.connect(self.handle_config_file_changed)
self.conf_widget.conf_changed.connect(self.handle_conf_changed)
self.conf_widget.setting_changed.connect(self.handle_setting_changed)
self.conf_widget.flight_plan.edit_alt.connect(self.edit_flightplan_gcs)
self.build_widget.spawn_program.connect(self.launch_program)
def init(self):
settings = utils.get_settings()
@@ -37,38 +38,36 @@ class ConfigurationPanel(QWidget, Ui_ConfigurationPanel):
def set_ac(self, ac: Aircraft):
if ac is None:
self.conf_widget.setDisabled(True)
self.currentAC = None
self.conf_widget.reset()
return
self.conf_widget.setDisabled(False)
self.currentAC = ac
self.conf_widget.set_ac(ac)
self.build_widget.update_targets(ac)
def display_config(self, config: AircraftConfig):
self.currentAC = None
self.conf_widget.setDisabled(False)
self.conf_widget.set_config(config)
def handle_setting_changed(self):
def make_setting(item: QListWidgetItem):
name = item.text()
state = True if item.checkState() == QtCore.Qt.Checked else False
return Setting(name, state)
self.apply_current_widget_config()
modules, settings = [], []
for i in range(self.conf_widget.settings.settings.count()):
item = self.conf_widget.settings.settings.item(i)
s = make_setting(item)
if item.text().startswith("module"):
modules.append(s)
else:
settings.append(s)
def handle_conf_changed(self):
self.apply_current_widget_config()
self.currentAC.settings_modules = modules
self.currentAC.settings = settings
def handle_config_file_changed(self, field: str, path: str):
if self.currentAC is None:
self.config_file_changed.emit(field, path)
def apply_current_widget_config(self):
if self.currentAC is None:
return
self.currentAC.set_config(self.conf_widget.get_config())
self.ac_edited.emit(self.currentAC)
# should we save each time a tiny change is made ? very inefficient !
# self.conf.save()
def handle_conf_changed(self):
self.currentAC.airframe = self.conf_widget.airframe.path
self.currentAC.flight_plan = self.conf_widget.flight_plan.path
self.currentAC.radio = self.conf_widget.radio.path
self.currentAC.telemetry = self.conf_widget.telemetry.path
self.ac_edited.emit(self.currentAC)
def handle_tools_changed(self, tools: Dict[str, Tool]):
if "Flight Plan Editor" in tools:
self.flight_plan_editor = tools["Flight Plan Editor"]
@@ -86,8 +85,9 @@ class ConfigurationPanel(QWidget, Ui_ConfigurationPanel):
subprocess.Popen(cmd)
# self.launch_program(self.flight_plan_editor.name, cmd, self.flight_plan_editor.icon)
def launch_program(self, shortname, cmd, icon, cb):
def launch_program(self, shortname, cmd, icon, cb, aircraft=None):
pw = ProgramWidget(shortname, cmd, icon, self.programs_widget)
pw.aircraft = aircraft if aircraft is not None else self.currentAC
self.programs_widget.layout().addWidget(pw)
pw.ready_read_stderr.connect(lambda: self.console_widget.handle_stderr(pw))
pw.ready_read_stdout.connect(lambda: self.console_widget.handle_stdout(pw))
+15 -1
View File
@@ -8,8 +8,9 @@ import utils
from program_widget import ProgramWidget
from dataclasses import dataclass
from enum import Enum
from typing import Dict, List
from typing import Dict, List, Optional
import re
from conf import Aircraft
class Level(Enum):
@@ -46,11 +47,16 @@ class ConsoleWidget(QWidget, Ui_Console):
self.setupUi(self)
self.records: List[Record] = []
self.p_checkboxes: Dict[ProgramWidget, QCheckBox] = {}
self.current_aircraft: Optional[Aircraft] = None
self.programs_checkbox.stateChanged.connect(self.handle_check_all)
self.log_level_slider.valueChanged.connect(self.log_level_changed)
self.clear_button.clicked.connect(self.clear)
self.splitter.setSizes([500, 100])
def set_aircraft(self, ac: Aircraft):
self.current_aircraft = ac
self.update_content()
def display_record(self, record):
if record.level == Level.ERROR:
bg = "background-color:red;"
@@ -100,7 +106,9 @@ class ConsoleWidget(QWidget, Ui_Console):
level = self.classify(line)
r = Record(level, line, pw, channel)
r.aircraft = getattr(pw, "aircraft", None)
self.records.append(r)
if self.filter(r):
self.display_record(r)
def handle_stdout(self, pw: ProgramWidget):
@@ -119,7 +127,9 @@ class ConsoleWidget(QWidget, Ui_Console):
def post_message(self, pw: ProgramWidget, msg):
r = Record(Level.ALL, msg, pw, Channel.MANAGEMENT)
r.aircraft = getattr(pw, "aircraft", None)
self.records.append(r)
if self.filter(r):
self.display_record(r)
def new_program(self, pw: ProgramWidget):
@@ -186,8 +196,12 @@ class ConsoleWidget(QWidget, Ui_Console):
if self.programs_checkbox.checkState() != Qt.Unchecked:
if r.emitter is None:
return False
if r.emitter not in self.p_checkboxes:
return False
if self.p_checkboxes[r.emitter].checkState() != Qt.Checked:
return False
if self.current_aircraft is not None:
return getattr(r, "aircraft", getattr(r.emitter, "aircraft", None)) == self.current_aircraft
return True
def update_content(self):
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'ui/ui_conf_header.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
@@ -17,88 +17,65 @@ class Ui_ConfHeader(object):
ConfHeader.resize(664, 192)
self.horizontalLayout = QtWidgets.QHBoxLayout(ConfHeader)
self.horizontalLayout.setObjectName("horizontalLayout")
self.color_button = QtWidgets.QToolButton(ConfHeader)
self.color_button.setStyleSheet("")
self.color_button.setText("")
self.color_button.setObjectName("color_button")
self.horizontalLayout.addWidget(self.color_button)
self.id_spinBox = QtWidgets.QSpinBox(ConfHeader)
self.id_spinBox.setMaximum(255)
self.id_spinBox.setObjectName("id_spinBox")
self.horizontalLayout.addWidget(self.id_spinBox)
self.ac_combo = QtWidgets.QComboBox(ConfHeader)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
self.label_4 = QtWidgets.QLabel(ConfHeader)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.ac_combo.sizePolicy().hasHeightForWidth())
self.ac_combo.setSizePolicy(sizePolicy)
self.ac_combo.setEditable(False)
self.ac_combo.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.ac_combo.setObjectName("ac_combo")
self.horizontalLayout.addWidget(self.ac_combo)
self.menu_button = QtWidgets.QToolButton(ConfHeader)
self.menu_button.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
self.menu_button.setText("")
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/icons/icons/menu.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.menu_button.setIcon(icon)
self.menu_button.setObjectName("menu_button")
self.horizontalLayout.addWidget(self.menu_button)
self.refresh_button = QtWidgets.QToolButton(ConfHeader)
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(":/icons/icons/refresh.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.refresh_button.setIcon(icon1)
self.refresh_button.setObjectName("refresh_button")
self.horizontalLayout.addWidget(self.refresh_button)
self.save_button = QtWidgets.QToolButton(ConfHeader)
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(":/icons/icons/save.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.save_button.setIcon(icon2)
self.save_button.setObjectName("save_button")
self.horizontalLayout.addWidget(self.save_button)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.label_4 = QtWidgets.QLabel(ConfHeader)
sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
self.label_4.setSizePolicy(sizePolicy)
self.label_4.setObjectName("label_4")
self.horizontalLayout.addWidget(self.label_4)
self.set_combo = QtWidgets.QComboBox(ConfHeader)
self.set_combo.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.set_combo.setObjectName("set_combo")
self.horizontalLayout.addWidget(self.set_combo)
self.refresh_button = QtWidgets.QToolButton(ConfHeader)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/icons/icons/refresh.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.refresh_button.setIcon(icon)
self.refresh_button.setObjectName("refresh_button")
self.horizontalLayout.addWidget(self.refresh_button)
self.save_button = QtWidgets.QToolButton(ConfHeader)
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(":/icons/icons/save.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.save_button.setIcon(icon1)
self.save_button.setObjectName("save_button")
self.horizontalLayout.addWidget(self.save_button)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.new_ac_action = QtWidgets.QAction(ConfHeader)
icon = QtGui.QIcon.fromTheme("document-new")
self.new_ac_action.setIcon(icon)
self.new_ac_action.setObjectName("new_ac_action")
self.remove_ac_action = QtWidgets.QAction(ConfHeader)
icon3 = QtGui.QIcon()
icon3.addPixmap(QtGui.QPixmap(":/icons/icons/delete.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.remove_ac_action.setIcon(icon3)
icon2 = QtGui.QIcon()
icon2.addPixmap(QtGui.QPixmap(":/icons/icons/delete.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.remove_ac_action.setIcon(icon2)
self.remove_ac_action.setObjectName("remove_ac_action")
self.duplicate_action = QtWidgets.QAction(ConfHeader)
icon4 = QtGui.QIcon()
icon4.addPixmap(QtGui.QPixmap(":/icons/icons/copy.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.duplicate_action.setIcon(icon4)
icon3 = QtGui.QIcon()
icon3.addPixmap(QtGui.QPixmap(":/icons/icons/copy.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.duplicate_action.setIcon(icon3)
self.duplicate_action.setObjectName("duplicate_action")
self.rename_action = QtWidgets.QAction(ConfHeader)
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap(":/icons/icons/edit.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.rename_action.setIcon(icon5)
icon4 = QtGui.QIcon()
icon4.addPixmap(QtGui.QPixmap(":/icons/icons/edit.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.rename_action.setIcon(icon4)
self.rename_action.setObjectName("rename_action")
self.retranslateUi(ConfHeader)
self.menu_button.clicked.connect(self.menu_button.showMenu) # type: ignore
QtCore.QMetaObject.connectSlotsByName(ConfHeader)
def retranslateUi(self, ConfHeader):
_translate = QtCore.QCoreApplication.translate
ConfHeader.setWindowTitle(_translate("ConfHeader", "Form"))
self.label_4.setText(_translate("ConfHeader", "Set"))
self.refresh_button.setToolTip(_translate("ConfHeader", "refresh Aircraft"))
self.refresh_button.setText(_translate("ConfHeader", "..."))
self.refresh_button.setShortcut(_translate("ConfHeader", "Ctrl+R"))
self.save_button.setToolTip(_translate("ConfHeader", "save conf"))
self.save_button.setText(_translate("ConfHeader", "..."))
self.save_button.setShortcut(_translate("ConfHeader", "Ctrl+S"))
self.label_4.setText(_translate("ConfHeader", "Set"))
self.new_ac_action.setText(_translate("ConfHeader", "New AC"))
self.remove_ac_action.setText(_translate("ConfHeader", "Remove"))
self.duplicate_action.setText(_translate("ConfHeader", "Duplicate"))
+40 -39
View File
@@ -15,8 +15,43 @@ class Ui_Console(object):
def setupUi(self, Console):
Console.setObjectName("Console")
Console.resize(709, 647)
Console.setLayoutDirection(QtCore.Qt.LeftToRight)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(Console)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_3 = QtWidgets.QLabel(Console)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
self.label_3.setSizePolicy(sizePolicy)
self.label_3.setObjectName("label_3")
self.horizontalLayout_2.addWidget(self.label_3)
self.log_level_slider = QtWidgets.QSlider(Console)
self.log_level_slider.setMaximumSize(QtCore.QSize(100, 16777215))
self.log_level_slider.setMaximum(3)
self.log_level_slider.setPageStep(1)
self.log_level_slider.setProperty("value", 3)
self.log_level_slider.setOrientation(QtCore.Qt.Horizontal)
self.log_level_slider.setTickPosition(QtWidgets.QSlider.TicksBelow)
self.log_level_slider.setTickInterval(1)
self.log_level_slider.setObjectName("log_level_slider")
self.horizontalLayout_2.addWidget(self.log_level_slider)
self.log_level_label = QtWidgets.QLabel(Console)
self.log_level_label.setObjectName("log_level_label")
self.horizontalLayout_2.addWidget(self.log_level_label)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem)
self.clear_button = QtWidgets.QPushButton(Console)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/icons/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.clear_button.setIcon(icon)
self.clear_button.setObjectName("clear_button")
self.horizontalLayout_2.addWidget(self.clear_button)
self.horizontalLayout_2.setStretch(1, 1)
self.horizontalLayout_2.setStretch(3, 2)
self.verticalLayout_2.addLayout(self.horizontalLayout_2)
self.splitter = QtWidgets.QSplitter(Console)
self.splitter.setOrientation(QtCore.Qt.Horizontal)
self.splitter.setObjectName("splitter")
@@ -48,46 +83,12 @@ class Ui_Console(object):
self.programs_widget.setObjectName("programs_widget")
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.programs_widget)
self.verticalLayout_4.setObjectName("verticalLayout_4")
spacerItem = QtWidgets.QSpacerItem(1, 167, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_4.addItem(spacerItem)
spacerItem1 = QtWidgets.QSpacerItem(1, 167, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_4.addItem(spacerItem1)
self.scrollArea.setWidget(self.programs_widget)
self.verticalLayout.addWidget(self.scrollArea)
self.verticalLayout_2.addWidget(self.splitter)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_3 = QtWidgets.QLabel(Console)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
self.label_3.setSizePolicy(sizePolicy)
self.label_3.setObjectName("label_3")
self.horizontalLayout_2.addWidget(self.label_3)
self.log_level_slider = QtWidgets.QSlider(Console)
self.log_level_slider.setMaximumSize(QtCore.QSize(100, 16777215))
self.log_level_slider.setMaximum(3)
self.log_level_slider.setPageStep(1)
self.log_level_slider.setProperty("value", 3)
self.log_level_slider.setOrientation(QtCore.Qt.Horizontal)
self.log_level_slider.setTickPosition(QtWidgets.QSlider.TicksBelow)
self.log_level_slider.setTickInterval(1)
self.log_level_slider.setObjectName("log_level_slider")
self.horizontalLayout_2.addWidget(self.log_level_slider)
self.log_level_label = QtWidgets.QLabel(Console)
self.log_level_label.setObjectName("log_level_label")
self.horizontalLayout_2.addWidget(self.log_level_label)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem1)
self.clear_button = QtWidgets.QPushButton(Console)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/icons/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.clear_button.setIcon(icon)
self.clear_button.setObjectName("clear_button")
self.horizontalLayout_2.addWidget(self.clear_button)
self.horizontalLayout_2.setStretch(1, 1)
self.horizontalLayout_2.setStretch(3, 2)
self.verticalLayout_2.addLayout(self.horizontalLayout_2)
self.verticalLayout_2.setStretch(0, 1)
self.verticalLayout_2.setStretch(1, 1)
self.retranslateUi(Console)
QtCore.QMetaObject.connectSlotsByName(Console)
@@ -95,9 +96,9 @@ class Ui_Console(object):
def retranslateUi(self, Console):
_translate = QtCore.QCoreApplication.translate
Console.setWindowTitle(_translate("Console", "Form"))
self.label.setText(_translate("Console", "Filters"))
self.programs_checkbox.setText(_translate("Console", "Programs"))
self.label_3.setText(_translate("Console", "Log level:"))
self.log_level_label.setText(_translate("Console", "All"))
self.clear_button.setText(_translate("Console", "Clear console"))
self.label.setText(_translate("Console", "Filters"))
self.programs_checkbox.setText(_translate("Console", "Programs"))
from generated import resources_rc
+1 -57
View File
@@ -1,52 +1,27 @@
# Copyright (C) 2008-2022 The Paparazzi Team
# released under GNU GPLv2 or later. See COPYING file.
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5 import QtCore
from generated.ui_conf_header import Ui_ConfHeader
from conf import Aircraft, Conf
import utils
import os
import sys
import paparazzi
from typing import List
class HeaderWidget(QWidget, Ui_ConfHeader):
set_changed = QtCore.pyqtSignal(str)
ac_changed = QtCore.pyqtSignal(str)
ac_edited = QtCore.pyqtSignal(Aircraft)
ac_rename = QtCore.pyqtSignal(Aircraft)
ac_delete = QtCore.pyqtSignal(Aircraft)
ac_duplicate = QtCore.pyqtSignal(Aircraft)
ac_save = QtCore.pyqtSignal(Aircraft)
ac_new = QtCore.pyqtSignal()
def __init__(self, parent=None):
QWidget.__init__(self, parent=parent)
self.setupUi(self)
self.currentAc: Aircraft = None
self.set_combo.currentTextChanged.connect(self.set_changed)
self.ac_combo.currentTextChanged.connect(self.ac_changed)
self.id_spinBox.valueChanged.connect(self.handle_id_changed)
self.refresh_button.clicked.connect(lambda: self.ac_edited.emit(self.currentAc))
self.color_button.clicked.connect(self.change_color)
self.save_button.clicked.connect(lambda: self.ac_save.emit(self.currentAc))
self.menu_button.addAction(self.rename_action)
self.rename_action.triggered.connect(lambda: self.ac_rename.emit(self.currentAc))
self.menu_button.addAction(self.remove_ac_action)
self.remove_ac_action.triggered.connect(lambda: self.ac_delete.emit(self.currentAc))
self.menu_button.addAction(self.duplicate_action)
self.duplicate_action.triggered.connect(lambda: self.ac_duplicate.emit(self.currentAc))
self.menu_button.addAction(self.new_ac_action)
self.new_ac_action.triggered.connect(self.ac_new)
def handle_id_changed(self, new_id):
self.currentAc.ac_id = new_id
self.ac_edited.emit(self.currentAc)
def update_sets(self):
sets = paparazzi.get_list_of_conf_files()
conf_init = Conf.get_current_conf()
@@ -54,39 +29,8 @@ class HeaderWidget(QWidget, Ui_ConfHeader):
if conf_init in sets:
self.set_combo.setCurrentText(conf_init)
def set_acs(self, acs: List[str]):
self.ac_combo.clear()
self.ac_combo.addItems(acs)
def set_ac(self, ac: Aircraft):
self.currentAc = ac
self.id_spinBox.blockSignals(True)
self.id_spinBox.setValue(ac.ac_id)
self.id_spinBox.blockSignals(False)
self.color_button.setStyleSheet("background-color: {};".format(ac.get_color()))
self.ac_combo.setCurrentText(ac.name)
def add_ac(self, ac: Aircraft):
self.ac_combo.addItem(ac.name)
def remove_ac(self, ac):
for i in range(self.ac_combo.count()):
if self.ac_combo.itemText(i) == ac.name:
self.ac_combo.removeItem(i)
break
def rename_ac(self, new_name):
i = self.ac_combo.currentIndex()
self.ac_combo.setItemText(i, new_name)
def change_color(self):
initial = QtGui.QColor(self.currentAc.get_color())
color = QColorDialog.getColor(initial, self, "AC color")
if color.isValid():
color_name = color.name()
self.currentAc.set_color(color_name)
self.color_button.setStyleSheet("background-color: {};".format(color_name))
self.ac_edited.emit(self.currentAc)
def disable_sets(self):
self.set_combo.setDisabled(True)
File diff suppressed because it is too large Load Diff
+2
View File
@@ -41,6 +41,8 @@ class ProgramWidget(QWidget, Ui_Program):
self.process.finished.connect(self.handle_finished)
self.process.started.connect(self.handle_started)
self.process.errorOccurred.connect(self.handle_error)
if icon is None:
icon = "default_tool_icon.svg"
i = QIcon(os.path.join(utils.PAPARAZZI_HOME, "data", "pictures", "tools_icons", icon))
self.icon_label.setPixmap(i.pixmap(20, 20))
self.icon_label.setToolTip(shortname)
+1
View File
@@ -155,6 +155,7 @@ class SessionWidget(QWidget, Ui_Session):
cmd = [os.path.join(utils.PAPARAZZI_SRC, tool.command)] + flat_args
pw = ProgramWidget(tool.name, cmd, tool.icon, self.programs_widget)
pw.aircraft = self.ac
self.program_widgets.append(pw)
lay: QVBoxLayout = self.programs_widget.layout()
lay.insertWidget(lay.count()-1, pw)
+8 -66
View File
@@ -15,49 +15,22 @@
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QToolButton" name="color_button">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="id_spinBox">
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="ac_combo">
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>false</bool>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
<property name="text">
<string>Set</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="menu_button">
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/icons/menu.png</normaloff>:/icons/icons/menu.png</iconset>
<widget class="QComboBox" name="set_combo">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
@@ -108,20 +81,6 @@
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Set</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="set_combo">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
<action name="new_ac_action">
<property name="icon">
@@ -163,22 +122,5 @@
<resources>
<include location="resources.qrc"/>
</resources>
<connections>
<connection>
<sender>menu_button</sender>
<signal>clicked()</signal>
<receiver>menu_button</receiver>
<slot>showMenu()</slot>
<hints>
<hint type="sourcelabel">
<x>302</x>
<y>95</y>
</hint>
<hint type="destinationlabel">
<x>302</x>
<y>95</y>
</hint>
</hints>
</connection>
</connections>
<connections/>
</ui>
+74 -71
View File
@@ -13,78 +13,10 @@
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,0">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<widget class="QTextEdit" name="console_textedit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
<widget class="QWidget" name="filter_widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Filters</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="programs_checkbox">
<property name="text">
<string>Programs</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="programs_widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>180</width>
<height>525</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>167</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1,0,2,0">
<item>
@@ -161,6 +93,77 @@
</item>
</layout>
</item>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTextEdit" name="console_textedit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
<widget class="QWidget" name="filter_widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Filters</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="programs_checkbox">
<property name="text">
<string>Programs</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="programs_widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>180</width>
<height>525</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>167</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources>