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

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

@ -15,7 +15,10 @@ from .WorkspaceDialog import WorkspaceDialog
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from cura.Settings.ExtruderManager import ExtruderManager 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 zipfile
import io import io
import configparser import configparser
@ -31,10 +34,14 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._dialog = WorkspaceDialog() self._dialog = WorkspaceDialog()
self._3mf_mesh_reader = None self._3mf_mesh_reader = None
self._container_registry = ContainerRegistry.getInstance() 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._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._instance_container_suffix = "." + ContainerRegistry.getMimeTypeForContainer(InstanceContainer).preferredSuffix
self._container_stack_suffix = ContainerRegistry.getMimeTypeForContainer(ContainerStack).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 = {} self._resolve_strategies = {}
@ -47,6 +54,47 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._id_mapping[old_id] = self._container_registry.uniqueName(old_id) self._id_mapping[old_id] = self._container_registry.uniqueName(old_id)
return self._id_mapping[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): def preRead(self, file_name, show_dialog=True, *args, **kwargs):
self._3mf_mesh_reader = Application.getInstance().getMeshFileHandler().getReaderForFile(file_name) 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: 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. # Check if there are any conflicts, so we can ask the user.
archive = zipfile.ZipFile(file_name, "r") archive = zipfile.ZipFile(file_name, "r")
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")] 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} # A few lists of containers in this project files.
machine_conflict = False # When loading the global stack file, it may be associated with those containers, which may or may not be
quality_changes_conflict = False # in Cura already, so we need to provide them as alternative search lists.
for container_stack_file in container_stack_files: definition_container_list = []
container_id = self._stripFileToId(container_stack_file) instance_container_list = []
serialized = archive.open(container_stack_file).read().decode("utf-8") material_container_list = []
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()
definition_container_files = [name for name in cura_file_names if name.endswith(self._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: for definition_container_file in definition_container_files:
@ -93,6 +131,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
else: else:
definition_container = definitions[0] definition_container = definitions[0]
definition_container_list.append(definition_container)
if definition_container.getMetaDataEntry("type") != "extruder": if definition_container.getMetaDataEntry("type") != "extruder":
machine_type = definition_container.getName() machine_type = definition_container.getName()
variant_type_name = definition_container.getMetaDataEntry("variants_name", variant_type_name) 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 # Deserialize InstanceContainer by converting read data from bytes to string
instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8")) instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"))
instance_container_list.append(instance_container)
container_type = instance_container.getMetaDataEntry("type") container_type = instance_container.getMetaDataEntry("type")
if container_type == "quality_changes": if container_type == "quality_changes":
quality_name = instance_container.getName() quality_name = instance_container.getName()
@ -151,6 +194,27 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
num_user_settings += len(instance_container._instances) num_user_settings += len(instance_container._instances)
Job.yieldThread() 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 num_visible_settings = 0
try: try:
temp_preferences = Preferences() temp_preferences = Preferences()
@ -199,6 +263,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
return WorkspaceReader.PreReadResult.accepted 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): def read(self, file_name):
archive = zipfile.ZipFile(file_name, "r") 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)] 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: for definition_container_file in definition_container_files:
container_id = self._stripFileToId(definition_container_file) 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: if not definitions:
definition_container = DefinitionContainer(container_id) definition_container = DefinitionContainer(container_id)
definition_container.deserialize(archive.open(definition_container_file).read().decode("utf-8")) 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)] 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: for material_container_file in material_container_files:
container_id = self._stripFileToId(material_container_file) 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: if not materials:
material_container = xml_material_profile(container_id) material_container = xml_material_profile(container_id)
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8")) material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"))
@ -290,7 +361,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
Job.yieldThread() Job.yieldThread()
if container_type == "user": if container_type == "user":
# Check if quality changes already exists. # 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: if not user_containers:
containers_to_add.append(instance_container) containers_to_add.append(instance_container)
else: else:
@ -336,21 +407,65 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# Get the stack(s) saved in the workspace. # Get the stack(s) saved in the workspace.
Logger.log("d", "Workspace loading is checking stacks containers...") 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 global_stack = None
extruder_stacks = [] extruder_stacks = []
extruder_stacks_added = []
container_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: try:
for container_stack_file in container_stack_files: for container_stack_file in container_stack_files:
container_id = self._stripFileToId(container_stack_file) container_id = self._stripFileToId(container_stack_file)
# Check if a stack by this ID already exists; # 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: if container_stacks:
stack = container_stacks[0] stack = container_stacks[0]
if self._resolve_strategies["machine"] == "override": if self._resolve_strategies["machine"] == "override":
# TODO: HACK # 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_id = container_stacks[0].getMetaDataEntry("network_authentication_id")
network_authentication_key = container_stacks[0].getMetaDataEntry("network_authentication_key") network_authentication_key = container_stacks[0].getMetaDataEntry("network_authentication_key")
container_stacks[0].deserialize(archive.open(container_stack_file).read().decode("utf-8")) 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) container_stacks[0].addMetaDataEntry("network_authentication_key", network_authentication_key)
elif self._resolve_strategies["machine"] == "new": elif self._resolve_strategies["machine"] == "new":
new_id = self.getNewId(container_id) 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")) stack.deserialize(archive.open(container_stack_file).read().decode("utf-8"))
# Ensure a unique ID and name # Ensure a unique ID and name
@ -371,7 +486,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
if stack.getMetaDataEntry("machine", None): if stack.getMetaDataEntry("machine", None):
stack.setMetaDataEntry("machine", self.getNewId(stack.getMetaDataEntry("machine"))) 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 # Only machines need a new name, stacks may be non-unique
stack.setName(self._container_registry.uniqueName(stack.getName())) stack.setName(self._container_registry.uniqueName(stack.getName()))
container_stacks_added.append(stack) container_stacks_added.append(stack)
@ -379,25 +493,26 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
else: else:
Logger.log("w", "Resolve strategy of %s for machine is not supported", self._resolve_strategies["machine"]) Logger.log("w", "Resolve strategy of %s for machine is not supported", self._resolve_strategies["machine"])
else: 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 # Deserialize stack by converting read data from bytes to string
stack.deserialize(archive.open(container_stack_file).read().decode("utf-8")) stack.deserialize(archive.open(container_stack_file).read().decode("utf-8"))
container_stacks_added.append(stack) container_stacks_added.append(stack)
self._container_registry.addContainer(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() Job.yieldThread()
except: except:
Logger.logException("w", "We failed to serialize the stack. Trying to clean up.") 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. # Something went really wrong. Try to remove any data that we added.
for container in containers_to_add: 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: 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 return None
@ -466,6 +581,14 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# Notify everything/one that is to notify about changes. # Notify everything/one that is to notify about changes.
global_stack.containersChanged.emit(global_stack.getTop()) 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: for stack in extruder_stacks:
stack.setNextStack(global_stack) stack.setNextStack(global_stack)
stack.containersChanged.emit(stack.getTop()) stack.containersChanged.emit(stack.getTop())