diff --git a/cura/ProfileReader.py b/cura/ProfileReader.py
index 36bb2c7177..266abd50a4 100644
--- a/cura/ProfileReader.py
+++ b/cura/ProfileReader.py
@@ -12,6 +12,6 @@ class ProfileReader(PluginObject):
## Read profile data from a file and return a filled profile.
#
- # \return \type{Profile} The profile that was obtained from the file.
+ # \return \type{Profile|Profile[]} The profile that was obtained from the file or a list of Profiles.
def read(self, file_name):
raise NotImplementedError("Profile reader plug-in was not correctly implemented. The read function was not implemented.")
\ No newline at end of file
diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py
index af7ca2e87e..990143f7bb 100644
--- a/cura/Settings/CuraContainerRegistry.py
+++ b/cura/Settings/CuraContainerRegistry.py
@@ -133,32 +133,45 @@ class CuraContainerRegistry(ContainerRegistry):
for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
profile_reader = plugin_registry.getPluginObject(plugin_id)
try:
- profile = profile_reader.read(file_name) #Try to open the file with the profile reader.
+ profile_or_list = profile_reader.read(file_name) # Try to open the file with the profile reader.
except Exception as e:
#Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
Logger.log("e", "Failed to import profile from %s: %s", file_name, str(e))
return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from {0}: {1}", file_name, str(e))}
- if profile: #Success!
- profile.setReadOnly(False)
-
- new_name = self.createUniqueName("quality", "", os.path.splitext(os.path.basename(file_name))[0],
- catalog.i18nc("@label", "Custom profile"))
- profile.setName(new_name)
- profile._id = new_name
-
- if self._machineHasOwnQualities():
- profile.setDefinition(self._activeDefinition())
- if self._machineHasOwnMaterials():
- profile.addMetaDataEntry("material", self._activeMaterialId())
+ if profile_or_list: # Success!
+ name_seed = os.path.splitext(os.path.basename(file_name))[0]
+ if type(profile_or_list) is not list:
+ profile = profile_or_list
+ self._configureProfile(profile, name_seed)
+ return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile.getName()) }
else:
- profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0])
- ContainerRegistry.getInstance().addContainer(profile)
+ for profile in profile_or_list:
+ self._configureProfile(profile, name_seed)
- return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile.getName()) }
+ if len(profile_or_list) == 1:
+ return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())}
+ else:
+ profile_names = ", ".join([profile.getName() for profile in profile_or_list])
+ return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profiles {0}", profile_names) }
#If it hasn't returned by now, none of the plugins loaded the profile successfully.
return { "status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type.", file_name)}
+ def _configureProfile(self, profile, name_seed):
+ profile.setReadOnly(False)
+
+ new_name = self.createUniqueName("quality", "", name_seed, catalog.i18nc("@label", "Custom profile"))
+ profile.setName(new_name)
+ profile._id = new_name
+
+ if self._machineHasOwnQualities():
+ profile.setDefinition(self._activeDefinition())
+ if self._machineHasOwnMaterials():
+ profile.addMetaDataEntry("material", self._activeMaterialId())
+ else:
+ profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0])
+ ContainerRegistry.getInstance().addContainer(profile)
+
## Gets a list of profile writer plugins
# \return List of tuples of (plugin_id, meta_data).
def _getIOPlugins(self, io_type):
diff --git a/plugins/CuraProfileReader/CuraProfileReader.py b/plugins/CuraProfileReader/CuraProfileReader.py
index b9c1f208ea..261e68a26b 100644
--- a/plugins/CuraProfileReader/CuraProfileReader.py
+++ b/plugins/CuraProfileReader/CuraProfileReader.py
@@ -3,7 +3,6 @@
import os.path
-from UM.Application import Application #To get the machine manager to create the new profile in.
from UM.Logger import Logger
from UM.Settings.InstanceContainer import InstanceContainer #The new profile to make.
from cura.ProfileReader import ProfileReader
diff --git a/plugins/GCodeProfileReader/GCodeProfileReader.py b/plugins/GCodeProfileReader/GCodeProfileReader.py
index 1e649b7dd4..24c92d08e9 100644
--- a/plugins/GCodeProfileReader/GCodeProfileReader.py
+++ b/plugins/GCodeProfileReader/GCodeProfileReader.py
@@ -1,10 +1,9 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
-import os
import re #Regular expressions for parsing escape characters in the settings.
+import json
-from UM.Application import Application #To get the machine manager to create the new profile in.
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Logger import Logger
from UM.i18n import i18nCatalog
@@ -22,7 +21,7 @@ class GCodeProfileReader(ProfileReader):
# It can only read settings with the same version as the version it was
# written with. If the file format is changed in a way that breaks reverse
# compatibility, increment this version number!
- version = 2
+ version = 3
## Dictionary that defines how characters are escaped when embedded in
# g-code.
@@ -66,21 +65,37 @@ class GCodeProfileReader(ProfileReader):
Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e))
return None
- # Un-escape the serialized profile.
- pattern = re.compile("|".join(GCodeProfileReader.escape_characters.keys()))
-
- # Perform the replacement with a regular expression.
- serialized = pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], serialized)
+ serialized = unescapeGcodeComment(serialized)
Logger.log("i", "Serialized the following from %s: %s" %(file_name, repr(serialized)))
- # Create an empty profile - the id and name will be changed by the ContainerRegistry
- profile = InstanceContainer("")
- try:
- profile.deserialize(serialized)
- except Exception as e: # Not a valid g-code file.
- Logger.log("e", "Unable to serialise the profile: %s", str(e))
- return None
+ json_data = json.loads(serialized)
- profile.addMetaDataEntry("type", "quality")
+ profile_strings = [json_data["global_quality"]]
+ profile_strings.extend(json_data.get("extruder_quality", []))
- return profile
+ return [readQualityProfileFromString(profile_string) for profile_string in profile_strings]
+
+## Unescape a string which has been escaped for use in a gcode comment.
+#
+# \param string The string to unescape.
+# \return \type{str} The unscaped string.
+def unescapeGcodeComment(string):
+ # Un-escape the serialized profile.
+ pattern = re.compile("|".join(GCodeProfileReader.escape_characters.keys()))
+
+ # Perform the replacement with a regular expression.
+ return pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], string)
+
+## Read in a profile from a serialized string.
+#
+# \param profile_string The profile data in serialized form.
+# \return \type{Profile} the resulting Profile object or None if it could not be read.
+def readQualityProfileFromString(profile_string):
+ # Create an empty profile - the id and name will be changed by the ContainerRegistry
+ profile = InstanceContainer("")
+ try:
+ profile.deserialize(profile_string)
+ except Exception as e: # Not a valid g-code file.
+ Logger.log("e", "Unable to serialise the profile: %s", str(e))
+ return None
+ return profile
diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py
index 4eb1f89134..ea21e33109 100644
--- a/plugins/GCodeWriter/GCodeWriter.py
+++ b/plugins/GCodeWriter/GCodeWriter.py
@@ -4,8 +4,11 @@
from UM.Mesh.MeshWriter import MeshWriter
from UM.Logger import Logger
from UM.Application import Application
-from UM.Settings.InstanceContainer import InstanceContainer #To create a complete setting profile to store in the g-code.
+
+from cura.Settings.ExtruderManager import ExtruderManager
+
import re #For escaping characters in the settings.
+import json
## Writes g-code to a file.
#
@@ -23,7 +26,7 @@ class GCodeWriter(MeshWriter):
# It can only read settings with the same version as the version it was
# written with. If the file format is changed in a way that breaks reverse
# compatibility, increment this version number!
- version = 2
+ version = 3
## Dictionary that defines how characters are escaped when embedded in
# g-code.
@@ -64,25 +67,33 @@ class GCodeWriter(MeshWriter):
#
# \param settings A container stack to serialise.
# \return A serialised string of the settings.
- def _serialiseSettings(self, settings):
+ def _serialiseSettings(self, stack):
prefix = ";SETTING_" + str(GCodeWriter.version) + " " # The prefix to put before each line.
prefix_length = len(prefix)
- global_stack = Application.getInstance().getGlobalContainerStack()
- container_with_profile = global_stack.findContainer({"type": "quality"})
+ container_with_profile = stack.findContainer({"type": "quality"})
serialized = container_with_profile.serialize()
+ data = {"global_quality": serialized}
+
+ manager = ExtruderManager.getInstance()
+ for extruder in manager.getMachineExtruders(stack.getBottom().getId()):
+ extruder_quality = extruder.findContainer({"type": "quality"})
+ extruder_serialized = extruder_quality.serialize()
+ data.setdefault("extruder_quality", []).append(extruder_serialized)
+
+ json_string = json.dumps(data)
+
# Escape characters that have a special meaning in g-code comments.
pattern = re.compile("|".join(GCodeWriter.escape_characters.keys()))
+
# Perform the replacement with a regular expression.
- serialized = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], serialized)
+ escaped_string = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], json_string)
# Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix.
result = ""
# Lines have 80 characters, so the payload of each line is 80 - prefix.
- for pos in range(0, len(serialized), 80 - prefix_length):
- result += prefix + serialized[pos : pos + 80 - prefix_length] + "\n"
- serialized = result
-
- return serialized
\ No newline at end of file
+ for pos in range(0, len(escaped_string), 80 - prefix_length):
+ result += prefix + escaped_string[pos : pos + 80 - prefix_length] + "\n"
+ return result