Merge branch 'master' of github.com:Ultimaker/Cura

This commit is contained in:
Jaime van Kessel 2017-05-09 14:02:09 +02:00
commit 6493bcd5b5
2 changed files with 163 additions and 38 deletions

View file

@ -351,7 +351,9 @@ class MachineManager(QObject):
def addMachine(self, name: str, definition_id: str) -> None:
new_stack = CuraStackBuilder.createMachine(name, definition_id)
if new_stack:
Application.getInstance().setGlobalContainerStack(new_stack)
# only set the global container stack if there was none
if self._global_container_stack is None:
Application.getInstance().setGlobalContainerStack(new_stack)
else:
Logger.log("w", "Failed creating a new machine!")

197
plugins/3MFReader/ThreeMFWorkspaceReader.py Normal file → Executable file
View file

@ -15,7 +15,10 @@ from .WorkspaceDialog import WorkspaceDialog
import xml.etree.ElementTree as ET
from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.ExtruderStack import ExtruderStack
from cura.Settings.GlobalStack import GlobalStack
from configparser import ConfigParser
import zipfile
import io
import configparser
@ -31,10 +34,14 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._dialog = WorkspaceDialog()
self._3mf_mesh_reader = None
self._container_registry = ContainerRegistry.getInstance()
self._definition_container_suffix = ContainerRegistry.getMimeTypeForContainer(DefinitionContainer).preferredSuffix
# suffixes registered with the MineTypes don't start with a dot '.'
self._definition_container_suffix = "." + ContainerRegistry.getMimeTypeForContainer(DefinitionContainer).preferredSuffix
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).preferredSuffix
self._container_stack_suffix = ContainerRegistry.getMimeTypeForContainer(ContainerStack).preferredSuffix
self._instance_container_suffix = "." + ContainerRegistry.getMimeTypeForContainer(InstanceContainer).preferredSuffix
self._container_stack_suffix = "." + ContainerRegistry.getMimeTypeForContainer(ContainerStack).preferredSuffix
self._extruder_stack_suffix = "." + ContainerRegistry.getMimeTypeForContainer(ExtruderStack).preferredSuffix
self._global_stack_suffix = "." + ContainerRegistry.getMimeTypeForContainer(GlobalStack).preferredSuffix
self._resolve_strategies = {}
@ -47,6 +54,47 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._id_mapping[old_id] = self._container_registry.uniqueName(old_id)
return self._id_mapping[old_id]
## Separates the given file list into a list of GlobalStack files and a list of ExtruderStack files.
#
# In old versions, extruder stack files have the same suffix as container stack files ".stack.cfg".
#
def _determineGlobalAndExtruderStackFiles(self, project_file_name, file_list):
archive = zipfile.ZipFile(project_file_name, "r")
global_stack_file_list = [name for name in file_list if name.endswith(self._global_stack_suffix)]
extruder_stack_file_list = [name for name in file_list if name.endswith(self._extruder_stack_suffix)]
# separate container stack files and extruder stack files
files_to_determine = [name for name in file_list if name.endswith(self._container_stack_suffix)]
for file_name in files_to_determine:
container_id = self._stripFileToId(file_name)
# FIXME: HACK!
# We need to know the type of the stack file, but we can only know it if we deserialize it.
# The default ContainerStack.deserialize() will connect signals, which is not desired in this case.
# Since we know that the stack files are INI files, so we directly use the ConfigParser to parse them.
serialized = archive.open(file_name).read().decode("utf-8")
stack_config = ConfigParser()
stack_config.read_string(serialized)
# sanity check
if not stack_config.has_option("metadata", "type"):
Logger.log("e", "%s in %s doesn't seem to be valid stack file", file_name, project_file_name)
continue
stack_type = stack_config.get("metadata", "type")
if stack_type == "extruder_train":
extruder_stack_file_list.append(file_name)
elif stack_type == "machine":
global_stack_file_list.append(file_name)
else:
Logger.log("w", "Unknown container stack type '%s' from %s in %s",
stack_type, file_name, project_file_name)
return global_stack_file_list, extruder_stack_file_list
## read some info so we can make decisions
# \param file_name
# \param show_dialog In case we use preRead() to check if a file is a valid project file, we don't want to show a dialog.
def preRead(self, file_name, show_dialog=True, *args, **kwargs):
self._3mf_mesh_reader = Application.getInstance().getMeshFileHandler().getReaderForFile(file_name)
if self._3mf_mesh_reader and self._3mf_mesh_reader.preRead(file_name) == WorkspaceReader.PreReadResult.accepted:
@ -63,23 +111,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# 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)]
self._resolve_strategies = {"machine": None, "quality_changes": None, "material": None}
machine_conflict = False
quality_changes_conflict = False
for container_stack_file in container_stack_files:
container_id = self._stripFileToId(container_stack_file)
serialized = archive.open(container_stack_file).read().decode("utf-8")
if machine_name == "":
machine_name = self._getMachineNameFromSerializedStack(serialized)
stacks = self._container_registry.findContainerStacks(id=container_id)
if stacks:
# Check if there are any changes at all in any of the container stacks.
id_list = self._getContainerIdListFromSerialized(serialized)
for index, container_id in enumerate(id_list):
if stacks[0].getContainer(index).getId() != container_id:
machine_conflict = True
Job.yieldThread()
# A few lists of containers in this project files.
# When loading the global stack file, it may be associated with those containers, which may or may not be
# in Cura already, so we need to provide them as alternative search lists.
definition_container_list = []
instance_container_list = []
material_container_list = []
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:
@ -93,6 +131,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
else:
definition_container = definitions[0]
definition_container_list.append(definition_container)
if definition_container.getMetaDataEntry("type") != "extruder":
machine_type = definition_container.getName()
variant_type_name = definition_container.getMetaDataEntry("variants_name", variant_type_name)
@ -131,6 +171,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# Deserialize InstanceContainer by converting read data from bytes to string
instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"))
instance_container_list.append(instance_container)
container_type = instance_container.getMetaDataEntry("type")
if container_type == "quality_changes":
quality_name = instance_container.getName()
@ -151,6 +194,27 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
num_user_settings += len(instance_container._instances)
Job.yieldThread()
# Load ContainerStack files and ExtruderStack files
container_stack_files, extruder_stack_files = self._determineGlobalAndExtruderStackFiles(
file_name, cura_file_names)
self._resolve_strategies = {"machine": None, "quality_changes": None, "material": None}
machine_conflict = False
quality_changes_conflict = False
for container_stack_file in container_stack_files + extruder_stack_files:
container_id = self._stripFileToId(container_stack_file)
serialized = archive.open(container_stack_file).read().decode("utf-8")
if machine_name == "":
machine_name = self._getMachineNameFromSerializedStack(serialized)
stacks = self._container_registry.findContainerStacks(id = container_id)
if stacks:
# Check if there are any changes at all in any of the container stacks.
id_list = self._getContainerIdListFromSerialized(serialized)
for index, container_id in enumerate(id_list):
if stacks[0].getContainer(index).getId() != container_id:
machine_conflict = True
Job.yieldThread()
num_visible_settings = 0
try:
temp_preferences = Preferences()
@ -199,6 +263,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
return WorkspaceReader.PreReadResult.accepted
## Read the project file
# Add all the definitions / materials / quality changes that do not exist yet. Then it loads
# all the stacks into the container registry. In some cases it will reuse the container for the global stack.
# It handles old style project files containing .stack.cfg as well as new style project files
# containing global.cfg / extruder.cfg
#
# \param file_name
def read(self, file_name):
archive = zipfile.ZipFile(file_name, "r")
@ -240,7 +311,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
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 = self._stripFileToId(definition_container_file)
definitions = self._container_registry.findDefinitionContainers(id=container_id)
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"))
@ -257,7 +328,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
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 = self._stripFileToId(material_container_file)
materials = self._container_registry.findInstanceContainers(id=container_id)
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"))
@ -290,7 +361,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
Job.yieldThread()
if container_type == "user":
# Check if quality changes already exists.
user_containers = self._container_registry.findInstanceContainers(id=container_id)
user_containers = self._container_registry.findInstanceContainers(id = container_id)
if not user_containers:
containers_to_add.append(instance_container)
else:
@ -336,21 +407,65 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# Get the stack(s) saved in the workspace.
Logger.log("d", "Workspace loading is checking stacks containers...")
container_stack_files = [name for name in cura_file_names if name.endswith(self._container_stack_suffix)]
container_stack_files, extruder_stack_files = self._determineGlobalAndExtruderStackFiles(file_name,
cura_file_names)
global_stack = None
extruder_stacks = []
extruder_stacks_added = []
container_stacks_added = []
# load extruder stack files
try:
for extruder_stack_file in extruder_stack_files:
container_id = self._stripFileToId(extruder_stack_file)
container_stacks = self._container_registry.findContainerStacks(id = container_id)
if container_stacks:
# this container stack already exists, try to resolve
stack = container_stacks[0]
if self._resolve_strategies["machine"] == "override":
pass # do nothing
elif self._resolve_strategies["machine"] == "new":
# create a new extruder stack from this one
new_id = self.getNewId(container_id)
stack = ExtruderStack(new_id)
stack.deserialize(archive.open(extruder_stack_file).read().decode("utf-8"))
# Ensure a unique ID and name
stack._id = new_id
self._container_registry.addContainer(stack)
extruder_stacks_added.append(stack)
else:
# container not found, create a new one
stack = ExtruderStack(container_id)
stack.deserialize(archive.open(extruder_stack_file).read().decode("utf-8"))
self._container_registry.addContainer(stack)
extruder_stacks_added.append(stack)
extruder_stacks.append(stack)
except:
Logger.logException("w", "We failed to serialize the stack. Trying to clean up.")
# Something went really wrong. Try to remove any data that we added.
for container in extruder_stacks:
self._container_registry.removeContainer(container.getId())
return None
# load global stack file
try:
for container_stack_file in container_stack_files:
container_id = self._stripFileToId(container_stack_file)
# Check if a stack by this ID already exists;
container_stacks = self._container_registry.findContainerStacks(id=container_id)
container_stacks = self._container_registry.findContainerStacks(id = container_id)
if container_stacks:
stack = container_stacks[0]
if self._resolve_strategies["machine"] == "override":
# TODO: HACK
# There is a machine, check if it has authenticationd data. If so, keep that data.
# There is a machine, check if it has authentication data. If so, keep that data.
network_authentication_id = container_stacks[0].getMetaDataEntry("network_authentication_id")
network_authentication_key = container_stacks[0].getMetaDataEntry("network_authentication_key")
container_stacks[0].deserialize(archive.open(container_stack_file).read().decode("utf-8"))
@ -360,7 +475,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
container_stacks[0].addMetaDataEntry("network_authentication_key", network_authentication_key)
elif self._resolve_strategies["machine"] == "new":
new_id = self.getNewId(container_id)
stack = ContainerStack(new_id)
stack = GlobalStack(new_id)
stack.deserialize(archive.open(container_stack_file).read().decode("utf-8"))
# Ensure a unique ID and name
@ -371,33 +486,33 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
if stack.getMetaDataEntry("machine", None):
stack.setMetaDataEntry("machine", self.getNewId(stack.getMetaDataEntry("machine")))
if stack.getMetaDataEntry("type") != "extruder_train":
# Only machines need a new name, stacks may be non-unique
stack.setName(self._container_registry.uniqueName(stack.getName()))
# Only machines need a new name, stacks may be non-unique
stack.setName(self._container_registry.uniqueName(stack.getName()))
container_stacks_added.append(stack)
self._container_registry.addContainer(stack)
else:
Logger.log("w", "Resolve strategy of %s for machine is not supported", self._resolve_strategies["machine"])
else:
stack = ContainerStack(container_id)
# no existing container stack, so we create a new one
stack = GlobalStack(container_id)
# Deserialize stack by converting read data from bytes to string
stack.deserialize(archive.open(container_stack_file).read().decode("utf-8"))
container_stacks_added.append(stack)
self._container_registry.addContainer(stack)
if stack.getMetaDataEntry("type") == "extruder_train":
extruder_stacks.append(stack)
else:
global_stack = stack
global_stack = stack
Job.yieldThread()
except:
Logger.logException("w", "We failed to serialize the stack. Trying to clean up.")
# Something went really wrong. Try to remove any data that we added.
for container in containers_to_add:
self._container_registry.getInstance().removeContainer(container.getId())
self._container_registry.removeContainer(container.getId())
for container in container_stacks_added:
self._container_registry.getInstance().removeContainer(container.getId())
self._container_registry.removeContainer(container.getId())
for container in extruder_stacks_added:
self._container_registry.removeContainer(container.getId())
return None
@ -466,6 +581,14 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# Notify everything/one that is to notify about changes.
global_stack.containersChanged.emit(global_stack.getTop())
# if we are reusing an existing global stack, it can already have extruders associated, so we need to remove
# them first
if global_stack.extruders:
old_extruder_stacks = global_stack.extruders
for extruder_stack in old_extruder_stacks:
self._container_registry.removeContainer(extruder_stack)
global_stack._extruders = []
for stack in extruder_stacks:
stack.setNextStack(global_stack)
stack.containersChanged.emit(stack.getTop())