Pre-read now checks for conflicts and asks the user what strategy for resolvement to use

CURA-1263
This commit is contained in:
Jaime van Kessel 2016-11-11 17:17:23 +01:00
parent 99e753d3be
commit cbcc48ff33
3 changed files with 221 additions and 41 deletions

View file

@ -2,23 +2,34 @@ from UM.Workspace.WorkspaceReader import WorkspaceReader
from UM.Application import Application
from UM.Logger import Logger
from UM.i18n import i18nCatalog
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Preferences import Preferences
from .WorkspaceDialog import WorkspaceDialog
import zipfile
import io
i18n_catalog = i18nCatalog("cura")
## Base implementation for reading 3MF workspace files.
class ThreeMFWorkspaceReader(WorkspaceReader):
def __init__(self):
super().__init__()
self._supported_extensions = [".3mf"]
self._dialog = WorkspaceDialog()
self._3mf_mesh_reader = None
self._container_registry = ContainerRegistry.getInstance()
self._definition_container_suffix = ContainerRegistry.getMimeTypeForContainer(DefinitionContainer).suffixes[0]
self._material_container_suffix = None # We have to wait until all other plugins are loaded before we can set it
self._instance_container_suffix = ContainerRegistry.getMimeTypeForContainer(InstanceContainer).suffixes[0]
self._container_stack_suffix = ContainerRegistry.getMimeTypeForContainer(ContainerStack).suffixes[0]
self._resolvement_strategy = None
def preRead(self, file_name):
self._3mf_mesh_reader = Application.getInstance().getMeshFileHandler().getReaderForFile(file_name)
@ -27,7 +38,45 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
else:
Logger.log("w", "Could not find reader that was able to read the scene data for 3MF workspace")
return WorkspaceReader.PreReadResult.failed
# TODO: Ask user if it's okay for the scene to be cleared
# Check if there are any conflicts, so we can ask the user.
archive = zipfile.ZipFile(file_name, "r")
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
container_stack_files = [name for name in cura_file_names if name.endswith(self._container_stack_suffix)]
conflict = False
for container_stack_file in container_stack_files:
container_id = self._stripFileToId(container_stack_file)
stacks = self._container_registry.findContainerStacks(id=container_id)
if stacks:
conflict = True
break
# Check if any quality_changes instance container is in conflict.
if not conflict:
instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)]
for instance_container_file in instance_container_files:
container_id = self._stripFileToId(instance_container_file)
instance_container = InstanceContainer(container_id)
# Deserialize InstanceContainer by converting read data from bytes to string
instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"))
container_type = instance_container.getMetaDataEntry("type")
if container_type == "quality_changes":
# Check if quality changes already exists.
quality_changes = self._container_registry.findInstanceContainers(id = container_id)
if quality_changes:
conflict = True
if conflict:
# There is a conflict; User should choose to either update the existing data, add everything as new data or abort
self._resolvement_strategy = None
self._dialog.show()
self._dialog.waitForClose()
if self._dialog.getResult() == "cancel":
return WorkspaceReader.PreReadResult.cancelled
self._resolvement_strategy = self._dialog.getResult()
pass
return WorkspaceReader.PreReadResult.accepted
def read(self, file_name):
@ -36,7 +85,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
if nodes is None:
nodes = []
container_registry = ContainerRegistry.getInstance()
archive = zipfile.ZipFile(file_name, "r")
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
@ -57,83 +105,84 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# TODO: It might be possible that we need to add smarter checking in the future.
# Get all the definition files & check if they exist. If not, add them.
definition_container_suffix = ContainerRegistry.getMimeTypeForContainer(DefinitionContainer).suffixes[0]
definition_container_files = [name for name in cura_file_names if name.endswith(definition_container_suffix)]
definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
for definition_container_file in definition_container_files:
container_id = definition_container_file.replace("Cura/", "")
container_id = container_id.replace(".%s" % definition_container_suffix, "")
definitions = container_registry.findDefinitionContainers(id=container_id)
container_id = self._stripFileToId(definition_container_file)
definitions = self._container_registry.findDefinitionContainers(id=container_id)
if not definitions:
definition_container = DefinitionContainer(container_id)
definition_container.deserialize(archive.open(definition_container_file).read().decode("utf-8"))
container_registry.addContainer(definition_container)
self._container_registry.addContainer(definition_container)
# Get all the material files and check if they exist. If not, add them.
xml_material_profile = None
for type_name, container_type in container_registry.getContainerTypes():
if type_name == "XmlMaterialProfile":
xml_material_profile = container_type
break
xml_material_profile = self._getXmlProfileClass()
if self._material_container_suffix is None:
self._material_container_suffix = ContainerRegistry.getMimeTypeForContainer(xml_material_profile).suffixes[0]
if xml_material_profile:
material_container_suffix = ContainerRegistry.getMimeTypeForContainer(xml_material_profile).suffixes[0]
material_container_files = [name for name in cura_file_names if name.endswith(material_container_suffix)]
material_container_files = [name for name in cura_file_names if name.endswith(self._material_container_suffix)]
for material_container_file in material_container_files:
container_id = material_container_file.replace("Cura/", "")
container_id = container_id.replace(".%s" % material_container_suffix, "")
materials = container_registry.findInstanceContainers(id=container_id)
container_id = self._stripFileToId(material_container_file)
materials = self._container_registry.findInstanceContainers(id=container_id)
if not materials:
material_container = xml_material_profile(container_id)
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"))
container_registry.addContainer(material_container)
self._container_registry.addContainer(material_container)
# Get quality_changes and user profiles saved in the workspace
instance_container_suffix = ContainerRegistry.getMimeTypeForContainer(InstanceContainer).suffixes[0]
instance_container_files = [name for name in cura_file_names if name.endswith(instance_container_suffix)]
instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)]
user_instance_containers = []
quality_changes_instance_containers = []
for instance_container_file in instance_container_files:
container_id = instance_container_file.replace("Cura/", "")
container_id = container_id.replace(".%s" % instance_container_suffix, "")
container_id = self._stripFileToId(instance_container_file)
instance_container = InstanceContainer(container_id)
# Deserialize InstanceContainer by converting read data from bytes to string
instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"))
container_type = instance_container.getMetaDataEntry("type")
if container_type == "user":
# Check if quality changes already exists.
user_containers = self._container_registry.findInstanceContainers(id=container_id)
if not user_containers:
self._container_registry.addContainer(instance_container)
user_instance_containers.append(instance_container)
elif container_type == "quality_changes":
# Check if quality changes already exists.
quality_changes = container_registry.findInstanceContainers(id = container_id)
quality_changes = self._container_registry.findInstanceContainers(id = container_id)
if not quality_changes:
container_registry.addContainer(instance_container)
self._container_registry.addContainer(instance_container)
quality_changes_instance_containers.append(instance_container)
else:
continue
# Get the stack(s) saved in the workspace.
'''container_stack_suffix = ContainerRegistry.getMimeTypeForContainer(ContainerStack).suffixes[0]
container_stack_files = [name for name in cura_file_names if name.endswith(container_stack_suffix)]
container_stack_files = [name for name in cura_file_names if name.endswith(self._container_stack_suffix)]
global_stack = None
extruder_stacks = []
for container_stack_file in container_stack_files:
container_id = container_stack_file.replace("Cura/", "")
container_id = container_id.replace(".%s" % container_stack_suffix, "")
# Check if a stack by this ID already exists;
container_stacks = container_registry.findContainerStacks(id = container_id)
if container_stacks:
print("CONTAINER ALREADY EXISTSSS")
#stack = ContainerStack(container_id)
container_id = self._stripFileToId(container_stack_file)
stack = ContainerStack(container_id)
# Deserialize stack by converting read data from bytes to string
stack.deserialize(archive.open(container_stack_file).read().decode("utf-8"))
# Check if a stack by this ID already exists;
container_stacks = self._container_registry.findContainerStacks(id = container_id)
if container_stacks:
print("CONTAINER ALREADY EXISTSSS")
if stack.getMetaDataEntry("type") == "extruder_train":
extruder_stacks.append(stack)
else:
global_stack = stack'''
global_stack = stack
return nodes
def _stripFileToId(self, file):
return file.replace("Cura/", "").split(".")[0]
def _getXmlProfileClass(self):
for type_name, container_type in self._container_registry.getContainerTypes():
print(type_name, container_type)
if type_name == "XmlMaterialProfile":
return container_type

View file

@ -0,0 +1,79 @@
from PyQt5.QtCore import Qt, QUrl, pyqtSignal, pyqtSlot, QObject
from PyQt5.QtQml import QQmlComponent, QQmlContext
from UM.PluginRegistry import PluginRegistry
from UM.Application import Application
import os
import threading
class WorkspaceDialog(QObject):
showDialogSignal = pyqtSignal()
def __init__(self, parent = None):
super().__init__(parent)
self._component = None
self._context = None
self._view = None
self._qml_url = "WorkspaceDialog.qml"
self._lock = threading.Lock()
self._result = None # What option did the user pick?
self._visible = False
self.showDialogSignal.connect(self.__show)
def getResult(self):
return self._result
def _createViewFromQML(self):
path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("3MFReader"), self._qml_url))
self._component = QQmlComponent(Application.getInstance()._engine, path)
self._context = QQmlContext(Application.getInstance()._engine.rootContext())
self._context.setContextProperty("manager", self)
self._view = self._component.create(self._context)
def show(self):
# Emit signal so the right thread actually shows the view.
self._lock.acquire()
self._result = None
self._visible = True
self.showDialogSignal.emit()
@pyqtSlot()
## Used to notify the dialog so the lock can be released.
def notifyClosed(self):
if self._result is None:
self._result = "cancel"
self._lock.release()
def hide(self):
self._visible = False
self._lock.release()
self._view.hide()
@pyqtSlot()
def onOverrideButtonClicked(self):
self._view.hide()
self.hide()
self._result = "override"
@pyqtSlot()
def onNewButtonClicked(self):
self._view.hide()
self.hide()
self._result = "new"
@pyqtSlot()
def onCancelButtonClicked(self):
self._view.hide()
self.hide()
self._result = "cancel"
## Block thread until the dialog is closed.
def waitForClose(self):
if self._visible:
self._lock.acquire()
self._lock.release()
def __show(self):
if self._view is None:
self._createViewFromQML()
self._view.show()

View file

@ -0,0 +1,52 @@
// Copyright (c) 2016 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.1
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
import UM 1.1 as UM
UM.Dialog
{
title: catalog.i18nc("@title:window", "Conflict")
width: 350 * Screen.devicePixelRatio;
minimumWidth: 350 * Screen.devicePixelRatio;
maximumWidth: 350 * Screen.devicePixelRatio;
height: 250 * Screen.devicePixelRatio;
minimumHeight: 250 * Screen.devicePixelRatio;
maximumHeight: 250 * Screen.devicePixelRatio;
onClosing: manager.notifyClosed()
Item
{
UM.I18nCatalog { id: catalog; name: "cura"; }
}
rightButtons: [
Button
{
id: override_button
text: catalog.i18nc("@action:button","Override");
onClicked: { manager.onOverrideButtonClicked() }
enabled: true
},
Button
{
id: create_new
text: catalog.i18nc("@action:button","Create new");
onClicked: { manager.onNewButtonClicked() }
enabled: true
},
Button
{
id: cancel_button
text: catalog.i18nc("@action:button","Cancel");
onClicked: { manager.onCancelButtonClicked() }
enabled: true
}
]
}