From 8c0f78846ba7849eddd85c6b4a7d73a56ae82657 Mon Sep 17 00:00:00 2001 From: Aravind Murali Date: Thu, 15 Jul 2021 14:24:31 +0530 Subject: [PATCH] fixed mainwindow tests --- mslib/msui/_tests/test_mscolab.py | 104 +++++++++++++---- mslib/msui/_tests/test_mss_pyui.py | 173 ++++++++++++++++++++--------- mslib/msui/mscolab.py | 25 +++-- mslib/msui/mss_pyui.py | 105 +++++++++++------ 4 files changed, 286 insertions(+), 121 deletions(-) diff --git a/mslib/msui/_tests/test_mscolab.py b/mslib/msui/_tests/test_mscolab.py index fc54cbdf7..fdef40629 100644 --- a/mslib/msui/_tests/test_mscolab.py +++ b/mslib/msui/_tests/test_mscolab.py @@ -37,7 +37,7 @@ from mslib.mscolab.models import Permission, User from mslib.msui.flighttrack import WaypointsTableModel from PyQt5 import QtCore, QtTest, QtWidgets -from mslib._tests.utils import mscolab_start_server +from mslib._tests.utils import mscolab_start_server, ExceptionMock import mslib.msui.mss_pyui as mss_pyui from mslib.msui import mscolab @@ -129,6 +129,16 @@ def _create_user(self, username, email, password): reason="multiprocessing needs currently start_method fork") class Test_Mscolab(object): sample_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "docs", "samples", "flight-tracks") + # import/export plugins + import_plugins = { + "Text": ["txt", "mslib.plugins.io.text", "load_from_txt"], + "FliteStar": ["fls", "mslib.plugins.io.flitestar", "load_from_flitestar"], + } + export_plugins = { + "Text": ["txt", "mslib.plugins.io.text", "save_to_txt"], + # "KML": ["kml", "mslib.plugins.io.kml", "save_to_kml"], + # "GPX": ["gpx", "mslib.plugins.io.gpx", "save_to_gpx"] + } def setup(self): self.process, self.url, self.app, _, self.cm, self.fm = mscolab_start_server(PORTS) @@ -142,6 +152,11 @@ def teardown(self): self.window.mscolab.version_window.close() if self.window.mscolab.conn: self.window.mscolab.conn.disconnect() + # force close all open views + while self.window.listViews.count() > 0: + self.window.listViews.item(0).window.handle_force_close() + # close all hanging project option windows + self.window.mscolab.close_external_windows() self.application.quit() QtWidgets.QApplication.processEvents() self.process.terminate() @@ -172,13 +187,25 @@ def test_view_open(self, mockbox): QtWidgets.QApplication.processEvents() assert len(self.window.get_active_views()) == 4 + project = self.window.mscolab.active_pid + uid = self.window.mscolab.user["id"] + active_windows = self.window.get_active_views() + topview = active_windows[1] + tableview = active_windows[0] + self.window.mscolab.handle_update_permission(project, uid, "viewer") + assert not tableview.btAddWayPointToFlightTrack.isEnabled() + assert any(action.text() == "Ins WP" and not action.isEnabled() for action in topview.mpl.navbar.actions()) + self.window.mscolab.handle_update_permission(project, uid, "creator") + assert tableview.btAddWayPointToFlightTrack.isEnabled() + assert any(action.text() == "Ins WP" and action.isEnabled() for action in topview.mpl.navbar.actions()) + @mock.patch("PyQt5.QtWidgets.QFileDialog.getSaveFileName", return_value=(fs.path.join(mscolab_settings.MSCOLAB_DATA_DIR, 'test_export.ftml'), None)) def test_handle_export(self, mockbox): self._connect_to_mscolab() self._login() self._activate_project_at_index(0) - self.window.actionExportFlightTrackFTML.trigger() + self.window.actionExportFlightTrackftml.trigger() QtWidgets.QApplication.processEvents() exported_waypoints = WaypointsTableModel(filename=fs.path.join(self.window.mscolab.data_dir, 'test_export.ftml')) wp_count = len(self.window.mscolab.waypoints_model.waypoints) @@ -186,28 +213,44 @@ def test_handle_export(self, mockbox): for i in range(wp_count): assert exported_waypoints.waypoint_data(i).lat == self.window.mscolab.waypoints_model.waypoint_data(i).lat - @pytest.mark.parametrize("ext", [".ftml", ".txt"]) + @pytest.mark.parametrize("ext", [".ftml", ".csv", ".txt"]) @mock.patch("PyQt5.QtWidgets.QMessageBox") - def test_import_file(self, mockExport, mockImport, mockMessage): - self._connect_to_mscolab() - self._login() - self._activate_project_at_index(0) - exported_wp = WaypointsTableModel(waypoints=self.window.mscolab.waypoints_model.waypoints) - self.window.actionExportFlightTrackFTML.trigger() - QtWidgets.QApplication.processEvents() - self.window.mscolab.waypoints_model.invert_direction() - QtWidgets.QApplication.processEvents() - QtTest.QTest.qWait(100) - assert exported_wp.waypoint_data(0).lat != self.window.mscolab.waypoints_model.waypoint_data(0).lat - self.window.actionImportFlightTrackFTML.trigger() - QtWidgets.QApplication.processEvents() - QtTest.QTest.qWait(100) - assert len(self.window.mscolab.waypoints_model.waypoints) == 2 - imported_wp = self.window.mscolab.waypoints_model - wp_count = len(imported_wp.waypoints) - assert wp_count == 2 - for i in range(wp_count): - assert exported_wp.waypoint_data(i).lat == imported_wp.waypoint_data(i).lat + def test_import_file(self, mockbox, ext): + with mock.patch("mslib.msui.mscolab.config_loader", return_value=self.import_plugins): + self.window.add_import_plugins("qt") + with mock.patch("mslib.msui.mscolab.config_loader", return_value=self.export_plugins): + self.window.add_export_plugins("qt") + with mock.patch("PyQt5.QtWidgets.QFileDialog.getSaveFileName", return_value=(fs.path.join( + mscolab_settings.MSCOLAB_DATA_DIR, f'test_import{ext}'), None)): + with mock.patch("PyQt5.QtWidgets.QFileDialog.getOpenFileName", return_value=(fs.path.join( + mscolab_settings.MSCOLAB_DATA_DIR, f'test_import{ext}'), None)): + self._connect_to_mscolab() + self._login() + self._activate_project_at_index(0) + exported_wp = WaypointsTableModel(waypoints=self.window.mscolab.waypoints_model.waypoints) + full_name = f"actionExportFlightTrack{ext[1:]}" + for action in self.window.menuExportActiveFlightTrack.actions(): + if action.objectName() == full_name: + action.trigger() + break + QtWidgets.QApplication.processEvents() + self.window.mscolab.waypoints_model.invert_direction() + QtWidgets.QApplication.processEvents() + QtTest.QTest.qWait(100) + assert exported_wp.waypoint_data(0).lat != self.window.mscolab.waypoints_model.waypoint_data(0).lat + full_name = f"actionImportFlightTrack{ext[1:]}" + for action in self.window.menuImportFlightTrack.actions(): + if action.objectName() == full_name: + action.trigger() + break + QtWidgets.QApplication.processEvents() + QtTest.QTest.qWait(100) + assert len(self.window.mscolab.waypoints_model.waypoints) == 2 + imported_wp = self.window.mscolab.waypoints_model + wp_count = len(imported_wp.waypoints) + assert wp_count == 2 + for i in range(wp_count): + assert exported_wp.waypoint_data(i).lat == imported_wp.waypoint_data(i).lat def test_work_locally_toggle(self): self._connect_to_mscolab() @@ -254,7 +297,20 @@ def test_add_project(self, mockbox): assert self.window.usernameLabel.text() == 'something' assert self.window.connectBtn.isVisible() is False self._create_project("Alpha", "Description Alpha") - assert self.window.listProjectsMSC.model().rowCount() == 1 + assert mockbox.return_value.showMessage.call_count == 2 + with mock.patch("PyQt5.QtWidgets.QLineEdit.text", return_value=None): + self._create_project("Alpha2", "Description Alpha") + with mock.patch("PyQt5.QtWidgets.QTextEdit.toPlainText", return_value=None): + self._create_project("Alpha3", "Description Alpha") + self._create_project("/", "Description Alpha") + assert mockbox.return_value.showMessage.call_count == 5 + assert self.window.listProjects.model().rowCount() == 1 + self._create_project("reproduce-test", "Description Test") + assert self.window.listProjects.model().rowCount() == 2 + self._activate_project_at_index(0) + assert self.window.active_project_name == "Alpha" + self._activate_project_at_index(1) + assert self.window.active_project_name == "reproduce-test" @mock.patch("mslib.msui.mscolab.QtWidgets.QInputDialog.getText", return_value=("flight7", True)) def test_handle_delete_project(self, mocktext): diff --git a/mslib/msui/_tests/test_mss_pyui.py b/mslib/msui/_tests/test_mss_pyui.py index 5cef0e6cf..28e3b6912 100644 --- a/mslib/msui/_tests/test_mss_pyui.py +++ b/mslib/msui/_tests/test_mss_pyui.py @@ -28,6 +28,7 @@ import sys import mock +import fs import os import pytest from urllib.request import urlopen @@ -36,7 +37,7 @@ from mslib._tests.constants import ROOT_DIR import mslib.msui.mss_pyui as mss_pyui from mslib._tests.utils import ExceptionMock -from mslib.plugins.io.text import load_from_txt, save_to_txt +from mslib.plugins.io.text import load_from_txt from mslib.plugins.io.flitestar import load_from_flitestar @@ -77,11 +78,27 @@ def test_shortcuts_present(self): class Test_MSSSideViewWindow(object): + # temporary file paths to test open feature sample_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "docs", "samples", "flight-tracks") + open_csv = os.path.join(sample_path, "example.csv") + open_ftml = os.path.join(sample_path, "example.ftml") + open_txt = os.path.join(sample_path, "example.txt") + open_fls = os.path.join(sample_path, "flitestar.txt") + # temporary file paths to test save feature save_csv = os.path.join(ROOT_DIR, "example.csv") save_ftml = os.path.join(ROOT_DIR, "example.ftml") save_ftml = save_ftml.replace('\\', '/') save_txt = os.path.join(ROOT_DIR, "example.txt") + # import/export plugins + import_plugins = { + "Text": ["txt", "mslib.plugins.io.text", "load_from_txt"], + "FliteStar": ["fls", "mslib.plugins.io.flitestar", "load_from_flitestar"], + } + export_plugins = { + "Text": ["txt", "mslib.plugins.io.text", "save_to_txt"], + # "KML": ["kml", "mslib.plugins.io.kml", "save_to_kml"], + # "GPX": ["gpx", "mslib.plugins.io.gpx", "save_to_gpx"] + } def setup(self): self.application = QtWidgets.QApplication(sys.argv) @@ -157,27 +174,86 @@ def test_open_about(self, mockbox): @mock.patch("PyQt5.QtWidgets.QMessageBox") def test_open_config(self, mockbox): + pytest.skip("To be done") self.window.actionConfigurationEditor.trigger() QtWidgets.QApplication.processEvents() self.window.config_editor.close() assert mockbox.critical.call_count == 0 @mock.patch("PyQt5.QtWidgets.QMessageBox") - def test_open_shotcut(self, mockbox): + def test_open_shortcut(self, mockbox): self.window.actionShortcuts.trigger() QtWidgets.QApplication.processEvents() assert mockbox.critical.call_count == 0 - @mock.patch("mslib.msui.mss_pyui.get_save_filename", return_value=save_ftml) - def test_plugin_ftml_saveas(self, mocksave): - assert self.window.listFlightTracks.count() == 1 - assert mocksave.call_count == 0 - self.window.last_save_directory = ROOT_DIR - self.window.actionSaveActiveFlightTrackAs.trigger() - QtWidgets.QApplication.processEvents() - assert mocksave.call_count == 1 - assert os.path.exists(self.save_ftml) - os.remove(self.save_ftml) + @pytest.mark.parametrize("save_file", [save_ftml, save_csv]) + def test_default_plugins_saveas(self, save_file): + with mock.patch("mslib.msui.mss_pyui.get_save_filename", return_value=save_file) as mocksave: + assert self.window.listFlightTracks.count() == 1 + assert mocksave.call_count == 0 + self.window.last_save_directory = ROOT_DIR + self.window.actionSaveActiveFlightTrackAs.trigger() + QtWidgets.QApplication.processEvents() + assert mocksave.call_count == 1 + assert os.path.exists(save_file) + os.remove(save_file) + + @pytest.mark.parametrize("save_file", [save_txt]) + def test_external_plugins_saveas(self, save_file): + with mock.patch("mslib.msui.mss_pyui.config_loader", return_value=self.export_plugins): + self.window.add_export_plugins("qt") + with mock.patch("mslib.msui.mss_pyui.get_save_filename", return_value=save_file) as mocksave: + assert self.window.listFlightTracks.count() == 1 + assert mocksave.call_count == 0 + self.window.last_save_directory = ROOT_DIR + self.window.actionSaveActiveFlightTrackAs.trigger() + QtWidgets.QApplication.processEvents() + assert mocksave.call_count == 1 + assert os.path.exists(save_file) + os.remove(save_file) + + @pytest.mark.parametrize("open_file", [open_ftml, open_csv]) + def test_default_plugins_open(self, open_file): + with mock.patch("mslib.msui.mss_pyui.get_open_filename", return_value=open_file) as mockopen: + assert self.window.listFlightTracks.count() == 1 + assert mockopen.call_count == 0 + self.window.last_save_directory = ROOT_DIR + self.window.actionOpenFlightTrack.trigger() + QtWidgets.QApplication.processEvents() + assert mockopen.call_count == 1 + assert self.window.listFlightTracks.count() == 2 + + @pytest.mark.parametrize("open_file", [open_txt]) + def test_external_plugins_open(self, open_file): + with mock.patch("mslib.msui.mss_pyui.config_loader", return_value=self.import_plugins): + self.window.add_import_plugins("qt") + with mock.patch("mslib.msui.mss_pyui.get_open_filename", return_value=open_file) as mockopen: + assert self.window.listFlightTracks.count() == 1 + assert mockopen.call_count == 0 + self.window.last_save_directory = ROOT_DIR + self.window.actionOpenFlightTrack.trigger() + QtWidgets.QApplication.processEvents() + assert mockopen.call_count == 1 + assert self.window.listFlightTracks.count() == 2 + + @pytest.mark.parametrize("save_file", [save_ftml, save_csv, save_txt]) + def test_plugin_export(self, save_file): + with mock.patch("mslib.msui.mss_pyui.config_loader", return_value=self.export_plugins): + self.window.add_export_plugins("qt") + with mock.patch("mslib.msui.mss_pyui.get_save_filename", return_value=save_file) as mocksave: + assert self.window.listFlightTracks.count() == 1 + assert mocksave.call_count == 0 + self.window.last_save_directory = ROOT_DIR + ext = fs.path.splitext(save_file)[-1][1:] + full_name = f"actionExportFlightTrack{ext}" + for action in self.window.menuExportActiveFlightTrack.actions(): + if action.objectName() == full_name: + action.trigger() + break + QtWidgets.QApplication.processEvents() + assert mocksave.call_count == 1 + assert os.path.exists(save_file) + os.remove(save_file) @mock.patch("mslib.msui.mss_pyui.get_open_filename", return_value=os.path.join(sample_path, u"example.csv")) def test_plugin_csv_read(self, mockopen): @@ -185,85 +261,72 @@ def test_plugin_csv_read(self, mockopen): assert self.window.listFlightTracks.count() == 1 assert mockopen.call_count == 0 self.window.last_save_directory = self.sample_path - self.window.actionImportFlightTrackCSV.trigger() + self.window.actionImportFlightTrackcsv.trigger() QtWidgets.QApplication.processEvents() assert self.window.listFlightTracks.count() == 2 assert mockopen.call_count == 1 - @mock.patch("mslib.msui.mss_pyui.get_save_filename", return_value=save_csv) - def test_plugin_csv_write(self, mocksave): - assert self.window.listFlightTracks.count() == 1 - assert mocksave.call_count == 0 - self.window.last_save_directory = ROOT_DIR - self.window.actionExportFlightTrackCSV.trigger() - assert mocksave.call_count == 1 - assert os.path.exists(self.save_csv) - os.remove(self.save_csv) - @mock.patch("mslib.msui.mss_pyui.get_open_filename", return_value=os.path.join(sample_path, u"example.txt")) def test_plugin_txt_read(self, mockopen): pytest.skip("To be done") - self.window.add_plugin_submenu("_TXT", "txt", plugin_type="Import") + self.window.add_plugin_submenu("Text", "txt", "qt", plugin_type="Import") self.window.import_plugins['txt'] = load_from_txt assert self.window.listFlightTracks.count() == 1 assert mockopen.call_count == 0 self.window.last_save_directory = self.sample_path - self.window.actionImportFlightTrack_TXT.trigger() + self.window.actionImportFlightTracktxt.trigger() assert mockopen.call_count == 1 QtWidgets.QApplication.processEvents() assert self.window.listFlightTracks.count() == 2 - @mock.patch("mslib.msui.mss_pyui.get_save_filename", return_value=save_txt) - def test_plugin_txt_write(self, mocksave): - self.window.add_plugin_submenu("_TXT", "txt", plugin_type="Export") - self.window.export_plugins['txt'] = save_to_txt - self.window.last_save_directory = ROOT_DIR - self.window.actionExportFlightTrack_TXT.trigger() - assert mocksave.call_count == 1 - QtWidgets.QApplication.processEvents() - assert self.window.listFlightTracks.count() == 1 - assert os.path.exists(self.save_txt) - os.remove(self.save_txt) - @mock.patch("mslib.msui.mss_pyui.get_open_filename", return_value=os.path.join(sample_path, u"flitestar.txt")) def test_plugin_flitestar(self, mockopen): pytest.skip("To be done") self.window.last_save_directory = self.sample_path - self.window.add_plugin_submenu("_FliteStar", "fls", plugin_type="Import") + self.window.add_plugin_submenu("FliteStar", "fls", "qt", plugin_type="Import") self.window.import_plugins['fls'] = load_from_flitestar assert self.window.listFlightTracks.count() == 1 - self.window.actionImportFlightTrack_FliteStar.trigger() + self.window.actionImportFlightTrackfls.trigger() QtWidgets.QApplication.processEvents() assert self.window.listFlightTracks.count() == 2 assert mockopen.call_count == 1 @mock.patch("PyQt5.QtWidgets.QMessageBox") - @mock.patch("mslib.msui.mss_pyui.config_loader", - return_value={"Text": ["txt", "mslib.plugins.io.text", "save_to_txt"]}) + @mock.patch("mslib.msui.mss_pyui.config_loader", return_value=export_plugins) def test_add_plugins(self, mockopen, mockbox): - assert len(self.window.menuImport_Flight_Track.actions()) == 1 - assert len(self.window.menuExport_Active_Flight_Track.actions()) == 1 - assert len(self.window._imported_plugins) == 0 - assert len(self.window._exported_plugins) == 0 - self.window.add_plugins() - assert len(self.window._imported_plugins) == 1 - assert len(self.window._exported_plugins) == 1 - assert len(self.window.menuImport_Flight_Track.actions()) == 2 - assert len(self.window.menuExport_Active_Flight_Track.actions()) == 2 + assert len(self.window.menuImportFlightTrack.actions()) == 2 + assert len(self.window.menuExportActiveFlightTrack.actions()) == 2 + assert len(self.window.import_plugins) == 1 + assert len(self.window.export_plugins) == 1 + + self.window.remove_plugins() + self.window.add_import_plugins("qt") + self.window.add_export_plugins("qt") + assert len(self.window.import_plugins) == 1 + assert len(self.window.export_plugins) == 1 + assert len(self.window.menuImportFlightTrack.actions()) == 2 + assert len(self.window.menuExportActiveFlightTrack.actions()) == 2 assert mockbox.critical.call_count == 0 + self.window.remove_plugins() with mock.patch("importlib.import_module", new=ExceptionMock(Exception()).raise_exc): - self.window.add_plugins() + self.window.add_import_plugins("qt") + self.window.add_export_plugins("qt") assert mockbox.critical.call_count == 2 - with mock.patch("mslib.msui.mss_pyui.MSSMainWindow.add_import_filter", + + self.window.remove_plugins() + with mock.patch("mslib.msui.mss_pyui.MSSMainWindow.add_plugin_submenu", new=ExceptionMock(Exception()).raise_exc): - self.window.add_plugins() + self.window.add_import_plugins("qt") + self.window.add_export_plugins("qt") assert mockbox.critical.call_count == 4 self.window.remove_plugins() - assert len(self.window.menuImport_Flight_Track.actions()) == 1 - assert len(self.window.menuExport_Active_Flight_Track.actions()) == 1 + assert len(self.window.import_plugins) == 0 + assert len(self.window.export_plugins) == 0 + assert len(self.window.menuImportFlightTrack.actions()) == 1 + assert len(self.window.menuExportActiveFlightTrack.actions()) == 1 @mock.patch("PyQt5.QtWidgets.QMessageBox.critical") @mock.patch("PyQt5.QtWidgets.QMessageBox.warning", return_value=QtWidgets.QMessageBox.Yes) diff --git a/mslib/msui/mscolab.py b/mslib/msui/mscolab.py index 32a57f43c..00f54d010 100644 --- a/mslib/msui/mscolab.py +++ b/mslib/msui/mscolab.py @@ -202,18 +202,18 @@ def connect_handler(self): # self.loginEmailLe.setFocus() else: self.set_status("Error", "Some unexpected error occurred. Please try again.") - except requests.exceptions.ConnectionError: - logging.debug("MSColab server isn't active") - self.set_status("Error", "MSColab server isn't active") + except requests.exceptions.SSLError: + logging.debug("Certificate Verification Failed") + self.set_status("Error", "Certificate Verification Failed") except requests.exceptions.InvalidSchema: logging.debug("invalid schema of url") self.set_status("Error", "Invalid Url Scheme!") except requests.exceptions.InvalidURL: logging.debug("invalid url") self.set_status("Error", "Invalid URL") - except requests.exceptions.SSLError: - logging.debug("Certificate Verification Failed") - self.set_status("Error", "Certificate Verification Failed") + except requests.exceptions.ConnectionError: + logging.debug("MSColab server isn't active") + self.set_status("Error", "MSColab server isn't active") except Exception as e: logging.debug("Error %s", str(e)) self.set_status("Error", "Some unexpected error occurred. Please try again.") @@ -819,6 +819,7 @@ def fetch_wp_mscolab(self): self.waypoints_model.dataChanged.connect(self.handle_waypoints_changed) self.reload_view_windows() show_popup(self.ui, "Success", "New Waypoints Fetched To Local File!", icon=1) + self.merge_dialog.close() self.merge_dialog = None else: show_popup(self.ui, "Error", "Your Connection is expired. New Login required!") @@ -839,6 +840,7 @@ def save_wp_mscolab(self, comment=None): self.waypoints_model.dataChanged.connect(self.handle_waypoints_changed) self.reload_view_windows() show_popup(self.ui, "Success", "New Waypoints Saved To Server!", icon=1) + self.merge_dialog.close() self.merge_dialog = None else: show_popup(self.ui, "Error", "Your Connection is expired. New Login required!") @@ -1174,7 +1176,7 @@ def reload_view_windows(self): except AttributeError as err: logging.error("%s" % err) - def handle_import_msc(self, extension): + def handle_import_msc(self, extension, pickertype): if self.verify_user_token(): if self.active_pid is None: return @@ -1182,7 +1184,9 @@ def handle_import_msc(self, extension): if self.ui.workLocallyCheckbox.isChecked() and extension != "ftml": self.ui.statusBar().showMessage("Work Locally only supports FTML filetypes for import") return - file_path = get_open_filename(self.ui, "Import to Server", "", f"Flight track (*.{extension})") + file_path = get_open_filename( + self.ui, "Import to Server", "", f"Flight track (*.{extension})", pickertype=pickertype + ) if file_path is None: return dir_path, file_name = fs.path.split(file_path) @@ -1218,7 +1222,7 @@ def handle_import_msc(self, extension): show_popup(self.ui, "Error", "Your Connection is expired. New Login required!") self.logout() - def handle_export_msc(self, extension): + def handle_export_msc(self, extension, pickertype): if self.verify_user_token(): if self.active_pid is None: return @@ -1227,7 +1231,8 @@ def handle_export_msc(self, extension): default_filename = f'{self.active_project_name}.{extension}' file_name = get_save_filename( self.ui, "Export From Server", - default_filename, f"Flight track (*.{extension})") + default_filename, f"Flight track (*.{extension})", + pickertype=pickertype) if file_name is None: return if file_name.endswith('.ftml'): diff --git a/mslib/msui/mss_pyui.py b/mslib/msui/mss_pyui.py index 2cdd362fc..f2468b8a6 100644 --- a/mslib/msui/mss_pyui.py +++ b/mslib/msui/mss_pyui.py @@ -270,7 +270,15 @@ def __init__(self, mscolab_data_dir=None, *args): self.listViews.itemActivated.connect(self.activate_sub_window) # Add default and plugins from settings - self.add_plugins() + picker_default = config_loader(dataset="filepicker_default") + self.add_plugin_submenu("FTML", "ftml", picker_default, plugin_type="Import") + self.add_plugin_submenu("FTML", "ftml", picker_default, plugin_type="Export") + self.add_plugin_submenu("CSV", "csv", picker_default, plugin_type="Import") + self.add_plugin_submenu("CSV", "csv", picker_default, plugin_type="Export") + self.import_plugins = {"csv": load_from_csv} + self.export_plugins = {"csv": save_to_csv} + self.add_import_plugins(picker_default) + self.add_export_plugins(picker_default) preload_urls = config_loader(dataset="WMS_preload") self.preload_wms(preload_urls) @@ -349,14 +357,14 @@ def menu_handler(self): self.actionSaveActiveFlightTrackAs.setEnabled(self.local_active) # self.actionCloseSelectedFlightTrack.setEnabled(self.local_active) - def add_plugin_submenu(self, name, extension, plugin_type="Import"): + def add_plugin_submenu(self, name, extension, pickertype, plugin_type="Import"): if plugin_type == "Import": menu = self.menuImportFlightTrack - action_name = "actionImportFlightTrack" + clean_string(name) + action_name = "actionImportFlightTrack" + clean_string(extension) handler = self.handle_import_local elif plugin_type == "Export": menu = self.menuExportActiveFlightTrack - action_name = "actionExportFlightTrack" + clean_string(name) + action_name = "actionExportFlightTrack" + clean_string(extension) handler = self.handle_export_local if hasattr(self, action_name): @@ -364,49 +372,63 @@ def add_plugin_submenu(self, name, extension, plugin_type="Import"): action = QtWidgets.QAction(self) action.setObjectName(action_name) action.setText(QtCore.QCoreApplication.translate("MSSMainWindow", name, None)) - action.triggered.connect(functools.partial(handler, extension)) + action.triggered.connect(functools.partial(handler, extension, pickertype)) menu.addAction(action) setattr(self, action_name, action) - def add_plugins(self): - self.add_plugin_submenu("FTML", "ftml", plugin_type="Import") - self.add_plugin_submenu("CSV", "csv", plugin_type="Import") - self.add_plugin_submenu("FTML", "ftml", plugin_type="Export") - self.add_plugin_submenu("CSV", "csv", plugin_type="Export") - # self.actionImportFlightTrackFTML.setVisible(False) - # self.actionExportFlightTrackFTML.setVisible(False) - - self.import_plugins = {"csv": load_from_csv} + def add_import_plugins(self, picker_default): plugins = config_loader(dataset="import_plugins") for name in plugins: extension, module, function = plugins[name][:3] + picker_type = picker_default + if len(plugins[name]) == 4: + picker_type = plugins[name][3] try: imported_module = importlib.import_module(module) - self.import_plugins[extension] = getattr(imported_module, function) - self.add_plugin_submenu(name, extension, plugin_type="Import") # wildcard exception to be resilient against error introduced by user code except Exception as ex: logging.error("Error on import: %s: %s", type(ex), ex) QtWidgets.QMessageBox.critical( self, self.tr("file io plugin error import plugins"), - self.tr(f"ERROR: Configuration\n\n{self._plugins,}\n\nthrows {type(ex)} error:\n{ex}")) + self.tr(f"ERROR: Configuration\n\n{plugins,}\n\nthrows {type(ex)} error:\n{ex}")) + continue + try: + self.add_plugin_submenu(name, extension, picker_type, plugin_type="Import") + # wildcard exception to be resilient against error introduced by user code + except Exception as ex: + logging.error("Error on installing plugin: %s: %s", type(ex), ex) + QtWidgets.QMessageBox.critical( + self, self.tr("file io plugin error import plugins"), + self.tr(f"ERROR: Configuration\n\n{self.import_plugins}\n\nthrows {type(ex)} error:\n{ex}")) continue + self.import_plugins[extension] = getattr(imported_module, function) - self.export_plugins = {"csv": save_to_csv} + def add_export_plugins(self, picker_default): plugins = config_loader(dataset="export_plugins") for name in plugins: extension, module, function = plugins[name][:3] + picker_type = picker_default + if len(plugins[name]) == 4: + picker_type = plugins[name][3] try: imported_module = importlib.import_module(module) - self.export_plugins[extension] = getattr(imported_module, function) - self.add_plugin_submenu(name, extension, plugin_type="Export") # wildcard exception to be resilient against error introduced by user code except Exception as ex: logging.error("Error on import: %s: %s", type(ex), ex) + QtWidgets.QMessageBox.critical( + self, self.tr("file io plugin error export plugins"), + self.tr(f"ERROR: Configuration\n\n{plugins,}\n\nthrows {type(ex)} error:\n{ex}")) + continue + try: + self.add_plugin_submenu(name, extension, picker_type, plugin_type="Export") + # wildcard exception to be resilient against error introduced by user code + except Exception as ex: + logging.error("Error on installing plugin: %s: %s", type(ex), ex) QtWidgets.QMessageBox.critical( self, self.tr("file io plugin error import plugins"), - self.tr(f"ERROR: Configuration\n\n{self._plugins,}\n\nthrows {type(ex)} error:\n{ex}")) + self.tr(f"ERROR: Configuration\n\n{self.export_plugins}\n\nthrows {type(ex)} error:\n{ex}")) continue + self.export_plugins[extension] = getattr(imported_module, function) def remove_plugins(self): for name in self.import_plugins: @@ -416,6 +438,7 @@ def remove_plugins(self): assert len(actions) == 1 self.menuImportFlightTrack.removeAction(actions[0]) delattr(self, full_name) + self.import_plugins = {} for name in self.export_plugins: full_name = "actionExportFlightTrack" + clean_string(name) @@ -424,14 +447,16 @@ def remove_plugins(self): assert len(actions) == 1 self.menuExportActiveFlightTrack.removeAction(actions[0]) delattr(self, full_name) + self.export_plugins = {} - def handle_import_local(self, extension): + def handle_import_local(self, extension, pickertype): if self.local_active: function = self.import_plugins[extension] filename = get_open_filename( self, "Import Flight Track", self.last_save_directory, - f"Flight Track (*.{extension})") + f"Flight Track (*.{extension})", + pickertype=pickertype) if filename is not None: self.last_save_directory = fs.path.dirname(filename) try: @@ -453,19 +478,28 @@ def handle_import_local(self, extension): self.listFlightTracks.setCurrentItem(listitem) self.activate_flight_track(listitem) else: - self.mscolab.handle_import_msc(extension) + self.mscolab.handle_import_msc(extension, pickertype) - def handle_export_local(self, extension): + def handle_export_local(self, extension, pickertype): if self.local_active: - function = self.export_plugins[extension] default_filename = f'{os.path.join(self.last_save_directory, self.active_flight_track.name)}.{extension}' filename = get_save_filename( self, "Export Flight Track", - default_filename, f"Flight Track (*.{extension})") + default_filename, f"Flight Track (*.{extension})", + pickertype=pickertype) if filename is not None: self.last_save_directory = fs.path.dirname(filename) try: - function(filename, self.active_flight_track.name, self.active_flight_track.waypoints) + if extension in self.export_plugins: + function = self.export_plugins[extension] + function(filename, self.active_flight_track.name, self.active_flight_track.waypoints) + elif filename.endswith('.ftml'): + doc = self.active_flight_track.get_xml_doc() + dirname, name = fs.path.split(filename) + file_dir = fs.open_fs(dirname) + with file_dir.open(name, 'w') as file_object: + doc.writexml(file_object, indent=" ", addindent=" ", newl="\n", encoding="utf-8") + file_dir.close() # wildcard exception to be resilient against error introduced by user code except Exception as ex: logging.error("file io plugin error: %s %s", type(ex), ex) @@ -473,7 +507,7 @@ def handle_export_local(self, extension): self, self.tr("file io plugin error"), self.tr(f"ERROR: {type(ex)} {ex}")) else: - self.mscolab.handle_export_msc(extension) + self.mscolab.handle_export_msc(extension, pickertype) def create_new_flight_track(self, template=None, filename=None): """Creates a new flight track model from a template. Adds a new entry to @@ -509,13 +543,20 @@ def create_new_flight_track(self, template=None, filename=None): try: ext = fs.path.splitext(filename)[-1][1:] function = self.import_plugins[ext] - ft_name, waypoints_model = function(filename) + ft_name, new_waypoints = function(filename) + waypoints_model = ft.WaypointsTableModel(name=ft_name, waypoints=new_waypoints) # wildcard exception to be resilient against error introduced by user code except Exception as ex: logging.error("file io plugin error: %s %s", type(ex), ex) QtWidgets.QMessageBox.critical( self, self.tr("file io plugin error"), self.tr(f"ERROR: {type(ex)} {ex}")) + # if waypoints_model is not None: + # for i in range(self.listFlightTracks.count()): + # fltr = self.listFlightTracks.item(i) + # if fltr.flighttrack_model.name == waypoints_model.name: + # waypoints_model.name += " - opened from file" + # break else: # Create a new flight track from the waypoints template. self.new_flight_track_counter += 1 @@ -537,7 +578,7 @@ def open_flight_track(self): """Slot for the 'Open Flight Track' menu entry. Opens a QFileDialog and passes the result to createNewFlightTrack(). """ - file_type = ["Flight track (*.ftml)"] + [f"Flight track (*.{ext})" for ext in self.export_plugins.keys()] + file_type = ["Flight track (*.ftml)"] + [f"Flight track (*.{ext})" for ext in self.import_plugins.keys()] filename = get_open_filename( self, "Open Flight Track", self.last_save_directory, ';;'.join(file_type), pickertag="filepicker_default") @@ -627,7 +668,7 @@ def close_selected_flight_track(self): self.tr("At least one flight track has to be open.")) return item = self.listFlightTracks.currentItem() - if item.flighttrack_model == self.active_flight_track and not self.local_active: + if item.flighttrack_model == self.active_flight_track and self.local_active: QtWidgets.QMessageBox.information(self, self.tr("Flight Track Management"), self.tr("Cannot close currently active flight track.")) return