diff --git a/cura/Machines/Models/QualityManagementModel.py b/cura/Machines/Models/QualityManagementModel.py index cf7a163d9c..6e5b37bbfa 100644 --- a/cura/Machines/Models/QualityManagementModel.py +++ b/cura/Machines/Models/QualityManagementModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, pyqtSlot from UM.Qt.ListModel import ListModel @@ -74,3 +74,45 @@ class QualityManagementModel(ListModel): item_list += quality_changes_item_list self.setItems(item_list) + + # TODO: Duplicated code here from InstanceContainersModel. Refactor and remove this later. + # + ## Gets a list of the possible file filters that the plugins have + # registered they can read or write. The convenience meta-filters + # "All Supported Types" and "All Files" are added when listing + # readers, but not when listing writers. + # + # \param io_type \type{str} name of the needed IO type + # \return A list of strings indicating file name filters for a file + # dialog. + @pyqtSlot(str, result = "QVariantList") + def getFileNameFilters(self, io_type): + from UM.i18n import i18nCatalog + catalog = i18nCatalog("uranium") + #TODO: This function should be in UM.Resources! + filters = [] + all_types = [] + for plugin_id, meta_data in self._getIOPlugins(io_type): + for io_plugin in meta_data[io_type]: + filters.append(io_plugin["description"] + " (*." + io_plugin["extension"] + ")") + all_types.append("*.{0}".format(io_plugin["extension"])) + + if "_reader" in io_type: + # if we're listing readers, add the option to show all supported files as the default option + filters.insert(0, catalog.i18nc("@item:inlistbox", "All Supported Types ({0})", " ".join(all_types))) + filters.append(catalog.i18nc("@item:inlistbox", "All Files (*)")) # Also allow arbitrary files, if the user so prefers. + return filters + + ## Gets a list of profile reader or writer plugins + # \return List of tuples of (plugin_id, meta_data). + def _getIOPlugins(self, io_type): + from UM.PluginRegistry import PluginRegistry + pr = PluginRegistry.getInstance() + active_plugin_ids = pr.getActivePlugins() + + result = [] + for plugin_id in active_plugin_ids: + meta_data = pr.getMetaData(plugin_id) + if io_type in meta_data: + result.append( (plugin_id, meta_data) ) + return result diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index af4924b8b3..6eb9aa0311 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -171,18 +171,6 @@ class ContainerManager(QObject): material_group = self._material_manager.getMaterialGroup(root_material_id) material_group.root_material_node.getContainer().setName(new_name) - ## Find instance containers matching certain criteria. - # - # This effectively forwards to - # ContainerRegistry::findInstanceContainersMetadata. - # - # \param criteria A dict of key - value pairs to search for. - # - # \return A list of container IDs that match the given criteria. - @pyqtSlot("QVariantMap", result = "QVariantList") - def findInstanceContainers(self, criteria): - return [entry["id"] for entry in self._container_registry.findInstanceContainersMetadata(**criteria)] - @pyqtSlot(str, result = str) def makeUniqueName(self, original_name): return self._container_registry.uniqueName(original_name) @@ -716,11 +704,13 @@ class ContainerManager(QObject): return return self._container_registry.importProfile(path) - @pyqtSlot("QVariantList", QUrl, str) - def exportProfile(self, instance_id: str, file_url: QUrl, file_type: str) -> None: + @pyqtSlot(QObject, QUrl, str) + def exportQualityChangesGroup(self, quality_changes_group, file_url: QUrl, file_type: str): if not file_url.isValid(): return path = file_url.toLocalFile() if not path: return - self._container_registry.exportProfile(instance_id, path, file_type) + + container_list = [n.getContainer() for n in quality_changes_group.getAllNodes()] + self._container_registry.exportQualityProfile(container_list, path, file_type) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 29ee5c4b95..7103167b9c 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -103,7 +103,7 @@ class CuraContainerRegistry(ContainerRegistry): # \param instance_ids \type{list} the IDs of the profiles to export. # \param file_name \type{str} the full path and filename to export to. # \param file_type \type{str} the file type with the format " (*.)" - def exportProfile(self, instance_ids, file_name, file_type): + def exportQualityProfile(self, container_list, file_name, file_type): # Parse the fileType to deduce what plugin can save the file format. # fileType has the format " (*.)" split = file_type.rfind(" (*.") # Find where the description ends and the extension starts. @@ -122,31 +122,10 @@ class CuraContainerRegistry(ContainerRegistry): catalog.i18nc("@label Don't translate the XML tag !", "The file {0} already exists. Are you sure you want to overwrite it?").format(file_name)) if result == QMessageBox.No: return - found_containers = [] - extruder_positions = [] - for instance_id in instance_ids: - containers = ContainerRegistry.getInstance().findInstanceContainers(id = instance_id) - if containers: - found_containers.append(containers[0]) - - # Determine the position of the extruder of this container - extruder_id = containers[0].getMetaDataEntry("extruder", "") - if extruder_id == "": - # Global stack - extruder_positions.append(-1) - else: - extruder_containers = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = extruder_id) - if extruder_containers: - extruder_positions.append(int(extruder_containers[0].get("position", 0))) - else: - extruder_positions.append(0) - # Ensure the profiles are always exported in order (global, extruder 0, extruder 1, ...) - found_containers = [containers for (positions, containers) in sorted(zip(extruder_positions, found_containers))] profile_writer = self._findProfileWriter(extension, description) - try: - success = profile_writer.write(file_name, found_containers) + success = profile_writer.write(file_name, container_list) except Exception as e: Logger.log("e", "Failed to export profile to %s: %s", file_name, str(e)) m = Message(catalog.i18nc("@info:status Don't translate the XML tags or !", "Failed to export profile to {0}: {1}", file_name, str(e)), diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index 57ec7a1222..ce8bd7dd3c 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -138,7 +138,7 @@ Item text: catalog.i18nc("@action:button", "Import") iconName: "document-import" onClicked: { - // TODO + importDialog.open(); } } @@ -149,7 +149,7 @@ Item iconName: "document-export" enabled: base.hasCurrentItem && !base.currentItem.is_read_only onClicked: { - // TODO + exportDialog.open(); } } } @@ -212,6 +212,57 @@ Item } } + // Dialog for importing a quality profile + FileDialog + { + id: importDialog + title: catalog.i18nc("@title:window", "Import Profile") + selectExisting: true + nameFilters: qualitiesModel.getFileNameFilters("profile_reader") // TODO: make this easier + folder: CuraApplication.getDefaultPath("dialog_profile_path") + onAccepted: + { + var result = Cura.ContainerManager.importProfile(fileUrl); + messageDialog.text = result.message; + if (result.status == "ok") { + messageDialog.icon = StandardIcon.Information; + } + else if (result.status == "duplicate") { + messageDialog.icon = StandardIcon.Warning; + } + else { + messageDialog.icon = StandardIcon.Critical; + } + messageDialog.open(); + CuraApplication.setDefaultPath("dialog_profile_path", folder); + } + } + + // Dialog for exporting a quality profile + FileDialog + { + id: exportDialog + title: catalog.i18nc("@title:window", "Export Profile") + selectExisting: false + nameFilters: qualitiesModel.getFileNameFilters("profile_writer") // TODO: make this easier + folder: CuraApplication.getDefaultPath("dialog_profile_path") + onAccepted: + { + // TODO: make this easier + var result = Cura.ContainerManager.exportQualityChangesGroup(base.currentItem.quality_changes_group, + fileUrl, selectedNameFilter); + + if (result && result.status == "error") { + messageDialog.icon = StandardIcon.Critical; + messageDialog.text = result.message; + messageDialog.open(); + } + + // else pop-up Message thing from python code + CuraApplication.setDefaultPath("dialog_profile_path", folder); + } + } + Item { id: contentsItem