diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index 0dfac6b84f..2a4f62e5fe 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -23,6 +23,7 @@ class CuraPackageManager(PackageManager): ## Returns a list of where the package is used # empty if it is never used. # It loops through all the package contents and see if some of the ids are used. + # The list consists of 3-tuples: (global_stack, extruder_nr, container_id) def packageUsed(self, package_id: str): ids = self.packageContainerIds(package_id) container_stacks = ContainerRegistry.getInstance().findContainerStacks() @@ -33,8 +34,8 @@ class CuraPackageManager(PackageManager): for global_stack in global_stacks: for extruder_nr, extruder_stack in global_stack.extruders.items(): if container_id == extruder_stack.material.getId() or container_id == extruder_stack.material.getMetaData().get("base_file"): - machine_with_materials.append((global_stack, extruder_nr)) + machine_with_materials.append((global_stack, extruder_nr, container_id)) if container_id == extruder_stack.quality.getId(): - machine_with_qualities.append((global_stack, extruder_nr)) + machine_with_qualities.append((global_stack, extruder_nr, container_id)) return machine_with_materials, machine_with_qualities diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 10d32b0a70..4f6c7c179e 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1445,6 +1445,8 @@ class MachineManager(QObject): def setQualityGroup(self, quality_group: QualityGroup, no_dialog: bool = False, global_stack: Optional["GlobalStack"] = None) -> None: if global_stack is not None and global_stack != self._global_container_stack: global_stack.quality = quality_group.node_for_global.getContainer() + for extruder_nr, extruder_stack in global_stack.extruders.items(): + extruder_stack.quality = quality_group.nodes_for_extruders[extruder_nr].getContainer() return self.blurSettings.emit() diff --git a/plugins/Toolbox/resources/qml/ToolboxConfirmUninstallResetDialog.qml b/plugins/Toolbox/resources/qml/ToolboxConfirmUninstallResetDialog.qml new file mode 100644 index 0000000000..487e2db894 --- /dev/null +++ b/plugins/Toolbox/resources/qml/ToolboxConfirmUninstallResetDialog.qml @@ -0,0 +1,91 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Window 2.1 + +import UM 1.3 as UM +import Cura 1.0 as Cura + + +UM.Dialog +{ + // This dialog asks the user whether he/she wants to open a project file as a project or import models. + id: base + + title: catalog.i18nc("@title:window", "Confirm uninstall ") + toolbox.pluginToUninstall + width: 450 * screenScaleFactor + height: 250 * screenScaleFactor + + maximumHeight: height + maximumWidth: width + minimumHeight: maximumHeight + minimumWidth: maximumWidth + + modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal + + Column + { + UM.I18nCatalog { id: catalog; name: "cura" } + + anchors.fill: parent + anchors.leftMargin: 20 * screenScaleFactor + anchors.rightMargin: 20 * screenScaleFactor + anchors.bottomMargin: 10 * screenScaleFactor + spacing: 10 * screenScaleFactor + + Label + { + id: dialogText + text: + { + var base_text = catalog.i18nc("@text:window", "You are uninstalling materials and/or profiles that are still in use. Confirming will reset the following materials/profiles to their defaults.") + var materials_text = catalog.i18nc("@text:window", "Materials") + var qualities_text = catalog.i18nc("@text:window", "Profiles") + var machines_with_materials = toolbox.uninstallUsedMaterials + var machines_with_qualities = toolbox.uninstallUsedQualities + if (machines_with_materials != "") + { + base_text += "\n\n" + materials_text +": \n" + machines_with_materials + } + if (machines_with_qualities != "") + { + base_text += "\n\n" + qualities_text + ": \n" + machines_with_qualities + } + return base_text + } + anchors.left: parent.left + anchors.right: parent.right + font: UM.Theme.getFont("default") + wrapMode: Text.WordWrap + } + + // Buttons + Item { + id: buttonBar + anchors.right: parent.right + anchors.left: parent.left + height: childrenRect.height + + Button { + id: cancelButton + text: catalog.i18nc("@action:button", "Cancel") + anchors.right: confirmButton.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width + isDefault: true + onClicked: toolbox.closeConfirmResetDialog() + } + + Button { + id: confirmButton + text: catalog.i18nc("@action:button", "Confirm") + anchors.right: parent.right + onClicked: toolbox.resetMaterialsQualitiesAndUninstall() + } + } + } +} diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 3e0749d612..ad9481f892 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -102,6 +102,9 @@ class Toolbox(QObject, Extension): self._active_package = None # type: Optional[Dict[str, Any]] self._dialog = None #type: Optional[QObject] + self._confirm_reset_dialog = None #type: Optional[QObject] + self._resetUninstallVariables() + self._restart_required = False #type: bool # variables for the license agreement dialog @@ -130,6 +133,13 @@ class Toolbox(QObject, Extension): filterChanged = pyqtSignal() metadataChanged = pyqtSignal() showLicenseDialog = pyqtSignal() + uninstallVariablesChanged = pyqtSignal() + + def _resetUninstallVariables(self): + self._package_id_to_uninstall = None + self._package_name_to_uninstall = "" + self._package_used_materials = [] + self._package_used_qualities = [] @pyqtSlot(result = str) def getLicenseDialogPluginName(self) -> str: @@ -235,7 +245,6 @@ class Toolbox(QObject, Extension): dialog = self._application.createQmlComponent(path, {"toolbox": self}) return dialog - def _convertPluginMetadata(self, plugin: Dict[str, Any]) -> Dict[str, Any]: formatted = { "package_id": plugin["id"], @@ -298,24 +307,57 @@ class Toolbox(QObject, Extension): # If the package is in use, you'll get a confirmation dialog to set everything to default @pyqtSlot(str) def checkPackageUsageAndUninstall(self, plugin_id: str) -> None: - print("checkPackageUsageAndUninstall...") package_used_materials, package_used_qualities = self._package_manager.packageUsed(plugin_id) if package_used_materials or package_used_qualities: + # Set up "uninstall variables" for resetMaterialsQualitiesAndUninstall + self._package_id_to_uninstall = plugin_id + package_info = self._package_manager.getInstalledPackageInfo(plugin_id) + self._package_name_to_uninstall = package_info.get("display_name", package_info.get("package_id")) + self._package_used_materials = package_used_materials + self._package_used_qualities = package_used_qualities # Ask change to default material / profile - # Cancel: just return - # Confirm: change to default material / profile - material_manager = CuraApplication.getInstance().getMaterialManager() - quality_manager = CuraApplication.getInstance().getQualityManager() - machine_manager = CuraApplication.getInstance().getMachineManager() - for global_stack, extruder_nr in package_used_materials: - default_material_node = material_manager.getDefaultMaterial(global_stack, extruder_nr, global_stack.extruders[extruder_nr].variant.getName()) - machine_manager.setMaterial(extruder_nr, default_material_node, global_stack = global_stack) - for global_stack, extruder_nr in package_used_qualities: - default_quality_group = quality_manager.getDefaultQualityType(global_stack) - machine_manager.setQualityGroup(default_quality_group, global_stack = global_stack) - # Change to default material / profile - self.uninstall(plugin_id) - return + if self._confirm_reset_dialog is None: + self._confirm_reset_dialog = self._createDialog("ToolboxConfirmUninstallResetDialog.qml") + self.uninstallVariablesChanged.emit() + self._confirm_reset_dialog.show() + else: + # Plain uninstall + self.uninstall(plugin_id) + + @pyqtProperty(str, notify = uninstallVariablesChanged) + def pluginToUninstall(self): + return self._package_name_to_uninstall + + @pyqtProperty(str, notify = uninstallVariablesChanged) + def uninstallUsedMaterials(self): + return "\n".join(["%s (%s)" % (str(global_stack.getName()), material) for global_stack, extruder_nr, material in self._package_used_materials]) + + @pyqtProperty(str, notify = uninstallVariablesChanged) + def uninstallUsedQualities(self): + return "\n".join(["%s (%s)" % (str(global_stack.getName()), quality) for global_stack, extruder_nr, quality in self._package_used_qualities]) + + @pyqtSlot() + def closeConfirmResetDialog(self): + if self._confirm_reset_dialog is not None: + self._confirm_reset_dialog.close() + + ## Uses "uninstall variables" to reset qualities and materials, then uninstall + # It's used as an action on Confirm reset on Uninstall + @pyqtSlot() + def resetMaterialsQualitiesAndUninstall(self): + application = CuraApplication.getInstance() + material_manager = application.getMaterialManager() + quality_manager = application.getQualityManager() + machine_manager = application.getMachineManager() + for global_stack, extruder_nr, _ in self._package_used_materials: + default_material_node = material_manager.getDefaultMaterial(global_stack, extruder_nr, global_stack.extruders[extruder_nr].variant.getName()) + machine_manager.setMaterial(extruder_nr, default_material_node, global_stack = global_stack) + for global_stack, extruder_nr, _ in self._package_used_qualities: + default_quality_group = quality_manager.getDefaultQualityType(global_stack) + machine_manager.setQualityGroup(default_quality_group, global_stack = global_stack) + self.uninstall(self._package_id_to_uninstall) + self._resetUninstallVariables() + self.closeConfirmResetDialog() @pyqtSlot(str) def uninstall(self, plugin_id: str) -> None: