From 00de7497a4c2986cf8fd13be8f598a0f615f3d63 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jan 2021 15:48:51 +0100 Subject: [PATCH] Move open dialogue to separate file provider plug-in We can now define plug-ins that specify where to open files from. This is one of the places where you can open files. This breaks the main button to open files in the interface. It needs to be redirected to trigger the plug-in to show the open file dialogue. Contributest o issue CURA-7868. --- .../LocalFileProvider/LocalFileProvider.py | 47 +++++ plugins/LocalFileProvider/OpenLocalFile.qml | 171 ++++++++++++++++++ plugins/LocalFileProvider/__init__.py | 11 ++ plugins/LocalFileProvider/plugin.json | 8 + resources/qml/Cura.qml | 147 --------------- 5 files changed, 237 insertions(+), 147 deletions(-) create mode 100644 plugins/LocalFileProvider/LocalFileProvider.py create mode 100644 plugins/LocalFileProvider/OpenLocalFile.qml create mode 100644 plugins/LocalFileProvider/__init__.py create mode 100644 plugins/LocalFileProvider/plugin.json diff --git a/plugins/LocalFileProvider/LocalFileProvider.py b/plugins/LocalFileProvider/LocalFileProvider.py new file mode 100644 index 0000000000..7917b649c2 --- /dev/null +++ b/plugins/LocalFileProvider/LocalFileProvider.py @@ -0,0 +1,47 @@ +# Copyright (c) 2021 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import os.path + +from UM.FileProvider import FileProvider # The plug-in type we're going to implement. +from UM.i18n import i18nCatalog +from UM.Logger import Logger +from UM.PluginRegistry import PluginRegistry # To get resources from the plug-in folder. +from cura.CuraApplication import CuraApplication # To create QML elements. + +i18n_catalog = i18nCatalog("cura") + + +class LocalFileProvider(FileProvider): + """ + Allows the user to open files from their local file system. + + These files will then be interpreted through the file handlers. + """ + + def __init__(self): + super().__init__() + self.menu_item_display_text = i18n_catalog.i18nc("@menu Open files from local disk", "Local disk") + self.shortcut = "Ctrl+O" + + self._dialog = None # Lazy-load this QML element. + + def _load_file_dialog(self): + """ + Loads the file dialog QML element into the QML context so that it can be shown. + :return: + """ + plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId()) + if plugin_path is None: + plugin_path = os.path.dirname(__file__) + path = os.path.join(plugin_path, "OpenLocalFile.qml") + self._dialog = CuraApplication.getInstance().createQmlComponent(path) + if self._dialog is None: + Logger.log("e", "Unable to create open file dialogue.") + + def run(self): + if self._dialog is None: + self._load_file_dialog() + if self._dialog is None: + return # Will already have logged an error in _load_file_dialog. + self._dialog.show() \ No newline at end of file diff --git a/plugins/LocalFileProvider/OpenLocalFile.qml b/plugins/LocalFileProvider/OpenLocalFile.qml new file mode 100644 index 0000000000..f78fe4b991 --- /dev/null +++ b/plugins/LocalFileProvider/OpenLocalFile.qml @@ -0,0 +1,171 @@ +// Copyright (c) 2021 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Dialogs 1.2 + +import UM 1.3 as UM +import Cura 1.1 as Cura + +Item +{ + id: base + + function show() + { + openDialog.visible = true; + } + + UM.I18nCatalog + { + id: catalog + name: "cura" + } + + FileDialog + { + id: openDialog; + + //: File open dialog title + title: catalog.i18nc("@title:window","Open file(s)") + modality: Qt.WindowModal + selectMultiple: true + nameFilters: UM.MeshFileHandler.supportedReadFileTypes; + folder: + { + //Because several implementations of the file dialog only update the folder when it is explicitly set. + folder = CuraApplication.getDefaultPath("dialog_load_path"); + return CuraApplication.getDefaultPath("dialog_load_path"); + } + onAccepted: + { + // Because several implementations of the file dialog only update the folder + // when it is explicitly set. + var f = folder; + folder = f; + + CuraApplication.setDefaultPath("dialog_load_path", folder); + + handleOpenFileUrls(fileUrls); + } + + // Yeah... I know... it is a mess to put all those things here. + // There are lots of user interactions in this part of the logic, such as showing a warning dialog here and there, + // etc. This means it will come back and forth from time to time between QML and Python. So, separating the logic + // and view here may require more effort but make things more difficult to understand. + function handleOpenFileUrls(fileUrlList) + { + // look for valid project files + var projectFileUrlList = []; + var hasGcode = false; + var nonGcodeFileList = []; + for (var i in fileUrlList) + { + var endsWithG = /\.g$/; + var endsWithGcode = /\.gcode$/; + if (endsWithG.test(fileUrlList[i]) || endsWithGcode.test(fileUrlList[i])) + { + continue; + } + else if (CuraApplication.checkIsValidProjectFile(fileUrlList[i])) + { + projectFileUrlList.push(fileUrlList[i]); + } + nonGcodeFileList.push(fileUrlList[i]); + } + hasGcode = nonGcodeFileList.length < fileUrlList.length; + + // show a warning if selected multiple files together with Gcode + var hasProjectFile = projectFileUrlList.length > 0; + var selectedMultipleFiles = fileUrlList.length > 1; + if (selectedMultipleFiles && hasGcode) + { + infoMultipleFilesWithGcodeDialog.selectedMultipleFiles = selectedMultipleFiles; + infoMultipleFilesWithGcodeDialog.hasProjectFile = hasProjectFile; + infoMultipleFilesWithGcodeDialog.fileUrls = nonGcodeFileList.slice(); + infoMultipleFilesWithGcodeDialog.projectFileUrlList = projectFileUrlList.slice(); + infoMultipleFilesWithGcodeDialog.open(); + } + else + { + handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList); + } + } + + function handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList) + { + // we only allow opening one project file + if (selectedMultipleFiles && hasProjectFile) + { + openFilesIncludingProjectsDialog.fileUrls = fileUrlList.slice(); + openFilesIncludingProjectsDialog.show(); + return; + } + + if (hasProjectFile) + { + var projectFile = projectFileUrlList[0]; + + // check preference + var choice = UM.Preferences.getValue("cura/choice_on_open_project"); + if (choice == "open_as_project") + { + openFilesIncludingProjectsDialog.loadProjectFile(projectFile); + } + else if (choice == "open_as_model") + { + openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice()); + } + else // always ask + { + // ask whether to open as project or as models + askOpenAsProjectOrModelsDialog.fileUrl = projectFile; + askOpenAsProjectOrModelsDialog.show(); + } + } + else + { + openFilesIncludingProjectsDialog.loadModelFiles(fileUrlList.slice()); + } + } + } + + MessageDialog + { + id: infoMultipleFilesWithGcodeDialog + title: catalog.i18nc("@title:window", "Open File(s)") + icon: StandardIcon.Information + standardButtons: StandardButton.Ok + text: catalog.i18nc("@text:window", "We have found one or more G-Code files within the files you have selected. You can only open one G-Code file at a time. If you want to open a G-Code file, please just select only one.") + + property var selectedMultipleFiles + property var hasProjectFile + property var fileUrls + property var projectFileUrlList + + onAccepted: + { + openDialog.handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList); + } + } + + Cura.AskOpenAsProjectOrModelsDialog + { + id: askOpenAsProjectOrModelsDialog + } + + Connections + { + target: CuraApplication + onOpenProjectFile: + { + askOpenAsProjectOrModelsDialog.fileUrl = project_file; + askOpenAsProjectOrModelsDialog.show(); + } + } + + Cura.OpenFilesIncludingProjectsDialog + { + id: openFilesIncludingProjectsDialog + } +} \ No newline at end of file diff --git a/plugins/LocalFileProvider/__init__.py b/plugins/LocalFileProvider/__init__.py new file mode 100644 index 0000000000..63845d7820 --- /dev/null +++ b/plugins/LocalFileProvider/__init__.py @@ -0,0 +1,11 @@ +# Copyright (c) 2021 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from . import LocalFileProvider + + +def getMetaData(): + return {} + +def register(app): + return { "file_provider": LocalFileProvider.LocalFileProvider() } diff --git a/plugins/LocalFileProvider/plugin.json b/plugins/LocalFileProvider/plugin.json new file mode 100644 index 0000000000..cd4d77eb98 --- /dev/null +++ b/plugins/LocalFileProvider/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Local File Provider", + "description": "Enables opening files from the local file system.", + "author": "Ultimaker B.V.", + "version": "1.0.0", + "api": "7.4.0", + "i18n-catalog": "cura" +} \ No newline at end of file diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index bb7b5ac19c..ea615b3c17 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -619,114 +619,6 @@ UM.MainWindow onTriggered: base.exitFullscreen() } - FileDialog - { - id: openDialog; - - //: File open dialog title - title: catalog.i18nc("@title:window","Open file(s)") - modality: Qt.WindowModal - selectMultiple: true - nameFilters: UM.MeshFileHandler.supportedReadFileTypes; - folder: - { - //Because several implementations of the file dialog only update the folder when it is explicitly set. - folder = CuraApplication.getDefaultPath("dialog_load_path"); - return CuraApplication.getDefaultPath("dialog_load_path"); - } - onAccepted: - { - // Because several implementations of the file dialog only update the folder - // when it is explicitly set. - var f = folder; - folder = f; - - CuraApplication.setDefaultPath("dialog_load_path", folder); - - handleOpenFileUrls(fileUrls); - } - - // Yeah... I know... it is a mess to put all those things here. - // There are lots of user interactions in this part of the logic, such as showing a warning dialog here and there, - // etc. This means it will come back and forth from time to time between QML and Python. So, separating the logic - // and view here may require more effort but make things more difficult to understand. - function handleOpenFileUrls(fileUrlList) - { - // look for valid project files - var projectFileUrlList = []; - var hasGcode = false; - var nonGcodeFileList = []; - for (var i in fileUrlList) - { - var endsWithG = /\.g$/; - var endsWithGcode = /\.gcode$/; - if (endsWithG.test(fileUrlList[i]) || endsWithGcode.test(fileUrlList[i])) - { - continue; - } - else if (CuraApplication.checkIsValidProjectFile(fileUrlList[i])) - { - projectFileUrlList.push(fileUrlList[i]); - } - nonGcodeFileList.push(fileUrlList[i]); - } - hasGcode = nonGcodeFileList.length < fileUrlList.length; - - // show a warning if selected multiple files together with Gcode - var hasProjectFile = projectFileUrlList.length > 0; - var selectedMultipleFiles = fileUrlList.length > 1; - if (selectedMultipleFiles && hasGcode) - { - infoMultipleFilesWithGcodeDialog.selectedMultipleFiles = selectedMultipleFiles; - infoMultipleFilesWithGcodeDialog.hasProjectFile = hasProjectFile; - infoMultipleFilesWithGcodeDialog.fileUrls = nonGcodeFileList.slice(); - infoMultipleFilesWithGcodeDialog.projectFileUrlList = projectFileUrlList.slice(); - infoMultipleFilesWithGcodeDialog.open(); - } - else - { - handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList); - } - } - - function handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList) - { - // we only allow opening one project file - if (selectedMultipleFiles && hasProjectFile) - { - openFilesIncludingProjectsDialog.fileUrls = fileUrlList.slice(); - openFilesIncludingProjectsDialog.show(); - return; - } - - if (hasProjectFile) - { - var projectFile = projectFileUrlList[0]; - - // check preference - var choice = UM.Preferences.getValue("cura/choice_on_open_project"); - if (choice == "open_as_project") - { - openFilesIncludingProjectsDialog.loadProjectFile(projectFile); - } - else if (choice == "open_as_model") - { - openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice()); - } - else // always ask - { - // ask whether to open as project or as models - askOpenAsProjectOrModelsDialog.fileUrl = projectFile; - askOpenAsProjectOrModelsDialog.show(); - } - } - else - { - openFilesIncludingProjectsDialog.loadModelFiles(fileUrlList.slice()); - } - } - } - MessageDialog { id: packageInstallDialog @@ -735,51 +627,12 @@ UM.MainWindow modality: Qt.ApplicationModal } - MessageDialog - { - id: infoMultipleFilesWithGcodeDialog - title: catalog.i18nc("@title:window", "Open File(s)") - icon: StandardIcon.Information - standardButtons: StandardButton.Ok - text: catalog.i18nc("@text:window", "We have found one or more G-Code files within the files you have selected. You can only open one G-Code file at a time. If you want to open a G-Code file, please just select only one.") - - property var selectedMultipleFiles - property var hasProjectFile - property var fileUrls - property var projectFileUrlList - - onAccepted: - { - openDialog.handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList); - } - } - Connections { target: Cura.Actions.open onTriggered: openDialog.open() } - OpenFilesIncludingProjectsDialog - { - id: openFilesIncludingProjectsDialog - } - - AskOpenAsProjectOrModelsDialog - { - id: askOpenAsProjectOrModelsDialog - } - - Connections - { - target: CuraApplication - onOpenProjectFile: - { - askOpenAsProjectOrModelsDialog.fileUrl = project_file; - askOpenAsProjectOrModelsDialog.show(); - } - } - Connections { target: Cura.Actions.showProfileFolder