diff --git a/.gitignore b/.gitignore index 343691fe08..b39dca8b94 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ *.gdb_history +.cache.xml + # Eclipse IDE project files *.cproject *.project diff --git a/paparazzi-python.sh b/paparazzi-python.sh new file mode 100755 index 0000000000..1a99867207 --- /dev/null +++ b/paparazzi-python.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env /bin/sh +cd $PAPARAZZI_SRC/sw/supervision/python +./main.py diff --git a/src/paparazzi b/src/paparazzi index 8895cccacb..9cda33fba4 100755 --- a/src/paparazzi +++ b/src/paparazzi @@ -9,7 +9,16 @@ let env = let value = if value = "." then Sys.getcwd () else value in Printf.sprintf "%s=%s" var value) [|"PAPARAZZI_SRC"; "PAPARAZZI_HOME"|] -let com = dirname // "sw/supervision/paparazzicenter";; +let com = + if Array.length (Sys.argv) > 1 then begin + Printf.printf "%d\n" (Array.length (Sys.argv)); + if Sys.argv.(1) = "-python" then + dirname // "paparazzi-python.sh" + else + dirname // "sw/supervision/paparazzicenter" + end + else + dirname // "sw/supervision/paparazzicenter";; Sys.argv.(0) <- com;; let env = Array.append env (Unix.environment ());; Unix.execve com Sys.argv env diff --git a/sw/supervision/python/.cache.dtd b/sw/supervision/python/.cache.dtd new file mode 100644 index 0000000000..3a3f843f05 --- /dev/null +++ b/sw/supervision/python/.cache.dtd @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sw/supervision/python/README.md b/sw/supervision/python/README.md new file mode 100644 index 0000000000..759f72a36b --- /dev/null +++ b/sw/supervision/python/README.md @@ -0,0 +1,69 @@ +############################################################################### +# Welcome in the future Paparazzi Center ! # +############################################################################### + +1) Run instructions : + +To run the new Paparazzi Center, you need : + +> A Linux or IOS system + +> A correctly installed version of Paparazzi UAV software + +> Python 3.4 (install package 'python3') + +> PyQt5 (install package 'python3-pyqt5') + +First "git pull" my part of code or copy the current "./sw/supervision/python" +version into your own "./sw/supervision" directory. No installation nor +compilation is needed. +Then, make the 'main.py' file executable ('chmod +x main.py') and launch the +program in a terminal ('./main.py') or from the paparazzi launcher (soon). + +############################################################################### + +2) Feedback : + +I'm an intern student at the ENAC and my mission is to reconceive the Paparazzi +Center in Python/Qt to make it more intuitive and 'maintainable' (that can +evolve easily). So I'd really like to have your feeling about my propositions ! + +If you have any question or comment about it, please contact me by +Github (my pseudo is 'floienac' and I use Gitter) or by mail +('f.bitard@gmail.com'). +I'd be glad to be criticized, especially if you're directly concerned as a +final user or a developer of Paparazzi UAV. + +Thanks by advance :) + +Florian BITARD - intern student at the ENAC's drones lab +(February to June 2016) + +http://wiki.paparazziuav.org/wiki/Paparazzi_Center/Evolutions +############################################################################### + +3) GNU License : + +As this new Paparazzi Center is a free and open source software part, feel +free to correct bugs, improve the functions and add new ones to it. + +# Paparazzi center utilities +# +# Copyright (C) 2016 ENAC, Florian BITARD (intern student) +# +# This file is part of paparazzi. +# +# paparazzi 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, or (at your option) +# any later version. +# +# paparazzi 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 paparazzi; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. diff --git a/sw/supervision/python/dialogs.py b/sw/supervision/python/dialogs.py new file mode 100644 index 0000000000..1f3c1c2097 --- /dev/null +++ b/sw/supervision/python/dialogs.py @@ -0,0 +1,103 @@ +# Paparazzi center utilities +# +# Copyright (C) 2016 ENAC, Florian BITARD (intern student) +# +# This file is part of paparazzi. +# +# paparazzi 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, or (at your option) +# any later version. +# +# paparazzi 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 paparazzi; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +############################################################################### +# [Imports] + +import ui.set_manager as manager +import ui.popup as popup + +import PyQt5.QtWidgets as Widgets +import logging +import os + + +############################################################################### +# [Constants] + +LOGGER = logging.getLogger("[DIALOGS]") + +UI_DIR = "ui" + +DATA_CHANGED_POPUP_TYPE = "data changed" +DATA_CHANGED_POPUP_HTML = "data_changed.html" +CREDITS_POPUP_TYPE = "credits" +CREDITS_POPUP_HTML = "credits.html" +TUTORIAL_POPUP_TYPE = "tutorial" +TUTORIAL_POPUP_HTML = "tutorial.html" + + +############################################################################### +# [SettingsManager class] + +class SettingsManager(Widgets.QDialog): + """Class to manage the settings manager HMI.""" + def __init__(self): + super(SettingsManager, self).__init__() + self.ui = manager.Ui_Dialog() + self.ui.setupUi(self) + + +############################################################################### +# [SettingsManager class] + +class Popup(Widgets.QDialog): + """Class to manage the settings manager HMI.""" + def __init__(self, popup_type=None): + super(Popup, self).__init__() + self.ui = popup.Ui_Dialog() + self.ui.setupUi(self) + self.ui.textBrowser.setOpenExternalLinks(True) + + self.type = popup_type + + self.set_popup_details() + + def set_popup_details(self): + """ + -> Set the title, the text and the dialog buttons of the popup window + according to its type. + """ + if self.type == DATA_CHANGED_POPUP_TYPE: + self.setWindowTitle("/!\ Some unsaved data found !") + self.ui.buttonBox.setStandardButtons( + Widgets.QDialogButtonBox.Cancel | Widgets.QDialogButtonBox.Save) + html_file = DATA_CHANGED_POPUP_HTML + elif self.type == CREDITS_POPUP_TYPE: + self.setWindowTitle("Paparazzi UAV Center credits " + "(Python/Qt version)") + self.ui.buttonBox.setStandardButtons(Widgets.QDialogButtonBox.Close) + html_file = CREDITS_POPUP_HTML + elif self.type == TUTORIAL_POPUP_TYPE: + self.setWindowTitle("Tutorials and documents") + self.ui.buttonBox.setStandardButtons(Widgets.QDialogButtonBox.Close) + html_file = TUTORIAL_POPUP_HTML + else: + self.setWindowTitle("Popup without specific type.") + html_file = "none.html" + + try: + html_config_path = os.path.join(UI_DIR, html_file) + with open(html_config_path, 'r') as popup_html_content: + self.ui.textBrowser.setHtml(popup_html_content.read()) + except FileNotFoundError: + print("Popup HTML configuration file not found ! " + "('%s' should be in 'ui' directory.)" % html_file) diff --git a/sw/supervision/python/hmi.py b/sw/supervision/python/hmi.py new file mode 100644 index 0000000000..406a606e62 --- /dev/null +++ b/sw/supervision/python/hmi.py @@ -0,0 +1,1768 @@ +# Paparazzi center utilities +# +# Copyright (C) 2016 ENAC, Florian BITARD (intern student) +# +# This file is part of paparazzi. +# +# paparazzi 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, or (at your option) +# any later version. +# +# paparazzi 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 paparazzi; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +############################################################################### +# [Imports] + +import ui.main_window as ui +import dialogs as dial +import lib.console as cs +import lib.database as db +import lib.environment as env +import lib.gui as gui +import parser +import processes as proc + +import PyQt5.QtCore as Core +import PyQt5.QtWidgets as Widgets +import functools +import logging +import os +import sys +import re +import shutil + +############################################################################### +# [Constants] + +LOGGER = logging.getLogger("[HMI]") + +CONF_PATH = env.PAPARAZZI_CONF +SET_SYMBOLIC_LINK = CONF_PATH + "/conf.xml" +CONF_XML_COPY = CONF_PATH + "/conf_copy.xml" + +OK_QPIXMAP = "icons/dialog-apply.svg" +ERROR_QPIXMAP = "icons/dialog-error.svg" +START_ICON = "icons/media-playback-start.svg" +STOP_ICON = "icons/process-stop.svg" +CHANGED_ICON = "icons/dialog-warning-symbolic.svg" + +UNKNOWN_VALUE = "---" + +PROGRESS_BAR_LINE = " " +PROGRESS_BAR_CURSOR = "***" +PROGRESS_BAR_SPEED = 10 + + +############################################################################### +# [Functions] System functions + +def control_cwd(): + """ + -> Get the current directory path and log it. + """ + cwd = os.getcwd() + LOGGER.info("Current directory : '%s'.", cwd) + return cwd + + +def point_symbol_link_to(set_file): + """ + :param set_file: + -> Make the 'conf.xml' symbolic file point to the given 'set_file'. + """ + if os.path.exists(SET_SYMBOLIC_LINK): + os.remove(SET_SYMBOLIC_LINK) + os.symlink(set_file, SET_SYMBOLIC_LINK) + LOGGER.debug("'%s' -> '%s'.", SET_SYMBOLIC_LINK, set_file) + + +def init_conf_xml_path(): + """ + -> Create a copy of the conf.xml file if it's an existing and real file. + -> Make the conf.xml file point to the copy created. + """ + if os.path.exists(SET_SYMBOLIC_LINK) \ + and not os.path.islink(SET_SYMBOLIC_LINK): + shutil.copy(SET_SYMBOLIC_LINK, CONF_XML_COPY) + point_symbol_link_to(CONF_XML_COPY) + + +############################################################################### +# [Functions] Widgets management functions + +def set_widgets_visibility(widgets, set_visible_bool): + """ + :param widgets: + :param set_visible_bool: + -> Set a list of widgets visible or not together. + """ + for widget in widgets: + widget.setVisible(set_visible_bool) + + +def set_widgets_availability(widgets, set_available_bool): + """ + :param widgets: + :param set_available_bool: + -> Set a list of widgets available or not together. + """ + for widget in widgets: + widget.setEnabled(set_available_bool) + + +def set_items_icons(list_widget_items, qicon): + """ + :param list_widget_items: + :param qicon: + -> Put the same QIcon to items of a QListWidget. + """ + for item in list_widget_items: + item.setIcon(qicon) + + +def clear_widgets(widgets): + """ + :param widgets: + -> Clear each widget in a list of widgets. + """ + for widget in widgets: + widget.clear() + + +def clear_list_widgets_selection(widgets): + """ + :param widgets: + -> Clear the selection of each widget in a list of QListWidget widgets. + """ + for widget in widgets: + widget.clearSelection() + widget.clearFocus() + + +############################################################################### +# [Hmi class] + +class Hmi(Widgets.QMainWindow): + """Class to manage the main HMI behavior.""" + def __init__(self): + """ + -> Creation of a MainWindow object generated by the command : + 'pyuic5 *.ui -o *.py' from the PyQt5 HMI designed under QtDesigner. + -> Creation of Dialog objects used from the main HMI. + -> Declaration of widgets groups for code factorization. + -> Declaration of HMI private attributes for its own methods. + """ + super(Hmi, self).__init__() + + self.ui = ui.Ui_MainWindow() + self.ui.setupUi(self) + + # Existing popups : + self.settings = None + self.data_changed_popup = None + self.credits_popup = None + self.tutorial_popup = None + + # Shortcuts for lists of widgets : + self.change_config_widgets = [self.ui.current_airframes, + self.ui.current_settings, + self.ui.current_flight_plan, + self.ui.current_radio, + self.ui.current_telemetry] + self.display_config_widgets = [self.ui.airframes_overview, + self.ui.settings_overview, + self.ui.flight_plan_overview, + self.ui.radio_overview, + self.ui.telemetry_overview, + self.ui.airframes_overview_2, + self.ui.settings_overview_2, + self.ui.flight_plan_overview_2, + self.ui.radio_overview_2, + self.ui.telemetry_overview_2] + self.flight_plan_buttons = [self.ui.open_gui, + self.ui.select_kml] + self.session_widgets = [self.ui.session, + self.ui.session_overview_1, + self.ui.session_overview_2] + self.display_programs_widgets = [self.ui.programs_overview_1, + self.ui.programs_overview_2] + self.target_widgets = [self.ui.quick_target, self.ui.target] + self.messages_level_widgets = [self.ui.important, + self.ui.custom, self.ui.all] + self.log_filters_widgets = [self.ui.display_default, + self.ui.display_info, + self.ui.display_warnings, + self.ui.display_errors] + self.messages_nb_widgets = [self.ui.info_nb, self.ui.warnings_nb, + self.ui.errors_nb] + self.start_all_buttons = [self.ui.start_all_button, + self.ui.quick_restart, + self.ui.quick_restart_3] + self.kill_all_buttons = [self.ui.kill_all_button, self.ui.quick_kill, + self.ui.quick_kill_2] + + # Icons used in widgets (can't be constants because qpixmap objects + # must be declared into a QApplication) : + self.ok_qpixmap = gui.generate_qpixmap(OK_QPIXMAP) + self.error_qpixmap = gui.generate_qpixmap(ERROR_QPIXMAP) + self.start_qicon = gui.generate_qicon(START_ICON) + self.stop_qicon = gui.generate_qicon(STOP_ICON) + self.changed_qicon = gui.generate_qicon(CHANGED_ICON) + + # Progress bar initialization : + self.max_result_field_size = self.ui.build_result.width() // 10 + self.progress_bar_to_left = False + + # HMI parameters initialization (could be set in settings menu) : + self.dev_mode = False + + self.current_log_filter = None + + self.data = None + self.current_set = None + self.current_config = None + self.current_item = None + + self.current_target = None + self.simulation_targets_names = ['nps', 'sim'] + self.change_target_bool = True + + self.current_device = None + self.current_session = None + self.current_program = None + self.current_option = None + + self.run_version = None + self.build_version = None + + self.configurations_changed = {} + self.sessions_changed = {} + + self.build = None + self.build_running = False + self.clean = None + self.clean_running = False + self.upload = None + self.upload_running = False + + self.running_programs = {} + + self.console = cs.Console(self.ui.console) + + # Logger output redirected to a special stream to allow an integrated + # console display (and logger level could be set in settings menu) : + self.logger_stream = proc.LoggerStream() + self.logger_stream.logger_log_sent.connect(self.write_log_in_console) + logging.basicConfig(level=logging.INFO, stream=self.logger_stream) + +############################################################################### +# [Hmi methods] Init HMI methods + + def init_hmi_data(self): + """ + -> Initialization of a 'Data' object with all necessary data from XML + files found in the 'CONF_PATH' Paparazzi UAV directory. + -> Loading of cache data (last settings used) to restore them. + -> Initialization of main HMI widgets with necessary data found. + """ + init_conf_xml_path() + + # Data object creation implies XML parsing in the parser module : + try: + self.data = parser.Data(CONF_PATH) + + # Cache parameters extracted from the cache dictionary : + last_geometry = self.data.cache[parser.LAST_GEOMETRY].split(" ") + last_x, last_y, last_width, last_height = map(int, last_geometry) + self.setGeometry(last_x, last_y, last_width, last_height) + + last_set_name = self.data.cache[parser.LAST_SET] + self.current_set = self.data.sets[last_set_name] + + point_symbol_link_to(self.current_set.name) + + last_config_name = self.data.cache[parser.LAST_CONFIG] + self.current_config = self.data.configurations[last_config_name] + + last_target_name = self.data.cache[parser.LAST_TARGET] + self.current_target = self.current_config.targets[last_target_name] + + self.ui.upload.setEnabled(self.current_target.name + not in self.simulation_targets_names) + + last_device_name = self.data.cache[parser.LAST_DEVICE] + self.current_device = self.data.devices[last_device_name] + + last_session_name = self.data.cache[parser.LAST_SESSION] + self.current_session = self.data.sessions[last_session_name] + + last_log_filters = self.data.cache[parser.LAST_LOG_FILTERS].split(" ") + last_level = last_log_filters[0] + last_default, last_info, last_warning, last_error =\ + map(int, last_log_filters[1:]) + self.current_log_filter = cs.LogFilter(last_level, + last_default, last_info, + last_warning, last_error) + except: + " if something goes wrong, delete cache and load again (to be improved)" + LOGGER.error("ERROR while load HMI cache" + "Original message : '%s'.", sys.exc_info()[0]) + print("HMI error in cache, load default instead") + parser.delete_cache() + self.init_hmi_data() + + # Run and build Paparazzi versions found by existing program + # './paparazzi_version' and file './var/build_version.txt' : + run_version_cmd = env.RUN_VERSION_EXE + self.run_version = os.popen(run_version_cmd).readline().strip() + build_version_cmd = " ".join(["cat", env.BUILD_VERSION_FILE]) + self.build_version = os.popen(build_version_cmd).readline().strip() + + def init_hmi_widgets(self): + """ + -> Initialization of all the HMI widgets content. + -> Initialization of existing popups. + """ + # Update functions used for initialization : + self.update_home_button() + self.update_mode_button() + self.update_versions() + self.update_set_combo() + self.update_config_combo() + self.update_config_items_lists() + self.update_target_combos(self.target_widgets) + self.update_log_filters() + self.change_messages_level() + self.update_device_combo() + self.update_session_combos() + self.update_programs_list() + self.update_program_options_list() + self.init_tools_menu() + + # Popup initialization : + self.settings = dial.SettingsManager() + self.data_changed_popup = dial.Popup(dial.DATA_CHANGED_POPUP_TYPE) + self.credits_popup = dial.Popup(dial.CREDITS_POPUP_TYPE) + self.tutorial_popup = dial.Popup(dial.TUTORIAL_POPUP_TYPE) + + def init_all_hmi(self): + """ + -> Centralize all initializations before the mainwindow is shown. + """ + control_cwd() + LOGGER.info("HMI init in progress...\n") + print("\nHMI init in progress...") + self.init_hmi_data() + self.init_hmi_widgets() + self.connect_signals() + LOGGER.info("HMI init finished.\n") + print("HMI init finished.\n") + +############################################################################### +# [Hmi methods] Connect signals methods + + def connect_signals(self): + """ + -> Connection of 'triggered' signals from 'QMenu' or 'QAction'. + -> Connection of 'currentIndexChanged' signals from 'QComboBox'. + -> Connection of 'clicked' signals from 'QPushButton'. + """ + ####################################################################### + # Actions in menus + + self.ui.actionQuit_Paparazzi_UAV.triggered.connect(self.close) + + self.ui.actionSetManager.triggered.connect(self.settings.show) + self.ui.actionSave_2.triggered.connect(self.save_current_config) + + self.ui.actionSave_3.triggered.connect(self.save_current_session) + + self.ui.actionFull_screen.triggered.connect(self.fullscreen_view) + + self.ui.actionAbout_Paparazzi_UAV.triggered.connect( + self.credits_popup.show) + self.ui.actionTutorial.triggered.connect(self.tutorial_popup.show) + + ####################################################################### + # Equipment tab + + for list_widget in self.change_config_widgets: + list_widget.itemSelectionChanged.connect(functools.partial( + self.change_current_item, list_widget)) + self.ui.current_settings.itemClicked.connect( + self.change_setting_check_state) + + self.ui.current_set.currentIndexChanged.connect(self.change_current_set) + self.ui.current_configuration.currentIndexChanged.connect( + self.change_current_config) + + self.ui.remove_item.clicked.connect(self.remove_item_from_config) + self.ui.add_item.clicked.connect(self.open_item_file_chooser) + self.ui.edit.clicked.connect(self.open_gedit) + + self.ui.quick_build.clicked.connect(self.quick_build) + + ####################################################################### + # Build / flash tab + + for combo_widget in self.target_widgets: + combo_widget.currentIndexChanged.connect(functools.partial( + self.change_current_target, combo_widget)) + + for widget in self.log_filters_widgets: + widget.clicked.connect(self.change_sensitivity_filters) + + for widget in self.messages_level_widgets: + widget.clicked.connect(self.change_messages_level) + + self.ui.clean.clicked.connect(self.clean_config) + self.ui.build.clicked.connect(self.build_config) + self.ui.upload.clicked.connect(self.flash_device) + self.ui.show_console.clicked.connect(functools.partial( + self.switch_to_tab, 3)) + + self.ui.device.currentIndexChanged.connect(self.change_current_device) + + ####################################################################### + # Session tab + + for combo_widget in self.session_widgets: + combo_widget.currentIndexChanged.connect(functools.partial( + self.change_current_session, combo_widget)) + + self.ui.programs.itemSelectionChanged.connect( + self.change_current_program) + self.ui.options.itemSelectionChanged.connect( + self.change_current_option) + + self.ui.play_stop_program.clicked.connect(functools.partial( + self.run_program, None)) + for button in self.start_all_buttons: + button.clicked.connect(self.start_all_programs) + for button in self.kill_all_buttons: + button.clicked.connect(self.kill_all_programs) + + self.ui.remove_program.clicked.connect( + self.remove_program_from_session) + self.ui.remove_option.clicked.connect(self.remove_option_from_program) + self.ui.add_option.clicked.connect(self.add_option_to_program) + self.ui.options.itemChanged.connect(self.edit_current_option) + + ####################################################################### + # Console tab + + self.ui.clean_console.clicked.connect(self.clean_console) + + ####################################################################### + # Other widgets connected + + self.ui.open_home_terminal.clicked.connect(self.open_terminal) + self.ui.switch_mode.clicked.connect(self.switch_current_mode) + +############################################################################### +# [Hmi methods] Reimplemented methods + + def closeEvent(self, event): + """ + :param event: QEvent object automatically filled when 'closed' signal + caught. + -> Called automatically when when 'closed' signal caught. + -> Kill all processes still running. + -> Update the cache file. + -> Call the real mainWindow closeEvent to finish the 'app.exec_' + events loop. + """ + for process in [self.clean, self.build, self.upload]: + # + list(self.running_programs.values()): + # /!\ REQUIREMENT : DON'T KILL SESSION PROCESSES WHEN THE + # APPLICATION EXITS... + if process is not None: + try: + process.subprocess.kill() + except ProcessLookupError: + print("Process : '%s' already stopped. " + "Can't be killed again !\n" % process.name) + else: + print("Process : '%s' killed !\n" % process.name) + + # Ask for a confirmation if some data are unsaved and quit saving + # these data : + if self.configurations_changed != {} or self.sessions_changed != {}: + if self.sessions_changed != {parser.SIMULATION_NAME: + parser.SIMULATION_SESSION} \ + and self.sessions_changed != {parser.REPLAY_NAME: + parser.REPLAY_SESSION}: + self.data_changed_popup.show() + self.data_changed_popup.accepted.connect( + self.save_all_changed_data) + self.data_changed_popup.rejected.connect( + self.save_cache_data) + else: + print("A special in-code defined session (Session or Replay) " + "has been changed and could not have been saved...\n") + self.save_cache_data() + else: + self.save_cache_data() + +############################################################################### +# [Hmi methods] Update widgets methods + + def update_home_button(self): + self.ui.open_home_terminal.setText(env.PAPARAZZI_HOME) + + def update_mode_button(self): + if self.dev_mode: + self.ui.switch_mode.setText("Developer") + else: + self.ui.switch_mode.setText("Standard") + + def update_versions(self): + for field, value in zip([self.ui.run_version, self.ui.build_version], + [self.run_version, self.build_version]): + if value is not None: + field.setText(value) + else: + field.setText(UNKNOWN_VALUE) + + def switch_to_tab(self, tab_index): + self.ui.main_tab.setCurrentIndex(tab_index) + + def init_tools_menu(self): + """ + -> Clear the 'Tools' menu actions (menubar). + -> Fill the 'Tools' menu with the names found in 'control_panel.xml' + -> Connect each action to the corresponding tool process. + """ + self.ui.menuTools.clear() + sorted_names = parser.sorted_tools_names(self.data.tools) + for name in sorted_names: + action = Widgets.QAction(name, self) + self.ui.menuTools.addAction(action) + for action in self.ui.menuTools.actions(): + action.triggered.connect(functools.partial( + self.add_program_to_session, self.data.tools[action.text()])) + + def fullscreen_view(self): + if self.isMaximized(): + self.resize(800, 600) + else: + self.showMaximized() + + def update_set_combo(self): + """ + -> Clear the 'Set' combobox. + -> Fill the combobox with the names found in 'conf.xml'. + """ + self.ui.current_set.clear() + sorted_names = parser.sorted_sets_names(self.data.sets) + self.ui.current_set.addItems(sorted_names) + self.ui.current_set.setCurrentText(self.current_set.name) + + def update_config_combo(self): + """ + -> Clear the 'Configuration' combobox. + -> Fill the combobox with the configurations names corresponding to the + current selected set. + -> Save the current settings check state when configuration changed. + (allows to the configuration to be recovered when selected again) + """ + self.ui.current_configuration.clear() + sorted_names = parser.sorted_current_configs_names( + self.data.configurations, + self.current_set) + self.ui.current_configuration.addItems(sorted_names) + self.ui.current_configuration.setCurrentText(self.current_config.name) + + def update_config_items_lists(self): + """ + -> Disable or hide widgets and buttons to init config. + -> Set config ID and color. + -> Set config items by category. + -> Make 'settings' items checkable and set check-boxes. + """ + set_widgets_visibility(self.display_config_widgets + + self.change_config_widgets, False) + set_widgets_availability([self.ui.remove_item, + self.ui.edit], False) + set_widgets_visibility(self.flight_plan_buttons, False) + + self.ui.current_id.setText(self.current_config.id) + color = gui.generate_qcolor(self.current_config.color[0]) + palette = gui.generate_widget_palette(self.ui.current_color, color) + self.ui.current_color.setPalette(palette) + + clear_widgets(self.change_config_widgets) + for widget, values in zip(self.change_config_widgets, + [self.current_config.airframes, + self.current_config.settings + + self.current_config.modules, + self.current_config.flight_plan, + self.current_config.radio, + self.current_config.telemetry]): + for value in values: + value = value.strip() + if value: + widget.addItem(value) + if values: + set_widgets_visibility([widget], True) + + for i in range(self.ui.current_settings.count()): + item = self.ui.current_settings.item(i) + item.setFlags(Core.Qt.ItemIsUserCheckable | Core.Qt.ItemIsEnabled | + Core.Qt.ItemIsSelectable) + if item.text().startswith("["): + item.setText(item.text().strip("[]")) + item.setCheckState(Core.Qt.Unchecked) + else: + item.setCheckState(Core.Qt.Checked) + + self.update_config_overview_lists() + + def update_config_overview_lists(self): + """ + -> Hide all the configuration overview widgets. + -> Clear these widgets. + -> Copy the content of the real configuration QListWidget widgets. + -> Keep only the basename of long items (put full name in tooltip). + -> Display the widgets only if not empty (save some vertical space). + """ + set_widgets_visibility(self.display_config_widgets, False) + clear_widgets(self.display_config_widgets) + for input_widget, display_widget in zip(self.change_config_widgets*2, + self.display_config_widgets): + items_full_names = [] + for i in range(input_widget.count()): + item_full_name = input_widget.item(i).text() + item_name = os.path.splitext( + os.path.basename(item_full_name))[0] + display_widget.addItem(item_name) + items_full_names.append(item_full_name) + display_widget.setToolTip("\n".join(items_full_names)) + if display_widget.count() != 0: + display_widget.setVisible(True) + + def update_target_combos(self, combo_widgets): + """ + :param combo_widgets: + -> Clear the 2 'Target' combo widgets. + -> Fill them with the targets available for the current configuration. + """ + clear_widgets(combo_widgets) + sorted_names = parser.sorted_current_targets_names( + self.current_config.targets) + for widget in combo_widgets: + widget.addItems(sorted_names) + widget.setCurrentText(self.current_target.name) + + def update_log_filters(self): + """ + -> Load the last filters from the cache. + -> Disable the filters if the level is minimum or maximum. + -> Set the filters check state by the current LogFilter object + properties. + """ + if self.current_log_filter.level == cs.MINIMAL_MESSAGES_LEVEL: + self.ui.important.setChecked(True) + set_widgets_availability(self.log_filters_widgets, False) + elif self.current_log_filter.level == cs.ALL_MESSAGES_LEVEL: + self.ui.all.setChecked(True) + set_widgets_availability(self.log_filters_widgets, False) + else: + self.ui.custom.setChecked(True) + set_widgets_availability(self.log_filters_widgets, True) + self.ui.display_default.setChecked(self.current_log_filter.default) + self.ui.display_info.setChecked(self.current_log_filter.info) + self.ui.display_warnings.setChecked(self.current_log_filter.warning) + self.ui.display_errors.setChecked(self.current_log_filter.error) + + def update_device_combo(self): + """ + -> Clear the 'Device' combobox. + -> Search compatible boards for current target. + -> Update the combo-box with devices corresponding to each board. + -> Deal with empty devices list because of default Wi-Fi flash. + """ + self.ui.device.clear() + current_devices = [] + for device in self.data.devices.values(): + if device.name != parser.DEFAULT_DEVICE_NAME: + for board_regex in device.boards_regex: + found_match = re.search(board_regex, self.current_target.board) + if found_match: + current_devices.append(device) + break + else: + current_devices.append(device) + sorted_names = parser.sorted_current_devices_names(current_devices) + self.ui.device.addItems(sorted_names) + self.ui.device.setCurrentText(self.current_device.name) + self.ui.device.setEnabled(True) + + def update_session_combos(self): + """ + -> Clear the 'Session' combobox. + -> Fill it with the sessions found in 'control_panel.xml' + """ + clear_widgets(self.session_widgets) + sorted_names = parser.sorted_sessions_names(self.data.sessions) + for widget in self.session_widgets: + widget.addItems(sorted_names) + sep_index = sorted_names.index(parser.SESSIONS_COMBO_SEP) + variant = Core.QVariant(not Core.Qt.ItemIsEnabled) + widget.setItemData(sep_index, variant, Core.Qt.UserRole - 1) + widget.setCurrentText(self.current_session.name) + + def update_programs_list(self): + """ + -> Clear the programs QListWidget. + -> Fill it with the programs available for the current session. + -> Update the programs overview widgets with the same programs. + -> Init the play/stop program button HMI. + """ + self.ui.programs.clear() + self.current_program = None + sorted_names = parser.sorted_current_programs_names( + self.current_session) + self.ui.programs.addItems(sorted_names) + for i in range(self.ui.programs.count()): + self.ui.programs.item(i).setIcon(self.stop_qicon) + self.update_programs_overviews() + self.update_program_button() + self.update_program_options_list() + + set_widgets_availability([self.ui.play_stop_program, + self.ui.remove_program, + self.ui.add_option] + + self.kill_all_buttons, False) + + def update_programs_overviews(self): + """ + -> Clear the programs overview QListWidget widgets. + -> Fill them with the current programs widget items. + -> Set their icon to stopped state. + """ + clear_widgets(self.display_programs_widgets) + for input_widget, display_widget in zip([self.ui.programs]*2, + self.display_programs_widgets): + for i in range(input_widget.count()): + display_widget.addItem(input_widget.item(i).text()) + item = display_widget.item(i) + item.setIcon(self.stop_qicon) + item.setFlags(Core.Qt.ItemIsEnabled) + + def update_program_options_list(self): + """ + -> Clear the options QListWidget. + -> Fill them with the current options of the selected program if + possible (and detect the option shape : tuple or str). + """ + self.ui.options.clear() + self.current_option = None + if self.current_program is not None: + options = self.current_program[1].options + for option in options: + option_str = option + if type(option) is tuple: + option_str = " ".join(option) + self.ui.options.addItem(option_str) + for i in range(self.ui.options.count()): + item = self.ui.options.item(i) + item.setFlags(Core.Qt.ItemIsEditable | Core.Qt.ItemIsEnabled | + Core.Qt.ItemIsSelectable) + self.ui.remove_option.setEnabled(False) + + def init_build_flash_hmi(self): + """ + -> Kill the build processes if already running. + -> Clear the build widgets. + """ + clear_widgets([self.ui.build_result_icon, + self.ui.build_result, + self.ui.flash_resut_icon, + self.ui.flash_result] + self.messages_nb_widgets) + + def update_flags_nb_hmi(self, compilation_object): + """ + :param compilation_object: + -> Update the flags number QLabel widgets at each log sent to the + console. + """ + self.ui.errors_nb.setText(str( + compilation_object.flags[cs.ERROR_FLAG])) + self.ui.warnings_nb.setText(str( + compilation_object.flags[cs.WARNING_FLAG])) + self.ui.info_nb.setText(str( + compilation_object.flags[cs.INFO_FLAG])) + +############################################################################### +# [Hmi methods] Clean methods + + def set_clean_started_hmi(self): + """ + -> Turn HMI to clean running state. + """ + self.init_build_flash_hmi() + self.clean_running = True + set_widgets_availability([self.ui.build, self.ui.upload, + self.ui.quick_build] + + self.target_widgets, False) + self.ui.clean.setText("Stop !") + self.setCursor(gui.generate_qcursor(gui.WAIT_CURSOR_SHAPE)) + + def set_clean_stopped_hmi(self): + """ + -> Turn HMI to clean stopped state. + -> Display the result of the clean process. + -> Switch to the build / flash tab. + """ + self.clean_running = False + set_widgets_availability([self.ui.build, self.ui.quick_build, + self.ui.clean] + + self.target_widgets, True) + if self.current_target.name not in self.simulation_targets_names: + self.ui.upload.setEnabled(True) + self.ui.clean.setText("Clean") + self.setCursor(gui.generate_qcursor(gui.NORMAL_CURSOR_SHAPE)) + self.update_flags_nb_hmi(self.clean) + errors = self.clean.flags[cs.ERROR_FLAG] + if self.clean.exit_code == proc.INTERRUPTED_EXIT_CODE: + self.ui.build_result.setText("Clean aborted by user !") + self.ui.build_result_icon.setPixmap(self.error_qpixmap) + elif errors != 0 or self.clean.exit_code == proc.DEFAULT_EXIT_CODE: + self.ui.build_result.setText("Error(s) found " + + "during clean. " + + "Please try again !") + self.ui.build_result_icon.setPixmap(self.error_qpixmap) + elif self.clean.exit_code == proc.SUCCESS_EXIT_CODE: + self.ui.build_result.setText("Clean done with success !") + self.ui.build_result_icon.setPixmap(self.ok_qpixmap) + else: + self.ui.build_result.setText("Unknown error !") + self.ui.build_result_icon.setPixmap(self.error_qpixmap) + self.clean = None + self.switch_to_tab(1) + +############################################################################### +# [Hmi methods] Build methods + + def set_build_started_hmi(self): + """ + -> Turn HMI to build running state. + """ + self.init_build_flash_hmi() + self.build_running = True + set_widgets_availability([self.ui.clean, self.ui.upload, + self.ui.quick_build] + + self.target_widgets, False) + self.ui.build.setText("Stop !") + self.setCursor(gui.generate_qcursor(gui.WAIT_CURSOR_SHAPE)) + + def set_build_stopped_hmi(self): + """ + -> Turn HMI to build stopped state. + -> Display the result of the build process. + -> Switch to the build / flash tab. + """ + self.build_running = False + set_widgets_availability([self.ui.clean, self.ui.quick_build, + self.ui.build] + + self.target_widgets, True) + if self.current_target.name not in self.simulation_targets_names: + self.ui.upload.setEnabled(True) + self.ui.build.setText("Build") + self.setCursor(gui.generate_qcursor(gui.NORMAL_CURSOR_SHAPE)) + self.update_flags_nb_hmi(self.build) + errors = self.build.flags[cs.ERROR_FLAG] + if self.build.exit_code == proc.INTERRUPTED_EXIT_CODE: + self.ui.build_result.setText("Build aborted by user !") + self.ui.build_result_icon.setPixmap(self.error_qpixmap) + elif errors != 0 or self.build.exit_code == proc.DEFAULT_EXIT_CODE: + self.ui.build_result.setText("Error(s) found " + + "during build. " + + "Please try again !") + self.ui.build_result_icon.setPixmap(self.error_qpixmap) + elif self.build.exit_code == proc.SUCCESS_EXIT_CODE: + self.ui.build_result.setText("Build done with success !") + self.ui.build_result_icon.setPixmap(self.ok_qpixmap) + else: + self.ui.build_result.setText("Unknown error !") + self.ui.build_result_icon.setPixmap(self.error_qpixmap) + self.build = None + self.switch_to_tab(1) + +############################################################################### +# [Hmi methods] Flash methods + + def set_flash_started_hmi(self): + """ + -> Turn HMI to flash running state. + """ + self.init_build_flash_hmi() + self.upload_running = True + set_widgets_availability([self.ui.clean, self.ui.build, + self.ui.quick_build, self.ui.device] + + self.target_widgets, False) + self.ui.upload.setText("Stop !") + self.setCursor(gui.generate_qcursor(gui.WAIT_CURSOR_SHAPE)) + clear_widgets([self.ui.flash_resut_icon, + self.ui.flash_result]) + + def set_flash_stopped_hmi(self): + """ + -> Turn HMI to flash stopped state. + -> Display the result of the upload process. + -> Switch to the build / flash tab. + """ + self.upload_running = False + set_widgets_availability([self.ui.clean, self.ui.build, + self.ui.upload, + self.ui.quick_build, self.ui.device] + + self.target_widgets, True) + self.ui.upload.setText("Upload") + self.setCursor(gui.generate_qcursor(gui.NORMAL_CURSOR_SHAPE)) + self.update_flags_nb_hmi(self.upload) + errors = self.upload.flags[cs.ERROR_FLAG] + if self.upload.exit_code == proc.INTERRUPTED_EXIT_CODE: + self.ui.flash_result.setText("Upload aborted by user !") + self.ui.flash_resut_icon.setPixmap(self.error_qpixmap) + elif errors != 0 or self.upload.exit_code == proc.DEFAULT_EXIT_CODE: + self.ui.flash_result.setText("Error(s) found " + + "during upload. " + + "Please try again !") + self.ui.flash_resut_icon.setPixmap(self.error_qpixmap) + elif self.upload.exit_code == proc.SUCCESS_EXIT_CODE: + self.ui.flash_result.setText("Upload done with success !") + self.ui.flash_resut_icon.setPixmap(self.ok_qpixmap) + else: + self.ui.flash_result.setText("Unknown error !") + self.ui.flash_resut_icon.setPixmap(self.error_qpixmap) + self.upload = None + self.switch_to_tab(1) + +############################################################################### +# [Hmi methods] Program methods + + def update_program_button(self): + """ + -> Update the play/stop program button HMI to correct state. + """ + if self.current_program is not None: + self.ui.play_stop_program.setEnabled(True) + if self.current_program[1].name in self.running_programs.keys(): + self.ui.play_stop_program.setIcon(self.stop_qicon) + self.ui.play_stop_program.setText("Stop") + else: + self.ui.play_stop_program.setIcon(self.start_qicon) + self.ui.play_stop_program.setText("Start") + else: + self.ui.play_stop_program.setEnabled(False) + self.ui.play_stop_program.setIcon(self.start_qicon) + + def set_program_started_hmi(self, program_process): + """ + :param program_process: + -> Turn HMI to program running state. + """ + self.running_programs[program_process.program.name] = program_process + + items = [widget.findItems(program_process.program.name, + Core.Qt.MatchExactly)[0] + for widget in [self.ui.programs, + self.ui.programs_overview_1, + self.ui.programs_overview_2]] + set_items_icons(items, self.start_qicon) + self.update_program_button() + + if self.running_programs != {}: + set_widgets_availability([self.ui.session, + self.ui.session_overview_1, + self.ui.session_overview_2, + self.ui.current_set], + False) + set_widgets_availability(self.kill_all_buttons, True) + set_widgets_availability(self.start_all_buttons, False) + + def set_program_stopped_hmi(self, program_process): + """ + :param program_process: + -> Turn HMI to program stopped state. + -> Display the result of the program process. + -> Switch to the sessions management tab. + """ + self.running_programs.pop(program_process.program.name) + + items = [widget.findItems(program_process.program.name, + Core.Qt.MatchExactly)[0] + for widget in [self.ui.programs, + self.ui.programs_overview_1, + self.ui.programs_overview_2]] + set_items_icons(items, self.stop_qicon) + self.update_program_button() + + if self.running_programs == {}: + set_widgets_availability([self.ui.session, + self.ui.session_overview_1, + self.ui.session_overview_2, + self.ui.current_set], + True) + set_widgets_availability(self.kill_all_buttons, False) + set_widgets_availability(self.start_all_buttons, True) + self.switch_to_tab(2) + +############################################################################### +# [Hmi methods] Write log methods + + def write_log_in_console(self, log, flag, log_type): + """ + :param log: + :param flag: + :param log_type: + -> Get the logs sent by the processes when a 'log_sent' signal is + caught. + -> Treat the log depending if it's an application or a process one. + -> Put color to the log line and write it into the console. + """ + if log_type == cs.PROCESS_MESSAGE_TYPE: + if self.current_log_filter.write_line_decision(flag): + str_color = cs.BACKGROUNDS_COLORS[flag] + background_qcolor = gui.generate_qcolor(str_color) + self.console.write(log, + backgroud_color=background_qcolor) + elif log_type == cs.APPLICATION_MESSAGE_TYPE: + if log != "\n": + str_color = cs.FONTS_COLORS[flag] + font_qcolor = gui.generate_qcolor(str_color) + self.console.write(log, font_color=font_qcolor) + + def animate_progress_bar(self): + """ + -> For all running build / flash processes, animate the progress bar. + -> Change the text displayed in the result field according to its size + and the current direction. + """ + for program, label in zip([self.clean, self.build, self.upload], + [self.ui.build_result, self.ui.build_result, + self.ui.flash_result]): + if program is not None: + last_result_text = label.text() + last_result_text_size = label.fontMetrics().\ + boundingRect(last_result_text).width() + field_width = label.width() + + if len(last_result_text) < PROGRESS_BAR_SPEED+1: + new_result_text = PROGRESS_BAR_LINE * PROGRESS_BAR_SPEED \ + + PROGRESS_BAR_CURSOR + self.progress_bar_to_left = False + elif last_result_text_size > field_width - 100\ + or self.progress_bar_to_left: + new_result_text = last_result_text[PROGRESS_BAR_SPEED:] + self.progress_bar_to_left = True + else: + new_result_text = PROGRESS_BAR_LINE * PROGRESS_BAR_SPEED \ + + last_result_text + self.progress_bar_to_left = False + label.setText(new_result_text) + +############################################################################### +# [Hmi methods] Widgets connected with signals methods + + def switch_current_mode(self): + """ + -> Real switch but no real effect (no dev mode yet...) + -> RELOAD MAIN WINDOW WITH NEW WIDGETS ? + """ + self.dev_mode = not self.dev_mode + self.update_mode_button() + + def change_current_set(self): + """ + -> Get current set when combobox value changed. + -> Symbolic link to 'config.xml' changed to the new set. + -> Update configuration and its items. + """ + self.save_all_changed_configurations() + current_set_name = self.ui.current_set.currentText() + if current_set_name: + self.current_set = self.data.sets[current_set_name] + point_symbol_link_to(self.current_set.name) + self.update_config_combo() + self.update_config_items_lists() + + def change_current_config(self): + """ + -> Get current configuration when combobox value changed. + -> Update target and device values. + -> Stop the build processes if some running. + -> Init the build / flash HMI to be ready to a new process. + """ + current_config_name = self.ui.current_configuration.currentText() + if current_config_name: + self.current_config = self.data.configurations[current_config_name] + self.update_config_items_lists() + self.update_target_combos([self.ui.target]) + self.update_device_combo() + self.init_build_flash_hmi() + + def change_current_item(self, list_widget): + """ + :param list_widget: + -> Update the current selected item : (widget, item, object). + -> Clear the others selected items in other categories. + -> Update the configuration management HMI (buttons). + -> Make the flight plan buttons appear or disappear. + -> Update all the check states of the settings list if the current + selected item is a setting. + """ + current_selected_item = list_widget.currentItem() + if current_selected_item is not None: + current_item_text = current_selected_item.text() + self.current_item = (list_widget, + current_selected_item, + current_item_text) + widgets_to_clear = [_ for _ in self.change_config_widgets] + widgets_to_clear.remove(list_widget) + clear_list_widgets_selection(widgets_to_clear) + set_widgets_availability([self.ui.remove_item, + self.ui.edit], True) + set_widgets_visibility(self.flight_plan_buttons, + list_widget.objectName() == + "current_flight_plan") + + def change_current_target(self, combo_widget): + """ + :param combo_widget: + -> Update the current target and the devices combobox (related). + -> Possible to change the target from 2 widgets. That's why the + solution found is strange... + """ + current_target_name = combo_widget.currentText() + if current_target_name and self.change_target_bool: + self.current_target = self.current_config.targets[ + current_target_name] + self.ui.upload.setEnabled(self.current_target.name + not in self.simulation_targets_names) + other_widgets = [_ for _ in self.target_widgets] + other_widgets.remove(combo_widget) + # TODO BETTER : AVOID THE SIGNALS PING-PONG BETWEEN TARGETS WIDGETS + self.change_target_bool = False + self.update_target_combos(other_widgets) + self.change_target_bool = True + self.update_device_combo() + + def change_sensitivity_filters(self): + """ + -> Get checkbox values to set the current filters. + -> Update the filters values for the console. + """ + self.current_log_filter.set_sensitivity_filter( + self.ui.display_default.checkState(), + self.ui.display_info.checkState(), + self.ui.display_warnings.checkState(), + self.ui.display_errors.checkState()) + self.update_log_filters() + + def change_messages_level(self): + """ + -> Get radiobutton value to set the current message level. + -> Update the level value for the console. + """ + if self.ui.all.isChecked(): + self.current_log_filter.set_level(cs.ALL_MESSAGES_LEVEL) + self.current_log_filter.set_sensitivity_filter(2, 2, 2, 2) + elif self.ui.custom.isChecked(): + self.current_log_filter.set_level(cs.CUSTOM_MESSAGES_LEVEL) + elif self.ui.important.isChecked(): + self.current_log_filter.set_level(cs.MINIMAL_MESSAGES_LEVEL) + self.current_log_filter.set_sensitivity_filter(0, 0, 2, 2) + self.update_log_filters() + + def change_current_device(self): + """ + -> Get current device when combobox value changed. + -> If no device available, the default flash mode is WiFi. + """ + current_device_name = self.ui.device.currentText() + if current_device_name: + self.current_device = self.data.devices[current_device_name] + + def change_current_session(self, combo_widget): + """ + :param combo_widget: + -> Get current session when combobox value changed. + -> Update programs HMI for the new selected session. + -> Deselect the last selected program and clear the options. + """ + current_session_name = combo_widget.currentText() + if current_session_name: + clear_list_widgets_selection([self.ui.programs] + + self.display_programs_widgets) + self.current_session = self.data.sessions[current_session_name] + widgets = [_ for _ in self.session_widgets] + widgets.remove(combo_widget) + for widget in widgets: + widget.setCurrentText(self.current_session.name) + self.update_programs_list() + self.update_program_options_list() + self.ui.options.clear() + + def change_current_program(self): + """ + -> Get current selected program when QListWidget selection changed. + -> Update options HMI for the new selected program. + """ + current_selected_item = self.ui.programs.currentItem() + if current_selected_item is not None: + item_name = current_selected_item.text() + if item_name in self.current_session.programs.keys(): + self.current_program = (current_selected_item, + self.current_session.programs[ + item_name]) + self.ui.remove_program.setEnabled(item_name not in + self.running_programs.keys()) + self.ui.add_option.setEnabled(True) + self.update_program_options_list() + self.update_program_button() + + def change_current_option(self): + """ + -> Get the current selected option when QListWidget selection changed. + -> Enable options management HMI. + """ + current_selected_item = self.ui.options.currentItem() + if current_selected_item is not None: + item_name = current_selected_item.text() + self.current_option = (current_selected_item, item_name) + self.ui.remove_option.setEnabled(True) + + def open_item_file_chooser(self): + """ + -> While left part of equipment tab is not finished, a simple file + chooser is used to select new equipment items. + -> Add the selected new item to the correct category if possible. + """ + file_chooser = Widgets.QFileDialog() + fc_title = "Select a file to add to '" +\ + self.current_config.name + "'..." + fc_dir = env.PAPARAZZI_CONF + fc_filter = "XML file(*.xml)" + path, other = file_chooser.getOpenFileName(file_chooser, + fc_title, + fc_dir, + fc_filter) + if path: + filename = parser.full_to_conf_path(path) + self.add_item_to_config(filename) + + def open_gedit(self): + """ + -> Open a text editor depending on the OS found. + """ + if self.current_item is not None: + + if env.OS in ["linux", "linux2"]: + file_path = os.path.join(CONF_PATH, self.current_item[2]) + command = " ".join(["gedit", file_path]) + try: + os.popen(command) + except ChildProcessError: + LOGGER.error("Gedit text editor is not available !\n") + self.switch_to_tab(3) + elif env.OS in []: + pass + else: + LOGGER.error("No text file editor found for your OS !\n") + self.switch_to_tab(3) + + def clean_console(self): + """ + -> Just clear the console QTextEdit widget. + """ + self.ui.console.clear() + + def open_terminal(self): + """ + -> Open a terminal in the Paparazzi home directory depending on the OS + found. + """ + if env.OS in ["linux", "linux2"]: + command_terms = ["gnome-terminal", "--working-directory ", + env.PAPARAZZI_HOME] + command = " ".join(command_terms) + os.popen(command) + else: + LOGGER.error("No terminal shortcut available for your OS !\n") + self.switch_to_tab(3) + +############################################################################### +# [Hmi methods] Processes management (connected with signals) methods + + def quick_build(self): + """ + -> Clean the current configuration when the quick build button is + clicked. + -> Give the information to the clean function that a build process + is planned after the clean process. + """ + self.switch_to_tab(1) + self.clean_config(is_quick_build=True) + + def clean_config(self, is_quick_build=False): + """ + :param is_quick_build: + -> Clean the current configuration if not already started. + -> Else, abort the current clean process and set the HMI to the + initial state. + """ + if not self.clean_running: + self.clean = proc.Process(proc.CLEAN, + configuration=self.current_config) + if self.clean.check_before_start(): + self.clean.start() + self.clean.subprocess.finished.connect(self.set_clean_stopped_hmi) + self.clean.process_log_sent.connect(functools.partial( + self.update_flags_nb_hmi, self.clean)) + self.clean.process_log_sent.connect(self.write_log_in_console) + self.clean.process_log_sent.connect(self.animate_progress_bar) + self.set_clean_started_hmi() + + if is_quick_build: + self.clean.subprocess.finished.connect(functools.partial( + self.build_config)) + else: + LOGGER.error("Incorrect config, can't clean !\n") + self.switch_to_tab(3) + else: + self.clean.process_killed.emit() + self.ui.clean.setEnabled(False) + + def build_config(self): + """ + -> Build the current configuration if not already started. + -> Else, abort the current build process and set the HMI to the + initial state. + """ + if not self.build_running: + self.build = proc.Process(proc.BUILD, + configuration=self.current_config, + target=self.current_target) + self.save_current_config() + + if self.build.check_before_start(): + self.build.start() + self.build.subprocess.finished.connect(self.set_build_stopped_hmi) + self.build.process_log_sent.connect(functools.partial( + self.update_flags_nb_hmi, self.build)) + self.build.process_log_sent.connect(self.write_log_in_console) + self.build.process_log_sent.connect(self.animate_progress_bar) + self.set_build_started_hmi() + else: + LOGGER.error("Incorrect config, can't build !\n") + self.switch_to_tab(3) + else: + self.build.process_killed.emit() + self.ui.build.setEnabled(False) + + def flash_device(self): + """ + -> Flash the current configuration if not already started. + -> Else, abort the current upload process and set the HMI to the + initial state. + """ + if not self.upload_running: + self.upload = proc.Process(proc.UPLOAD, + configuration=self.current_config, + target=self.current_target, + device=self.current_device) + if self.upload.check_before_start(): + self.upload.start() + self.upload.subprocess.finished.connect(self.set_flash_stopped_hmi) + self.upload.process_log_sent.connect(functools.partial( + self.update_flags_nb_hmi, self.upload)) + self.upload.process_log_sent.connect(self.write_log_in_console) + self.upload.process_log_sent.connect(self.animate_progress_bar) + self.set_flash_started_hmi() + else: + LOGGER.error("Incorrect device, can't upload !\n") + self.switch_to_tab(3) + else: + self.upload.process_killed.emit() + self.ui.upload.setEnabled(False) + + def run_program(self, program): + """ + :param program: + -> Run the current selected program if not already started. + Else, abort it and set the HMI to the initial state. + """ + if program is None and self.current_program is not None: + program_item, program = self.current_program + if program.name not in self.running_programs.keys(): + new_program_proc = proc.Process(proc.PROGRAM, + program=program, + configuration=self.current_config, + target=self.current_target) + if new_program_proc.check_before_start(): + new_program_proc.start() + new_program_proc.subprocess.finished.connect(functools.partial( + self.set_program_stopped_hmi, new_program_proc)) + new_program_proc.process_log_sent.connect( + self.write_log_in_console) + self.set_program_started_hmi(new_program_proc) + else: + self.running_programs[program.name].process_killed.emit() + self.ui.play_stop_program.setEnabled(False) + + def start_all_programs(self): + """ + -> Kill all running programs of the current session. + -> Start all programs not running (so it's a restart). + -> Keep the current selected program if there's one. + """ + for program in self.current_session.programs.values(): + if program.name not in self.running_programs.keys(): + self.run_program(program) + + def kill_all_programs(self): + """ + -> Kill all running programs of the current session. + -> Keep the current selected program if there's one. + """ + for program in self.current_session.programs.values(): + if program.name in self.running_programs.keys(): + self.run_program(program) + +############################################################################### +# [Hmi methods] Data management (connected with signals) methods + + def change_setting_check_state(self, clicked_item): + """ + :param clicked_item: + -> Save the new check state of settings items. + -> Add the current config to changed configs => ask to save + before quitting the application. + """ + if self.current_config is not None\ + and self.current_item is not None: + list_widget, list_item, item_name = self.current_item + setting_name = item_name + setting_state = list_item.checkState() + # TODO BETTER : NO SOLUTION (OR SIGNAL) FOUND TO DETECT A CLICK ON + # : TODO : 'QListWidgetItem' SO ONLY CURRENT ITEM CHECKSTATE + # : TODO : SHOULD BE CHANGED... (NOT WORKING WELL) + if clicked_item == list_item: + if parser.SETTINGS_REF in setting_name: + settings_modules = self.current_config.settings + else: + settings_modules = self.current_config.modules + settings_modules_names = [item.strip("[]") for item in settings_modules] + setting_index = settings_modules_names.index(setting_name) + settings_modules.pop(setting_index) + if not setting_state: + setting_name = "[" + setting_name + "]" + settings_modules.insert(setting_index, setting_name) + + self.configurations_changed[self.current_config.name] = \ + self.current_config + index = self.ui.current_configuration.currentIndex() + self.ui.current_configuration.setItemIcon(index, self.changed_qicon) + else: + clicked_item.setCheckState(setting_state) + + def remove_item_from_config(self): + """ + -> Remove an item from the current selected configuration. + -> Impossible to remove all airframes because it doesn't make sense. + """ + if self.current_config is not None \ + and self.current_item is not None: + list_widget, list_item, item_name = self.current_item + if not (list_widget is self.ui.current_airframes and + len(list_widget) == 1): + if list_widget == self.ui.current_settings and \ + not list_item.checkState(): + item_name = "[" + item_name + "]" + + config_attributes_lists = [self.current_config.airframes, + self.current_config.settings, + self.current_config.modules, + self.current_config.flight_plan, + self.current_config.radio, + self.current_config.telemetry] + for attributes_list in config_attributes_lists: + if item_name in attributes_list: + attributes_list.remove(item_name) + break + + list_widget.takeItem(list_widget.row(list_item)) + list_widget.clearSelection() + self.current_item = None + self.ui.remove_item.setEnabled(False) + if not list_widget: + list_widget.setVisible(False) + if list_widget is self.ui.current_flight_plan: + set_widgets_visibility(self.flight_plan_buttons, False) + self.update_config_overview_lists() + + self.configurations_changed[self.current_config.name] = \ + self.current_config + index = self.ui.current_configuration.currentIndex() + self.ui.current_configuration.setItemIcon(index, + self.changed_qicon) + else: + LOGGER.error("You can't remove all airframes from a " + "configuration !\n") + self.switch_to_tab(3) + + def add_item_to_config(self, filename): + """ + :param filename: + -> Add the selected filename to the correct category if it's possible. + -> Else, an error is raised in the console. + -> If a setting is added, it's checked by default. + """ + if self.current_config is not None: + keywords = [parser.AIRFRAME_REF, parser.SETTINGS_REF, + parser.MODULES_REF, parser.FP_REF, + parser.RADIO_REF, parser.TELEMETRY_REF] + fields = [self.current_config.airframes, + self.current_config.settings, + self.current_config.modules, + self.current_config.flight_plan, + self.current_config.radio, + self.current_config.telemetry] + widgets = self.change_config_widgets.copy() + widgets.insert(2, self.ui.current_settings) + + match_found = False + for keyword, field, widget in zip(keywords, fields, widgets): + if keyword in filename: + field.append(filename) + widget.addItem(filename) + last_item = widget.item(widget.count()-1) + if widget == self.ui.current_settings: + last_item.setFlags(Core.Qt.ItemIsUserCheckable | + Core.Qt.ItemIsEnabled | + Core.Qt.ItemIsSelectable) + last_item.setCheckState(Core.Qt.Checked) + widget.setCurrentItem(last_item) + match_found = True + break + + if match_found: + self.configurations_changed[self.current_config.name] = \ + self.current_config + index = self.ui.current_configuration.currentIndex() + self.ui.current_configuration.setItemIcon(index, + self.changed_qicon) + else: + LOGGER.error("The selected file '%s' is not an acceptable file in" + " a configuration !\n", filename) + self.switch_to_tab(3) + + def remove_program_from_session(self): + """ + -> Remove the selected program from the current selected session. + -> Add the current session to changed sessions => ask to save + before quitting the application. + """ + if self.current_session is not None \ + and self.current_program is not None: + program_item, program = self.current_program + + self.current_session.programs.pop(program.name) + self.update_programs_list() + + self.sessions_changed[self.current_session.name] = \ + self.current_session + index = self.ui.session.currentIndex() + self.ui.session.setItemIcon(index, self.changed_qicon) + self.switch_to_tab(2) + + def add_program_to_session(self, program): + """ + :param program: + -> Add a program object (only a copy of a tool from the Tools menu + for now) to the current selected session. + """ + if self.current_session is not None: + + new_program_name = parser.find_unused_item_name( + self.current_session.programs, program.name) + new_program = db.Program(new_program_name, program.command, + program.options) + self.current_session.programs[new_program.name] = new_program + self.update_programs_list() + new_item = self.ui.programs.findItems(new_program.name, + Core.Qt.MatchExactly)[0] + self.ui.programs.setCurrentItem(new_item) + + self.sessions_changed[self.current_session.name] = \ + self.current_session + index = self.ui.session.currentIndex() + self.ui.session.setItemIcon(index, self.changed_qicon) + self.switch_to_tab(2) + + def remove_option_from_program(self): + """ + -> Remove the selected option from the current selected program. + """ + if self.current_program is not None \ + and self.current_option is not None: + program_item, program = self.current_program + option_item, option = self.current_option + + if type(option) is str: + program.options.remove(option) + else: + program.options.remove(tuple(option.split())) + self.update_program_options_list() + + self.sessions_changed[self.current_session.name] = \ + self.current_session + index = self.ui.session.currentIndex() + self.ui.session.setItemIcon(index, self.changed_qicon) + + def add_option_to_program(self): + """ + -> Add an option to the current selected program. + """ + if self.current_session is not None \ + and self.current_program is not None: + program_item, program = self.current_program + new_option = "" + program.options.append(new_option) + self.update_program_options_list() + last_item = self.ui.options.item(self.ui.options.count()-1) + self.ui.options.setCurrentItem(last_item) + + self.sessions_changed[self.current_session.name] = \ + self.current_session + index = self.ui.session.currentIndex() + self.ui.session.setItemIcon(index, self.changed_qicon) + + def edit_current_option(self): + """ + -> Edit the selected option when enter pressed after changes. + """ + if self.current_program is not None \ + and self.current_option is not None: + program_item, program = self.current_program + + old_option_item, old_option = self.current_option + old_option_index = program.options.index(old_option) + program.options.remove(old_option) + self.change_current_option() + new_option_item, new_option = self.current_option + program.options.insert(old_option_index, new_option) + + self.sessions_changed[self.current_session.name] = \ + self.current_session + index = self.ui.session.currentIndex() + self.ui.session.setItemIcon(index, self.changed_qicon) + +############################################################################### +# [Hmi methods] Data management : XML writing methods + + def save_current_config(self): + """ + -> Update the new values of the current configuration in the + corresponding XML file. + """ + if self.current_config.name in self.configurations_changed.keys(): + self.configurations_changed.pop(self.current_config.name) + index = self.ui.current_configuration.currentIndex() + self.ui.current_configuration.setItemIcon(index, + gui.generate_qicon()) + + LOGGER.info("Saving '%s' configuration...", + self.current_config.name) + parser.save_configuration(self.current_set.name, + self.current_config) + LOGGER.info("'%s' configuration saved.\n", + self.current_config.name) + else: + LOGGER.info("Nothing to save, configuration already up-to-date.\n") + + def save_current_session(self): + """ + -> Update the new values of the current session in the + corresponding 'control_panel.xml' file. + -> Deal with the in-code defined sessions (Simulation, Replay). + """ + if self.current_session.name in self.sessions_changed.keys(): + self.sessions_changed.pop(self.current_session.name) + index = self.ui.session.currentIndex() + self.ui.session.setItemIcon(index, gui.generate_qicon()) + if self.current_session.name not in [parser.SIMULATION_NAME, + parser.REPLAY_NAME]: + LOGGER.info("Saving '%s' session...", self.current_session.name) + parser.save_session(parser.CONTROL_PANEL_FILE, + self.current_session) + LOGGER.info("'%s' session saved.\n", self.current_session.name) + else: + LOGGER.error("'%s' is an in-code defined session. " + "Impossible to save it !\n", + self.current_session.name) + print("'%s' is an in-code defined session. " + "Impossible to save it !\n" + % self.current_session.name) + self.switch_to_tab(3) + else: + LOGGER.info("Nothing to save, session already up-to-date.\n") + + def save_all_changed_configurations(self): + changed_configs = [_ for _ in self.configurations_changed.values()] + for config in changed_configs: + self.current_config = config + self.save_current_config() + + def save_all_changed_sessions(self): + changed_sessions = [_ for _ in self.sessions_changed.values()] + for session in changed_sessions: + self.current_session = session + self.save_current_session() + + def save_all_changed_data(self): + self.save_all_changed_configurations() + self.save_all_changed_sessions() + print("All changes in data saved.\n") + self.save_cache_data() + + def save_cache_data(self): + """ + -> Change values in cache with the 'write' mode of the common parsing + function. + """ + LOGGER.info("Cache updating...") + print("Cache updating...") + current_geometry_str = " ".join(map(str, + [self.geometry().x(), + self.geometry().y(), + self.geometry().width(), + self.geometry().height()])) + current_log_filters_str = " ".join(map(str, + [self.current_log_filter.level, + self.current_log_filter.default, + self.current_log_filter.info, + self.current_log_filter.warning, + self.current_log_filter.error])) + + parser.parse_cache_file("w", self.data.cache_file, + geometry_str=current_geometry_str, + set_str=self.current_set.name, + config_str=self.current_config.name, + target_str=self.current_target.name, + log_filters_str=current_log_filters_str, + device_str=self.current_device.name, + session_str=self.current_session.name) + LOGGER.info("Cache now up-to-date.\n") + print("Cache now up-to-date.\n") diff --git a/sw/supervision/python/icons/accessories-text-editor.svg b/sw/supervision/python/icons/accessories-text-editor.svg new file mode 100644 index 0000000000..03db109a64 --- /dev/null +++ b/sw/supervision/python/icons/accessories-text-editor.svg @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/icons/dialog-apply.svg b/sw/supervision/python/icons/dialog-apply.svg new file mode 100644 index 0000000000..f009b90ee3 --- /dev/null +++ b/sw/supervision/python/icons/dialog-apply.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/icons/dialog-error.svg b/sw/supervision/python/icons/dialog-error.svg new file mode 100644 index 0000000000..a3d6de4d72 --- /dev/null +++ b/sw/supervision/python/icons/dialog-error.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/icons/dialog-warning-symbolic.svg b/sw/supervision/python/icons/dialog-warning-symbolic.svg new file mode 100644 index 0000000000..e9474a9d14 --- /dev/null +++ b/sw/supervision/python/icons/dialog-warning-symbolic.svg @@ -0,0 +1,17 @@ + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + Gnome Symbolic Icon Theme + + + + diff --git a/sw/supervision/python/icons/dialog-warning.png b/sw/supervision/python/icons/dialog-warning.png new file mode 100644 index 0000000000..d1020c6d9e Binary files /dev/null and b/sw/supervision/python/icons/dialog-warning.png differ diff --git a/sw/supervision/python/icons/edit-clear.svg b/sw/supervision/python/icons/edit-clear.svg new file mode 100644 index 0000000000..568fe9de66 --- /dev/null +++ b/sw/supervision/python/icons/edit-clear.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/icons/go-up.svg b/sw/supervision/python/icons/go-up.svg new file mode 100644 index 0000000000..e5e58672cd --- /dev/null +++ b/sw/supervision/python/icons/go-up.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/icons/gtk-info.svg b/sw/supervision/python/icons/gtk-info.svg new file mode 100644 index 0000000000..f1ec53786a --- /dev/null +++ b/sw/supervision/python/icons/gtk-info.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/icons/list-add.svg b/sw/supervision/python/icons/list-add.svg new file mode 100644 index 0000000000..f40a948324 --- /dev/null +++ b/sw/supervision/python/icons/list-add.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/icons/list-remove.svg b/sw/supervision/python/icons/list-remove.svg new file mode 100644 index 0000000000..ebc6bdbe8f --- /dev/null +++ b/sw/supervision/python/icons/list-remove.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/icons/media-playback-pause.svg b/sw/supervision/python/icons/media-playback-pause.svg new file mode 100644 index 0000000000..fde2760829 --- /dev/null +++ b/sw/supervision/python/icons/media-playback-pause.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/icons/media-playback-start.svg b/sw/supervision/python/icons/media-playback-start.svg new file mode 100644 index 0000000000..87c34d3025 --- /dev/null +++ b/sw/supervision/python/icons/media-playback-start.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/icons/penguin_icon.png b/sw/supervision/python/icons/penguin_icon.png new file mode 100644 index 0000000000..f61d8024e9 Binary files /dev/null and b/sw/supervision/python/icons/penguin_icon.png differ diff --git a/sw/supervision/python/icons/process-stop.svg b/sw/supervision/python/icons/process-stop.svg new file mode 100644 index 0000000000..1c9162caab --- /dev/null +++ b/sw/supervision/python/icons/process-stop.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/icons/search_field.png b/sw/supervision/python/icons/search_field.png new file mode 100644 index 0000000000..c3264f66d9 Binary files /dev/null and b/sw/supervision/python/icons/search_field.png differ diff --git a/sw/supervision/python/icons/system-run.png b/sw/supervision/python/icons/system-run.png new file mode 100644 index 0000000000..ad6d80b0de Binary files /dev/null and b/sw/supervision/python/icons/system-run.png differ diff --git a/sw/supervision/python/icons/utilities-terminal.svg b/sw/supervision/python/icons/utilities-terminal.svg new file mode 100644 index 0000000000..add2893910 --- /dev/null +++ b/sw/supervision/python/icons/utilities-terminal.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/lib/console.py b/sw/supervision/python/lib/console.py new file mode 100644 index 0000000000..945712cc7a --- /dev/null +++ b/sw/supervision/python/lib/console.py @@ -0,0 +1,155 @@ +# Paparazzi center utilities +# +# Copyright (C) 2016 ENAC, Florian BITARD (intern student) +# +# This file is part of paparazzi. +# +# paparazzi 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, or (at your option) +# any later version. +# +# paparazzi 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 paparazzi; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +############################################################################### +# [Imports] + +import lib.gui as gui + +import PyQt5.QtCore as Core +import re + + +############################################################################### +# [Constants] + +DEFAULT_FONT_QCOLOR = gui.generate_qcolor("black") +DEFAULT_BACKGROUND_QCOLOR = gui.generate_qcolor("white") +DEFAULT_LOGGER_FONT_QCOLOR = gui.generate_qcolor("blue") + +ALL_MESSAGES_LEVEL = "all" +CUSTOM_MESSAGES_LEVEL = "custom" +MINIMAL_MESSAGES_LEVEL = "minimal" + +PROCESS_MESSAGE_TYPE = "process" +APPLICATION_MESSAGE_TYPE = "application" + +ERROR_FLAG = "error" +WARNING_FLAG = "warning" +INFO_FLAG = "info" +DEFAULT_FLAG = "default" + +BACKGROUNDS_COLORS = {ERROR_FLAG: "red", + WARNING_FLAG: "yellow", + INFO_FLAG: "green", + DEFAULT_FLAG: "white"} + +FONTS_COLORS = {ERROR_FLAG: "red", + WARNING_FLAG: "orange", + INFO_FLAG: "blue", + DEFAULT_FLAG: "black"} + +ERROR_REGEX = re.compile(r"[Ee][Rr]{2}[Oo][Rr][ :]") +WARNING_REGEX = re.compile(r"[Ww][Aa][Rr][Nn][Ii][Nn][Gg][ :]") +INFO_REGEX = re.compile(r"[Ii][Nn][Ff][Oo][ :]") + + +def analyse_log_line(line): + if ERROR_REGEX.search(line) is not None: + flag = ERROR_FLAG + elif WARNING_REGEX.search(line) is not None: + flag = WARNING_FLAG + elif INFO_REGEX.search(line) is not None: + flag = INFO_FLAG + else: + flag = DEFAULT_FLAG + return line, flag + + +############################################################################### +# [Console class] + +class Console(Core.QObject): + """ Class to define a Console object.""" + def __init__(self, q_text_edit_widget): + super(Console, self).__init__() + + self.edit = q_text_edit_widget + + def write(self, line, font_color=DEFAULT_FONT_QCOLOR, + backgroud_color=DEFAULT_BACKGROUND_QCOLOR): + + self.edit.setTextColor(font_color) + self.edit.setTextBackgroundColor(backgroud_color) + self.edit.append(line) + + +############################################################################### +# [LogFilter class] + +class LogFilter(object): + """Class to define a LogFilter object to manage the messages written in the + integrated console.""" + def __init__(self, init_level_str, init_default_bool, init_info_bool, + init_warning_bool, init_error_bool): + """ + -> 'level' sets what sort of messages to display in the console and is + linked to the HMI radio-button choice. + -> 'info', 'warning' and 'error' are booleans to set what logs to + display in the console and is linked to the HMI check-boxes. + """ + self.level = init_level_str + + self.default = init_default_bool + self.info = init_info_bool + self.warning = init_warning_bool + self.error = init_error_bool + + def __repr__(self): + string = "\t| level = {!s:<10} | default = {!s:<5} | info = {!s:<5} |"\ + " warning = {!s:<5} | error = {!s:<5} |" + format_string = string.format(self.level, self.default, self.info, + self.warning, self.error) + return format_string + + def set_level(self, level): + """ + :param level: + -> level = 'minimum' (only important messages) + 'basic' (customized messages) + 'all' (all messages emitted by Makefiles and compilers). + """ + self.level = level + + def set_sensitivity_filter(self, default, info, warning, error): + """ + :param default: + :param info: + :param warning: + :param error: + -> info, warnings and error are booleans. + """ + self.default = default + self.info = info + self.warning = warning + self.error = error + + def write_line_decision(self, flag): + if flag == ERROR_FLAG and self.error: + return True + elif flag == WARNING_FLAG and self.warning: + return True + elif flag == INFO_FLAG and self.info: + return True + elif flag == DEFAULT_FLAG and self.default: + return True + else: + return False diff --git a/sw/supervision/python/lib/database.py b/sw/supervision/python/lib/database.py new file mode 100644 index 0000000000..1a67a0b5f7 --- /dev/null +++ b/sw/supervision/python/lib/database.py @@ -0,0 +1,161 @@ +# Paparazzi center utilities +# +# Copyright (C) 2016 ENAC, Florian BITARD (intern student) +# +# This file is part of paparazzi. +# +# paparazzi 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, or (at your option) +# any later version. +# +# paparazzi 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 paparazzi; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +############################################################################### +# [Imports] + + +############################################################################### +# [Configuration class] + +class Configuration(object): + """Class to define a Configuration (old aircraft) object.""" + def __init__(self, name, config_id, airframes, targets): + # Necessary parameters : + self.name = name + self.id = config_id + self.airframes = airframes + + # Not necessary parameters : + self.settings = [] + self.modules = [] + self.flight_plan = [] + self.radio = [] + self.telemetry = [] + + self.color = [] + + self.targets = targets + + def __repr__(self): + string = "| name = {!s:<25} | id = {!s:<3} | airframe = {!s:<65} |" + \ + "targets = {!s:<250} | color = {!s:<15} | " + \ + "radio = {!s:<40} | telemetry = {!s:<45} | " + \ + "flight_plan = {!s:<75} | settings = {!s:<265} | " + \ + "modules = {!s:<265} |" + format_string = string.format(self.name, self.id, self.airframes, + [_ for _ in self.targets.values()], + self.color, self.radio, + self.telemetry, self.flight_plan, + self.settings, self.modules) + return format_string + + +############################################################################### +# [Set class] + +class Set(object): + """Class to define a Set (old configuration) object.""" + def __init__(self, name): + self.name = name + self.configs_names = [] + + def __repr__(self): + string = "| name = {!s:<75} | configs_names = {!s:<680} |" + format_string = string.format(self.name, self.configs_names) + return format_string + + def is_config_in_set(self, config): + return config.id in self.configs_names + + +############################################################################### +# [Target class] + +class Target(object): + """Class to define a Target object.""" + def __init__(self, name, board, ): + self.name = name + self.board = board + + self.subsystems = None + self.defines = None + + def __repr__(self): + string = "| name = {!s:<15} | board = {!s:<20} |" + format_string = string.format(self.name, self.board) + return format_string + + +############################################################################### +# [Device class] + +class Device(object): + """Class to define a Device (old flash mode) object.""" + def __init__(self, name, variable_name="", variable_value=""): + self.name = name + self.variable = (variable_name, variable_value) + self.boards_regex = [] + + def __repr__(self): + string = "| name = {!s:<25} | variable = {!s:<35} | " + \ + "boards_regex = {!s:<125} |" + format_string = string.format(self.name, self.variable, + self.boards_regex) + return format_string + + +############################################################################### +# [Session class] + +class Session(object): + """Class to define a Session object.""" + def __init__(self, name, programs): + self.name = name + self.programs = programs + + def __repr__(self): + string = "| name = {!s:<30} | programs = {!s:<1300} |" + format_string = string.format(self.name, + [_ for _ in self.programs.values()]) + return format_string + + +############################################################################### +# [Program class] + +class Program(object): + """Class to define a Program (old tool) object.""" + def __init__(self, name, command, options): + self.name = name + self.command = command + self.options = options + + def __repr__(self): + string = "\t| name = {!s:<30} | command = {!s:<60} | options = {!s:<70} |" + format_string = string.format(self.name, self.command, self.options) + return format_string + + +############################################################################### +# [Cache class] + +class Cache(object): + """Class to define a Cache (old %gconf) object.""" + def __init__(self, last_update_time): + self.last_update = last_update_time + + self.main_window_shape = (0, 0) + self.last_set = None + self.last_configuration = None + self.last_target = None + self.last_session = None + self.last_device = None diff --git a/sw/supervision/python/lib/default_cache.xml b/sw/supervision/python/lib/default_cache.xml new file mode 100644 index 0000000000..0201937d78 --- /dev/null +++ b/sw/supervision/python/lib/default_cache.xml @@ -0,0 +1,15 @@ + +- +- +- +- +- +- +- +- +- +- +- +- +- +- diff --git a/sw/supervision/python/lib/environment.py b/sw/supervision/python/lib/environment.py new file mode 100644 index 0000000000..6315673439 --- /dev/null +++ b/sw/supervision/python/lib/environment.py @@ -0,0 +1,91 @@ +# Paparazzi center utilities +# +# Copyright (C) 2016 ENAC, Florian BITARD (intern student) +# +# This file is part of paparazzi. +# +# paparazzi 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, or (at your option) +# any later version. +# +# paparazzi 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 paparazzi; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +############################################################################### +# [Imports] + +import os +from sys import platform as os_name +import logging + + +############################################################################### +# [Constants] + +LOGGER = logging.getLogger("[ENV]") +OS = os_name + +SRC_NAME = "PAPARAZZI_SRC" +HERE_TO_SRC = "../../../.." +CONF_NAME = "PAPARAZZI_CONF" +HOME_TO_CONF = "conf" +HOME_NAME = "PAPARAZZI_HOME" +HOME_TO_VAR = "var" +IVY_BUS_NAME = "IVY_BUS" + + +def get_src_dir(src_name): + current_dir = os.path.dirname(os.path.abspath(__file__)) + src_dir = os.path.normpath(os.path.join(current_dir, HERE_TO_SRC)) + return os.getenv(src_name, src_dir) + +PAPARAZZI_SRC = get_src_dir(SRC_NAME) +LOGGER.debug("%s=%s", SRC_NAME, PAPARAZZI_SRC) + + +def get_home_dir(home_name): + return os.getenv(home_name, get_src_dir(SRC_NAME)) + +PAPARAZZI_HOME = get_home_dir(HOME_NAME) +LOGGER.info("%s=%s", HOME_NAME, PAPARAZZI_HOME) + + +def get_conf_dir(conf_name): + conf_dir = os.path.join(PAPARAZZI_HOME, HOME_TO_CONF) + if os.path.exists(conf_dir): + return os.getenv(conf_name, conf_dir) + else: + LOGGER.error("'%s' directory doesn't exist !", conf_dir) + +PAPARAZZI_CONF = get_conf_dir(CONF_NAME) + + +def get_ivy_bus(ivy_bus_name): + supposed_ivy_bus = os.getenv(ivy_bus_name) + if supposed_ivy_bus is not None: + return supposed_ivy_bus + elif OS == 'linux': + return "127.255.255.255:2010" + elif OS == 'linux2': + return "127.255.255.255:2010" + elif OS == 'darwin': + return "224.5.6.7:8910" + LOGGER.error("Unknown Ivy bus for the current OS !") + +IVY_BUS = get_ivy_bus(IVY_BUS_NAME) +LOGGER.debug("%s=%s", HOME_NAME, PAPARAZZI_HOME) + +RUN_VERSION_EXE_NAME = "paparazzi_version" +RUN_VERSION_EXE = os.path.join(PAPARAZZI_HOME, RUN_VERSION_EXE_NAME) + +BUILD_VERSION_FILE_NAME = "build_version.txt" +BUILD_VERSION_FILE = os.path.join(PAPARAZZI_HOME, HOME_TO_VAR, + BUILD_VERSION_FILE_NAME) diff --git a/sw/supervision/python/lib/gui.py b/sw/supervision/python/lib/gui.py new file mode 100644 index 0000000000..cd9c2f19ee --- /dev/null +++ b/sw/supervision/python/lib/gui.py @@ -0,0 +1,69 @@ +# Paparazzi center utilities +# +# Copyright (C) 2016 ENAC, Florian BITARD (intern student) +# +# This file is part of paparazzi. +# +# paparazzi 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, or (at your option) +# any later version. +# +# paparazzi 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 paparazzi; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +############################################################################### +# [Imports] + +import PyQt5.QtGui as Gui + + +############################################################################### +# [Constants] + +NORMAL_CURSOR_SHAPE = "normal" +WAIT_CURSOR_SHAPE = "wait" + + +############################################################################### +# [Functions] + +def generate_qcolor(color): + qcolor = Gui.QColor() + qcolor.setNamedColor(color) + return qcolor + + +def generate_widget_palette(widget, qcolor): + qpalette = Gui.QPalette(widget.palette()) + qpalette.setColor(Gui.QPalette.Background, qcolor) + return qpalette + + +def generate_qcursor(shape): + qcursor = Gui.QCursor() + if shape == WAIT_CURSOR_SHAPE: + qcursor.setShape(16) + elif shape == NORMAL_CURSOR_SHAPE: + qcursor.setShape(0) + else: + qcursor.setShape(0) + return qcursor + + +def generate_qpixmap(picture_path=""): + return Gui.QPixmap(picture_path) + + +def generate_qicon(icon_path=None): + if icon_path is not None: + return Gui.QIcon(icon_path) + else: + return Gui.QIcon() diff --git a/sw/supervision/python/main.py b/sw/supervision/python/main.py new file mode 100755 index 0000000000..1913ad2018 --- /dev/null +++ b/sw/supervision/python/main.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +# Paparazzi center utilities +# +# Copyright (C) 2016 ENAC, Florian BITARD (intern student) +# +# This file is part of paparazzi. +# +# paparazzi 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, or (at your option) +# any later version. +# +# paparazzi 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 paparazzi; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +############################################################################### +# [Imports] + +import lib.environment as env +import hmi + +import PyQt5.QtWidgets as Widgets +import sys +import os + + +############################################################################### +# [Main function] +def main(): + """Main program : creates the main window and starts the main loop.""" + + # Set the environment variables (useful for some processes) : + os.putenv(env.HOME_NAME, env.PAPARAZZI_HOME) + os.putenv(env.SRC_NAME, env.PAPARAZZI_SRC) + + app = Widgets.QApplication(sys.argv) + + # TODO : SET APPLICATION ICON IN THE LAUNCHER (UBUNTU : UNITY) + + main_window = hmi.Hmi() + main_window.init_all_hmi() + main_window.show() + + sys.exit(app.exec_()) + + +if __name__ == "__main__": + main() diff --git a/sw/supervision/python/parser.py b/sw/supervision/python/parser.py new file mode 100644 index 0000000000..c5369b474d --- /dev/null +++ b/sw/supervision/python/parser.py @@ -0,0 +1,821 @@ +# Paparazzi center utilities +# +# Copyright (C) 2016 ENAC, Florian BITARD (intern student) +# +# This file is part of paparazzi. +# +# paparazzi 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, or (at your option) +# any later version. +# +# paparazzi 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 paparazzi; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +############################################################################### +# [Imports] + +import lib.database as db +import lib.environment as env + +import xml.etree.ElementTree as Et +import os +import re +import logging +import time +import shutil + + +############################################################################### +# [Constants] + +LOGGER = logging.getLogger("[PARSER]") + +XML_EXT = ".xml" +TEST_XML = "test.xml" + +# GLOBAL XML TREE REFERENCES : +NAME_REF = "name" +VALUE_REF = "value" +CONSTANT_REF = "constant" +VARIABLE_REF = "variable" +MODE_REF = "mode" + +# REFERENCES AND STRUCTURES OF CONF XML FILES : +# CONF_STRUCTURE = [(node_name, [node_attributes], +# [(child_name, [child_attributes], +# [children_of_child...]), +# (...) +# ]) +# ] + +# CACHE : +CACHE = "cache" +CACHE_FILE = "." + CACHE + XML_EXT +DEFAULT_CACHE_FILE = "default_cache" + XML_EXT +DEFAULT_CACHE_PATH = os.path.join("lib", DEFAULT_CACHE_FILE) + +LAST_GEOMETRY = "last_geometry" +LAST_GEOMETRY_REF = "system/window/geometry" +LAST_SET = "last_set" +LAST_SET_REF = "data/set" +LAST_CONFIG = "last_config" +LAST_CONFIG_REF = "data/config" +LAST_TARGET = "last target" +LAST_TARGET_REF = "data/target" +LAST_LOG_FILTERS = "last log filters" +LAST_LOG_FILTERS_REF = "system/filters" +LAST_DEVICE = "last device" +LAST_DEVICE_REF = "data/device" +LAST_SESSION = "last session" +LAST_SESSION_REF = "data/session" + +# SET : +SET = "conf" +SET_REGEX = re.compile(r"" + SET + "([_-][\w]*)?\.xml$") + +CONF_REF = "aircraft" +ID_REF = "ac_id" +AIRFRAME_REF = "airframe" +SETTINGS_REF = "settings" +MODULES_REF = "module" +FP_REF = "flight_plan" +RADIO_REF = "radio" +TELEMETRY_REF = "telemetry" +SETTINGS_MODULES_REF = "settings_modules" +COLOR_REF = "gui_color" + +CONFIG_TAG_REF = CONF_REF + +SET_STRUCTURE = [(SET, [], + [(CONF_REF, [NAME_REF, ID_REF, AIRFRAME_REF, RADIO_REF, + TELEMETRY_REF, FP_REF, SETTINGS_REF, + SETTINGS_MODULES_REF, COLOR_REF], + []) + ]) + ] + + +# DEVICE : +DEVICE = "flash_modes" +DEVICES_FILE = DEVICE + XML_EXT + +BOARD_REF = "board" +BOARDS_REF = "boards" + +DEVICE_TAG_REF = MODE_REF + +DEVICE_STRUCTURE = [(DEVICE, [], + [(MODE_REF, [NAME_REF], + [(VARIABLE_REF, [NAME_REF, VALUE_REF], + [(BOARDS_REF, [], + [(BOARD_REF, [NAME_REF], + []) + ]) + ]) + ]) + ]) + ] + +# DEFAULT DEVICE : +DEFAULT_DEVICE_NAME = " __Default__ " +DEFAULT_DEVICE = db.Device(DEFAULT_DEVICE_NAME) + +# CONTROL_PANEL : +CONTROL_PANEL = "control_panel" +CONTROL_PANEL_FILE = CONTROL_PANEL + XML_EXT + +SECTION_REF = "section" +PROGRAM_REF = "program" +SESSION_REF = "session" +COMMAND_REF = "command" +FLAG_REF = "flag" +OPTION_REF = "arg" + +PROGRAM_TAG_REF = "/".join((SECTION_REF, PROGRAM_REF)) +SESSION_TAG_REF = "/".join((SECTION_REF, SESSION_REF)) + +CONTROL_PANEL_STRUCTURE = [(CONTROL_PANEL, [NAME_REF], + [(SECTION_REF, [NAME_REF], + [(PROGRAM_REF, [NAME_REF, COMMAND_REF], + [(OPTION_REF, [FLAG_REF, CONSTANT_REF], + []) + ]) + ]), + (SECTION_REF, [NAME_REF], + [(SESSION_REF, [NAME_REF], + [(PROGRAM_REF, [NAME_REF], + [(OPTION_REF, [FLAG_REF, CONSTANT_REF], + []) + ]) + ]) + ]) + ]) + ] + +# DEFAULT SESSIONS : +SIMULATOR_OBJECT = db.Program("Simulator", "sw/simulator/pprzsim-launch", + [("-a", "@AIRCRAFT"), ("-t", "@TARGET"), + "--boot", "--norc"]) +GCS_OBJECT = db.Program("GCS", "sw/ground_segment/cockpit/gcs", []) +SERVER_OBJECT = db.Program("Server", "sw/ground_segment/tmtc/server", ["-n"]) +PLAYER_OBJECT = db.Program("Log File Player", "sw/logalizer/play", []) + +SIMULATION_NAME = "Simulation" +SIMULATION_SESSION = db.Session(SIMULATION_NAME, + {SIMULATOR_OBJECT.name: SIMULATOR_OBJECT, + GCS_OBJECT.name: GCS_OBJECT, + SERVER_OBJECT.name: SERVER_OBJECT}) +SESSIONS_COMBO_SEP = " - - - - - - - - - - " +REPLAY_NAME = "Replay" +REPLAY_SESSION = db.Session(REPLAY_NAME, + {PLAYER_OBJECT.name: PLAYER_OBJECT, + SERVER_OBJECT.name: SERVER_OBJECT, + GCS_OBJECT.name: GCS_OBJECT}) + +# AIRFRAME : +FIRMWARE_REF = "firmware" +TARGET_REF = "target" +BOARD_REF = "board" + +TARGET_TAG_REF = "/".join((FIRMWARE_REF, TARGET_REF)) + +AIRFRAME_STRUCTURE = [] # TODO + + +############################################################################### +# [Functions] + +def full_path_to_filename(path): + return os.path.basename(path) + + +def full_to_conf_path(full_path): + return os.path.relpath(full_path, start=env.PAPARAZZI_CONF) + + +def filename_to_conf_path(filename): + return os.path.join(env.PAPARAZZI_CONF, filename) + + +def find_unused_item_name(dictionary, name): + new_name = name + nb = 0 + while new_name in dictionary.keys(): + nb += 1 + new_name = new_name.split("[")[0] + "[" + str(nb) + "]" + return new_name + + +############################################################################### +# [Functions] Data management functions + +def sorted_sets_names(sets): + sets_names = [set_object.name for set_object in sets.values()] + return sorted(sets_names) + + +def sorted_current_configs_names(configurations, current_set): + configs_names = [configurations[config_name].name + for config_name in current_set.configs_names] + return sorted(configs_names) + + +def sorted_current_targets_names(targets): + target_names = [target.name for target in targets.values()] + return sorted(target_names) + + +def sorted_sessions_names(sessions): + sessions_names = [session.name for session in sessions.values()] + sorted_names = sorted(sessions_names) + old_simulation_index = sorted_names.index(SIMULATION_NAME) + sorted_names.insert(0, sorted_names.pop(old_simulation_index)) + old_replay_index = sorted_names.index(REPLAY_NAME) + sorted_names.insert(1, sorted_names.pop(old_replay_index)) + sorted_names.insert(2, SESSIONS_COMBO_SEP) + return sorted_names + + +def sorted_current_programs_names(current_session): + programs_names = [program.name for program + in current_session.programs.values()] + return sorted(programs_names) + + +def sorted_current_devices_names(devices): + devices_names = [device.name for device in devices] + return sorted(devices_names) + + +def sorted_tools_names(tools): + tools_names = [tool.name for tool in tools.values()] + return sorted(tools_names) + + +############################################################################### +# [Functions] Load cache from '.cache' file + +def parse_cache_file(mode, cache_file, + geometry_str=None, set_str=None, config_str=None, + target_str=None, log_filters_str=None, + device_str=None, session_str=None): + """ + :param mode: + :param cache_file: + :param geometry_str: + :param set_str: + :param config_str: + :param target_str: + :param log_filters_str: + :param device_str: + :param session_str: + -> Scan 'cache_file' to get tags by name reference. + -> Read values of tags in 'r' mode. + -> Write new values of tags in 'w' mode. + -> Except an incorrect XML format and raise ERROR. + """ + cache_dict = {} + try: + try: + cache_tree = Et.parse(cache_file) + except FileNotFoundError: + cache_tree = Et.parse(DEFAULT_CACHE_PATH) + + last_geometry_tag = cache_tree.find(LAST_GEOMETRY_REF) + last_set_tag = cache_tree.find(LAST_SET_REF) + last_config_tag = cache_tree.find(LAST_CONFIG_REF) + last_target_tag = cache_tree.find(LAST_TARGET_REF) + last_log_filters_tag = cache_tree.find(LAST_LOG_FILTERS_REF) + last_device_tag = cache_tree.find(LAST_DEVICE_REF) + last_session_tag = cache_tree.find(LAST_SESSION_REF) + + last_tags_list = [last_geometry_tag, last_set_tag, + last_config_tag, last_target_tag, + last_log_filters_tag, last_device_tag, + last_session_tag] + if mode == "r": + for key, tag in zip([LAST_GEOMETRY, LAST_SET, + LAST_CONFIG, LAST_TARGET, + LAST_LOG_FILTERS, LAST_DEVICE, + LAST_SESSION], + last_tags_list): + cache_dict[key] = tag.get(VALUE_REF) + return cache_dict + + elif mode == "w": + for tag, string in zip(last_tags_list, + [geometry_str, set_str, + config_str, target_str, + log_filters_str, device_str, + session_str]): + tag.set(VALUE_REF, string) + cache_tree.write(cache_file) + + except Et.ParseError as msg: + LOGGER.error("ERROR in syntax of XML file : '%s'. " + "Original message : '%s'.", cache_file, msg) + + +def load_cache(): + """ + -> Scan the 'python' current directory to find the '.cache.xml' file. + -> Parse the cache file withe the 'w' mode to load the last values. + -> Except multiple cache files and raise an ERROR if so. + """ + cache_file = None + for root, dirs, files in os.walk("./"): + for file in files: + ext = os.path.splitext(file)[1] + if ext == XML_EXT and file == CACHE_FILE: + cache_file = os.path.join(root, file) + break + if cache_file is not None: + cache = parse_cache_file("r", cache_file) + else: + cache = parse_cache_file("r", DEFAULT_CACHE_PATH) + cache_file = CACHE_FILE + + LOGGER.error("No cache file found ! Default cache '%s' loaded.", + DEFAULT_CACHE_PATH) + print("No cache file found ! Default cache '%s' loaded." % + DEFAULT_CACHE_PATH) + + if logging.DEBUG: + LOGGER.debug("Cache dictionary :\n%s", cache) + return cache, cache_file + +def delete_cache(): + """ + -> Scan the 'python' current directory to find the '.cache.xml' file. + -> Remove if found + """ + cache_file = None + for root, dirs, files in os.walk("./"): + for file in files: + ext = os.path.splitext(file)[1] + if ext == XML_EXT and file == CACHE_FILE: + cache_file = os.path.join(root, file) + break + if cache_file is not None: + cache = os.remove(cache_file) + LOGGER.debug("Deleting cache file '%s'\n", cache_file) + else: + LOGGER.debug("No cache to delete\n") + + +############################################################################### +# [Functions] Load files to init HMI + +def load_init_files(conf_path): + """ + :param conf_path: + -> Scan the conf directory to find the configuration files : all files + matching with '*conf*.xml', 'control_panel.xml' and 'flash_modes.xml'. + -> Show the result of scan if DEBUG mode is on (main.py) + """ + conf_files, cp_files, devices_files = [], [], [] + for root, dirs, files in os.walk(conf_path): + for file in files: + ext = os.path.splitext(file)[1] + if ext == XML_EXT: + xml_file = os.path.join(root, file) + if SET_REGEX.search(file) is not None \ + and file != "%gconf.xml"\ + and xml_file != env.PAPARAZZI_CONF+"/conf.xml": + conf_files.append(xml_file) + elif file == CONTROL_PANEL_FILE and "airframes" not in root: + cp_files.append(xml_file) + elif file == DEVICES_FILE: + devices_files.append(xml_file) + + result = "{} startup files found." + files_nb = sum((len(conf_files), len(cp_files), len(devices_files))) + info = result.format(files_nb) + + if logging.DEBUG: + LOGGER.debug("'conf' files :") + for file in conf_files: + LOGGER.debug(file) + LOGGER.debug("'devices' file(s) :") + for file in devices_files: + LOGGER.debug(file) + LOGGER.debug("'control_panel' file(s) :") + for file in cp_files: + LOGGER.debug(file) + return conf_files, cp_files, devices_files, info + + +############################################################################### +# [Functions] Load sets and configurations from 'conf' files + +def set_config_details(config_object, tag): + """ + :param config_object: + :param tag: + -> Parse values of a configuration in the given XML tag element. + -> Fills attributes of the given configuration object. + """ + variables = [[] for _ in range(6)] + keys = [SETTINGS_REF, SETTINGS_MODULES_REF, FP_REF, RADIO_REF, + TELEMETRY_REF, COLOR_REF] + for i, key in zip(range(6), keys): + items = tag.get(key) + if items: + for item in items.split(" "): + variables[i].append(item) + + config_object.settings = variables[0] + config_object.modules = variables[1] + config_object.flight_plan = variables[2] + config_object.radio = variables[3] + config_object.telemetry = variables[4] + config_object.color = variables[5] + + +def parse_targets(airframe_file): + """ + :param airframe_file: + -> Parse values of a target in the airframe file given. + -> Except an incorrect XML format and raise ERROR. + """ + available_targets = {} + try: + airframe_tree = Et.parse(filename_to_conf_path(airframe_file)) + targets_tags = airframe_tree.findall(TARGET_TAG_REF) + for target_tag in targets_tags: + name = target_tag.get(NAME_REF) + board = target_tag.get(BOARD_REF) + target_object = db.Target(name, board) + available_targets[target_object.name] = target_object + + except Et.ParseError as msg: + LOGGER.error("ERROR in syntax of XML file : '%s'. " + "Original message : '%s'.", airframe_file, msg) + except (FileNotFoundError, IOError) as msg: + LOGGER.error("ERROR file '%s' not found or IO error" + "Original message : '%s'.", airframe_file, msg) + return available_targets + + +def parse_conf_files(set_files): + """ + :param set_files: + -> Parse values of a target in the airframe file given. + -> Except an incorrect XML format and raise ERROR. + """ + configs, sets = {}, {} + for set_file in set_files: + try: + set_name = full_to_conf_path(set_file) + set_object = db.Set(set_name) + set_tree = Et.parse(set_file) + config_tags = set_tree.findall(CONFIG_TAG_REF) + for tag in config_tags: + config_name = tag.get(NAME_REF) + config_id = tag.get(ID_REF) + config_airframes = tag.get(AIRFRAME_REF).split(" ") + config_targets = parse_targets(config_airframes[0]) + config_object = db.Configuration(config_name, config_id, + config_airframes, + config_targets) + if config_object.id not in set_object.configs_names: + set_config_details(config_object, tag) + configs[config_object.name] = config_object + set_object.configs_names.append(config_object.name) + else: + LOGGER.error("'%s' configuration can't be loaded in " + "'%s' set because ID already exists !", + config_object.name, set_object.name) + sets[set_name] = set_object + + except Et.ParseError as msg: + LOGGER.error("ERROR in syntax of XML file : '%s'. " + "Original message : '%s'.", set_file, msg) + return configs, sets + + +def load_sets_and_configurations(set_files): + """ + :param set_files: + -> Parse the sets and the configurations from the '*conf*.xml' files. + -> Show the result of scan if DEBUG mode is on (main.py) + """ + configurations, sets = parse_conf_files(set_files) + result = "{} sets and {} configurations found." + sets_nb, configs_nb = len(sets), len(configurations) + info = result.format(sets_nb, configs_nb) + + if logging.DEBUG: + sets_str = "Sets :\n" + for set_object in sets.values(): + sets_str += str(set_object) + "\n" + LOGGER.debug(sets_str) + configs_str = "Configurations :\n" + for config_object in configurations.values(): + configs_str += str(config_object) + "\n" + LOGGER.debug(configs_str) + return sets, configurations, info + + +def save_configuration(conf_file, config_object): + """ + :param conf_file: + :param config_object: + -> Set the new values of the configuration object given into the + corresponding XML file. + """ + conf_file_path = filename_to_conf_path(conf_file) + + conf_tree = Et.parse(conf_file_path) + config_tags = conf_tree.findall(CONFIG_TAG_REF) + for tag in config_tags: + if tag.get(NAME_REF) == config_object.name: + attributes = [AIRFRAME_REF, SETTINGS_REF, + SETTINGS_MODULES_REF, FP_REF, + RADIO_REF, TELEMETRY_REF] + new_values = [config_object.airframes, config_object.settings, + config_object.modules, config_object.flight_plan, + config_object.radio, config_object.telemetry] + for attribute, values in zip(attributes, new_values): + string = " ".join(values) + tag.set(attribute, string) + conf_tree.write(conf_file_path) + + +############################################################################### +# [Functions] Load sets devices from 'flash_modes' file + +def parse_devices_file(devices_file): + """ + :param devices_file: + -> Parse values of a device in the 'flash_modes' file given. + -> Except an incorrect XML format and raise ERROR. + """ + devices = {} + try: + devices_tree = Et.parse(devices_file) + devices_tags = devices_tree.findall(DEVICE_TAG_REF) + for device_tag in devices_tags: + device_name = device_tag.get(NAME_REF) + variable_tag = device_tag.find(VARIABLE_REF) + variable_name = variable_tag.get(NAME_REF) + variable_value = variable_tag.get(VALUE_REF) + device_object = db.Device(device_name, + variable_name, variable_value) + for board_tag in device_tag.find(BOARDS_REF): + board_name = board_tag.get(NAME_REF) + device_object.boards_regex.append(board_name) + devices[device_name] = device_object + except Et.ParseError as msg: + LOGGER.error("ERROR in syntax of XML file : '%s'. " + "Original message : '%s'.", devices_file, msg) + return devices + + +def load_devices(devices_file): + """ + :param devices_file: + -> Parse the devices from the 'flash_modes.xml' files. + -> Show the result of scan if DEBUG mode is on (main.py) + """ + devices = parse_devices_file(devices_file) + default_object = db.Device("Default") + devices[default_object.name] = default_object + + devices[DEFAULT_DEVICE.name] = DEFAULT_DEVICE + + result = "{} devices found." + devices_nb = len(devices) + info = result.format(devices_nb) + + if logging.DEBUG: + devices_str = "Devices :\n" + for device_object in devices.values(): + devices_str += str(device_object) + "\n" + LOGGER.debug(devices_str) + + return devices, info + + +############################################################################### +# [Functions] Load tools and sessions from 'control_panel' file + +def parse_arg_option(option_tag): + """ + :param option_tag: + -> Fill an option triplet (flag, argument type : 'constant' or 'variable', + value of the argument) from an option tag in the XML tree. + """ + option_flag = option_tag.get(FLAG_REF) + constant = option_tag.get(CONSTANT_REF) + variable = option_tag.get(VARIABLE_REF) + if constant is not None: + option = (option_flag, constant) + elif variable is not None: + option = (option_flag, variable) + else: + option = option_flag + return option + + +def parse_tools(cp_file): + """ + :param cp_file: + -> Parse all tools in the 'control_panel' file given. + -> Except an incorrect XML format and raise ERROR. + """ + tools = {} + try: + cp_tree = Et.parse(cp_file) + tools_tags = cp_tree.findall(PROGRAM_TAG_REF) + for tool_tag in tools_tags: + tool_name = tool_tag.get(NAME_REF) + tool_command = tool_tag.get(COMMAND_REF) + options = [] + for option_tag in tool_tag: + option = parse_arg_option(option_tag) + options.append(option) + tool_object = db.Program(tool_name, tool_command, options) + tools[tool_name] = tool_object + + except Et.ParseError as msg: + LOGGER.error("ERROR in syntax of XML file : '%s'. " + "Original message : '%s'.", cp_file, msg) + return tools + + +def parse_sessions(cp_file, tools): + """ + :param cp_file: + :param tools: + -> Parse all sessions in the 'control_panel.xml' file given. + -> Except an incorrect XML format and raise ERROR. + """ + sessions = {} + try: + cp_tree = Et.parse(cp_file) + sessions_tags = cp_tree.findall(SESSION_TAG_REF) + for session_tag in sessions_tags: + session_name = session_tag.get(NAME_REF) + session_programs = {} + for program_tag in session_tag: + program_name = program_tag.get(NAME_REF) + command = tools[program_name].command + options = [] + for option_tag in program_tag: + option = parse_arg_option(option_tag) + options.append(option) + program_object = db.Program(program_name, command, options) + session_programs[program_name] = program_object + session_object = db.Session(session_name, session_programs) + sessions[session_name] = session_object + + except Et.ParseError as msg: + LOGGER.error("ERROR in syntax of XML file : '%s'. " + "Original message : '%s'.", cp_file, msg) + return sessions + + +def load_sessions_and_programs(cp_file): + """ + :param cp_file: + -> Parse the tools and sessions from the 'control_panel.xml' file. + -> Add the default sessions 'simulation' & replay. + -> Show the result of scan if DEBUG mode is on (main.py) + """ + tools = parse_tools(cp_file) + sessions = parse_sessions(cp_file, tools) + + sessions[SIMULATION_SESSION.name] = SIMULATION_SESSION + sessions[REPLAY_SESSION.name] = REPLAY_SESSION + + result = "{} tools and {} sessions found." + tools_nb, sessions_nb = len(tools), len(sessions) + info = result.format(tools_nb, sessions_nb) + + if logging.DEBUG: + tools_str = "Tools :\n" + for tool_object in tools.values(): + tools_str += str(tool_object) + "\n" + LOGGER.debug(tools_str) + sessions_str = "Sessions :\n" + for session_object in sessions.values(): + sessions_str += str(session_object) + "\n" + LOGGER.debug(sessions_str) + + return tools, sessions, info + + +def save_session(cp_file, session_object): + """ + :param cp_file: + :param session_object: + -> Set the new values of the session object given into the + corresponding XML file. + """ + cp_file_path = filename_to_conf_path(cp_file) + + cp_tree = Et.parse(cp_file_path) + cp_tags = cp_tree.findall(SESSION_TAG_REF) + for session_tag in cp_tags: + if session_tag.get(NAME_REF) == session_object.name: + session_tag.clear() + session_tag.set(NAME_REF, session_object.name) + for program in session_object.programs.values(): + program_tag = Et.SubElement(session_tag, PROGRAM_REF, + {NAME_REF: program.name}) + for option in program.options: + attributes = {} + if type(option) is tuple: + attributes[FLAG_REF] = option[0] + attributes[CONSTANT_REF] = option[1] + else: + attributes[FLAG_REF] = option + _option_tag = Et.SubElement(program_tag, OPTION_REF, + attributes) + cp_tree.write(cp_file_path) + + +############################################################################### +# [Data class] + +class Data(object): + """Class to manage a Data meta-object that gathers all the useful data + parsed in the various XML files of the /conf directory.""" + def __init__(self, conf_path): + self.conf_path = conf_path + + self.cache_file = None + self.conf_files = [] + self.devices_file = None + self.cp_file = None + + self.cache = {} + self.configurations = {} + self.sets = {} + self.devices = {} + self.tools = {} + self.sessions = {} + + self.init_data() + +############################################################################### +# [Data methods] Init data methods + + def init_data(self): + init = time.clock() + + self.load_cache() + self.load_conf_files() + self.load_sets_and_configs() + self.load_devices() + self.load_sessions_and_programs() + + LOGGER.info("All data loaded in : %s seconds.\n", + time.clock() - init) + + def load_cache(self): + LOGGER.info("Loading cache...") + self.cache, self.cache_file = load_cache() + LOGGER.info("Cache loaded.\n") + + def load_conf_files(self): + LOGGER.info("Scanning current directory...") + self.conf_files, self.cp_file, self.devices_file, load_info = \ + load_init_files(self.conf_path) + LOGGER.debug(load_info) + LOGGER.info("End of scan.\n") + + def load_sets_and_configs(self): + LOGGER.info("Loading sets and configurations...") + self.sets, self.configurations, \ + load_info = load_sets_and_configurations(self.conf_files) + LOGGER.debug(load_info) + LOGGER.info("Sets and configurations loaded.\n") + + def load_devices(self): + LOGGER.info("Loading devices...") + if len(self.devices_file) == 1: + self.devices, load_info = load_devices(self.devices_file[0]) + LOGGER.debug(load_info) + LOGGER.info("Devices loaded.\n") + else: + LOGGER.error("Multiple '%s' XML files !", DEVICE) + + def load_sessions_and_programs(self): + LOGGER.info("Loading programs and sessions...") + if len(self.cp_file) == 1: + self.tools, self.sessions, \ + load_info = load_sessions_and_programs(self.cp_file[0]) + LOGGER.debug(load_info) + LOGGER.info("Programs and sessions loaded.\n") + else: + LOGGER.error("ERROR : Multiple '%s' XML files !", CONTROL_PANEL) diff --git a/sw/supervision/python/processes.py b/sw/supervision/python/processes.py new file mode 100644 index 0000000000..0a8b2c0096 --- /dev/null +++ b/sw/supervision/python/processes.py @@ -0,0 +1,238 @@ +# Paparazzi center utilities +# +# Copyright (C) 2016 ENAC, Florian BITARD (intern student) +# +# This file is part of paparazzi. +# +# paparazzi 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, or (at your option) +# any later version. +# +# paparazzi 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 paparazzi; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +############################################################################### +# [Imports] + +import lib.environment as env +import lib.console as cs + +import PyQt5.QtCore as Core +import logging +import os +import signal + + +############################################################################### +# [Constants] + +LOGGER = logging.getLogger("[PROCESSES]") + +AC_MAKEFILE_NAME = "Makefile.ac" +CONF_FLAG_NAME = "AIRCRAFT" +DEVICE_FLAG_NAME = "FLASH_MODE" + +DEFAULT_EXIT_CODE = 2 +INTERRUPTED_EXIT_CODE = -1 +SUCCESS_EXIT_CODE = 0 + +CLEAN = "clean" +BUILD = "build" +UPLOAD = "upload" +PROGRAM = "program" +TOOL = "tool" + +CLEAN_TARGET_KEY = "clean_ac" +BUILD_TARGET_KEY = ".compile" +UPLOAD_TARGET_KEY = ".upload" + +CONF_FLAG = "@" + CONF_FLAG_NAME +TARGET_FLAG = "@TARGET" +CONF_ID_FLAG = "@AC_ID" + + +############################################################################### +# [Stream class] + +class LoggerStream(Core.QObject): + """ Class to define a Stream object.""" + logger_log_sent = Core.pyqtSignal(str, str, str) + + def __init__(self): + super(LoggerStream, self).__init__() + + # Reimplemented method in order to write the logs in the console : + def write(self, line): + log, flag = cs.analyse_log_line(line) + log_type = cs.APPLICATION_MESSAGE_TYPE + self.logger_log_sent.emit(log, flag, log_type) + + +############################################################################### +# [Process class] + +class Process(Core.QObject): + """Class to upload the built code to a device.""" + process_killed = Core.pyqtSignal() + process_log_sent = Core.pyqtSignal(str, str, str) + + def __init__(self, process_type, + configuration=None, target=None, device=None, program=None): + """ + :param process_type: + :param configuration: + :param target: + :param device: + :param program: + -> Declare a Process object as a QObject derivative. + -> Give it a name and the necessary parameters for its type. + -> Generate a command for the system call by the Popen object that + manages the subprocess and allows to redirect the output. + -> The process runs into an independent thread. + -> A queue is used to collect the logs from the Popen output and + an other one is used to send it to the QTextEdit integrated console. + -> Logs flags are collected for information. + -> Exit code is initialized to default value. Must change in case of + normal exit, error or user interruption. + """ + super(Process, self).__init__() + + self.type = process_type + self.name = None + + self.config = configuration + self.target = target + self.device = device + self.program = program + + if self.type == CLEAN: + self.command = self.generate_make_command(CLEAN_TARGET_KEY) + self.name = " - ".join([self.type.upper(), + self.config.name]) + elif self.type == BUILD: + self.command = self.generate_make_command(BUILD_TARGET_KEY) + self.name = " - ".join([self.type.upper(), self.config.name, + self.target.name]) + elif self.type == UPLOAD: + self.command = self.generate_make_command(UPLOAD_TARGET_KEY) + self.name = " - ".join([self.type.upper(), self.config.name, + self.target.name, self.device.name]) + else: + self.command = self.generate_program_command() + self.name = " - ".join([self.type.upper(), + self.program.name]) + + self.subprocess = Core.QProcess() + self.subprocess.setProcessChannelMode(Core.QProcess.MergedChannels) + self.subprocess.setReadChannel(Core.QProcess.StandardOutput) + + self.process_killed.connect(self.emergency_stop) + self.exit_code = DEFAULT_EXIT_CODE + + self.flags = {cs.ERROR_FLAG: 0, + cs.WARNING_FLAG: 0, + cs.INFO_FLAG: 0} + + def generate_make_command(self, target_key): + """ + :param target_key: + -> Generate a system command to compile files by a Makefile and + putting the right arguments if given. + """ + if self.config is not None: + aircraft_term = CONF_FLAG_NAME + "=" + self.config.name + + if self.target is not None: + target_key = self.target.name + target_key + + command_terms = ["make", "-C", env.PAPARAZZI_HOME, "-f", + AC_MAKEFILE_NAME, + aircraft_term, target_key] + + if self.device is not None and self.device.variable[1]: + device_term = DEVICE_FLAG_NAME + "=" + self.device.variable[1] + command_terms.insert(-1, device_term) + + return " ".join(command_terms) + + def generate_program_command(self): + """ + -> Generate a system command to run a program and add its options + if it has some. + """ + if self.program is not None: + full_command = os.path.join(env.PAPARAZZI_HOME, + self.program.command) + + for option in self.program.options: + if type(option) is tuple: + flag, value = option + if value == CONF_FLAG: + full_command += " " + flag + " " + self.config.name + elif value == TARGET_FLAG: + full_command += " " + flag + " " + self.target.name + elif value == CONF_ID_FLAG: + full_command += " " + flag + " " + self.config.id + else: + full_command += " " + flag + " " + value + else: + full_command += " " + option + + return full_command + + def check_before_start(self): + # TODO IF NECESSARY !!! + return self == self + + def start(self): + """ + -> Start the thread => start the worker => call the run method. + """ + LOGGER.info("'%s' process running ... (command='%s')", + self.name, self.command) + self.subprocess.start(self.command) + self.subprocess.readyReadStandardOutput.connect(self.send_text) + self.subprocess.finished.connect(self.finish_process) + + def send_text(self): + """ + -> Analyse an process output line to find a flag in it. + -> Send the item by the sending queue object. + -> Collect the flag found. + """ + q_byte_array = self.subprocess.readAllStandardOutput() + string = str(q_byte_array, encoding="utf-8").strip() + for line in string.split("\n"): + log, flag = cs.analyse_log_line(line) + log_type = cs.PROCESS_MESSAGE_TYPE + self.process_log_sent.emit(log, flag, log_type) + if flag != cs.DEFAULT_FLAG: + self.flags[flag] += 1 + + def finish_process(self): + """ + -> If the process finished, get the exit code. + -> Else, the process crashed... + """ + if self.subprocess.exitStatus() == Core.QProcess.NormalExit: + self.exit_code = self.subprocess.exitCode() + LOGGER.info("'%s' process finished with exit code %s.\n", + self.name, self.exit_code) + else: + self.exit_code = INTERRUPTED_EXIT_CODE + LOGGER.error("'%s' process crashed, probably stopped by user !\n", + self.name) + + def emergency_stop(self): + """ + -> Kill the subprocess by getting its ProcessID. + """ + os.kill(self.subprocess.pid(), signal.SIGKILL) diff --git a/sw/supervision/python/ui/credits.html b/sw/supervision/python/ui/credits.html new file mode 100644 index 0000000000..e97422d6fd --- /dev/null +++ b/sw/supervision/python/ui/credits.html @@ -0,0 +1,16 @@ + + + + + + +
+ Paparazzi UAV icon unreachable... +

Paparazzi Center

+

(Python/Qt version)

+
+

Copyright (C) 2007-2008 ENAC, Pascal Brisset

+

License GPLv2

+

http://paparazziuav.org

+ + diff --git a/sw/supervision/python/ui/data_changed.html b/sw/supervision/python/ui/data_changed.html new file mode 100644 index 0000000000..ba8ef77ead --- /dev/null +++ b/sw/supervision/python/ui/data_changed.html @@ -0,0 +1,13 @@ + + + + + + +
+ /!\ WARNING : +
+

Values have been modified locally but not into the original XML files...

+

Do you want to save or ignore them ?

+ + diff --git a/sw/supervision/python/ui/icons/accessories-text-editor.svg b/sw/supervision/python/ui/icons/accessories-text-editor.svg new file mode 100644 index 0000000000..03db109a64 --- /dev/null +++ b/sw/supervision/python/ui/icons/accessories-text-editor.svg @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/ui/icons/dialog-apply.svg b/sw/supervision/python/ui/icons/dialog-apply.svg new file mode 100644 index 0000000000..f009b90ee3 --- /dev/null +++ b/sw/supervision/python/ui/icons/dialog-apply.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/ui/icons/dialog-error.svg b/sw/supervision/python/ui/icons/dialog-error.svg new file mode 100644 index 0000000000..a3d6de4d72 --- /dev/null +++ b/sw/supervision/python/ui/icons/dialog-error.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/ui/icons/dialog-warning-symbolic.svg b/sw/supervision/python/ui/icons/dialog-warning-symbolic.svg new file mode 100644 index 0000000000..e9474a9d14 --- /dev/null +++ b/sw/supervision/python/ui/icons/dialog-warning-symbolic.svg @@ -0,0 +1,17 @@ + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + Gnome Symbolic Icon Theme + + + + diff --git a/sw/supervision/python/ui/icons/dialog-warning.png b/sw/supervision/python/ui/icons/dialog-warning.png new file mode 100644 index 0000000000..d1020c6d9e Binary files /dev/null and b/sw/supervision/python/ui/icons/dialog-warning.png differ diff --git a/sw/supervision/python/ui/icons/edit-clear.svg b/sw/supervision/python/ui/icons/edit-clear.svg new file mode 100644 index 0000000000..568fe9de66 --- /dev/null +++ b/sw/supervision/python/ui/icons/edit-clear.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/ui/icons/go-up.svg b/sw/supervision/python/ui/icons/go-up.svg new file mode 100644 index 0000000000..e5e58672cd --- /dev/null +++ b/sw/supervision/python/ui/icons/go-up.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/ui/icons/gtk-info.svg b/sw/supervision/python/ui/icons/gtk-info.svg new file mode 100644 index 0000000000..f1ec53786a --- /dev/null +++ b/sw/supervision/python/ui/icons/gtk-info.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/ui/icons/list-add.svg b/sw/supervision/python/ui/icons/list-add.svg new file mode 100644 index 0000000000..f40a948324 --- /dev/null +++ b/sw/supervision/python/ui/icons/list-add.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/ui/icons/list-remove.svg b/sw/supervision/python/ui/icons/list-remove.svg new file mode 100644 index 0000000000..ebc6bdbe8f --- /dev/null +++ b/sw/supervision/python/ui/icons/list-remove.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/ui/icons/media-playback-pause.svg b/sw/supervision/python/ui/icons/media-playback-pause.svg new file mode 100644 index 0000000000..fde2760829 --- /dev/null +++ b/sw/supervision/python/ui/icons/media-playback-pause.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/ui/icons/media-playback-start.svg b/sw/supervision/python/ui/icons/media-playback-start.svg new file mode 100644 index 0000000000..87c34d3025 --- /dev/null +++ b/sw/supervision/python/ui/icons/media-playback-start.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/ui/icons/penguin_icon.png b/sw/supervision/python/ui/icons/penguin_icon.png new file mode 100644 index 0000000000..f61d8024e9 Binary files /dev/null and b/sw/supervision/python/ui/icons/penguin_icon.png differ diff --git a/sw/supervision/python/ui/icons/process-stop.svg b/sw/supervision/python/ui/icons/process-stop.svg new file mode 100644 index 0000000000..1c9162caab --- /dev/null +++ b/sw/supervision/python/ui/icons/process-stop.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/ui/icons/search_field.png b/sw/supervision/python/ui/icons/search_field.png new file mode 100644 index 0000000000..c3264f66d9 Binary files /dev/null and b/sw/supervision/python/ui/icons/search_field.png differ diff --git a/sw/supervision/python/ui/icons/system-run.png b/sw/supervision/python/ui/icons/system-run.png new file mode 100644 index 0000000000..ad6d80b0de Binary files /dev/null and b/sw/supervision/python/ui/icons/system-run.png differ diff --git a/sw/supervision/python/ui/icons/utilities-terminal.svg b/sw/supervision/python/ui/icons/utilities-terminal.svg new file mode 100644 index 0000000000..add2893910 --- /dev/null +++ b/sw/supervision/python/ui/icons/utilities-terminal.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/sw/supervision/python/ui/main_window.py b/sw/supervision/python/ui/main_window.py new file mode 100644 index 0000000000..6363b7abff --- /dev/null +++ b/sw/supervision/python/ui/main_window.py @@ -0,0 +1,1700 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'ui/main_window.ui' +# +# Created: Tue Jun 28 14:49:52 2016 +# by: PyQt5 UI code generator 5.2.1 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(744, 481) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) + MainWindow.setSizePolicy(sizePolicy) + MainWindow.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.centralwidget = QtWidgets.QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.gridLayout_10 = QtWidgets.QGridLayout(self.centralwidget) + self.gridLayout_10.setObjectName("gridLayout_10") + self.current_set_and_configuration = QtWidgets.QFrame(self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.current_set_and_configuration.sizePolicy().hasHeightForWidth()) + self.current_set_and_configuration.setSizePolicy(sizePolicy) + self.current_set_and_configuration.setMinimumSize(QtCore.QSize(0, 40)) + self.current_set_and_configuration.setMaximumSize(QtCore.QSize(16777215, 40)) + self.current_set_and_configuration.setAutoFillBackground(False) + self.current_set_and_configuration.setStyleSheet("") + self.current_set_and_configuration.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.current_set_and_configuration.setFrameShadow(QtWidgets.QFrame.Plain) + self.current_set_and_configuration.setObjectName("current_set_and_configuration") + self.gridLayout = QtWidgets.QGridLayout(self.current_set_and_configuration) + self.gridLayout.setObjectName("gridLayout") + self.label = QtWidgets.QLabel(self.current_set_and_configuration) + self.label.setToolTip("") + self.label.setObjectName("label") + self.gridLayout.addWidget(self.label, 0, 0, 1, 1) + self.current_configuration = QtWidgets.QComboBox(self.current_set_and_configuration) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.current_configuration.sizePolicy().hasHeightForWidth()) + self.current_configuration.setSizePolicy(sizePolicy) + self.current_configuration.setMinimumSize(QtCore.QSize(0, 20)) + self.current_configuration.setObjectName("current_configuration") + self.gridLayout.addWidget(self.current_configuration, 0, 3, 1, 1) + self.current_color = QtWidgets.QLabel(self.current_set_and_configuration) + self.current_color.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.current_color.sizePolicy().hasHeightForWidth()) + self.current_color.setSizePolicy(sizePolicy) + self.current_color.setMinimumSize(QtCore.QSize(30, 0)) + self.current_color.setMaximumSize(QtCore.QSize(40, 16777215)) + self.current_color.setMouseTracking(False) + self.current_color.setFocusPolicy(QtCore.Qt.NoFocus) + self.current_color.setAutoFillBackground(True) + self.current_color.setStyleSheet("") + self.current_color.setFrameShape(QtWidgets.QFrame.Box) + self.current_color.setMidLineWidth(1) + self.current_color.setText("") + self.current_color.setAlignment(QtCore.Qt.AlignCenter) + self.current_color.setObjectName("current_color") + self.gridLayout.addWidget(self.current_color, 0, 7, 1, 1) + self.label_2 = QtWidgets.QLabel(self.current_set_and_configuration) + self.label_2.setToolTip("") + self.label_2.setObjectName("label_2") + self.gridLayout.addWidget(self.label_2, 0, 2, 1, 1) + self.label_3 = QtWidgets.QLabel(self.current_set_and_configuration) + self.label_3.setToolTip("") + self.label_3.setObjectName("label_3") + self.gridLayout.addWidget(self.label_3, 0, 4, 1, 1) + self.current_id = QtWidgets.QLabel(self.current_set_and_configuration) + self.current_id.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.current_id.sizePolicy().hasHeightForWidth()) + self.current_id.setSizePolicy(sizePolicy) + self.current_id.setMinimumSize(QtCore.QSize(30, 0)) + self.current_id.setMaximumSize(QtCore.QSize(40, 16777215)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.current_id.setFont(font) + self.current_id.setMouseTracking(False) + self.current_id.setFocusPolicy(QtCore.Qt.NoFocus) + self.current_id.setAutoFillBackground(True) + self.current_id.setStyleSheet("") + self.current_id.setFrameShape(QtWidgets.QFrame.Box) + self.current_id.setMidLineWidth(1) + self.current_id.setText("") + self.current_id.setAlignment(QtCore.Qt.AlignCenter) + self.current_id.setObjectName("current_id") + self.gridLayout.addWidget(self.current_id, 0, 5, 1, 1) + self.label_27 = QtWidgets.QLabel(self.current_set_and_configuration) + self.label_27.setToolTip("") + self.label_27.setObjectName("label_27") + self.gridLayout.addWidget(self.label_27, 0, 6, 1, 1) + self.current_set = QtWidgets.QComboBox(self.current_set_and_configuration) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.current_set.sizePolicy().hasHeightForWidth()) + self.current_set.setSizePolicy(sizePolicy) + self.current_set.setMinimumSize(QtCore.QSize(0, 20)) + self.current_set.setObjectName("current_set") + self.gridLayout.addWidget(self.current_set, 0, 1, 1, 1) + self.gridLayout_10.addWidget(self.current_set_and_configuration, 0, 0, 1, 1) + self.main_tab = QtWidgets.QTabWidget(self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.main_tab.sizePolicy().hasHeightForWidth()) + self.main_tab.setSizePolicy(sizePolicy) + self.main_tab.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.main_tab.setToolTip("") + self.main_tab.setLayoutDirection(QtCore.Qt.LeftToRight) + self.main_tab.setTabPosition(QtWidgets.QTabWidget.North) + self.main_tab.setTabShape(QtWidgets.QTabWidget.Triangular) + self.main_tab.setElideMode(QtCore.Qt.ElideNone) + self.main_tab.setUsesScrollButtons(True) + self.main_tab.setDocumentMode(False) + self.main_tab.setTabsClosable(False) + self.main_tab.setObjectName("main_tab") + self.equipment_tab = QtWidgets.QWidget() + self.equipment_tab.setObjectName("equipment_tab") + self.gridLayout_11 = QtWidgets.QGridLayout(self.equipment_tab) + self.gridLayout_11.setObjectName("gridLayout_11") + spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_11.addItem(spacerItem, 6, 2, 1, 1) + self.search_item = QtWidgets.QLineEdit(self.equipment_tab) + self.search_item.setEnabled(False) + self.search_item.setMinimumSize(QtCore.QSize(0, 0)) + self.search_item.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.search_item.setObjectName("search_item") + self.gridLayout_11.addWidget(self.search_item, 0, 1, 1, 1) + self.all_items_tree = QtWidgets.QTreeWidget(self.equipment_tab) + self.all_items_tree.setEnabled(False) + self.all_items_tree.setObjectName("all_items_tree") + self.gridLayout_11.addWidget(self.all_items_tree, 1, 0, 7, 2) + self.frame_4 = QtWidgets.QFrame(self.equipment_tab) + self.frame_4.setMinimumSize(QtCore.QSize(0, 40)) + self.frame_4.setMaximumSize(QtCore.QSize(16777215, 40)) + self.frame_4.setFrameShape(QtWidgets.QFrame.NoFrame) + self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame_4.setObjectName("frame_4") + self.gridLayout_9 = QtWidgets.QGridLayout(self.frame_4) + self.gridLayout_9.setObjectName("gridLayout_9") + self.quick_target = QtWidgets.QComboBox(self.frame_4) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.quick_target.sizePolicy().hasHeightForWidth()) + self.quick_target.setSizePolicy(sizePolicy) + self.quick_target.setMinimumSize(QtCore.QSize(0, 20)) + self.quick_target.setMaximumSize(QtCore.QSize(16777215, 20)) + self.quick_target.setObjectName("quick_target") + self.gridLayout_9.addWidget(self.quick_target, 0, 2, 1, 1) + self.label_19 = QtWidgets.QLabel(self.frame_4) + self.label_19.setMaximumSize(QtCore.QSize(16777215, 20)) + self.label_19.setObjectName("label_19") + self.gridLayout_9.addWidget(self.label_19, 0, 0, 1, 1) + self.quick_build = QtWidgets.QPushButton(self.frame_4) + self.quick_build.setMinimumSize(QtCore.QSize(0, 20)) + self.quick_build.setMaximumSize(QtCore.QSize(16777215, 20)) + self.quick_build.setLayoutDirection(QtCore.Qt.LeftToRight) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("icons/system-run.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.quick_build.setIcon(icon) + self.quick_build.setObjectName("quick_build") + self.gridLayout_9.addWidget(self.quick_build, 0, 3, 1, 1) + self.gridLayout_11.addWidget(self.frame_4, 7, 3, 1, 1) + self.remove_item = QtWidgets.QPushButton(self.equipment_tab) + self.remove_item.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.remove_item.sizePolicy().hasHeightForWidth()) + self.remove_item.setSizePolicy(sizePolicy) + self.remove_item.setMinimumSize(QtCore.QSize(50, 50)) + self.remove_item.setMaximumSize(QtCore.QSize(50, 50)) + self.remove_item.setText("") + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("icons/list-remove.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.remove_item.setIcon(icon1) + self.remove_item.setAutoDefault(False) + self.remove_item.setObjectName("remove_item") + self.gridLayout_11.addWidget(self.remove_item, 4, 2, 1, 1) + spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_11.addItem(spacerItem1, 1, 2, 1, 1) + self.edit = QtWidgets.QPushButton(self.equipment_tab) + self.edit.setMinimumSize(QtCore.QSize(50, 50)) + self.edit.setMaximumSize(QtCore.QSize(50, 50)) + self.edit.setText("") + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("icons/accessories-text-editor.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.edit.setIcon(icon2) + self.edit.setIconSize(QtCore.QSize(24, 24)) + self.edit.setObjectName("edit") + self.gridLayout_11.addWidget(self.edit, 5, 2, 1, 1) + self.add_item = QtWidgets.QPushButton(self.equipment_tab) + self.add_item.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.add_item.sizePolicy().hasHeightForWidth()) + self.add_item.setSizePolicy(sizePolicy) + self.add_item.setMinimumSize(QtCore.QSize(50, 50)) + self.add_item.setMaximumSize(QtCore.QSize(50, 50)) + self.add_item.setText("") + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap("icons/list-add.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.add_item.setIcon(icon3) + self.add_item.setAutoDefault(False) + self.add_item.setObjectName("add_item") + self.gridLayout_11.addWidget(self.add_item, 2, 2, 1, 1) + self.scrollArea_3 = QtWidgets.QScrollArea(self.equipment_tab) + self.scrollArea_3.setWidgetResizable(True) + self.scrollArea_3.setObjectName("scrollArea_3") + self.scrollAreaWidgetContents_3 = QtWidgets.QWidget() + self.scrollAreaWidgetContents_3.setGeometry(QtCore.QRect(0, 0, 298, 622)) + self.scrollAreaWidgetContents_3.setObjectName("scrollAreaWidgetContents_3") + self.gridLayout_4 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_3) + self.gridLayout_4.setObjectName("gridLayout_4") + self.current_flight_plan = QtWidgets.QListWidget(self.scrollAreaWidgetContents_3) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.current_flight_plan.sizePolicy().hasHeightForWidth()) + self.current_flight_plan.setSizePolicy(sizePolicy) + self.current_flight_plan.setMinimumSize(QtCore.QSize(0, 0)) + self.current_flight_plan.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.current_flight_plan.setObjectName("current_flight_plan") + self.gridLayout_4.addWidget(self.current_flight_plan, 5, 1, 1, 1) + self.current_airframes = QtWidgets.QListWidget(self.scrollAreaWidgetContents_3) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.current_airframes.sizePolicy().hasHeightForWidth()) + self.current_airframes.setSizePolicy(sizePolicy) + self.current_airframes.setMinimumSize(QtCore.QSize(0, 0)) + self.current_airframes.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.current_airframes.setObjectName("current_airframes") + self.gridLayout_4.addWidget(self.current_airframes, 1, 1, 1, 1) + self.current_telemetry = QtWidgets.QListWidget(self.scrollAreaWidgetContents_3) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.current_telemetry.sizePolicy().hasHeightForWidth()) + self.current_telemetry.setSizePolicy(sizePolicy) + self.current_telemetry.setMinimumSize(QtCore.QSize(0, 0)) + self.current_telemetry.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.current_telemetry.setObjectName("current_telemetry") + self.gridLayout_4.addWidget(self.current_telemetry, 11, 1, 1, 1) + self.current_flight_plan_label = QtWidgets.QLabel(self.scrollAreaWidgetContents_3) + self.current_flight_plan_label.setMinimumSize(QtCore.QSize(0, 20)) + self.current_flight_plan_label.setMaximumSize(QtCore.QSize(16777215, 20)) + self.current_flight_plan_label.setObjectName("current_flight_plan_label") + self.gridLayout_4.addWidget(self.current_flight_plan_label, 4, 1, 1, 1) + self.current_airframes_label = QtWidgets.QLabel(self.scrollAreaWidgetContents_3) + self.current_airframes_label.setMinimumSize(QtCore.QSize(0, 20)) + self.current_airframes_label.setMaximumSize(QtCore.QSize(16777215, 20)) + self.current_airframes_label.setObjectName("current_airframes_label") + self.gridLayout_4.addWidget(self.current_airframes_label, 0, 1, 1, 1) + self.current_settings = QtWidgets.QListWidget(self.scrollAreaWidgetContents_3) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.current_settings.sizePolicy().hasHeightForWidth()) + self.current_settings.setSizePolicy(sizePolicy) + self.current_settings.setMinimumSize(QtCore.QSize(0, 0)) + self.current_settings.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.current_settings.setObjectName("current_settings") + self.gridLayout_4.addWidget(self.current_settings, 3, 1, 1, 1) + self.current_radio_label = QtWidgets.QLabel(self.scrollAreaWidgetContents_3) + self.current_radio_label.setMinimumSize(QtCore.QSize(0, 20)) + self.current_radio_label.setMaximumSize(QtCore.QSize(16777215, 20)) + self.current_radio_label.setObjectName("current_radio_label") + self.gridLayout_4.addWidget(self.current_radio_label, 8, 1, 1, 1) + self.select_kml = QtWidgets.QPushButton(self.scrollAreaWidgetContents_3) + self.select_kml.setEnabled(False) + self.select_kml.setMinimumSize(QtCore.QSize(0, 24)) + self.select_kml.setObjectName("select_kml") + self.gridLayout_4.addWidget(self.select_kml, 7, 1, 1, 1) + self.current_radio = QtWidgets.QListWidget(self.scrollAreaWidgetContents_3) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.current_radio.sizePolicy().hasHeightForWidth()) + self.current_radio.setSizePolicy(sizePolicy) + self.current_radio.setMinimumSize(QtCore.QSize(0, 0)) + self.current_radio.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.current_radio.setObjectName("current_radio") + self.gridLayout_4.addWidget(self.current_radio, 9, 1, 1, 1) + self.open_gui = QtWidgets.QPushButton(self.scrollAreaWidgetContents_3) + self.open_gui.setEnabled(False) + self.open_gui.setMinimumSize(QtCore.QSize(0, 24)) + self.open_gui.setObjectName("open_gui") + self.gridLayout_4.addWidget(self.open_gui, 6, 1, 1, 1) + self.current_settings_label = QtWidgets.QLabel(self.scrollAreaWidgetContents_3) + self.current_settings_label.setMinimumSize(QtCore.QSize(0, 20)) + self.current_settings_label.setMaximumSize(QtCore.QSize(16777215, 20)) + self.current_settings_label.setObjectName("current_settings_label") + self.gridLayout_4.addWidget(self.current_settings_label, 2, 1, 1, 1) + self.current_telemetry_label = QtWidgets.QLabel(self.scrollAreaWidgetContents_3) + self.current_telemetry_label.setMinimumSize(QtCore.QSize(0, 20)) + self.current_telemetry_label.setMaximumSize(QtCore.QSize(16777215, 20)) + self.current_telemetry_label.setObjectName("current_telemetry_label") + self.gridLayout_4.addWidget(self.current_telemetry_label, 10, 1, 1, 1) + self.scrollArea_3.setWidget(self.scrollAreaWidgetContents_3) + self.gridLayout_11.addWidget(self.scrollArea_3, 0, 3, 7, 1) + self.search_item_icon = QtWidgets.QLabel(self.equipment_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_item_icon.sizePolicy().hasHeightForWidth()) + self.search_item_icon.setSizePolicy(sizePolicy) + self.search_item_icon.setMinimumSize(QtCore.QSize(0, 0)) + self.search_item_icon.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.search_item_icon.setToolTip("") + self.search_item_icon.setFrameShape(QtWidgets.QFrame.NoFrame) + self.search_item_icon.setText("") + self.search_item_icon.setPixmap(QtGui.QPixmap("icons/search_field.png")) + self.search_item_icon.setObjectName("search_item_icon") + self.gridLayout_11.addWidget(self.search_item_icon, 0, 0, 1, 1) + self.main_tab.addTab(self.equipment_tab, "") + self.build_flash_tab = QtWidgets.QWidget() + self.build_flash_tab.setObjectName("build_flash_tab") + self.gridLayout_3 = QtWidgets.QGridLayout(self.build_flash_tab) + self.gridLayout_3.setObjectName("gridLayout_3") + self.flash_resut_icon = QtWidgets.QLabel(self.build_flash_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.flash_resut_icon.sizePolicy().hasHeightForWidth()) + self.flash_resut_icon.setSizePolicy(sizePolicy) + self.flash_resut_icon.setMinimumSize(QtCore.QSize(30, 0)) + self.flash_resut_icon.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.flash_resut_icon.setToolTip("") + self.flash_resut_icon.setFrameShape(QtWidgets.QFrame.NoFrame) + self.flash_resut_icon.setText("") + self.flash_resut_icon.setObjectName("flash_resut_icon") + self.gridLayout_3.addWidget(self.flash_resut_icon, 7, 2, 1, 1) + self.flash_result = QtWidgets.QLabel(self.build_flash_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.flash_result.sizePolicy().hasHeightForWidth()) + self.flash_result.setSizePolicy(sizePolicy) + self.flash_result.setMinimumSize(QtCore.QSize(0, 20)) + self.flash_result.setMaximumSize(QtCore.QSize(16777215, 20)) + self.flash_result.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.flash_result.setText("") + self.flash_result.setObjectName("flash_result") + self.gridLayout_3.addWidget(self.flash_result, 7, 3, 1, 8) + self.info_nb = QtWidgets.QLabel(self.build_flash_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.info_nb.sizePolicy().hasHeightForWidth()) + self.info_nb.setSizePolicy(sizePolicy) + self.info_nb.setMinimumSize(QtCore.QSize(30, 20)) + self.info_nb.setMaximumSize(QtCore.QSize(30, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.info_nb.setFont(font) + self.info_nb.setStyleSheet("border:2px solid #00ff00;") + self.info_nb.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.info_nb.setText("") + self.info_nb.setObjectName("info_nb") + self.gridLayout_3.addWidget(self.info_nb, 11, 2, 1, 1) + self.label_25 = QtWidgets.QLabel(self.build_flash_tab) + font = QtGui.QFont() + font.setPointSize(14) + font.setBold(True) + font.setWeight(75) + self.label_25.setFont(font) + self.label_25.setObjectName("label_25") + self.gridLayout_3.addWidget(self.label_25, 5, 0, 1, 2) + self.label_10 = QtWidgets.QLabel(self.build_flash_tab) + self.label_10.setObjectName("label_10") + self.gridLayout_3.addWidget(self.label_10, 11, 8, 1, 1) + self.line_3 = QtWidgets.QFrame(self.build_flash_tab) + self.line_3.setFrameShape(QtWidgets.QFrame.HLine) + self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_3.setObjectName("line_3") + self.gridLayout_3.addWidget(self.line_3, 4, 0, 1, 12) + self.label_26 = QtWidgets.QLabel(self.build_flash_tab) + self.label_26.setMinimumSize(QtCore.QSize(0, 0)) + self.label_26.setMaximumSize(QtCore.QSize(60, 16777215)) + self.label_26.setObjectName("label_26") + self.gridLayout_3.addWidget(self.label_26, 1, 0, 1, 2) + self.label_11 = QtWidgets.QLabel(self.build_flash_tab) + self.label_11.setObjectName("label_11") + self.gridLayout_3.addWidget(self.label_11, 11, 5, 1, 1) + self.label_13 = QtWidgets.QLabel(self.build_flash_tab) + self.label_13.setMinimumSize(QtCore.QSize(0, 0)) + self.label_13.setMaximumSize(QtCore.QSize(60, 16777215)) + self.label_13.setObjectName("label_13") + self.gridLayout_3.addWidget(self.label_13, 7, 0, 1, 2) + self.label_28 = QtWidgets.QLabel(self.build_flash_tab) + self.label_28.setMinimumSize(QtCore.QSize(20, 20)) + self.label_28.setMaximumSize(QtCore.QSize(20, 20)) + self.label_28.setText("") + self.label_28.setPixmap(QtGui.QPixmap("icons/gtk-info.svg")) + self.label_28.setObjectName("label_28") + self.gridLayout_3.addWidget(self.label_28, 11, 0, 1, 1) + self.label_23 = QtWidgets.QLabel(self.build_flash_tab) + font = QtGui.QFont() + font.setPointSize(14) + font.setBold(True) + font.setWeight(75) + self.label_23.setFont(font) + self.label_23.setObjectName("label_23") + self.gridLayout_3.addWidget(self.label_23, 0, 0, 1, 2) + self.warnings_nb = QtWidgets.QLabel(self.build_flash_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.warnings_nb.sizePolicy().hasHeightForWidth()) + self.warnings_nb.setSizePolicy(sizePolicy) + self.warnings_nb.setMinimumSize(QtCore.QSize(30, 20)) + self.warnings_nb.setMaximumSize(QtCore.QSize(30, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.warnings_nb.setFont(font) + self.warnings_nb.setStyleSheet("border:2px solid #ffaa00;") + self.warnings_nb.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.warnings_nb.setText("") + self.warnings_nb.setObjectName("warnings_nb") + self.gridLayout_3.addWidget(self.warnings_nb, 11, 6, 1, 1) + self.label_9 = QtWidgets.QLabel(self.build_flash_tab) + self.label_9.setMinimumSize(QtCore.QSize(0, 30)) + self.label_9.setMaximumSize(QtCore.QSize(60, 16777215)) + self.label_9.setObjectName("label_9") + self.gridLayout_3.addWidget(self.label_9, 3, 0, 1, 2) + self.label_12 = QtWidgets.QLabel(self.build_flash_tab) + self.label_12.setMinimumSize(QtCore.QSize(0, 0)) + self.label_12.setMaximumSize(QtCore.QSize(60, 16777215)) + self.label_12.setObjectName("label_12") + self.gridLayout_3.addWidget(self.label_12, 6, 0, 1, 2) + spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_3.addItem(spacerItem2, 11, 10, 1, 1) + self.label_14 = QtWidgets.QLabel(self.build_flash_tab) + self.label_14.setObjectName("label_14") + self.gridLayout_3.addWidget(self.label_14, 11, 1, 1, 1) + self.clean = QtWidgets.QPushButton(self.build_flash_tab) + self.clean.setMinimumSize(QtCore.QSize(130, 30)) + self.clean.setMaximumSize(QtCore.QSize(130, 30)) + self.clean.setLayoutDirection(QtCore.Qt.LeftToRight) + icon4 = QtGui.QIcon() + icon4.addPixmap(QtGui.QPixmap("icons/edit-clear.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.clean.setIcon(icon4) + self.clean.setObjectName("clean") + self.gridLayout_3.addWidget(self.clean, 1, 11, 1, 1) + self.line_4 = QtWidgets.QFrame(self.build_flash_tab) + self.line_4.setFrameShape(QtWidgets.QFrame.HLine) + self.line_4.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_4.setObjectName("line_4") + self.gridLayout_3.addWidget(self.line_4, 9, 0, 1, 12) + self.label_15 = QtWidgets.QLabel(self.build_flash_tab) + self.label_15.setMinimumSize(QtCore.QSize(20, 20)) + self.label_15.setMaximumSize(QtCore.QSize(20, 20)) + self.label_15.setText("") + self.label_15.setPixmap(QtGui.QPixmap("icons/dialog-error.svg")) + self.label_15.setObjectName("label_15") + self.gridLayout_3.addWidget(self.label_15, 11, 7, 1, 1) + self.label_16 = QtWidgets.QLabel(self.build_flash_tab) + self.label_16.setMinimumSize(QtCore.QSize(20, 20)) + self.label_16.setMaximumSize(QtCore.QSize(20, 20)) + self.label_16.setText("") + self.label_16.setPixmap(QtGui.QPixmap("icons/dialog-warning.png")) + self.label_16.setObjectName("label_16") + self.gridLayout_3.addWidget(self.label_16, 11, 4, 1, 1) + self.target = QtWidgets.QComboBox(self.build_flash_tab) + self.target.setMinimumSize(QtCore.QSize(0, 30)) + self.target.setMaximumSize(QtCore.QSize(16777215, 30)) + self.target.setObjectName("target") + self.gridLayout_3.addWidget(self.target, 1, 2, 1, 8) + self.errors_nb = QtWidgets.QLabel(self.build_flash_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.errors_nb.sizePolicy().hasHeightForWidth()) + self.errors_nb.setSizePolicy(sizePolicy) + self.errors_nb.setMinimumSize(QtCore.QSize(30, 20)) + self.errors_nb.setMaximumSize(QtCore.QSize(30, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.errors_nb.setFont(font) + self.errors_nb.setAutoFillBackground(False) + self.errors_nb.setStyleSheet("border:2px solid #ff0000;") + self.errors_nb.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.errors_nb.setText("") + self.errors_nb.setObjectName("errors_nb") + self.gridLayout_3.addWidget(self.errors_nb, 11, 9, 1, 1) + self.device = QtWidgets.QComboBox(self.build_flash_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.device.sizePolicy().hasHeightForWidth()) + self.device.setSizePolicy(sizePolicy) + self.device.setMinimumSize(QtCore.QSize(0, 30)) + self.device.setMaximumSize(QtCore.QSize(16777215, 30)) + self.device.setObjectName("device") + self.gridLayout_3.addWidget(self.device, 6, 2, 1, 8) + self.show_console = QtWidgets.QPushButton(self.build_flash_tab) + self.show_console.setMinimumSize(QtCore.QSize(0, 30)) + self.show_console.setMaximumSize(QtCore.QSize(16777215, 20)) + self.show_console.setLayoutDirection(QtCore.Qt.LeftToRight) + icon5 = QtGui.QIcon() + icon5.addPixmap(QtGui.QPixmap("icons/utilities-terminal.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.show_console.setIcon(icon5) + self.show_console.setObjectName("show_console") + self.gridLayout_3.addWidget(self.show_console, 11, 11, 1, 1, QtCore.Qt.AlignVCenter) + self.build = QtWidgets.QPushButton(self.build_flash_tab) + self.build.setMinimumSize(QtCore.QSize(130, 30)) + self.build.setMaximumSize(QtCore.QSize(130, 30)) + self.build.setLayoutDirection(QtCore.Qt.LeftToRight) + self.build.setIcon(icon) + self.build.setObjectName("build") + self.gridLayout_3.addWidget(self.build, 2, 11, 1, 1) + spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_3.addItem(spacerItem3, 12, 11, 1, 1) + self.upload = QtWidgets.QPushButton(self.build_flash_tab) + self.upload.setEnabled(True) + self.upload.setMinimumSize(QtCore.QSize(130, 30)) + self.upload.setMaximumSize(QtCore.QSize(130, 30)) + icon6 = QtGui.QIcon() + icon6.addPixmap(QtGui.QPixmap("icons/go-up.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.upload.setIcon(icon6) + self.upload.setObjectName("upload") + self.gridLayout_3.addWidget(self.upload, 6, 11, 1, 1) + self.build_result = QtWidgets.QLabel(self.build_flash_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.build_result.sizePolicy().hasHeightForWidth()) + self.build_result.setSizePolicy(sizePolicy) + self.build_result.setMinimumSize(QtCore.QSize(0, 20)) + self.build_result.setMaximumSize(QtCore.QSize(16777215, 20)) + self.build_result.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.build_result.setText("") + self.build_result.setObjectName("build_result") + self.gridLayout_3.addWidget(self.build_result, 3, 3, 1, 8) + self.build_result_icon = QtWidgets.QLabel(self.build_flash_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.build_result_icon.sizePolicy().hasHeightForWidth()) + self.build_result_icon.setSizePolicy(sizePolicy) + self.build_result_icon.setMinimumSize(QtCore.QSize(0, 0)) + self.build_result_icon.setMaximumSize(QtCore.QSize(30, 16777215)) + self.build_result_icon.setToolTip("") + self.build_result_icon.setFrameShape(QtWidgets.QFrame.NoFrame) + self.build_result_icon.setText("") + self.build_result_icon.setObjectName("build_result_icon") + self.gridLayout_3.addWidget(self.build_result_icon, 3, 2, 1, 1) + self.main_tab.addTab(self.build_flash_tab, "") + self.sessions_tab = QtWidgets.QWidget() + self.sessions_tab.setObjectName("sessions_tab") + self.gridLayout_2 = QtWidgets.QGridLayout(self.sessions_tab) + self.gridLayout_2.setObjectName("gridLayout_2") + self.programs = QtWidgets.QListWidget(self.sessions_tab) + self.programs.setMinimumSize(QtCore.QSize(0, 0)) + self.programs.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.programs.setLayoutDirection(QtCore.Qt.LeftToRight) + self.programs.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + self.programs.setIconSize(QtCore.QSize(10, 10)) + self.programs.setObjectName("programs") + self.gridLayout_2.addWidget(self.programs, 1, 2, 3, 1) + self.label_8 = QtWidgets.QLabel(self.sessions_tab) + self.label_8.setObjectName("label_8") + self.gridLayout_2.addWidget(self.label_8, 1, 0, 1, 2) + spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_2.addItem(spacerItem4, 2, 3, 1, 1) + self.play_stop_program = QtWidgets.QPushButton(self.sessions_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.play_stop_program.sizePolicy().hasHeightForWidth()) + self.play_stop_program.setSizePolicy(sizePolicy) + self.play_stop_program.setMinimumSize(QtCore.QSize(0, 50)) + self.play_stop_program.setMaximumSize(QtCore.QSize(16777215, 50)) + icon7 = QtGui.QIcon() + icon7.addPixmap(QtGui.QPixmap("icons/media-playback-start.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.play_stop_program.setIcon(icon7) + self.play_stop_program.setAutoDefault(False) + self.play_stop_program.setObjectName("play_stop_program") + self.gridLayout_2.addWidget(self.play_stop_program, 1, 3, 1, 1) + self.label_7 = QtWidgets.QLabel(self.sessions_tab) + self.label_7.setObjectName("label_7") + self.gridLayout_2.addWidget(self.label_7, 4, 0, 1, 2) + self.start_all_button = QtWidgets.QPushButton(self.sessions_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.start_all_button.sizePolicy().hasHeightForWidth()) + self.start_all_button.setSizePolicy(sizePolicy) + self.start_all_button.setMinimumSize(QtCore.QSize(0, 50)) + self.start_all_button.setMaximumSize(QtCore.QSize(16777215, 50)) + font = QtGui.QFont() + font.setBold(True) + font.setItalic(False) + font.setUnderline(False) + font.setWeight(75) + font.setStrikeOut(False) + font.setKerning(True) + self.start_all_button.setFont(font) + self.start_all_button.setIcon(icon7) + self.start_all_button.setObjectName("start_all_button") + self.gridLayout_2.addWidget(self.start_all_button, 4, 3, 1, 1) + spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_2.addItem(spacerItem5, 6, 2, 1, 1) + self.kill_all_button = QtWidgets.QPushButton(self.sessions_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.kill_all_button.sizePolicy().hasHeightForWidth()) + self.kill_all_button.setSizePolicy(sizePolicy) + self.kill_all_button.setMinimumSize(QtCore.QSize(0, 50)) + self.kill_all_button.setMaximumSize(QtCore.QSize(16777215, 50)) + font = QtGui.QFont() + font.setBold(True) + font.setItalic(False) + font.setUnderline(False) + font.setWeight(75) + font.setStrikeOut(False) + font.setKerning(True) + self.kill_all_button.setFont(font) + self.kill_all_button.setLayoutDirection(QtCore.Qt.LeftToRight) + icon8 = QtGui.QIcon() + icon8.addPixmap(QtGui.QPixmap("icons/process-stop.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.kill_all_button.setIcon(icon8) + self.kill_all_button.setObjectName("kill_all_button") + self.gridLayout_2.addWidget(self.kill_all_button, 3, 3, 1, 1) + self.session = QtWidgets.QComboBox(self.sessions_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.session.sizePolicy().hasHeightForWidth()) + self.session.setSizePolicy(sizePolicy) + self.session.setObjectName("session") + self.gridLayout_2.addWidget(self.session, 0, 2, 1, 1) + self.options = QtWidgets.QListWidget(self.sessions_tab) + self.options.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.options.setObjectName("options") + self.gridLayout_2.addWidget(self.options, 4, 2, 2, 1) + self.label_4 = QtWidgets.QLabel(self.sessions_tab) + self.label_4.setObjectName("label_4") + self.gridLayout_2.addWidget(self.label_4, 0, 0, 1, 2) + self.remove_program = QtWidgets.QPushButton(self.sessions_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.remove_program.sizePolicy().hasHeightForWidth()) + self.remove_program.setSizePolicy(sizePolicy) + self.remove_program.setMinimumSize(QtCore.QSize(30, 30)) + self.remove_program.setMaximumSize(QtCore.QSize(30, 30)) + self.remove_program.setText("") + self.remove_program.setIcon(icon1) + self.remove_program.setAutoDefault(False) + self.remove_program.setObjectName("remove_program") + self.gridLayout_2.addWidget(self.remove_program, 3, 0, 1, 1) + self.add_program = QtWidgets.QPushButton(self.sessions_tab) + self.add_program.setEnabled(False) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.add_program.sizePolicy().hasHeightForWidth()) + self.add_program.setSizePolicy(sizePolicy) + self.add_program.setMinimumSize(QtCore.QSize(30, 30)) + self.add_program.setMaximumSize(QtCore.QSize(30, 30)) + self.add_program.setText("") + self.add_program.setIcon(icon3) + self.add_program.setAutoDefault(False) + self.add_program.setObjectName("add_program") + self.gridLayout_2.addWidget(self.add_program, 3, 1, 1, 1) + self.remove_option = QtWidgets.QPushButton(self.sessions_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.remove_option.sizePolicy().hasHeightForWidth()) + self.remove_option.setSizePolicy(sizePolicy) + self.remove_option.setMinimumSize(QtCore.QSize(30, 30)) + self.remove_option.setMaximumSize(QtCore.QSize(30, 30)) + self.remove_option.setText("") + self.remove_option.setIcon(icon1) + self.remove_option.setAutoDefault(False) + self.remove_option.setObjectName("remove_option") + self.gridLayout_2.addWidget(self.remove_option, 5, 0, 1, 1) + self.add_option = QtWidgets.QPushButton(self.sessions_tab) + self.add_option.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.add_option.sizePolicy().hasHeightForWidth()) + self.add_option.setSizePolicy(sizePolicy) + self.add_option.setMinimumSize(QtCore.QSize(30, 30)) + self.add_option.setMaximumSize(QtCore.QSize(30, 30)) + self.add_option.setText("") + self.add_option.setIcon(icon3) + self.add_option.setAutoDefault(False) + self.add_option.setObjectName("add_option") + self.gridLayout_2.addWidget(self.add_option, 5, 1, 1, 1) + self.main_tab.addTab(self.sessions_tab, "") + self.console_tab = QtWidgets.QWidget() + self.console_tab.setObjectName("console_tab") + self.gridLayout_5 = QtWidgets.QGridLayout(self.console_tab) + self.gridLayout_5.setObjectName("gridLayout_5") + self.line_7 = QtWidgets.QFrame(self.console_tab) + self.line_7.setFrameShape(QtWidgets.QFrame.HLine) + self.line_7.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_7.setObjectName("line_7") + self.gridLayout_5.addWidget(self.line_7, 10, 1, 1, 1) + self.console = QtWidgets.QTextEdit(self.console_tab) + self.console.setEnabled(True) + font = QtGui.QFont() + font.setFamily("Ubuntu") + self.console.setFont(font) + self.console.setLayoutDirection(QtCore.Qt.LeftToRight) + self.console.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.console.setTextInteractionFlags(QtCore.Qt.TextSelectableByKeyboard|QtCore.Qt.TextSelectableByMouse) + self.console.setObjectName("console") + self.gridLayout_5.addWidget(self.console, 0, 0, 13, 1) + self.label_24 = QtWidgets.QLabel(self.console_tab) + font = QtGui.QFont() + font.setPointSize(14) + font.setBold(True) + font.setWeight(75) + self.label_24.setFont(font) + self.label_24.setObjectName("label_24") + self.gridLayout_5.addWidget(self.label_24, 0, 1, 1, 1) + self.important = QtWidgets.QRadioButton(self.console_tab) + self.important.setToolTip("") + self.important.setChecked(False) + self.important.setObjectName("important") + self.gridLayout_5.addWidget(self.important, 2, 1, 1, 1) + self.custom = QtWidgets.QRadioButton(self.console_tab) + self.custom.setToolTip("") + self.custom.setChecked(True) + self.custom.setObjectName("custom") + self.gridLayout_5.addWidget(self.custom, 3, 1, 1, 1) + self.all = QtWidgets.QRadioButton(self.console_tab) + self.all.setToolTip("") + self.all.setChecked(False) + self.all.setObjectName("all") + self.gridLayout_5.addWidget(self.all, 4, 1, 1, 1) + self.clean_console = QtWidgets.QPushButton(self.console_tab) + self.clean_console.setIcon(icon4) + self.clean_console.setObjectName("clean_console") + self.gridLayout_5.addWidget(self.clean_console, 12, 1, 1, 1) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.display_info = QtWidgets.QCheckBox(self.console_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.display_info.sizePolicy().hasHeightForWidth()) + self.display_info.setSizePolicy(sizePolicy) + icon9 = QtGui.QIcon() + icon9.addPixmap(QtGui.QPixmap("icons/gtk-info.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.display_info.setIcon(icon9) + self.display_info.setObjectName("display_info") + self.horizontalLayout_2.addWidget(self.display_info) + self.gridLayout_5.addLayout(self.horizontalLayout_2, 7, 1, 1, 1) + self.horizontalLayout_4 = QtWidgets.QHBoxLayout() + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.display_warnings = QtWidgets.QCheckBox(self.console_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.display_warnings.sizePolicy().hasHeightForWidth()) + self.display_warnings.setSizePolicy(sizePolicy) + icon10 = QtGui.QIcon() + icon10.addPixmap(QtGui.QPixmap("icons/dialog-warning.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.display_warnings.setIcon(icon10) + self.display_warnings.setChecked(False) + self.display_warnings.setObjectName("display_warnings") + self.horizontalLayout_4.addWidget(self.display_warnings) + self.gridLayout_5.addLayout(self.horizontalLayout_4, 8, 1, 1, 1) + spacerItem6 = QtWidgets.QSpacerItem(20, 261, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_5.addItem(spacerItem6, 11, 1, 1, 1) + self.line_5 = QtWidgets.QFrame(self.console_tab) + self.line_5.setFrameShape(QtWidgets.QFrame.HLine) + self.line_5.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_5.setObjectName("line_5") + self.gridLayout_5.addWidget(self.line_5, 5, 1, 1, 1) + self.horizontalLayout_5 = QtWidgets.QHBoxLayout() + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.display_errors = QtWidgets.QCheckBox(self.console_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.display_errors.sizePolicy().hasHeightForWidth()) + self.display_errors.setSizePolicy(sizePolicy) + icon11 = QtGui.QIcon() + icon11.addPixmap(QtGui.QPixmap("icons/dialog-error.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.display_errors.setIcon(icon11) + self.display_errors.setChecked(False) + self.display_errors.setObjectName("display_errors") + self.horizontalLayout_5.addWidget(self.display_errors) + self.gridLayout_5.addLayout(self.horizontalLayout_5, 9, 1, 1, 1) + self.line_6 = QtWidgets.QFrame(self.console_tab) + self.line_6.setFrameShape(QtWidgets.QFrame.HLine) + self.line_6.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_6.setObjectName("line_6") + self.gridLayout_5.addWidget(self.line_6, 1, 1, 1, 1) + self.horizontalLayout_6 = QtWidgets.QHBoxLayout() + self.horizontalLayout_6.setObjectName("horizontalLayout_6") + self.display_default = QtWidgets.QCheckBox(self.console_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.display_default.sizePolicy().hasHeightForWidth()) + self.display_default.setSizePolicy(sizePolicy) + self.display_default.setObjectName("display_default") + self.horizontalLayout_6.addWidget(self.display_default) + self.gridLayout_5.addLayout(self.horizontalLayout_6, 6, 1, 1, 1) + self.main_tab.addTab(self.console_tab, "") + self.gridLayout_10.addWidget(self.main_tab, 1, 0, 1, 1) + self.frame = QtWidgets.QFrame(self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) + self.frame.setSizePolicy(sizePolicy) + self.frame.setMaximumSize(QtCore.QSize(16777215, 20)) + self.frame.setFrameShape(QtWidgets.QFrame.NoFrame) + self.frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame.setLineWidth(0) + self.frame.setObjectName("frame") + self.gridLayout_6 = QtWidgets.QGridLayout(self.frame) + self.gridLayout_6.setContentsMargins(-1, 0, -1, 0) + self.gridLayout_6.setVerticalSpacing(0) + self.gridLayout_6.setObjectName("gridLayout_6") + self.line_2 = QtWidgets.QFrame(self.frame) + self.line_2.setFrameShape(QtWidgets.QFrame.VLine) + self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_2.setObjectName("line_2") + self.gridLayout_6.addWidget(self.line_2, 0, 5, 1, 1) + self.label_20 = QtWidgets.QLabel(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_20.sizePolicy().hasHeightForWidth()) + self.label_20.setSizePolicy(sizePolicy) + self.label_20.setMinimumSize(QtCore.QSize(0, 20)) + self.label_20.setMaximumSize(QtCore.QSize(16777215, 20)) + self.label_20.setObjectName("label_20") + self.gridLayout_6.addWidget(self.label_20, 0, 0, 1, 1) + self.open_home_terminal = QtWidgets.QPushButton(self.frame) + self.open_home_terminal.setMinimumSize(QtCore.QSize(0, 20)) + self.open_home_terminal.setMaximumSize(QtCore.QSize(16777215, 20)) + self.open_home_terminal.setText("") + self.open_home_terminal.setDefault(False) + self.open_home_terminal.setFlat(False) + self.open_home_terminal.setObjectName("open_home_terminal") + self.gridLayout_6.addWidget(self.open_home_terminal, 0, 1, 1, 1) + self.run_version = QtWidgets.QLabel(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.run_version.sizePolicy().hasHeightForWidth()) + self.run_version.setSizePolicy(sizePolicy) + self.run_version.setMinimumSize(QtCore.QSize(0, 20)) + self.run_version.setMaximumSize(QtCore.QSize(16777215, 20)) + self.run_version.setFrameShape(QtWidgets.QFrame.NoFrame) + self.run_version.setText("") + self.run_version.setObjectName("run_version") + self.gridLayout_6.addWidget(self.run_version, 0, 7, 1, 1) + self.label_29 = QtWidgets.QLabel(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_29.sizePolicy().hasHeightForWidth()) + self.label_29.setSizePolicy(sizePolicy) + self.label_29.setMinimumSize(QtCore.QSize(0, 20)) + self.label_29.setMaximumSize(QtCore.QSize(16777215, 20)) + self.label_29.setObjectName("label_29") + self.gridLayout_6.addWidget(self.label_29, 0, 9, 1, 1) + self.build_version = QtWidgets.QLabel(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.build_version.sizePolicy().hasHeightForWidth()) + self.build_version.setSizePolicy(sizePolicy) + self.build_version.setMinimumSize(QtCore.QSize(0, 20)) + self.build_version.setMaximumSize(QtCore.QSize(16777215, 20)) + self.build_version.setFrameShape(QtWidgets.QFrame.NoFrame) + self.build_version.setText("") + self.build_version.setObjectName("build_version") + self.gridLayout_6.addWidget(self.build_version, 0, 10, 1, 1) + self.label_22 = QtWidgets.QLabel(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_22.sizePolicy().hasHeightForWidth()) + self.label_22.setSizePolicy(sizePolicy) + self.label_22.setMinimumSize(QtCore.QSize(0, 20)) + self.label_22.setMaximumSize(QtCore.QSize(16777215, 20)) + self.label_22.setObjectName("label_22") + self.gridLayout_6.addWidget(self.label_22, 0, 6, 1, 1) + self.switch_mode = QtWidgets.QPushButton(self.frame) + self.switch_mode.setEnabled(False) + self.switch_mode.setMinimumSize(QtCore.QSize(0, 20)) + self.switch_mode.setMaximumSize(QtCore.QSize(16777215, 20)) + self.switch_mode.setText("") + self.switch_mode.setFlat(False) + self.switch_mode.setObjectName("switch_mode") + self.gridLayout_6.addWidget(self.switch_mode, 0, 4, 1, 1) + self.line_8 = QtWidgets.QFrame(self.frame) + self.line_8.setFrameShape(QtWidgets.QFrame.VLine) + self.line_8.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_8.setObjectName("line_8") + self.gridLayout_6.addWidget(self.line_8, 0, 8, 1, 1) + self.line = QtWidgets.QFrame(self.frame) + self.line.setFrameShape(QtWidgets.QFrame.VLine) + self.line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line.setObjectName("line") + self.gridLayout_6.addWidget(self.line, 0, 2, 1, 1) + self.label_21 = QtWidgets.QLabel(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_21.sizePolicy().hasHeightForWidth()) + self.label_21.setSizePolicy(sizePolicy) + self.label_21.setMinimumSize(QtCore.QSize(0, 20)) + self.label_21.setMaximumSize(QtCore.QSize(16777215, 20)) + self.label_21.setObjectName("label_21") + self.gridLayout_6.addWidget(self.label_21, 0, 3, 1, 1) + spacerItem7 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_6.addItem(spacerItem7, 0, 11, 1, 1) + self.gridLayout_10.addWidget(self.frame, 2, 0, 1, 2) + self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget) + self.stackedWidget.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.stackedWidget.sizePolicy().hasHeightForWidth()) + self.stackedWidget.setSizePolicy(sizePolicy) + self.stackedWidget.setMinimumSize(QtCore.QSize(200, 0)) + self.stackedWidget.setMaximumSize(QtCore.QSize(200, 16777215)) + self.stackedWidget.setToolTip("") + self.stackedWidget.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.stackedWidget.setFrameShadow(QtWidgets.QFrame.Plain) + self.stackedWidget.setObjectName("stackedWidget") + self.widget_2 = QtWidgets.QWidget() + self.widget_2.setObjectName("widget_2") + self.gridLayout_7 = QtWidgets.QGridLayout(self.widget_2) + self.gridLayout_7.setObjectName("gridLayout_7") + self.quick_restart = QtWidgets.QPushButton(self.widget_2) + self.quick_restart.setEnabled(True) + font = QtGui.QFont() + font.setBold(True) + font.setItalic(False) + font.setUnderline(False) + font.setWeight(75) + font.setStrikeOut(False) + font.setKerning(True) + self.quick_restart.setFont(font) + self.quick_restart.setIcon(icon7) + self.quick_restart.setObjectName("quick_restart") + self.gridLayout_7.addWidget(self.quick_restart, 6, 0, 1, 1) + self.label_33 = QtWidgets.QLabel(self.widget_2) + self.label_33.setObjectName("label_33") + self.gridLayout_7.addWidget(self.label_33, 3, 0, 1, 1) + self.programs_overview_1 = QtWidgets.QListWidget(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.programs_overview_1.sizePolicy().hasHeightForWidth()) + self.programs_overview_1.setSizePolicy(sizePolicy) + self.programs_overview_1.setMinimumSize(QtCore.QSize(0, 0)) + self.programs_overview_1.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.programs_overview_1.setObjectName("programs_overview_1") + self.gridLayout_7.addWidget(self.programs_overview_1, 4, 0, 1, 1) + self.label_32 = QtWidgets.QLabel(self.widget_2) + self.label_32.setObjectName("label_32") + self.gridLayout_7.addWidget(self.label_32, 1, 0, 1, 1) + self.session_overview_1 = QtWidgets.QComboBox(self.widget_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.session_overview_1.sizePolicy().hasHeightForWidth()) + self.session_overview_1.setSizePolicy(sizePolicy) + self.session_overview_1.setObjectName("session_overview_1") + self.gridLayout_7.addWidget(self.session_overview_1, 2, 0, 1, 1) + self.label_6 = QtWidgets.QLabel(self.widget_2) + self.label_6.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.label_6.setTextFormat(QtCore.Qt.AutoText) + self.label_6.setScaledContents(False) + self.label_6.setWordWrap(False) + self.label_6.setObjectName("label_6") + self.gridLayout_7.addWidget(self.label_6, 0, 0, 1, 1) + spacerItem8 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem8, 10, 0, 1, 1) + self.quick_kill = QtWidgets.QPushButton(self.widget_2) + self.quick_kill.setEnabled(True) + font = QtGui.QFont() + font.setBold(True) + font.setItalic(False) + font.setUnderline(False) + font.setWeight(75) + font.setStrikeOut(False) + font.setKerning(True) + self.quick_kill.setFont(font) + self.quick_kill.setIcon(icon8) + self.quick_kill.setObjectName("quick_kill") + self.gridLayout_7.addWidget(self.quick_kill, 5, 0, 1, 1) + self.stackedWidget.addWidget(self.widget_2) + self.area = QtWidgets.QWidget() + self.area.setObjectName("area") + self.gridLayout_8 = QtWidgets.QGridLayout(self.area) + self.gridLayout_8.setObjectName("gridLayout_8") + self.scrollArea = QtWidgets.QScrollArea(self.area) + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents = QtWidgets.QWidget() + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 148, 598)) + self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") + self.gridLayout_13 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents) + self.gridLayout_13.setObjectName("gridLayout_13") + self.label_5 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_5.setMinimumSize(QtCore.QSize(0, 30)) + self.label_5.setMaximumSize(QtCore.QSize(16777215, 30)) + self.label_5.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.label_5.setTextFormat(QtCore.Qt.AutoText) + self.label_5.setScaledContents(False) + self.label_5.setObjectName("label_5") + self.gridLayout_13.addWidget(self.label_5, 0, 0, 1, 1) + self.label_35 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_35.setMinimumSize(QtCore.QSize(0, 20)) + self.label_35.setMaximumSize(QtCore.QSize(16777215, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_35.setFont(font) + self.label_35.setObjectName("label_35") + self.gridLayout_13.addWidget(self.label_35, 1, 0, 1, 1) + self.airframes_overview = QtWidgets.QListWidget(self.scrollAreaWidgetContents) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.airframes_overview.sizePolicy().hasHeightForWidth()) + self.airframes_overview.setSizePolicy(sizePolicy) + self.airframes_overview.setMinimumSize(QtCore.QSize(0, 0)) + self.airframes_overview.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.airframes_overview.setToolTip("") + self.airframes_overview.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.airframes_overview.setAutoScroll(True) + self.airframes_overview.setTextElideMode(QtCore.Qt.ElideLeft) + self.airframes_overview.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerItem) + self.airframes_overview.setResizeMode(QtWidgets.QListView.Adjust) + self.airframes_overview.setLayoutMode(QtWidgets.QListView.SinglePass) + self.airframes_overview.setObjectName("airframes_overview") + self.gridLayout_13.addWidget(self.airframes_overview, 2, 0, 1, 1) + self.label_34 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_34.setMinimumSize(QtCore.QSize(0, 20)) + self.label_34.setMaximumSize(QtCore.QSize(16777215, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_34.setFont(font) + self.label_34.setObjectName("label_34") + self.gridLayout_13.addWidget(self.label_34, 3, 0, 1, 1) + self.settings_overview = QtWidgets.QListWidget(self.scrollAreaWidgetContents) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.settings_overview.sizePolicy().hasHeightForWidth()) + self.settings_overview.setSizePolicy(sizePolicy) + self.settings_overview.setMinimumSize(QtCore.QSize(0, 0)) + self.settings_overview.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.settings_overview.setToolTip("") + self.settings_overview.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.settings_overview.setAutoScroll(True) + self.settings_overview.setTextElideMode(QtCore.Qt.ElideLeft) + self.settings_overview.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerItem) + self.settings_overview.setResizeMode(QtWidgets.QListView.Adjust) + self.settings_overview.setLayoutMode(QtWidgets.QListView.SinglePass) + self.settings_overview.setObjectName("settings_overview") + self.gridLayout_13.addWidget(self.settings_overview, 4, 0, 1, 1) + self.label_36 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_36.setMinimumSize(QtCore.QSize(0, 20)) + self.label_36.setMaximumSize(QtCore.QSize(16777215, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_36.setFont(font) + self.label_36.setObjectName("label_36") + self.gridLayout_13.addWidget(self.label_36, 5, 0, 1, 1) + self.flight_plan_overview = QtWidgets.QListWidget(self.scrollAreaWidgetContents) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.flight_plan_overview.sizePolicy().hasHeightForWidth()) + self.flight_plan_overview.setSizePolicy(sizePolicy) + self.flight_plan_overview.setMinimumSize(QtCore.QSize(0, 0)) + self.flight_plan_overview.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.flight_plan_overview.setToolTip("") + self.flight_plan_overview.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.flight_plan_overview.setAutoScroll(True) + self.flight_plan_overview.setTextElideMode(QtCore.Qt.ElideLeft) + self.flight_plan_overview.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerItem) + self.flight_plan_overview.setResizeMode(QtWidgets.QListView.Adjust) + self.flight_plan_overview.setLayoutMode(QtWidgets.QListView.SinglePass) + self.flight_plan_overview.setObjectName("flight_plan_overview") + self.gridLayout_13.addWidget(self.flight_plan_overview, 6, 0, 1, 1) + self.label_37 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_37.setMinimumSize(QtCore.QSize(0, 20)) + self.label_37.setMaximumSize(QtCore.QSize(16777215, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_37.setFont(font) + self.label_37.setObjectName("label_37") + self.gridLayout_13.addWidget(self.label_37, 7, 0, 1, 1) + self.radio_overview = QtWidgets.QListWidget(self.scrollAreaWidgetContents) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.radio_overview.sizePolicy().hasHeightForWidth()) + self.radio_overview.setSizePolicy(sizePolicy) + self.radio_overview.setMinimumSize(QtCore.QSize(0, 0)) + self.radio_overview.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.radio_overview.setToolTip("") + self.radio_overview.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.radio_overview.setAutoScroll(True) + self.radio_overview.setTextElideMode(QtCore.Qt.ElideLeft) + self.radio_overview.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerItem) + self.radio_overview.setResizeMode(QtWidgets.QListView.Adjust) + self.radio_overview.setLayoutMode(QtWidgets.QListView.SinglePass) + self.radio_overview.setObjectName("radio_overview") + self.gridLayout_13.addWidget(self.radio_overview, 8, 0, 1, 1) + self.label_38 = QtWidgets.QLabel(self.scrollAreaWidgetContents) + self.label_38.setMinimumSize(QtCore.QSize(0, 20)) + self.label_38.setMaximumSize(QtCore.QSize(16777215, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_38.setFont(font) + self.label_38.setObjectName("label_38") + self.gridLayout_13.addWidget(self.label_38, 9, 0, 1, 1) + self.telemetry_overview = QtWidgets.QListWidget(self.scrollAreaWidgetContents) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.telemetry_overview.sizePolicy().hasHeightForWidth()) + self.telemetry_overview.setSizePolicy(sizePolicy) + self.telemetry_overview.setMinimumSize(QtCore.QSize(0, 0)) + self.telemetry_overview.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.telemetry_overview.setToolTip("") + self.telemetry_overview.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.telemetry_overview.setAutoScroll(True) + self.telemetry_overview.setTextElideMode(QtCore.Qt.ElideLeft) + self.telemetry_overview.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerItem) + self.telemetry_overview.setResizeMode(QtWidgets.QListView.Adjust) + self.telemetry_overview.setLayoutMode(QtWidgets.QListView.SinglePass) + self.telemetry_overview.setObjectName("telemetry_overview") + self.gridLayout_13.addWidget(self.telemetry_overview, 10, 0, 1, 1) + self.scrollArea.setWidget(self.scrollAreaWidgetContents) + self.gridLayout_8.addWidget(self.scrollArea, 0, 0, 1, 1) + self.stackedWidget.addWidget(self.area) + self.area_2 = QtWidgets.QWidget() + self.area_2.setObjectName("area_2") + self.gridLayout_12 = QtWidgets.QGridLayout(self.area_2) + self.gridLayout_12.setObjectName("gridLayout_12") + self.scrollArea_2 = QtWidgets.QScrollArea(self.area_2) + self.scrollArea_2.setWidgetResizable(True) + self.scrollArea_2.setObjectName("scrollArea_2") + self.scrollAreaWidgetContents_2 = QtWidgets.QWidget() + self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 148, 598)) + self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2") + self.gridLayout_15 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_2) + self.gridLayout_15.setObjectName("gridLayout_15") + self.label_18 = QtWidgets.QLabel(self.scrollAreaWidgetContents_2) + self.label_18.setMinimumSize(QtCore.QSize(0, 30)) + self.label_18.setMaximumSize(QtCore.QSize(16777215, 30)) + self.label_18.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.label_18.setTextFormat(QtCore.Qt.AutoText) + self.label_18.setScaledContents(False) + self.label_18.setObjectName("label_18") + self.gridLayout_15.addWidget(self.label_18, 0, 0, 1, 1) + self.label_39 = QtWidgets.QLabel(self.scrollAreaWidgetContents_2) + self.label_39.setMinimumSize(QtCore.QSize(0, 20)) + self.label_39.setMaximumSize(QtCore.QSize(16777215, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_39.setFont(font) + self.label_39.setObjectName("label_39") + self.gridLayout_15.addWidget(self.label_39, 1, 0, 1, 1) + self.airframes_overview_2 = QtWidgets.QListWidget(self.scrollAreaWidgetContents_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.airframes_overview_2.sizePolicy().hasHeightForWidth()) + self.airframes_overview_2.setSizePolicy(sizePolicy) + self.airframes_overview_2.setMinimumSize(QtCore.QSize(0, 0)) + self.airframes_overview_2.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.airframes_overview_2.setToolTip("") + self.airframes_overview_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.airframes_overview_2.setTextElideMode(QtCore.Qt.ElideLeft) + self.airframes_overview_2.setObjectName("airframes_overview_2") + self.gridLayout_15.addWidget(self.airframes_overview_2, 2, 0, 1, 1) + self.label_40 = QtWidgets.QLabel(self.scrollAreaWidgetContents_2) + self.label_40.setMinimumSize(QtCore.QSize(0, 20)) + self.label_40.setMaximumSize(QtCore.QSize(16777215, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_40.setFont(font) + self.label_40.setObjectName("label_40") + self.gridLayout_15.addWidget(self.label_40, 3, 0, 1, 1) + self.settings_overview_2 = QtWidgets.QListWidget(self.scrollAreaWidgetContents_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.settings_overview_2.sizePolicy().hasHeightForWidth()) + self.settings_overview_2.setSizePolicy(sizePolicy) + self.settings_overview_2.setMinimumSize(QtCore.QSize(0, 0)) + self.settings_overview_2.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.settings_overview_2.setToolTip("") + self.settings_overview_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.settings_overview_2.setTextElideMode(QtCore.Qt.ElideLeft) + self.settings_overview_2.setObjectName("settings_overview_2") + self.gridLayout_15.addWidget(self.settings_overview_2, 4, 0, 1, 1) + self.label_41 = QtWidgets.QLabel(self.scrollAreaWidgetContents_2) + self.label_41.setMinimumSize(QtCore.QSize(0, 20)) + self.label_41.setMaximumSize(QtCore.QSize(16777215, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_41.setFont(font) + self.label_41.setObjectName("label_41") + self.gridLayout_15.addWidget(self.label_41, 5, 0, 1, 1) + self.flight_plan_overview_2 = QtWidgets.QListWidget(self.scrollAreaWidgetContents_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.flight_plan_overview_2.sizePolicy().hasHeightForWidth()) + self.flight_plan_overview_2.setSizePolicy(sizePolicy) + self.flight_plan_overview_2.setMinimumSize(QtCore.QSize(0, 0)) + self.flight_plan_overview_2.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.flight_plan_overview_2.setToolTip("") + self.flight_plan_overview_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.flight_plan_overview_2.setTextElideMode(QtCore.Qt.ElideLeft) + self.flight_plan_overview_2.setObjectName("flight_plan_overview_2") + self.gridLayout_15.addWidget(self.flight_plan_overview_2, 6, 0, 1, 1) + self.label_42 = QtWidgets.QLabel(self.scrollAreaWidgetContents_2) + self.label_42.setMinimumSize(QtCore.QSize(0, 20)) + self.label_42.setMaximumSize(QtCore.QSize(16777215, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_42.setFont(font) + self.label_42.setObjectName("label_42") + self.gridLayout_15.addWidget(self.label_42, 7, 0, 1, 1) + self.radio_overview_2 = QtWidgets.QListWidget(self.scrollAreaWidgetContents_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.radio_overview_2.sizePolicy().hasHeightForWidth()) + self.radio_overview_2.setSizePolicy(sizePolicy) + self.radio_overview_2.setMinimumSize(QtCore.QSize(0, 0)) + self.radio_overview_2.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.radio_overview_2.setToolTip("") + self.radio_overview_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.radio_overview_2.setTextElideMode(QtCore.Qt.ElideLeft) + self.radio_overview_2.setObjectName("radio_overview_2") + self.gridLayout_15.addWidget(self.radio_overview_2, 8, 0, 1, 1) + self.label_43 = QtWidgets.QLabel(self.scrollAreaWidgetContents_2) + self.label_43.setMinimumSize(QtCore.QSize(0, 20)) + self.label_43.setMaximumSize(QtCore.QSize(16777215, 20)) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_43.setFont(font) + self.label_43.setObjectName("label_43") + self.gridLayout_15.addWidget(self.label_43, 9, 0, 1, 1) + self.telemetry_overview_2 = QtWidgets.QListWidget(self.scrollAreaWidgetContents_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.telemetry_overview_2.sizePolicy().hasHeightForWidth()) + self.telemetry_overview_2.setSizePolicy(sizePolicy) + self.telemetry_overview_2.setMinimumSize(QtCore.QSize(0, 0)) + self.telemetry_overview_2.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.telemetry_overview_2.setToolTip("") + self.telemetry_overview_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.telemetry_overview_2.setTextElideMode(QtCore.Qt.ElideLeft) + self.telemetry_overview_2.setObjectName("telemetry_overview_2") + self.gridLayout_15.addWidget(self.telemetry_overview_2, 10, 0, 1, 1) + self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2) + self.gridLayout_12.addWidget(self.scrollArea_2, 0, 0, 1, 1) + self.stackedWidget.addWidget(self.area_2) + self.widget = QtWidgets.QWidget() + self.widget.setObjectName("widget") + self.gridLayout_14 = QtWidgets.QGridLayout(self.widget) + self.gridLayout_14.setObjectName("gridLayout_14") + self.label_17 = QtWidgets.QLabel(self.widget) + self.label_17.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.label_17.setTextFormat(QtCore.Qt.AutoText) + self.label_17.setScaledContents(False) + self.label_17.setWordWrap(False) + self.label_17.setObjectName("label_17") + self.gridLayout_14.addWidget(self.label_17, 0, 0, 1, 1) + self.label_45 = QtWidgets.QLabel(self.widget) + self.label_45.setObjectName("label_45") + self.gridLayout_14.addWidget(self.label_45, 1, 0, 1, 1) + self.session_overview_2 = QtWidgets.QComboBox(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.session_overview_2.sizePolicy().hasHeightForWidth()) + self.session_overview_2.setSizePolicy(sizePolicy) + self.session_overview_2.setObjectName("session_overview_2") + self.gridLayout_14.addWidget(self.session_overview_2, 2, 0, 1, 1) + self.label_46 = QtWidgets.QLabel(self.widget) + self.label_46.setObjectName("label_46") + self.gridLayout_14.addWidget(self.label_46, 3, 0, 1, 1) + self.programs_overview_2 = QtWidgets.QListWidget(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.programs_overview_2.sizePolicy().hasHeightForWidth()) + self.programs_overview_2.setSizePolicy(sizePolicy) + self.programs_overview_2.setMinimumSize(QtCore.QSize(0, 0)) + self.programs_overview_2.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.programs_overview_2.setObjectName("programs_overview_2") + self.gridLayout_14.addWidget(self.programs_overview_2, 4, 0, 1, 1) + self.quick_kill_2 = QtWidgets.QPushButton(self.widget) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.quick_kill_2.setFont(font) + self.quick_kill_2.setIcon(icon8) + self.quick_kill_2.setObjectName("quick_kill_2") + self.gridLayout_14.addWidget(self.quick_kill_2, 5, 0, 1, 1) + self.quick_restart_3 = QtWidgets.QPushButton(self.widget) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.quick_restart_3.setFont(font) + self.quick_restart_3.setIcon(icon7) + self.quick_restart_3.setObjectName("quick_restart_3") + self.gridLayout_14.addWidget(self.quick_restart_3, 6, 0, 1, 1) + spacerItem9 = QtWidgets.QSpacerItem(20, 94, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_14.addItem(spacerItem9, 7, 0, 1, 1) + self.stackedWidget.addWidget(self.widget) + self.gridLayout_10.addWidget(self.stackedWidget, 0, 1, 2, 1) + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 744, 25)) + self.menubar.setObjectName("menubar") + self.menuSet = QtWidgets.QMenu(self.menubar) + self.menuSet.setObjectName("menuSet") + self.menuConfiguration = QtWidgets.QMenu(self.menubar) + self.menuConfiguration.setObjectName("menuConfiguration") + self.menuMenu = QtWidgets.QMenu(self.menubar) + self.menuMenu.setObjectName("menuMenu") + self.menuTools = QtWidgets.QMenu(self.menubar) + self.menuTools.setObjectName("menuTools") + self.menuView = QtWidgets.QMenu(self.menubar) + self.menuView.setObjectName("menuView") + self.menuHelp = QtWidgets.QMenu(self.menubar) + self.menuHelp.setObjectName("menuHelp") + self.menuSession = QtWidgets.QMenu(self.menubar) + self.menuSession.setObjectName("menuSession") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + self.actionNew = QtWidgets.QAction(MainWindow) + self.actionNew.setEnabled(False) + self.actionNew.setObjectName("actionNew") + self.actionOpen = QtWidgets.QAction(MainWindow) + self.actionOpen.setEnabled(False) + self.actionOpen.setObjectName("actionOpen") + self.actionSave = QtWidgets.QAction(MainWindow) + self.actionSave.setEnabled(False) + self.actionSave.setObjectName("actionSave") + self.actionRemove = QtWidgets.QAction(MainWindow) + self.actionRemove.setEnabled(False) + self.actionRemove.setObjectName("actionRemove") + self.actionSetManager = QtWidgets.QAction(MainWindow) + self.actionSetManager.setObjectName("actionSetManager") + self.actionNew_2 = QtWidgets.QAction(MainWindow) + self.actionNew_2.setEnabled(False) + self.actionNew_2.setObjectName("actionNew_2") + self.actionOpen_2 = QtWidgets.QAction(MainWindow) + self.actionOpen_2.setEnabled(False) + self.actionOpen_2.setObjectName("actionOpen_2") + self.actionSave_2 = QtWidgets.QAction(MainWindow) + self.actionSave_2.setObjectName("actionSave_2") + self.actionRemove_2 = QtWidgets.QAction(MainWindow) + self.actionRemove_2.setEnabled(False) + self.actionRemove_2.setObjectName("actionRemove_2") + self.actionMode = QtWidgets.QAction(MainWindow) + self.actionMode.setEnabled(False) + self.actionMode.setObjectName("actionMode") + self.actionStandard = QtWidgets.QAction(MainWindow) + self.actionStandard.setCheckable(False) + self.actionStandard.setChecked(False) + self.actionStandard.setEnabled(False) + self.actionStandard.setIconVisibleInMenu(False) + self.actionStandard.setObjectName("actionStandard") + self.actionDeveloper = QtWidgets.QAction(MainWindow) + self.actionDeveloper.setEnabled(False) + self.actionDeveloper.setObjectName("actionDeveloper") + self.actionSettings = QtWidgets.QAction(MainWindow) + self.actionSettings.setEnabled(False) + self.actionSettings.setObjectName("actionSettings") + self.actionQuit_Paparazzi_UAV = QtWidgets.QAction(MainWindow) + self.actionQuit_Paparazzi_UAV.setObjectName("actionQuit_Paparazzi_UAV") + self.actionFull_screen = QtWidgets.QAction(MainWindow) + self.actionFull_screen.setObjectName("actionFull_screen") + self.actionTutorial = QtWidgets.QAction(MainWindow) + self.actionTutorial.setEnabled(True) + self.actionTutorial.setObjectName("actionTutorial") + self.actionAbout_Paparazzi_UAV = QtWidgets.QAction(MainWindow) + self.actionAbout_Paparazzi_UAV.setEnabled(True) + self.actionAbout_Paparazzi_UAV.setObjectName("actionAbout_Paparazzi_UAV") + self.actionNew_empty_session = QtWidgets.QAction(MainWindow) + self.actionNew_empty_session.setEnabled(False) + self.actionNew_empty_session.setObjectName("actionNew_empty_session") + self.actionOpen_3 = QtWidgets.QAction(MainWindow) + self.actionOpen_3.setEnabled(False) + self.actionOpen_3.setObjectName("actionOpen_3") + self.actionSave_3 = QtWidgets.QAction(MainWindow) + self.actionSave_3.setObjectName("actionSave_3") + self.actionRemove_3 = QtWidgets.QAction(MainWindow) + self.actionRemove_3.setEnabled(False) + self.actionRemove_3.setObjectName("actionRemove_3") + self.actionUndo = QtWidgets.QAction(MainWindow) + self.actionUndo.setEnabled(False) + self.actionUndo.setObjectName("actionUndo") + self.actionRedo = QtWidgets.QAction(MainWindow) + self.actionRedo.setEnabled(False) + self.actionRedo.setObjectName("actionRedo") + self.actionSimulator = QtWidgets.QAction(MainWindow) + self.actionSimulator.setObjectName("actionSimulator") + self.actionGCS = QtWidgets.QAction(MainWindow) + self.actionGCS.setObjectName("actionGCS") + self.actionMeteo = QtWidgets.QAction(MainWindow) + self.actionMeteo.setObjectName("actionMeteo") + self.actionServer = QtWidgets.QAction(MainWindow) + self.actionServer.setObjectName("actionServer") + self.actionMessages = QtWidgets.QAction(MainWindow) + self.actionMessages.setObjectName("actionMessages") + self.action = QtWidgets.QAction(MainWindow) + self.action.setObjectName("action") + self.actionWILL_BE_AVAILABLE_LATER = QtWidgets.QAction(MainWindow) + self.actionWILL_BE_AVAILABLE_LATER.setEnabled(False) + self.actionWILL_BE_AVAILABLE_LATER.setObjectName("actionWILL_BE_AVAILABLE_LATER") + self.actionSet_default_cache_at_current_state = QtWidgets.QAction(MainWindow) + self.actionSet_default_cache_at_current_state.setObjectName("actionSet_default_cache_at_current_state") + self.actionRestore_default_state = QtWidgets.QAction(MainWindow) + self.actionRestore_default_state.setObjectName("actionRestore_default_state") + self.menuSet.addAction(self.actionSetManager) + self.menuConfiguration.addAction(self.actionNew_2) + self.menuConfiguration.addAction(self.actionOpen_2) + self.menuConfiguration.addAction(self.actionSave_2) + self.menuConfiguration.addAction(self.actionRemove_2) + self.menuMenu.addAction(self.actionMode) + self.menuMenu.addAction(self.actionStandard) + self.menuMenu.addAction(self.actionDeveloper) + self.menuMenu.addSeparator() + self.menuMenu.addAction(self.actionSettings) + self.menuMenu.addSeparator() + self.menuMenu.addAction(self.actionQuit_Paparazzi_UAV) + self.menuView.addAction(self.actionFull_screen) + self.menuHelp.addAction(self.actionTutorial) + self.menuHelp.addAction(self.actionAbout_Paparazzi_UAV) + self.menuSession.addAction(self.actionNew_empty_session) + self.menuSession.addAction(self.actionOpen_3) + self.menuSession.addAction(self.actionSave_3) + self.menuSession.addAction(self.actionRemove_3) + self.menubar.addAction(self.menuMenu.menuAction()) + self.menubar.addAction(self.menuSet.menuAction()) + self.menubar.addAction(self.menuConfiguration.menuAction()) + self.menubar.addAction(self.menuSession.menuAction()) + self.menubar.addAction(self.menuTools.menuAction()) + self.menubar.addAction(self.menuView.menuAction()) + self.menubar.addAction(self.menuHelp.menuAction()) + + self.retranslateUi(MainWindow) + self.main_tab.setCurrentIndex(0) + self.stackedWidget.setCurrentIndex(0) + self.main_tab.currentChanged['int'].connect(self.stackedWidget.setCurrentIndex) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "Paparazzi Center")) + self.label.setText(_translate("MainWindow", "Set :")) + self.current_configuration.setToolTip(_translate("MainWindow", "Current configuration")) + self.current_color.setToolTip(_translate("MainWindow", "Current configuration color")) + self.label_2.setText(_translate("MainWindow", "Configuration :")) + self.label_3.setText(_translate("MainWindow", "ID :")) + self.current_id.setToolTip(_translate("MainWindow", "Current configuration ID")) + self.label_27.setText(_translate("MainWindow", "Color :")) + self.current_set.setToolTip(_translate("MainWindow", "Current set of configurations")) + self.search_item.setToolTip(_translate("MainWindow", "Search an XML")) + self.search_item.setText(_translate("MainWindow", "COMING SOON")) + self.all_items_tree.setToolTip(_translate("MainWindow", "Select an XML file")) + self.all_items_tree.headerItem().setText(0, _translate("MainWindow", "COMING SOON")) + self.quick_target.setToolTip(_translate("MainWindow", "Select a target to build quickly")) + self.label_19.setText(_translate("MainWindow", "Quick build :")) + self.quick_build.setToolTip(_translate("MainWindow", "Clean & build quickly the current configuration")) + self.quick_build.setText(_translate("MainWindow", "Clean && build")) + self.remove_item.setToolTip(_translate("MainWindow", "Remove the XML file selected from the current configuration")) + self.edit.setToolTip(_translate("MainWindow", "Edit the selected XML file")) + self.add_item.setToolTip(_translate("MainWindow", "Add the selected XML file to the current configuration")) + self.scrollArea_3.setToolTip(_translate("MainWindow", "Current configuration")) + self.current_flight_plan.setToolTip(_translate("MainWindow", "Current flight plan (click to edit by GUI)")) + self.current_airframes.setToolTip(_translate("MainWindow", "Current airframes")) + self.current_telemetry.setToolTip(_translate("MainWindow", "Current telemetry")) + self.current_flight_plan_label.setText(_translate("MainWindow", "Flight plan :")) + self.current_airframes_label.setText(_translate("MainWindow", "Airframes :")) + self.current_settings.setToolTip(_translate("MainWindow", "Current settings")) + self.current_radio_label.setText(_translate("MainWindow", "Radio :")) + self.select_kml.setToolTip(_translate("MainWindow", "Change the flight plan by KML to XML tool")) + self.select_kml.setText(_translate("MainWindow", "Select KML (Maps)")) + self.current_radio.setToolTip(_translate("MainWindow", "Current radio")) + self.open_gui.setToolTip(_translate("MainWindow", "Change the flight plan by GCS")) + self.open_gui.setText(_translate("MainWindow", "Open a GUI (GCS)")) + self.current_settings_label.setText(_translate("MainWindow", "Settings :")) + self.current_telemetry_label.setText(_translate("MainWindow", "Telemetry :")) + self.main_tab.setTabText(self.main_tab.indexOf(self.equipment_tab), _translate("MainWindow", "Equipment")) + self.main_tab.setTabToolTip(self.main_tab.indexOf(self.equipment_tab), _translate("MainWindow", "Tab to manage the equipments of the current configuration")) + self.flash_result.setToolTip(_translate("MainWindow", "Flash result")) + self.info_nb.setToolTip(_translate("MainWindow", "Info found during build")) + self.label_25.setText(_translate("MainWindow", "Flash")) + self.label_10.setText(_translate("MainWindow", "Errors :")) + self.label_26.setText(_translate("MainWindow", "Target :")) + self.label_11.setText(_translate("MainWindow", "Warnings :")) + self.label_13.setText(_translate("MainWindow", "Result :")) + self.label_23.setText(_translate("MainWindow", "Build")) + self.warnings_nb.setToolTip(_translate("MainWindow", "Warnings found during build")) + self.label_9.setText(_translate("MainWindow", "Result :")) + self.label_12.setText(_translate("MainWindow", "Device :")) + self.label_14.setText(_translate("MainWindow", "Info :")) + self.clean.setToolTip(_translate("MainWindow", "Clean the current configuration")) + self.clean.setText(_translate("MainWindow", "Clean")) + self.target.setToolTip(_translate("MainWindow", "Current target")) + self.errors_nb.setToolTip(_translate("MainWindow", "Errors found during build")) + self.device.setToolTip(_translate("MainWindow", "Current device")) + self.show_console.setToolTip(_translate("MainWindow", "Switch to the console tab")) + self.show_console.setText(_translate("MainWindow", "Show console")) + self.build.setToolTip(_translate("MainWindow", "Build the current configuration")) + self.build.setText(_translate("MainWindow", "Build")) + self.upload.setToolTip(_translate("MainWindow", "Flash the current device")) + self.upload.setText(_translate("MainWindow", "Upload")) + self.build_result.setToolTip(_translate("MainWindow", "Clean & build result")) + self.main_tab.setTabText(self.main_tab.indexOf(self.build_flash_tab), _translate("MainWindow", "Build / Flash")) + self.main_tab.setTabToolTip(self.main_tab.indexOf(self.build_flash_tab), _translate("MainWindow", "Tab to build and flash the current configuration")) + self.programs.setToolTip(_translate("MainWindow", "Programs running (green) and stopped (red)")) + self.label_8.setText(_translate("MainWindow", "Programs :")) + self.play_stop_program.setToolTip(_translate("MainWindow", "Start / stop the selected program")) + self.play_stop_program.setText(_translate("MainWindow", "Start")) + self.label_7.setText(_translate("MainWindow", "Options :")) + self.start_all_button.setToolTip(_translate("MainWindow", "Kill & restart all programs")) + self.start_all_button.setText(_translate("MainWindow", "Start all")) + self.kill_all_button.setToolTip(_translate("MainWindow", "Kill all running programs")) + self.kill_all_button.setText(_translate("MainWindow", "Kill all")) + self.session.setToolTip(_translate("MainWindow", "Current session")) + self.options.setToolTip(_translate("MainWindow", "Options of the selected program")) + self.label_4.setText(_translate("MainWindow", "Session :")) + self.remove_program.setToolTip(_translate("MainWindow", "Remove a programs from the session")) + self.add_program.setToolTip(_translate("MainWindow", "Add a programs to the session")) + self.remove_option.setToolTip(_translate("MainWindow", "Remove the selected option")) + self.add_option.setToolTip(_translate("MainWindow", "Add an option to the selected program")) + self.main_tab.setTabText(self.main_tab.indexOf(self.sessions_tab), _translate("MainWindow", "Run session")) + self.main_tab.setTabToolTip(self.main_tab.indexOf(self.sessions_tab), _translate("MainWindow", "Tab to launch and customize the current session")) + self.console.setToolTip(_translate("MainWindow", "Console to display the logs")) + self.console.setHtml(_translate("MainWindow", "\n" +"\n" +"


")) + self.label_24.setText(_translate("MainWindow", "Logs")) + self.important.setText(_translate("MainWindow", "Important only")) + self.custom.setText(_translate("MainWindow", "Customized")) + self.all.setText(_translate("MainWindow", "All")) + self.clean_console.setToolTip(_translate("MainWindow", "Clean the console (all logs will be deleted !)")) + self.clean_console.setText(_translate("MainWindow", "Clean console")) + self.display_info.setText(_translate("MainWindow", "Info")) + self.display_warnings.setText(_translate("MainWindow", "Warnings")) + self.display_errors.setText(_translate("MainWindow", "Errors")) + self.display_default.setText(_translate("MainWindow", "Default (no flag)")) + self.main_tab.setTabText(self.main_tab.indexOf(self.console_tab), _translate("MainWindow", "Console")) + self.main_tab.setTabToolTip(self.main_tab.indexOf(self.console_tab), _translate("MainWindow", "Tab where informations are displayed in a real customizable console")) + self.label_20.setText(_translate("MainWindow", "HOME = ")) + self.open_home_terminal.setToolTip(_translate("MainWindow", "Home directory of Paparazzi UAV (click to open a terminal here)")) + self.run_version.setToolTip(_translate("MainWindow", "Current version of Paparazzi UAV")) + self.label_29.setText(_translate("MainWindow", "BUILD_VERSION = ")) + self.build_version.setToolTip(_translate("MainWindow", "Current version of Paparazzi UAV")) + self.label_22.setText(_translate("MainWindow", "RUN_VERSION = ")) + self.switch_mode.setToolTip(_translate("MainWindow", "Current mode (click to switch the mode)")) + self.label_21.setText(_translate("MainWindow", "MODE = ")) + self.quick_restart.setToolTip(_translate("MainWindow", "Kill & restart all programs")) + self.quick_restart.setText(_translate("MainWindow", "Start all")) + self.label_33.setText(_translate("MainWindow", "Processus :")) + self.programs_overview_1.setToolTip(_translate("MainWindow", "Programs running (green) and stopped (red)")) + self.label_32.setText(_translate("MainWindow", "Session :")) + self.session_overview_1.setToolTip(_translate("MainWindow", "Current session")) + self.label_6.setText(_translate("MainWindow", "Session overview")) + self.quick_kill.setToolTip(_translate("MainWindow", "Kill all running programs")) + self.quick_kill.setText(_translate("MainWindow", "Kill all")) + self.label_5.setText(_translate("MainWindow", "Settings overview")) + self.label_35.setText(_translate("MainWindow", "Airframes")) + self.label_34.setText(_translate("MainWindow", "Settings")) + self.label_36.setText(_translate("MainWindow", "Flight plan")) + self.label_37.setText(_translate("MainWindow", "Radio")) + self.label_38.setText(_translate("MainWindow", "Telemetry")) + self.label_18.setText(_translate("MainWindow", "Settings overview")) + self.label_39.setText(_translate("MainWindow", "Airframes")) + self.label_40.setText(_translate("MainWindow", "Settings")) + self.label_41.setText(_translate("MainWindow", "Flight plan")) + self.label_42.setText(_translate("MainWindow", "Radio")) + self.label_43.setText(_translate("MainWindow", "Telemetry")) + self.label_17.setText(_translate("MainWindow", "Session overview")) + self.label_45.setText(_translate("MainWindow", "Session :")) + self.session_overview_2.setToolTip(_translate("MainWindow", "Current session")) + self.label_46.setText(_translate("MainWindow", "Processus :")) + self.programs_overview_2.setToolTip(_translate("MainWindow", "Programs running (green) and stopped (red)")) + self.quick_kill_2.setToolTip(_translate("MainWindow", "Kill all running programs")) + self.quick_kill_2.setText(_translate("MainWindow", "Kill all")) + self.quick_restart_3.setToolTip(_translate("MainWindow", "Kill & restart all programs")) + self.quick_restart_3.setText(_translate("MainWindow", "Start all")) + self.menuSet.setTitle(_translate("MainWindow", "Set")) + self.menuConfiguration.setTitle(_translate("MainWindow", "Configuration")) + self.menuMenu.setTitle(_translate("MainWindow", "Menu")) + self.menuTools.setTitle(_translate("MainWindow", "Tools")) + self.menuView.setTitle(_translate("MainWindow", "View")) + self.menuHelp.setTitle(_translate("MainWindow", "Help")) + self.menuSession.setTitle(_translate("MainWindow", "Session")) + self.actionNew.setText(_translate("MainWindow", "New empty set")) + self.actionNew.setShortcut(_translate("MainWindow", "Ctrl+Shift+N")) + self.actionOpen.setText(_translate("MainWindow", "Save set as...")) + self.actionSave.setText(_translate("MainWindow", "Save")) + self.actionRemove.setText(_translate("MainWindow", "Remove")) + self.actionSetManager.setText(_translate("MainWindow", "Set manager... (COMING SOON)")) + self.actionSetManager.setShortcut(_translate("MainWindow", "Ctrl+Shift+M")) + self.actionNew_2.setText(_translate("MainWindow", "New empty configuration")) + self.actionNew_2.setShortcut(_translate("MainWindow", "Ctrl+N")) + self.actionOpen_2.setText(_translate("MainWindow", "Save configuration as...")) + self.actionOpen_2.setShortcut(_translate("MainWindow", "Ctrl+S")) + self.actionSave_2.setText(_translate("MainWindow", "Save")) + self.actionRemove_2.setText(_translate("MainWindow", "Remove")) + self.actionMode.setText(_translate("MainWindow", "Active mode :")) + self.actionStandard.setText(_translate("MainWindow", "Standard")) + self.actionStandard.setToolTip(_translate("MainWindow", "Switch to standard user mode")) + self.actionDeveloper.setText(_translate("MainWindow", "Developer")) + self.actionDeveloper.setToolTip(_translate("MainWindow", "Switch to developer mode")) + self.actionSettings.setText(_translate("MainWindow", "Settings...")) + self.actionSettings.setToolTip(_translate("MainWindow", "Change global settings of the Paparazzi Center")) + self.actionQuit_Paparazzi_UAV.setText(_translate("MainWindow", "Quit Paparazzi UAV")) + self.actionQuit_Paparazzi_UAV.setShortcut(_translate("MainWindow", "Ctrl+Q")) + self.actionFull_screen.setText(_translate("MainWindow", "Full screen")) + self.actionFull_screen.setShortcut(_translate("MainWindow", "F11")) + self.actionTutorial.setText(_translate("MainWindow", "Tutorials...")) + self.actionTutorial.setShortcut(_translate("MainWindow", "Ctrl+H")) + self.actionAbout_Paparazzi_UAV.setText(_translate("MainWindow", "Credits...")) + self.actionNew_empty_session.setText(_translate("MainWindow", "New empty session")) + self.actionNew_empty_session.setShortcut(_translate("MainWindow", "Ctrl+Shift+N, Ctrl+Shift+N")) + self.actionOpen_3.setText(_translate("MainWindow", "Save session as...")) + self.actionOpen_3.setShortcut(_translate("MainWindow", "Ctrl+Shift+S")) + self.actionSave_3.setText(_translate("MainWindow", "Save")) + self.actionRemove_3.setText(_translate("MainWindow", "Remove")) + self.actionUndo.setText(_translate("MainWindow", "Undo")) + self.actionUndo.setToolTip(_translate("MainWindow", "Undo the last action")) + self.actionUndo.setShortcut(_translate("MainWindow", "Ctrl+Z")) + self.actionRedo.setText(_translate("MainWindow", "Redo")) + self.actionRedo.setToolTip(_translate("MainWindow", "Redo the last action")) + self.actionRedo.setShortcut(_translate("MainWindow", "Ctrl+Y")) + self.actionSimulator.setText(_translate("MainWindow", "Simulator")) + self.actionGCS.setText(_translate("MainWindow", "GCS")) + self.actionMeteo.setText(_translate("MainWindow", "Meteo")) + self.actionServer.setText(_translate("MainWindow", "Server")) + self.actionMessages.setText(_translate("MainWindow", "Messages")) + self.action.setText(_translate("MainWindow", "...")) + self.actionWILL_BE_AVAILABLE_LATER.setText(_translate("MainWindow", "WILL BE AVAILABLE LATER :")) + self.actionSet_default_cache_at_current_state.setText(_translate("MainWindow", "Set default app state to current")) + self.actionRestore_default_state.setText(_translate("MainWindow", "Restore default app state")) + diff --git a/sw/supervision/python/ui/main_window.ui b/sw/supervision/python/ui/main_window.ui new file mode 100644 index 0000000000..728ad997a1 --- /dev/null +++ b/sw/supervision/python/ui/main_window.ui @@ -0,0 +1,3829 @@ + + + MainWindow + + + + 0 + 0 + 744 + 481 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Paparazzi Center + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 16777215 + 40 + + + + false + + + + + + QFrame::StyledPanel + + + QFrame::Plain + + + + + + + + + Set : + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + Current configuration + + + + + + + true + + + + 0 + 0 + + + + + 30 + 0 + + + + + 40 + 16777215 + + + + false + + + Qt::NoFocus + + + Current configuration color + + + true + + + + + + QFrame::Box + + + 1 + + + + + + Qt::AlignCenter + + + + + + + + + + Configuration : + + + + + + + + + + ID : + + + + + + + true + + + + 0 + 0 + + + + + 30 + 0 + + + + + 40 + 16777215 + + + + + 75 + true + + + + false + + + Qt::NoFocus + + + Current configuration ID + + + true + + + + + + QFrame::Box + + + 1 + + + + + + Qt::AlignCenter + + + + + + + + + + Color : + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + Current set of configurations + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + Qt::LeftToRight + + + QTabWidget::North + + + QTabWidget::Triangular + + + 0 + + + Qt::ElideNone + + + true + + + false + + + false + + + + Equipment + + + Tab to manage the equipments of the current configuration + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Search an XML + + + COMING SOON + + + + + + + false + + + Select an XML file + + + + COMING SOON + + + + + + + + + 0 + 40 + + + + + 16777215 + 40 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Select a target to build quickly + + + + + + + + 16777215 + 20 + + + + Quick build : + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Clean & build quickly the current configuration + + + Qt::LeftToRight + + + Clean && build + + + + icons/system-run.pngicons/system-run.png + + + + + + + + + + true + + + + 0 + 0 + + + + + 50 + 50 + + + + + 50 + 50 + + + + Remove the XML file selected from the current configuration + + + + + + + icons/list-remove.svgicons/list-remove.svg + + + false + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 50 + 50 + + + + + 50 + 50 + + + + Edit the selected XML file + + + + + + + icons/accessories-text-editor.svgicons/accessories-text-editor.svg + + + + 24 + 24 + + + + + + + + true + + + + 0 + 0 + + + + + 50 + 50 + + + + + 50 + 50 + + + + Add the selected XML file to the current configuration + + + + + + + icons/list-add.svgicons/list-add.svg + + + false + + + + + + + Current configuration + + + true + + + + + 0 + 0 + 298 + 622 + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Current flight plan (click to edit by GUI) + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Current airframes + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Current telemetry + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Flight plan : + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Airframes : + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Current settings + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Radio : + + + + + + + false + + + + 0 + 24 + + + + Change the flight plan by KML to XML tool + + + Select KML (Maps) + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Current radio + + + + + + + false + + + + 0 + 24 + + + + Change the flight plan by GCS + + + Open a GUI (GCS) + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Settings : + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Telemetry : + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::NoFrame + + + + + + icons/search_field.png + + + + + + + + Build / Flash + + + Tab to build and flash the current configuration + + + + + + + 0 + 0 + + + + + 30 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::NoFrame + + + + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Flash result + + + QFrame::StyledPanel + + + + + + + + + + + 0 + 0 + + + + + 30 + 20 + + + + + 30 + 20 + + + + + 75 + true + + + + Info found during build + + + border:2px solid #00ff00; + + + QFrame::StyledPanel + + + + + + + + + + + 14 + 75 + true + + + + Flash + + + + + + + Errors : + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 60 + 16777215 + + + + Target : + + + + + + + Warnings : + + + + + + + + 0 + 0 + + + + + 60 + 16777215 + + + + Result : + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + icons/gtk-info.svg + + + + + + + + 14 + 75 + true + + + + Build + + + + + + + + 0 + 0 + + + + + 30 + 20 + + + + + 30 + 20 + + + + + 75 + true + + + + Warnings found during build + + + border:2px solid #ffaa00; + + + QFrame::StyledPanel + + + + + + + + + + + 0 + 30 + + + + + 60 + 16777215 + + + + Result : + + + + + + + + 0 + 0 + + + + + 60 + 16777215 + + + + Device : + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Info : + + + + + + + + 130 + 30 + + + + + 130 + 30 + + + + Clean the current configuration + + + Qt::LeftToRight + + + Clean + + + + icons/edit-clear.svgicons/edit-clear.svg + + + + + + + Qt::Horizontal + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + icons/dialog-error.svg + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + icons/dialog-warning.png + + + + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + Current target + + + + + + + + 0 + 0 + + + + + 30 + 20 + + + + + 30 + 20 + + + + + 75 + true + + + + Errors found during build + + + false + + + border:2px solid #ff0000; + + + QFrame::StyledPanel + + + + + + + + + + + 0 + 0 + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + Current device + + + + + + + + 0 + 30 + + + + + 16777215 + 20 + + + + Switch to the console tab + + + Qt::LeftToRight + + + Show console + + + + icons/utilities-terminal.svgicons/utilities-terminal.svg + + + + + + + + 130 + 30 + + + + + 130 + 30 + + + + Build the current configuration + + + Qt::LeftToRight + + + Build + + + + icons/system-run.pngicons/system-run.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + + 130 + 30 + + + + + 130 + 30 + + + + Flash the current device + + + Upload + + + + icons/go-up.svgicons/go-up.svg + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Clean & build result + + + QFrame::StyledPanel + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 30 + 16777215 + + + + + + + QFrame::NoFrame + + + + + + + + + + + Run session + + + Tab to launch and customize the current session + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Programs running (green) and stopped (red) + + + Qt::LeftToRight + + + QAbstractItemView::SelectRows + + + + 10 + 10 + + + + + + + + Programs : + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 0 + 50 + + + + + 16777215 + 50 + + + + Start / stop the selected program + + + Start + + + + icons/media-playback-start.svgicons/media-playback-start.svg + + + false + + + + + + + Options : + + + + + + + + 0 + 0 + + + + + 0 + 50 + + + + + 16777215 + 50 + + + + + 75 + false + true + false + false + true + + + + Kill & restart all programs + + + Start all + + + + icons/media-playback-start.svgicons/media-playback-start.svg + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 0 + 50 + + + + + 16777215 + 50 + + + + + 75 + false + true + false + false + true + + + + Kill all running programs + + + Qt::LeftToRight + + + Kill all + + + + icons/process-stop.svgicons/process-stop.svg + + + + + + + + 0 + 0 + + + + Current session + + + + + + + + 16777215 + 16777215 + + + + Options of the selected program + + + + + + + Session : + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + Remove a programs from the session + + + + + + + icons/list-remove.svgicons/list-remove.svg + + + false + + + + + + + false + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + Add a programs to the session + + + + + + + icons/list-add.svgicons/list-add.svg + + + false + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + Remove the selected option + + + + + + + icons/list-remove.svgicons/list-remove.svg + + + false + + + + + + + true + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + Add an option to the selected program + + + + + + + icons/list-add.svgicons/list-add.svg + + + false + + + + + + + + Console + + + Tab where informations are displayed in a real customizable console + + + + + + Qt::Horizontal + + + + + + + true + + + + Ubuntu + + + + Console to display the logs + + + Qt::LeftToRight + + + Qt::ScrollBarAsNeeded + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 14 + 75 + true + + + + Logs + + + + + + + + + + Important only + + + false + + + + + + + + + + Customized + + + true + + + + + + + + + + All + + + false + + + + + + + Clean the console (all logs will be deleted !) + + + Clean console + + + + icons/edit-clear.svgicons/edit-clear.svg + + + + + + + + + + 0 + 0 + + + + Info + + + + icons/gtk-info.svgicons/gtk-info.svg + + + + + + + + + + + + 0 + 0 + + + + Warnings + + + + icons/dialog-warning.pngicons/dialog-warning.png + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 261 + + + + + + + + Qt::Horizontal + + + + + + + + + + 0 + 0 + + + + Errors + + + + icons/dialog-error.svgicons/dialog-error.svg + + + false + + + + + + + + + Qt::Horizontal + + + + + + + + + + 0 + 0 + + + + Default (no flag) + + + + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 20 + + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + HOME = + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Home directory of Paparazzi UAV (click to open a terminal here) + + + + + + false + + + false + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Current version of Paparazzi UAV + + + QFrame::NoFrame + + + + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + BUILD_VERSION = + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Current version of Paparazzi UAV + + + QFrame::NoFrame + + + + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + RUN_VERSION = + + + + + + + false + + + + 0 + 20 + + + + + 16777215 + 20 + + + + Current mode (click to switch the mode) + + + + + + false + + + + + + + Qt::Vertical + + + + + + + Qt::Vertical + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + MODE = + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + true + + + + 0 + 0 + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + + + + QFrame::StyledPanel + + + QFrame::Plain + + + 0 + + + + + + + true + + + + 75 + false + true + false + false + true + + + + Kill & restart all programs + + + Start all + + + + icons/media-playback-start.svgicons/media-playback-start.svg + + + + + + + Processus : + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Programs running (green) and stopped (red) + + + + + + + Session : + + + + + + + + 0 + 0 + + + + Current session + + + + + + + QFrame::StyledPanel + + + Session overview + + + Qt::AutoText + + + false + + + false + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + + 75 + false + true + false + false + true + + + + Kill all running programs + + + Kill all + + + + icons/process-stop.svgicons/process-stop.svg + + + + + + + + + + + true + + + + + 0 + 0 + 148 + 598 + + + + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + QFrame::StyledPanel + + + Settings overview + + + Qt::AutoText + + + false + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + + 75 + true + + + + Airframes + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::StyledPanel + + + true + + + Qt::ElideLeft + + + QAbstractItemView::ScrollPerItem + + + QListView::Adjust + + + QListView::SinglePass + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + + 75 + true + + + + Settings + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::StyledPanel + + + true + + + Qt::ElideLeft + + + QAbstractItemView::ScrollPerItem + + + QListView::Adjust + + + QListView::SinglePass + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + + 75 + true + + + + Flight plan + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::StyledPanel + + + true + + + Qt::ElideLeft + + + QAbstractItemView::ScrollPerItem + + + QListView::Adjust + + + QListView::SinglePass + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + + 75 + true + + + + Radio + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::StyledPanel + + + true + + + Qt::ElideLeft + + + QAbstractItemView::ScrollPerItem + + + QListView::Adjust + + + QListView::SinglePass + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + + 75 + true + + + + Telemetry + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::StyledPanel + + + true + + + Qt::ElideLeft + + + QAbstractItemView::ScrollPerItem + + + QListView::Adjust + + + QListView::SinglePass + + + + + + + + + + + + + + + true + + + + + 0 + 0 + 148 + 598 + + + + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + QFrame::StyledPanel + + + Settings overview + + + Qt::AutoText + + + false + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + + 75 + true + + + + Airframes + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::StyledPanel + + + Qt::ElideLeft + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + + 75 + true + + + + Settings + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::StyledPanel + + + Qt::ElideLeft + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + + 75 + true + + + + Flight plan + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::StyledPanel + + + Qt::ElideLeft + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + + 75 + true + + + + Radio + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::StyledPanel + + + Qt::ElideLeft + + + + + + + + 0 + 20 + + + + + 16777215 + 20 + + + + + 75 + true + + + + Telemetry + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + QFrame::StyledPanel + + + Qt::ElideLeft + + + + + + + + + + + + + + + QFrame::StyledPanel + + + Session overview + + + Qt::AutoText + + + false + + + false + + + + + + + Session : + + + + + + + + 0 + 0 + + + + Current session + + + + + + + Processus : + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Programs running (green) and stopped (red) + + + + + + + + 75 + true + + + + Kill all running programs + + + Kill all + + + + icons/process-stop.svgicons/process-stop.svg + + + + + + + + 75 + true + + + + Kill & restart all programs + + + Start all + + + + icons/media-playback-start.svgicons/media-playback-start.svg + + + + + + + Qt::Vertical + + + + 20 + 94 + + + + + + + + + + + + + + 0 + 0 + 744 + 25 + + + + + Set + + + + + + Configuration + + + + + + + + + Menu + + + + + + + + + + + + Tools + + + + + View + + + + + + Help + + + + + + + Session + + + + + + + + + + + + + + + + + + false + + + New empty set + + + Ctrl+Shift+N + + + + + false + + + Save set as... + + + + + false + + + Save + + + + + false + + + Remove + + + + + Set manager... (COMING SOON) + + + Ctrl+Shift+M + + + + + false + + + New empty configuration + + + Ctrl+N + + + + + false + + + Save configuration as... + + + Ctrl+S + + + + + Save + + + + + false + + + Remove + + + + + false + + + Active mode : + + + + + false + + + false + + + false + + + Standard + + + Switch to standard user mode + + + false + + + + + false + + + Developer + + + Switch to developer mode + + + + + false + + + Settings... + + + Change global settings of the Paparazzi Center + + + + + Quit Paparazzi UAV + + + Ctrl+Q + + + + + Full screen + + + F11 + + + + + true + + + Tutorials... + + + Ctrl+H + + + + + true + + + Credits... + + + + + false + + + New empty session + + + Ctrl+Shift+N, Ctrl+Shift+N + + + + + false + + + Save session as... + + + Ctrl+Shift+S + + + + + Save + + + + + false + + + Remove + + + + + false + + + Undo + + + Undo the last action + + + Ctrl+Z + + + + + false + + + Redo + + + Redo the last action + + + Ctrl+Y + + + + + Simulator + + + + + GCS + + + + + Meteo + + + + + Server + + + + + Messages + + + + + ... + + + + + false + + + WILL BE AVAILABLE LATER : + + + + + Set default app state to current + + + + + Restore default app state + + + + + + + main_tab + currentChanged(int) + stackedWidget + setCurrentIndex(int) + + + 130 + 88 + + + 690 + 126 + + + + + diff --git a/sw/supervision/python/ui/popup.py b/sw/supervision/python/ui/popup.py new file mode 100644 index 0000000000..66e119393a --- /dev/null +++ b/sw/supervision/python/ui/popup.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'ui/popup.ui' +# +# Created: Mon Jun 20 16:13:37 2016 +# by: PyQt5 UI code generator 5.2.1 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(500, 350) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth()) + Dialog.setSizePolicy(sizePolicy) + self.gridLayout = QtWidgets.QGridLayout(Dialog) + self.gridLayout.setObjectName("gridLayout") + self.textBrowser = QtWidgets.QTextBrowser(Dialog) + self.textBrowser.setFrameShape(QtWidgets.QFrame.NoFrame) + self.textBrowser.setObjectName("textBrowser") + self.gridLayout.addWidget(self.textBrowser, 0, 0, 1, 1) + self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.buttonBox.sizePolicy().hasHeightForWidth()) + self.buttonBox.setSizePolicy(sizePolicy) + self.buttonBox.setMaximumSize(QtCore.QSize(16777215, 30)) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.NoButton) + self.buttonBox.setObjectName("buttonBox") + self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 1) + + self.retranslateUi(Dialog) + self.buttonBox.rejected.connect(Dialog.reject) + self.buttonBox.accepted.connect(Dialog.accept) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Dialog")) + diff --git a/sw/supervision/python/ui/popup.ui b/sw/supervision/python/ui/popup.ui new file mode 100644 index 0000000000..7b515cc852 --- /dev/null +++ b/sw/supervision/python/ui/popup.ui @@ -0,0 +1,86 @@ + + + Dialog + + + + 0 + 0 + 500 + 350 + + + + + 0 + 0 + + + + Dialog + + + + + + QFrame::NoFrame + + + + + + + + 0 + 0 + + + + + 16777215 + 30 + + + + QDialogButtonBox::NoButton + + + + + + + + + buttonBox + rejected() + Dialog + reject() + + + 217 + 181 + + + 217 + 211 + + + + + buttonBox + accepted() + Dialog + accept() + + + 97 + 174 + + + 99 + 203 + + + + + diff --git a/sw/supervision/python/ui/set_manager.py b/sw/supervision/python/ui/set_manager.py new file mode 100644 index 0000000000..ddbe24e1b7 --- /dev/null +++ b/sw/supervision/python/ui/set_manager.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'set_manager.ui' +# +# Created: Wed Mar 9 08:54:50 2016 +# by: PyQt5 UI code generator 5.2.1 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(400, 300) + self.gridLayout_2 = QtWidgets.QGridLayout(Dialog) + self.gridLayout_2.setObjectName("gridLayout_2") + self.verticalLayout_3 = QtWidgets.QVBoxLayout() + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.label_2 = QtWidgets.QLabel(Dialog) + self.label_2.setObjectName("label_2") + self.horizontalLayout.addWidget(self.label_2) + self.sets_combo = QtWidgets.QComboBox(Dialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.sets_combo.sizePolicy().hasHeightForWidth()) + self.sets_combo.setSizePolicy(sizePolicy) + self.sets_combo.setMinimumSize(QtCore.QSize(0, 40)) + self.sets_combo.setMaximumSize(QtCore.QSize(16777215, 40)) + self.sets_combo.setObjectName("sets_combo") + self.sets_combo.addItem("") + self.sets_combo.addItem("") + self.sets_combo.addItem("") + self.horizontalLayout.addWidget(self.sets_combo) + self.verticalLayout_3.addLayout(self.horizontalLayout) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.verticalLayout_2 = QtWidgets.QVBoxLayout() + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.label = QtWidgets.QLabel(Dialog) + self.label.setMaximumSize(QtCore.QSize(16777215, 40)) + self.label.setObjectName("label") + self.verticalLayout_2.addWidget(self.label) + self.configs_combo = QtWidgets.QComboBox(Dialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.configs_combo.sizePolicy().hasHeightForWidth()) + self.configs_combo.setSizePolicy(sizePolicy) + self.configs_combo.setMinimumSize(QtCore.QSize(0, 40)) + self.configs_combo.setMaximumSize(QtCore.QSize(16777215, 40)) + self.configs_combo.setObjectName("configs_combo") + self.configs_combo.addItem("") + self.configs_combo.addItem("") + self.configs_combo.addItem("") + self.configs_combo.addItem("") + self.configs_combo.addItem("") + self.configs_combo.addItem("") + self.verticalLayout_2.addWidget(self.configs_combo) + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.gridLayout = QtWidgets.QGridLayout() + self.gridLayout.setObjectName("gridLayout") + self.remove_button = QtWidgets.QPushButton(Dialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.remove_button.sizePolicy().hasHeightForWidth()) + self.remove_button.setSizePolicy(sizePolicy) + self.remove_button.setMinimumSize(QtCore.QSize(50, 50)) + self.remove_button.setMaximumSize(QtCore.QSize(50, 50)) + self.remove_button.setText("") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("list-remove.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.remove_button.setIcon(icon) + self.remove_button.setAutoDefault(False) + self.remove_button.setObjectName("remove_button") + self.gridLayout.addWidget(self.remove_button, 0, 0, 1, 1) + self.add_button = QtWidgets.QPushButton(Dialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.add_button.sizePolicy().hasHeightForWidth()) + self.add_button.setSizePolicy(sizePolicy) + self.add_button.setMinimumSize(QtCore.QSize(50, 50)) + self.add_button.setMaximumSize(QtCore.QSize(50, 50)) + self.add_button.setText("") + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("list-add.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.add_button.setIcon(icon1) + self.add_button.setAutoDefault(False) + self.add_button.setObjectName("add_button") + self.gridLayout.addWidget(self.add_button, 0, 1, 1, 1) + self.verticalLayout.addLayout(self.gridLayout) + self.remove_all = QtWidgets.QPushButton(Dialog) + self.remove_all.setMinimumSize(QtCore.QSize(0, 40)) + self.remove_all.setAutoDefault(False) + self.remove_all.setObjectName("remove_all") + self.verticalLayout.addWidget(self.remove_all) + self.verticalLayout_2.addLayout(self.verticalLayout) + self.horizontalLayout_2.addLayout(self.verticalLayout_2) + self.configs_list = QtWidgets.QListView(Dialog) + self.configs_list.setObjectName("configs_list") + self.horizontalLayout_2.addWidget(self.configs_list) + self.verticalLayout_3.addLayout(self.horizontalLayout_2) + self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout_3.addWidget(self.buttonBox) + self.gridLayout_2.addLayout(self.verticalLayout_3, 0, 0, 1, 1) + + self.retranslateUi(Dialog) + self.buttonBox.accepted.connect(Dialog.accept) + self.buttonBox.rejected.connect(Dialog.reject) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Dialog")) + self.label_2.setText(_translate("Dialog", "Set :")) + self.sets_combo.setItemText(0, _translate("Dialog", "Set1")) + self.sets_combo.setItemText(1, _translate("Dialog", "Set2")) + self.sets_combo.setItemText(2, _translate("Dialog", "Set3")) + self.label.setText(_translate("Dialog", "Configurations of the set :")) + self.configs_combo.setItemText(0, _translate("Dialog", "Config1")) + self.configs_combo.setItemText(1, _translate("Dialog", "Config2")) + self.configs_combo.setItemText(2, _translate("Dialog", "Config3")) + self.configs_combo.setItemText(3, _translate("Dialog", "Config4")) + self.configs_combo.setItemText(4, _translate("Dialog", "Config5")) + self.configs_combo.setItemText(5, _translate("Dialog", "Config6")) + self.remove_all.setText(_translate("Dialog", "Remove all")) + diff --git a/sw/supervision/python/ui/set_manager.ui b/sw/supervision/python/ui/set_manager.ui new file mode 100644 index 0000000000..06c54a3043 --- /dev/null +++ b/sw/supervision/python/ui/set_manager.ui @@ -0,0 +1,280 @@ + + + Dialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + + + + + + Set : + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 16777215 + 40 + + + + + Set1 + + + + + Set2 + + + + + Set3 + + + + + + + + + + + + + + + 16777215 + 40 + + + + Configurations of the set : + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 16777215 + 40 + + + + + Config1 + + + + + Config2 + + + + + Config3 + + + + + Config4 + + + + + Config5 + + + + + Config6 + + + + + + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + list-remove.svglist-remove.svg + + + false + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + + list-add.svglist-add.svg + + + false + + + + + + + + + + 0 + 40 + + + + Remove all + + + false + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 258 + 289 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 326 + 289 + + + 286 + 274 + + + + + diff --git a/sw/supervision/python/ui/tutorial.html b/sw/supervision/python/ui/tutorial.html new file mode 100644 index 0000000000..47a79b9a45 --- /dev/null +++ b/sw/supervision/python/ui/tutorial.html @@ -0,0 +1,38 @@ + + + + + + +
+ Paparazzi UAV icon unreachable... +

About Paparazzi UAV :

+
+ + +