Artemis 4 RC1

This commit is contained in:
Marco Dalla Tiezza
2024-05-28 22:40:45 +02:00
parent acc44c93b3
commit 528c816508
254 changed files with 14757 additions and 30137 deletions

354
artemis/ui/artemis.py Normal file
View File

@@ -0,0 +1,354 @@
import uuid
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, Slot, Signal
from artemis.utils.constants import Constants, Messages
from artemis.utils.sys_utils import open_directory, pack_db, unpack_db
from artemis.utils.sql_utils import ArtemisDatabase, ArtemisSignal
from artemis.utils.path_utils import check_data_dir
from artemis.utils.network_utils import NetworkManager
from artemis.utils.generic_utils import generate_filter_query
from artemis.utils.path_utils import normalize_dialog_path
from artemis.ui.preferences import UIPreferences
from artemis.ui.dbmanager import UIdbmanager
from artemis.ui.signaleditor import UIsignaleditor
from artemis.ui.downloader import UIDownloader
from artemis.ui.spaceweather import UIspaceweather
from artemis.ui.documentsmanager import UIdocumentsmanager
from artemis.ui.categoryeditor import UIcategoryeditor
import artemis.resources
class UIArtemis(QObject):
# Python > QML Signals
populate_sig_list = Signal(list)
populate_sig_details = Signal(list)
populate_filter_modulation = Signal(list)
clear_list = Signal()
clear_signal_page = Signal()
clear_filter_page = Signal()
lock_audio_player = Signal()
lock_menu = Signal(bool)
show_dialog_popup = Signal(str, str, str)
show_dialog_download_db = Signal(str, str, str)
show_dialog_download_art = Signal(str, str, str)
update_info_bar = Signal(str, str)
def __init__(self):
super().__init__()
# Main UI initialization
self._engine = QQmlApplicationEngine()
self._engine.rootContext().setContextProperty('APPLICATION_VERSION', Constants.APPLICATION_VERSION)
self._engine.rootContext().setContextProperty('PYTHON_VERSION', Constants.PYTHON_VERSION)
self._engine.rootContext().setContextProperty('QT_VERSION', Constants.QT_VERSION)
self._engine.load('qrc:/ui/Artemis.qml')
self._window = self._engine.rootObjects()[0]
self._window_filter = self._window.findChild(QObject, "filterPageObj")
self._window_signal = self._window.findChild(QObject, "signalPageObj")
self.loaded_db = None
self._connect()
# Creating istances for other windows
self.preferences = UIPreferences(self)
self.dbmanager = UIdbmanager(self)
self.downloader = UIDownloader(self)
self.spaceweather = UIspaceweather(self)
self.docmanager = UIdocumentsmanager(self)
self.sigeditor = UIsignaleditor(self)
self.cateditor = UIcategoryeditor(self)
self.network_manager = NetworkManager(self)
check_data_dir()
def _connect(self):
# QML > Python connections
self._window.showDBmanager.connect(self.show_dbmanager_ui)
self._window.loadSignal.connect(self.load_sig)
self._window.showPref.connect(self.show_pref_ui)
self._window.openSigEditor.connect(self.open_sig_editor)
self._window.startDownloader.connect(self.start_download_db)
self._window.checkDbUpdates.connect(self.check_update_db)
self._window.showSpaceWeather.connect(self.show_space_weather_ui)
self._window.openDbDirectory.connect(self.open_db_directory)
self._window.showCatManager.connect(self.open_cat_manager)
self._window.newDb.connect(self.new_db)
self._window.exportDb.connect(self.export_db)
self._window.importDb.connect(self.import_db)
self._window_filter.applyFilter.connect(self.apply_filter)
self._window_filter.sendBottomAlert.connect(self.bottom_info_bar)
self._window_signal.openDocManager.connect(self.show_documentsmanager_ui)
self._window_signal.openSigEditor.connect(self.open_sig_editor)
self._window_signal.deleteCatTag.connect(self.delete_cat_tag)
self._window_signal.addCatTag.connect(self.add_cat_tag)
# Python > QML connections
self.populate_sig_list.connect(self._window.populateList)
self.clear_list.connect(self._window.clearList)
self.update_info_bar.connect(self._window.bottomInfoBar)
self.show_dialog_popup.connect(self._window.openGeneralDialog)
self.show_dialog_download_db.connect(self._window.openDialogDownloadDb)
self.show_dialog_download_art.connect(self._window.openDialogDownloadArtemis)
self.lock_menu.connect(self._window.lockMenu)
self.populate_sig_details.connect(self._window_signal.populateSignalParam)
self.lock_audio_player.connect(self._window_signal.lockPlayer)
self.clear_signal_page.connect(self._window_signal.resetAll)
self.clear_filter_page.connect(self._window_filter.resetAll)
self.populate_filter_modulation.connect(self._window_filter.loadLists)
def load_db(self, db_dir_name):
""" Load the DB and populate the signals list
Args:
db_dir_name (str): folder name in the data folder
"""
# Loading DB
self.loaded_db = ArtemisDatabase(db_dir_name)
self.loaded_db.load()
# Clearing UI
self.lock_menu.emit(False)
self.clear_signal_page.emit()
self.clear_filter_page.emit()
# Populating UI
self.load_filter_lists()
self.populate_sig_list.emit(self.loaded_db.all_signals)
# Updating status bar
total_signals = len(self.loaded_db.all_signals)
self.bottom_info_bar("Database loaded with {} signals".format(total_signals), "info")
@Slot(int)
def load_sig(self, sig_id):
""" Load the selected signal and populate the SignalPage
Args:
sig_id (int): SIG_ID of the signal to be loaded
"""
self.loaded_sig = ArtemisSignal(self.loaded_db)
self.loaded_sig.load(sig_id)
sig_dic = self.loaded_sig.generate_dic()
self.populate_sig_details.emit([sig_dic])
def load_filter_lists(self):
""" Populates the 3 listviews in the FilterPage
"""
self.populate_filter_modulation.emit([{
'modulation': self.loaded_db.all_modulation,
'location': self.loaded_db.all_location,
'category': self.loaded_db.all_category_labels
}])
@Slot(dict)
def apply_filter(self, filter_status):
""" Update the signal list according to the selected filters in the FilterPage.
Args:
filter_status (dic): dictionary containing the active filters with all
the details to generate a search query
"""
filter_status = filter_status.toVariant()
if filter_status != {}:
filter_query = generate_filter_query(filter_status)
self.loaded_db.select_by_filter(filter_query)
self.clear_signal_page.emit()
self.populate_sig_list.emit(self.loaded_db.all_signals)
total_signals = len(self.loaded_db.all_signals)
self.bottom_info_bar("FILTERS ACTIVE: {} signals found".format(total_signals), "warning")
else:
self.load_db(self.loaded_db.db_dir_name)
def show_pref_ui(self):
""" Load the preference windows
"""
self.preferences.load_preferences_ui()
def show_dbmanager_ui(self):
""" Load the DB manager windows
"""
self.dbmanager.load_dbmanager_ui()
@Slot(str, list, bool)
def open_sig_editor(self, type, sig_param, is_new):
""" Open the signal editor windows
Called when the user want to add, edit or delete the signal or its parametes.
"""
self.sigeditor.load_signaleditor_ui(type, sig_param, is_new)
def show_space_weather_ui(self):
""" Open the space weather windows
"""
self.spaceweather.load_spaceweather_ui()
def show_documentsmanager_ui(self):
""" Open the documents manager windows
"""
self.docmanager.load_documentsmanager_ui()
def check_update_db(self):
""" User manual check for updates db updates
"""
self.network_manager.show_popup = True
self.network_manager.check_updates()
def start_download_db(self):
""" Show the downloader and start the download of the sigid db
"""
self.downloader.show_ui.emit()
self.downloader.on_start()
def dialog_download_db(self, message_type, title, message):
""" Dialog popup for DB download confirmation
"""
self.show_dialog_download_db.emit(message_type, title, message)
def dialog_download_artemis(self, message_type, title, message):
""" Dialog popup for artemis download confirmation
"""
self.show_dialog_download_art.emit(message_type, title, message)
def open_db_directory(self):
""" Open the local folder of the loaded DB
"""
open_directory(self.loaded_db.db_dir)
@Slot(str)
def new_db(self, name):
""" Create a new local DB
Args:
name (str): name of the new DB, hardcoded in sql info table
"""
try:
new_db = ArtemisDatabase(str(uuid.uuid4()))
new_db.create(name)
self.load_db(new_db.db_dir_name)
self.dialog_popup(
Messages.DIALOG_TYPE_INFO,
Messages.GENERIC_SUCCESS,
Messages.DB_CREATION_SUCCESS_MSG
)
except Exception as e:
self.dialog_popup(
Messages.DIALOG_TYPE_ERROR,
Messages.GENERIC_ERROR,
Messages.GENERIC_ERROR_MSG.format(e)
)
@Slot(str)
def export_db(self, save_path):
""" Export the load DB in a tar file. Does not use compression
Args:
save_path (str): destination path of the generated .tar file
"""
try:
dest_path = normalize_dialog_path(save_path)
pack_db(dest_path, self.loaded_db.db_dir)
self.dialog_popup(
Messages.DIALOG_TYPE_INFO,
Messages.GENERIC_SUCCESS,
Messages.EXPORTING_SUCCESS_MSG
)
except Exception as e:
self.dialog_popup(
Messages.DIALOG_TYPE_ERROR,
Messages.GENERIC_ERROR,
Messages.GENERIC_ERROR_MSG.format(e)
)
@Slot(str)
def import_db(self, tar_path):
""" Import a new DB in the Artemis data folder
Args:
tar_path (str): Path of the archive to be imported
"""
try:
origin_path = normalize_dialog_path(tar_path)
unpack_db(origin_path, str(uuid.uuid4()))
self.dialog_popup(
Messages.DIALOG_TYPE_INFO,
Messages.GENERIC_SUCCESS,
Messages.IMPORTING_SUCCESS_MSG
)
except Exception as e:
self.dialog_popup(
Messages.DIALOG_TYPE_ERROR,
Messages.GENERIC_ERROR,
Messages.GENERIC_ERROR_MSG.format(e)
)
@Slot(int)
def add_cat_tag(self, clb_id):
self.loaded_sig.insert_category(clb_id)
self.load_db(self.loaded_db.db_dir_name)
@Slot(int)
def delete_cat_tag(self, cat_id):
self.loaded_sig.delete_category(cat_id)
self.load_db(self.loaded_db.db_dir_name)
def open_cat_manager(self):
""" Open the category manager windows
"""
self.cateditor.load_cateditor_ui()
def dialog_popup(self, message_type, title, message):
""" Opens a general dialog popup
Args:
message_type (str): 'info', 'question', 'warn', 'error'
title (str): header of the dialoog
message (sstr): description inside the dialog
"""
self.show_dialog_popup.emit(message_type, title, message)
@Slot(str, str)
def bottom_info_bar(self, message, message_type):
""" Manage the footer info bar
Args:
message (str): text to be shown in the info bar
message_type (str): 'info', 'warning'
"""
self.update_info_bar.emit(message, message_type)

View File

@@ -0,0 +1,67 @@
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, Signal, Slot
from artemis.utils.path_utils import *
from artemis.utils.generic_utils import *
class UIcategoryeditor(QObject):
# Python > QML Signals
show_ui = Signal()
load = Signal(list)
def __init__(self, parent):
super().__init__()
self._parent = parent
self._engine = QQmlApplicationEngine()
self._engine.load('qrc:/ui/CategoryEditor.qml')
self._window = self._engine.rootObjects()[0]
self._connect()
def _connect(self):
# QML > Python connections
self._window.saveParam.connect(self.save)
self._window.deleteParam.connect(self.delete)
# Python > QML connections
self.show_ui.connect(self._window.show)
self.load.connect(self._window.loadList)
def load_cateditor_ui(self):
""" Load the list with existing category tags and show the UI
"""
all_cat = self._parent.loaded_db.all_category_labels
self.load.emit(all_cat)
self.show_ui.emit()
@Slot(list, bool)
def save(self, data, is_new):
""" Save new category tag or update the existing ones.
"""
data = data.toVariant()
if is_new:
self._parent.loaded_db.insert_category_label(data[0])
else:
self._parent.loaded_db.update_category_label(data[1], data[0])
self._parent.load_db(self._parent.loaded_db.db_dir_name)
self.load_cateditor_ui()
@Slot(int)
def delete(self, clb_id):
""" Delete a database category tag.
All the entries in the documents table are automatically beign deleted due to
foreign-key cascade propagation
"""
self._parent.loaded_db.delete_category_label(clb_id)
self._parent.load_db(self._parent.loaded_db.db_dir_name)
self.load_cateditor_ui()

105
artemis/ui/dbmanager.py Normal file
View File

@@ -0,0 +1,105 @@
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, Signal, Slot
from artemis.utils.path_utils import *
from artemis.utils.generic_utils import *
from artemis.utils.sql_utils import ArtemisDatabase
from artemis.utils.constants import Constants
class UIdbmanager(QObject):
# Python > QML Signals
show_ui = Signal()
close_ui = Signal()
populate_db_list = Signal(list)
def __init__(self, parent):
super().__init__()
self._parent = parent
self._engine = QQmlApplicationEngine()
self._engine.load('qrc:/ui/DbManager.qml')
self._window = self._engine.rootObjects()[0]
self._connect()
def _connect(self):
# QML > Python connections
self._window.loadDB.connect(self.load_db)
self._window.deleteDB.connect(self.delete_db)
self._window.renameDB.connect(self.rename_db)
# Python > QML connections
self.show_ui.connect(self._window.show)
self.close_ui.connect(self._window.close)
self.populate_db_list.connect(self._window.loadList)
def load_dbmanager_ui(self):
self.load_local_db_list()
self.show_ui.emit()
def load_local_db_list(self):
""" Scan for all the valid DBs in the data folder and show them on the list
"""
valid_db_list = self.scan_db_dir()
self.populate_db_list.emit(valid_db_list)
def load_db(self, db_dir_name):
""" Load the selected DB (from the DB Manager list) in the main artemis window
"""
self._parent.load_db(db_dir_name)
self.close_ui.emit()
@Slot(str)
def delete_db(self, db_dir_name):
""" Delete the DB folder.
Clear the main UI if the database to be deleted is the selected one
"""
if self._parent.loaded_db is not None:
if self._parent.loaded_db.db_dir_name == db_dir_name:
self._parent.lock_menu.emit(True)
self._parent.clear_list.emit()
self._parent.clear_signal_page.emit()
delete_db_dir(db_dir_name)
self.load_local_db_list()
@Slot(str, str)
def rename_db(self, db_dir_name, new_name):
""" Rename db in the data folder
"""
database = ArtemisDatabase(db_dir_name)
database.rename(new_name)
self.load_local_db_list()
def scan_db_dir(self):
""" Scans the data directory for valid databases and
return a dictionary containing only the valid ones with a summary
"""
valid_db_list = []
db_dirs = next(os.walk(Constants.DB_DIR))[1]
for db_dir_name in db_dirs:
if valid_db(db_dir_name):
database = ArtemisDatabase(db_dir_name)
database.load()
valid_db_list.append(
{
'name': database.name,
'db_dir_name': database.db_dir_name,
'documents_n': database.stats['documents'],
'signals_n': database.stats['signals'],
'images_n': database.stats['images'],
'audio_n': database.stats['audio']
}
)
return valid_db_list

View File

@@ -0,0 +1,128 @@
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, Signal, Slot
from artemis.utils.path_utils import *
from artemis.utils.generic_utils import *
from artemis.utils.sys_utils import *
class UIdocumentsmanager(QObject):
# Python > QML Signals
show_ui = Signal()
close_ui = Signal()
populate_documents_list = Signal(list)
def __init__(self, parent):
super().__init__()
self._parent = parent
self._engine = QQmlApplicationEngine()
self._engine.load('qrc:/ui/DocumentsManager.qml')
self._window = self._engine.rootObjects()[0]
self._connect()
def _connect(self):
# QML > Python connections
self._window.saveNewDoc.connect(self.save_new_doc)
self._window.deleteDoc.connect(self.delete_doc)
self._window.updateDoc.connect(self.update_doc)
self._window.openDoc.connect(self.open_doc)
# Python > QML connections
self.show_ui.connect(self._window.show)
self.close_ui.connect(self._window.close)
self.populate_documents_list.connect(self._window.loadList)
def load_documentsmanager_ui(self):
self.load_documents_list()
self.show_ui.emit()
def load_documents_list(self):
""" Load the documents of the selected signal and populate the documents list
"""
self._parent.loaded_sig.select_documents()
all_documents = self._parent.loaded_sig.documents
keys = (
'doc_id',
'extension',
'name',
'description',
'type',
'preview'
)
doc_lst = [dict(zip(keys, values)) for values in all_documents]
self.populate_documents_list.emit(doc_lst)
@Slot(list)
def save_new_doc(self, doc_lst):
""" Save the new document (identified by the DOC_ID = -1) and reload the document list.
doc_param contains all the details of the new documents.
"""
doc_param = doc_lst.toVariant()
file_extension = os.path.splitext(doc_param[0])[1][1:]
doc_id = self._parent.loaded_sig.insert_document([
-1,
file_extension,
doc_param[1],
doc_param[2],
doc_param[3],
0
])
local_file_name = '{}.{}'.format(str(doc_id), file_extension)
origin_path = normalize_dialog_path(doc_param[0])
copy_file(origin_path, self._parent.loaded_db.media_dir / local_file_name)
self.load_documents_list()
@Slot(list)
def update_doc(self, doc_lst):
""" Update the details of the existent document
"""
doc_list = doc_lst.toVariant()
for doc in doc_list:
self._parent.loaded_sig.update_documents(doc[0], doc[1], doc[2], doc[3], doc[4])
self.load_documents_list()
@Slot(str, str)
def open_doc(self, doc_id, extension):
""" Open the selected document with the proper system application (if any)
"""
try:
open_file(self._parent.loaded_db.media_dir / '{}.{}'.format(doc_id, extension))
except Exception as e:
self.close_ui.emit()
self._parent.dialog_popup(
Messages.DIALOG_TYPE_ERROR,
Messages.GENERIC_ERROR,
str(e)
)
@Slot(str, str, str, bool)
def delete_doc(self, doc_id, doc_extension, doc_type, doc_preview):
""" Delete the selected document
"""
doc_file_name = '{}.{}'.format(doc_id, doc_extension)
doc_file_path = self._parent.loaded_db.media_dir / doc_file_name
self._parent.loaded_sig.delete_document(doc_id)
if doc_preview:
if doc_type == 'Audio':
self._parent.lock_audio_player.emit()
delete_file(doc_file_path)
self.load_documents_list()

133
artemis/ui/downloader.py Normal file
View File

@@ -0,0 +1,133 @@
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, Slot, Signal, QUrl, QSaveFile, QDir, QIODevice
from PySide6.QtNetwork import QNetworkReply, QNetworkRequest, QNetworkAccessManager
from artemis.utils.config_utils import *
from artemis.utils.sys_utils import delete_file, match_hash, unpack_db
from artemis.utils.constants import Messages
from artemis.utils.sys_utils import delete_db_dir
class UIDownloader(QObject):
# Python > QML Signals
show_ui = Signal()
close_ui = Signal()
def __init__(self, parent):
super().__init__()
self._parent = parent
self._engine = QQmlApplicationEngine()
self._engine.load('qrc:/ui/Downloader.qml')
self._window = self._engine.rootObjects()[0]
self._progress_bar = self._window.findChild(QObject, "progressBar")
self._label_progress = self._window.findChild(QObject, "labelProgress")
self._connect()
def _connect(self):
# QML > Python connections
self._window.onAbort.connect(self.on_abort)
# Python > QML connections
self.show_ui.connect(self._window.show)
self.close_ui.connect(self._window.close)
@Slot()
def on_start(self):
""" Start the download of the DB taking the needed url and size from
the attributes of the UpdatesController class
"""
url_file = QUrl(self._parent.network_manager.remote_db_url)
dest_path = QDir(Constants.DB_DIR)
self.dest_file = dest_path.filePath(url_file.fileName())
self.file = QSaveFile(self.dest_file)
if self.file.open(QIODevice.WriteOnly):
# Start a GET HTTP request
self.manager = QNetworkAccessManager(self)
self.reply = self.manager.get(QNetworkRequest(url_file))
self.reply.downloadProgress.connect(self.on_progress)
self.reply.finished.connect(self.on_finished)
self.reply.readyRead.connect(self.on_ready_read)
self.reply.errorOccurred.connect(self.on_error)
else:
self.close_ui.emit()
self.show_popup_error(
self.file.errorString()
)
@Slot()
def on_abort(self):
""" Stop the download when user press abort button """
if self.reply:
self.reply.abort()
self._progress_bar.setProperty("value", 0)
if self.file:
self.file.cancelWriting()
self.close_ui.emit()
@Slot()
def on_ready_read(self):
""" Get available bytes and store them into the file """
if self.reply:
if self.reply.error() == QNetworkReply.NoError:
self.file.write(self.reply.readAll())
@Slot()
def on_finished(self):
""" Delete reply, close the file, check the hash for integrity,
extract the database and delete the downloaded zip
"""
if self.reply:
self.reply.deleteLater()
if self.file:
self.file.commit()
self._label_progress.setProperty("text", "Checking DB integrity (SHA-256)")
if match_hash(self.dest_file, self._parent.network_manager.remote_db_hash):
self._label_progress.setProperty("text", "Unpacking archive...")
delete_db_dir('SigID')
unpack_db(self.dest_file, 'SigID')
delete_file(self.dest_file)
self._parent.load_db('SigID')
self.close_ui.emit()
@Slot(int, int)
def on_progress(self, bytesReceived: int):
""" Update progress bar and label
"""
total_bytes = self._parent.network_manager.remote_db_size
self._label_progress.setProperty("text", "{:.1f} Mb / {:.1f} Mb".format(bytesReceived/10**6, total_bytes/10**6))
self._progress_bar.setProperty("to", total_bytes)
self._progress_bar.setProperty("value", bytesReceived)
@Slot(QNetworkReply.NetworkError)
def on_error(self, code: QNetworkReply.NetworkError):
""" Show a message if an error happen during download
"""
if self.reply:
self.close_ui.emit()
self.show_popup_error(
self.reply.errorString()
)
def show_popup_error(self, error_msg):
self._parent.dialog_popup(
Messages.DIALOG_TYPE_ERROR,
Messages.GENERIC_ERROR,
Messages.GENERIC_ERROR_MSG.format(error_msg)
)

56
artemis/ui/preferences.py Normal file
View File

@@ -0,0 +1,56 @@
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, Slot, Signal
from artemis.utils.config_utils import *
class UIPreferences(QObject):
# Python > QML Signals
show_ui = Signal()
load_material_accent = Signal(str)
load_material_theme = Signal(str)
def __init__(self, parent):
super().__init__()
self._parent = parent
self._engine = QQmlApplicationEngine()
self._engine.load('qrc:/ui/Preferences.qml')
self._window = self._engine.rootObjects()[0]
self._connect()
def _connect(self):
# QML > Python connections
self._window.saveMaterialAccent.connect(self.save_material_accent)
self._window.saveMaterialTheme.connect(self.save_material_theme)
# Python > QML connections
self.show_ui.connect(self._window.show)
self.load_material_accent.connect(self._window.loadMaterialAccent)
self.load_material_theme.connect(self._window.loadMaterialTheme)
def load_preferences_ui(self):
""" Loading all the initial preferences from the conf file to the UI
"""
self.load_material_accent.emit(CONFIGURE_QT.get_or_default("Material", "Accent", "Green"))
self.load_material_theme.emit(CONFIGURE_QT.get_or_default("Material", "Theme", "System"))
self.show_ui.emit()
@Slot(str)
def save_material_accent(self, material_accent):
""" Saving material accent setting
"""
CONFIGURE_QT.set("Material", "Accent", material_accent)
@Slot(str)
def save_material_theme(self, material_theme):
""" Saving material theme setting
"""
CONFIGURE_QT.set("Material", "Theme", material_theme)

124
artemis/ui/signaleditor.py Normal file
View File

@@ -0,0 +1,124 @@
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, Signal, Slot
from artemis.utils.path_utils import *
from artemis.utils.generic_utils import *
from artemis.utils.sql_utils import ArtemisSignal
class UIsignaleditor(QObject):
# Python > QML Signals
show_ui = Signal()
load = Signal(str, list, bool)
def __init__(self, parent):
super().__init__()
self._parent = parent
self._engine = QQmlApplicationEngine()
self._engine.load('qrc:/ui/SignalEditor.qml')
self._window = self._engine.rootObjects()[0]
self._connect()
def _connect(self):
# QML > Python connections
self._window.saveParam.connect(self.save)
self._window.deleteParam.connect(self.delete)
# Python > QML connections
self.show_ui.connect(self._window.show)
self.load.connect(self._window.load)
def load_signaleditor_ui(self, param_type, sig_param, is_new):
""" Load all the details of the selected signal
Args:
param_type (str): Signal, Frequency, Bandwidth, Modulation, Mode,
ACF, Location
sig_param (list): a list formed as [id, value, description]
is_new (bool): If true, the windows open in an empty state ready to
be compiled by the user. If false, the windows will open all the
current parameter for the loaded signal, for editing or deleting purposes.
"""
if param_type == 'Signal' and not is_new:
sig_param = [
self._parent.loaded_sig.sig_id,
self._parent.loaded_sig.name,
self._parent.loaded_sig.description
]
self.load.emit(param_type, sig_param, is_new)
self.show_ui.emit()
@Slot(str, list, bool)
def save(self, param_type, data, is_new):
""" Save new signal parameters or update the existing ones.
"""
data = data.toVariant()
if is_new:
if param_type == 'Signal':
self._parent.loaded_sig = ArtemisSignal(self._parent.loaded_db)
self._parent.loaded_sig.insert_signal(data[1], data[2])
elif param_type == 'Frequency':
self._parent.loaded_sig.insert_frequency(int(data[1]), data[2])
elif param_type == 'Bandwidth':
self._parent.loaded_sig.insert_bandwidth(int(data[1]), data[2])
elif param_type == 'Modulation':
self._parent.loaded_sig.insert_modulation(data[1], data[2])
elif param_type == 'Mode':
self._parent.loaded_sig.insert_mode(data[1], data[2])
elif param_type == 'ACF':
self._parent.loaded_sig.insert_acf(data[1], data[2])
elif param_type == 'Location':
self._parent.loaded_sig.insert_location(data[1], data[2])
else:
if param_type == 'Signal':
self._parent.loaded_sig.update_signal(data[0], data[1], data[2])
elif param_type == 'Frequency':
self._parent.loaded_sig.update_frequency(data[0], int(data[1]), data[2])
elif param_type == 'Bandwidth':
self._parent.loaded_sig.update_bandwidth(data[0], int(data[1]), data[2])
elif param_type == 'Modulation':
self._parent.loaded_sig.update_modulation(data[0], data[1], data[2])
elif param_type == 'Mode':
self._parent.loaded_sig.update_mode(data[0], data[1], data[2])
elif param_type == 'ACF':
self._parent.loaded_sig.update_acf(data[0], data[1], data[2])
elif param_type == 'Location':
self._parent.loaded_sig.update_location(data[0], data[1], data[2])
self._parent.load_db(self._parent.loaded_db.db_dir_name)
@Slot(str, int)
def delete(self, param_type, id):
""" Delete a signal parameter or the signal itself (with all the parameters and documents).
All the entries in the documents table are automatically beign deleted due to
foreign-key cascade propagation
"""
if param_type == 'Signal':
self._parent.loaded_sig.delete_signal()
for doc in self._parent.loaded_sig.documents:
doc_file_name = '{}.{}'.format(str(doc[0]), doc[1])
doc_file_path = self._parent.loaded_db.media_dir / doc_file_name
delete_file(doc_file_path)
elif param_type == 'Frequency':
self._parent.loaded_sig.delete_frequency(id)
elif param_type == 'Bandwidth':
self._parent.loaded_sig.delete_bandwidth(id)
elif param_type == 'Modulation':
self._parent.loaded_sig.delete_modulation(id)
elif param_type == 'Mode':
self._parent.loaded_sig.delete_mode(id)
elif param_type == 'ACF':
self._parent.loaded_sig.delete_acf(id)
elif param_type == 'Location':
self._parent.loaded_sig.delete_location(id)
self._parent.load_db(self._parent.loaded_db.db_dir_name)

View File

@@ -0,0 +1,65 @@
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QObject, Signal
from artemis.utils.path_utils import *
from artemis.utils.generic_utils import *
from artemis.utils.constants import Constants
class UIspaceweather(QObject):
# Python > QML Signals
show_ui = Signal()
load_poseidon_report = Signal(dict)
load_poseidon_forecast_report = Signal(dict)
update_bottom_bar = Signal(str)
def __init__(self, parent):
super().__init__()
self._parent = parent
self._engine = QQmlApplicationEngine()
self._engine.load('qrc:/ui/SpaceWeather.qml')
self._window = self._engine.rootObjects()[0]
self._window_current = self._window.findChild(QObject, "spaceWeatherCurrentObj")
self._window_forecast = self._window.findChild(QObject, "spaceWeatherForecastObj")
self._connect()
def _connect(self):
# QML > Python connections
# Python > QML connections
self.show_ui.connect(self._window.show)
self.update_bottom_bar.connect(self._window.updateBottomBar)
self.load_poseidon_report.connect(self._window_current.loadReport)
self.load_poseidon_forecast_report.connect(self._window_forecast.loadForecastReport)
def load_spaceweather_ui(self):
""" Before opening the windows, poseidon report (data.json) is read online
"""
self.download_poseidon_report()
def download_poseidon_report(self):
network_manager = self._parent.network_manager
network_manager.show_popup = True
poseidon_data = network_manager.fetch_remote_json(
Constants.POSEIDON_REPORT
)
if poseidon_data:
self.load_poseidon_report.emit(poseidon_data)
self.load_poseidon_forecast_report.emit(poseidon_data)
self.update_bottom_bar.emit(
'Loaded Poseidon report issued on {} at {} UTC'.format(
poseidon_data['JSON_INFO']['utc_date'],
poseidon_data['JSON_INFO']['utc_time']
)
)
self.show_ui.emit()