mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-07 22:13:58 -06:00
Merge pull request #4044 from Ultimaker/remove-package-check
CURA-5389 Check and confirmation before Remove package
This commit is contained in:
commit
461b191648
29 changed files with 356 additions and 115 deletions
|
@ -963,7 +963,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
if not extruder_info:
|
||||
continue
|
||||
if "enabled" not in extruder_stack.getMetaData():
|
||||
extruder_stack.addMetaDataEntry("enabled", "True")
|
||||
extruder_stack.setMetaDataEntry("enabled", "True")
|
||||
extruder_stack.setMetaDataEntry("enabled", str(extruder_info.enabled))
|
||||
|
||||
def _updateActiveMachine(self, global_stack):
|
||||
|
|
|
@ -75,7 +75,7 @@ class CuraProfileReader(ProfileReader):
|
|||
def _loadProfile(self, serialized, profile_id):
|
||||
# Create an empty profile.
|
||||
profile = InstanceContainer(profile_id)
|
||||
profile.addMetaDataEntry("type", "quality_changes")
|
||||
profile.setMetaDataEntry("type", "quality_changes")
|
||||
try:
|
||||
profile.deserialize(serialized)
|
||||
except ContainerFormatError as e:
|
||||
|
|
|
@ -127,11 +127,11 @@ class GCodeWriter(MeshWriter):
|
|||
flat_global_container = self._createFlattenedContainerInstance(stack.userChanges, container_with_profile)
|
||||
# If the quality changes is not set, we need to set type manually
|
||||
if flat_global_container.getMetaDataEntry("type", None) is None:
|
||||
flat_global_container.addMetaDataEntry("type", "quality_changes")
|
||||
flat_global_container.setMetaDataEntry("type", "quality_changes")
|
||||
|
||||
# Ensure that quality_type is set. (Can happen if we have empty quality changes).
|
||||
if flat_global_container.getMetaDataEntry("quality_type", None) is None:
|
||||
flat_global_container.addMetaDataEntry("quality_type", stack.quality.getMetaDataEntry("quality_type", "normal"))
|
||||
flat_global_container.setMetaDataEntry("quality_type", stack.quality.getMetaDataEntry("quality_type", "normal"))
|
||||
|
||||
# Get the machine definition ID for quality profiles
|
||||
machine_definition_id_for_quality = getMachineDefinitionIDForQualitySearch(stack.definition)
|
||||
|
@ -151,15 +151,15 @@ class GCodeWriter(MeshWriter):
|
|||
flat_extruder_quality = self._createFlattenedContainerInstance(extruder.userChanges, extruder_quality)
|
||||
# If the quality changes is not set, we need to set type manually
|
||||
if flat_extruder_quality.getMetaDataEntry("type", None) is None:
|
||||
flat_extruder_quality.addMetaDataEntry("type", "quality_changes")
|
||||
flat_extruder_quality.setMetaDataEntry("type", "quality_changes")
|
||||
|
||||
# Ensure that extruder is set. (Can happen if we have empty quality changes).
|
||||
if flat_extruder_quality.getMetaDataEntry("position", None) is None:
|
||||
flat_extruder_quality.addMetaDataEntry("position", extruder.getMetaDataEntry("position"))
|
||||
flat_extruder_quality.setMetaDataEntry("position", extruder.getMetaDataEntry("position"))
|
||||
|
||||
# Ensure that quality_type is set. (Can happen if we have empty quality changes).
|
||||
if flat_extruder_quality.getMetaDataEntry("quality_type", None) is None:
|
||||
flat_extruder_quality.addMetaDataEntry("quality_type", extruder.quality.getMetaDataEntry("quality_type", "normal"))
|
||||
flat_extruder_quality.setMetaDataEntry("quality_type", extruder.quality.getMetaDataEntry("quality_type", "normal"))
|
||||
|
||||
# Change the default definition
|
||||
flat_extruder_quality.setMetaDataEntry("definition", machine_definition_id_for_quality)
|
||||
|
|
|
@ -145,9 +145,9 @@ class LegacyProfileReader(ProfileReader):
|
|||
if len(profile.getAllKeys()) == 0:
|
||||
Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.")
|
||||
|
||||
profile.addMetaDataEntry("type", "profile")
|
||||
profile.setMetaDataEntry("type", "profile")
|
||||
# don't know what quality_type it is based on, so use "normal" by default
|
||||
profile.addMetaDataEntry("quality_type", "normal")
|
||||
profile.setMetaDataEntry("quality_type", "normal")
|
||||
profile.setName(profile_id)
|
||||
profile.setDirty(True)
|
||||
|
||||
|
|
|
@ -135,10 +135,7 @@ class MachineSettingsAction(MachineAction):
|
|||
|
||||
material_node = None
|
||||
if has_materials:
|
||||
if "has_materials" in self._global_container_stack.getMetaData():
|
||||
self._global_container_stack.setMetaDataEntry("has_materials", True)
|
||||
else:
|
||||
self._global_container_stack.addMetaDataEntry("has_materials", True)
|
||||
self._global_container_stack.setMetaDataEntry("has_materials", True)
|
||||
else:
|
||||
# The metadata entry is stored in an ini, and ini files are parsed as strings only.
|
||||
# Because any non-empty string evaluates to a boolean True, we have to remove the entry to make it False.
|
||||
|
|
|
@ -248,7 +248,7 @@ class PostProcessingPlugin(QObject, Extension):
|
|||
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if "post_processing_scripts" not in global_stack.getMetaData():
|
||||
global_stack.addMetaDataEntry("post_processing_scripts", "")
|
||||
global_stack.setMetaDataEntry("post_processing_scripts", "")
|
||||
Application.getInstance().getGlobalContainerStack().setMetaDataEntry("post_processing_scripts", script_list_strs)
|
||||
|
||||
## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection.
|
||||
|
|
|
@ -48,7 +48,7 @@ class Script:
|
|||
self._stack.addContainer(self._definition)
|
||||
self._instance = InstanceContainer(container_id="ScriptInstanceContainer")
|
||||
self._instance.setDefinition(self._definition.getId())
|
||||
self._instance.addMetaDataEntry("setting_version", self._definition.getMetaDataEntry("setting_version", default = 0))
|
||||
self._instance.setMetaDataEntry("setting_version", self._definition.getMetaDataEntry("setting_version", default = 0))
|
||||
self._stack.addContainer(self._instance)
|
||||
self._stack.propertyChanged.connect(self._onPropertyChanged)
|
||||
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
// 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: 50 * screenScaleFactor + dialogText.height + buttonBar.height
|
||||
|
||||
maximumWidth: 450 * screenScaleFactor
|
||||
maximumHeight: 450 * screenScaleFactor
|
||||
minimumWidth: 450 * screenScaleFactor
|
||||
minimumHeight: 150 * screenScaleFactor
|
||||
|
||||
modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal
|
||||
|
||||
Column
|
||||
{
|
||||
UM.I18nCatalog { id: catalog; name: "cura" }
|
||||
|
||||
anchors
|
||||
{
|
||||
fill: parent
|
||||
leftMargin: Math.round(20 * screenScaleFactor)
|
||||
rightMargin: Math.round(20 * screenScaleFactor)
|
||||
topMargin: Math.round(10 * screenScaleFactor)
|
||||
bottomMargin: Math.round(10 * screenScaleFactor)
|
||||
}
|
||||
spacing: Math.round(15 * 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -83,7 +83,7 @@ Column
|
|||
font: UM.Theme.getFont("default")
|
||||
}
|
||||
}
|
||||
onClicked: toolbox.uninstall(model.id)
|
||||
onClicked: toolbox.checkPackageUsageAndUninstall(model.id)
|
||||
Connections
|
||||
{
|
||||
target: toolbox
|
||||
|
|
|
@ -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:
|
||||
|
@ -246,7 +256,6 @@ class Toolbox(QObject, Extension):
|
|||
raise Exception("Failed to create toolbox dialog")
|
||||
return dialog
|
||||
|
||||
|
||||
def _convertPluginMetadata(self, plugin: Dict[str, Any]) -> Dict[str, Any]:
|
||||
formatted = {
|
||||
"package_id": plugin["id"],
|
||||
|
@ -305,9 +314,90 @@ class Toolbox(QObject, Extension):
|
|||
self._restart_required = True
|
||||
self.restartRequiredChanged.emit()
|
||||
|
||||
## Check package usage and uninstall
|
||||
# If the package is in use, you'll get a confirmation dialog to set everything to default
|
||||
@pyqtSlot(str)
|
||||
def uninstall(self, plugin_id: str) -> None:
|
||||
self._package_manager.removePackage(plugin_id, force_add = True)
|
||||
def checkPackageUsageAndUninstall(self, package_id: str) -> None:
|
||||
package_used_materials, package_used_qualities = self._package_manager.getMachinesUsingPackage(package_id)
|
||||
if package_used_materials or package_used_qualities:
|
||||
# Set up "uninstall variables" for resetMaterialsQualitiesAndUninstall
|
||||
self._package_id_to_uninstall = package_id
|
||||
package_info = self._package_manager.getInstalledPackageInfo(package_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
|
||||
if self._confirm_reset_dialog is None:
|
||||
self._confirm_reset_dialog = self._createDialog("ToolboxConfirmUninstallResetDialog.qml")
|
||||
self.uninstallVariablesChanged.emit()
|
||||
if self._confirm_reset_dialog is None:
|
||||
Logger.log("e", "ToolboxConfirmUninstallResetDialog should have been initialized, but it is not. Not showing dialog and not uninstalling package.")
|
||||
else:
|
||||
self._confirm_reset_dialog.show()
|
||||
else:
|
||||
# Plain uninstall
|
||||
self.uninstall(package_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, container_id 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, container_id in self._package_used_qualities:
|
||||
default_quality_group = quality_manager.getDefaultQualityType(global_stack)
|
||||
machine_manager.setQualityGroup(default_quality_group, global_stack = global_stack)
|
||||
|
||||
self._markPackageMaterialsAsToBeUninstalled(self._package_id_to_uninstall)
|
||||
|
||||
self.uninstall(self._package_id_to_uninstall)
|
||||
self._resetUninstallVariables()
|
||||
self.closeConfirmResetDialog()
|
||||
|
||||
def _markPackageMaterialsAsToBeUninstalled(self, package_id: str) -> None:
|
||||
container_registry = self._application.getContainerRegistry()
|
||||
|
||||
all_containers = self._package_manager.getPackageContainerIds(package_id)
|
||||
for container_id in all_containers:
|
||||
containers = container_registry.findInstanceContainers(id = container_id)
|
||||
if not containers:
|
||||
continue
|
||||
container = containers[0]
|
||||
if container.getMetaDataEntry("type") != "material":
|
||||
continue
|
||||
root_material_id = container.getMetaDataEntry("base_file")
|
||||
root_material_containers = container_registry.findInstanceContainers(id = root_material_id)
|
||||
if not root_material_containers:
|
||||
continue
|
||||
root_material_container = root_material_containers[0]
|
||||
root_material_container.setMetaDataEntry("removed", True)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def uninstall(self, package_id: str) -> None:
|
||||
self._package_manager.removePackage(package_id, force_add = True)
|
||||
self.installChanged.emit()
|
||||
self._updateInstalledModels()
|
||||
self.metadataChanged.emit()
|
||||
|
|
|
@ -108,8 +108,8 @@ class DiscoverUM3Action(MachineAction):
|
|||
# Find all the places where there is the same group name and change it accordingly
|
||||
CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "connect_group_name", value = previous_connect_group_name, new_value = group_name)
|
||||
else:
|
||||
global_container_stack.addMetaDataEntry("connect_group_name", group_name)
|
||||
global_container_stack.addMetaDataEntry("hidden", False)
|
||||
global_container_stack.setMetaDataEntry("connect_group_name", group_name)
|
||||
global_container_stack.setMetaDataEntry("hidden", False)
|
||||
|
||||
if self._network_plugin:
|
||||
# Ensure that the connection states are refreshed.
|
||||
|
@ -130,7 +130,7 @@ class DiscoverUM3Action(MachineAction):
|
|||
global_container_stack.removeMetaDataEntry("network_authentication_key")
|
||||
CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "um_network_key", value = previous_network_key, new_value = key)
|
||||
else:
|
||||
global_container_stack.addMetaDataEntry("um_network_key", key)
|
||||
global_container_stack.setMetaDataEntry("um_network_key", key)
|
||||
|
||||
if self._network_plugin:
|
||||
# Ensure that the connection states are refreshed.
|
||||
|
|
|
@ -455,18 +455,18 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
self.setAuthenticationState(AuthState.AuthenticationDenied)
|
||||
self._authentication_failed_message.show()
|
||||
|
||||
def _saveAuthentication(self):
|
||||
def _saveAuthentication(self) -> None:
|
||||
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
if self._authentication_key is None:
|
||||
Logger.log("e", "Authentication key is None, nothing to save.")
|
||||
return
|
||||
if self._authentication_id is None:
|
||||
Logger.log("e", "Authentication id is None, nothing to save.")
|
||||
return
|
||||
if global_container_stack:
|
||||
if "network_authentication_key" in global_container_stack.getMetaData():
|
||||
global_container_stack.setMetaDataEntry("network_authentication_key", self._authentication_key)
|
||||
else:
|
||||
global_container_stack.addMetaDataEntry("network_authentication_key", self._authentication_key)
|
||||
global_container_stack.setMetaDataEntry("network_authentication_key", self._authentication_key)
|
||||
|
||||
if "network_authentication_id" in global_container_stack.getMetaData():
|
||||
global_container_stack.setMetaDataEntry("network_authentication_id", self._authentication_id)
|
||||
else:
|
||||
global_container_stack.addMetaDataEntry("network_authentication_id", self._authentication_id)
|
||||
global_container_stack.setMetaDataEntry("network_authentication_id", self._authentication_id)
|
||||
|
||||
# Force save so we are sure the data is not lost.
|
||||
CuraApplication.getInstance().saveStack(global_container_stack)
|
||||
|
@ -637,4 +637,4 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
result = "********" + result
|
||||
return result
|
||||
|
||||
return self._authentication_key
|
||||
return self._authentication_key
|
||||
|
|
|
@ -47,10 +47,7 @@ class UM2UpgradeSelection(MachineAction):
|
|||
variant_container = global_container_stack.extruders["0"].variant
|
||||
|
||||
if has_variants:
|
||||
if "has_variants" in global_container_stack.getMetaData():
|
||||
global_container_stack.setMetaDataEntry("has_variants", True)
|
||||
else:
|
||||
global_container_stack.addMetaDataEntry("has_variants", True)
|
||||
global_container_stack.setMetaDataEntry("has_variants", True)
|
||||
|
||||
# Set the variant container to a sane default
|
||||
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue