Merge pull request #4044 from Ultimaker/remove-package-check

CURA-5389 Check and confirmation before Remove package
This commit is contained in:
jack 2018-07-12 14:15:13 +02:00 committed by GitHub
commit 461b191648
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 356 additions and 115 deletions

View file

@ -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):

View file

@ -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:

View file

@ -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)

View file

@ -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)

View file

@ -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.

View file

@ -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.

View file

@ -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)

View file

@ -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()
}
}
}
}

View file

@ -83,7 +83,7 @@ Column
font: UM.Theme.getFont("default")
}
}
onClicked: toolbox.uninstall(model.id)
onClicked: toolbox.checkPackageUsageAndUninstall(model.id)
Connections
{
target: toolbox

View file

@ -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()

View file

@ -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.

View file

@ -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

View file

@ -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()