Store the Quality profile for the 'global' and extruders in the gcode. Read in all of the quality profile during import.

Contributes to CURA-1727 GCode Profile reading/writing: Broken and needs update
This commit is contained in:
Simon Edwards 2016-07-12 12:10:07 +02:00
parent 26612e17b6
commit 64ecb114b8
5 changed files with 84 additions and 46 deletions

View file

@ -12,6 +12,6 @@ class ProfileReader(PluginObject):
## Read profile data from a file and return a filled profile. ## 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): def read(self, file_name):
raise NotImplementedError("Profile reader plug-in was not correctly implemented. The read function was not implemented.") raise NotImplementedError("Profile reader plug-in was not correctly implemented. The read function was not implemented.")

View file

@ -133,32 +133,45 @@ class CuraContainerRegistry(ContainerRegistry):
for plugin_id, meta_data in self._getIOPlugins("profile_reader"): for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
profile_reader = plugin_registry.getPluginObject(plugin_id) profile_reader = plugin_registry.getPluginObject(plugin_id)
try: 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: 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. #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)) 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 <filename>{0}</filename>: <message>{1}</message>", file_name, str(e))} return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, str(e))}
if profile: #Success! if profile_or_list: # Success!
profile.setReadOnly(False) name_seed = os.path.splitext(os.path.basename(file_name))[0]
if type(profile_or_list) is not list:
new_name = self.createUniqueName("quality", "", os.path.splitext(os.path.basename(file_name))[0], profile = profile_or_list
catalog.i18nc("@label", "Custom profile")) self._configureProfile(profile, name_seed)
profile.setName(new_name) return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile.getName()) }
profile._id = new_name
if self._machineHasOwnQualities():
profile.setDefinition(self._activeDefinition())
if self._machineHasOwnMaterials():
profile.addMetaDataEntry("material", self._activeMaterialId())
else: else:
profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0]) for profile in profile_or_list:
ContainerRegistry.getInstance().addContainer(profile) 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. #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)} 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 ## Gets a list of profile writer plugins
# \return List of tuples of (plugin_id, meta_data). # \return List of tuples of (plugin_id, meta_data).
def _getIOPlugins(self, io_type): def _getIOPlugins(self, io_type):

View file

@ -3,7 +3,6 @@
import os.path 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.Logger import Logger
from UM.Settings.InstanceContainer import InstanceContainer #The new profile to make. from UM.Settings.InstanceContainer import InstanceContainer #The new profile to make.
from cura.ProfileReader import ProfileReader from cura.ProfileReader import ProfileReader

View file

@ -1,10 +1,9 @@
# Copyright (c) 2015 Ultimaker B.V. # Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher. # 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 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.Settings.InstanceContainer import InstanceContainer
from UM.Logger import Logger from UM.Logger import Logger
from UM.i18n import i18nCatalog 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 # 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 # written with. If the file format is changed in a way that breaks reverse
# compatibility, increment this version number! # compatibility, increment this version number!
version = 2 version = 3
## Dictionary that defines how characters are escaped when embedded in ## Dictionary that defines how characters are escaped when embedded in
# g-code. # g-code.
@ -66,21 +65,37 @@ class GCodeProfileReader(ProfileReader):
Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e)) Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e))
return None return None
# Un-escape the serialized profile. serialized = unescapeGcodeComment(serialized)
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)
Logger.log("i", "Serialized the following from %s: %s" %(file_name, repr(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 json_data = json.loads(serialized)
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
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

View file

@ -4,8 +4,11 @@
from UM.Mesh.MeshWriter import MeshWriter from UM.Mesh.MeshWriter import MeshWriter
from UM.Logger import Logger from UM.Logger import Logger
from UM.Application import Application 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 re #For escaping characters in the settings.
import json
## Writes g-code to a file. ## 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 # 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 # written with. If the file format is changed in a way that breaks reverse
# compatibility, increment this version number! # compatibility, increment this version number!
version = 2 version = 3
## Dictionary that defines how characters are escaped when embedded in ## Dictionary that defines how characters are escaped when embedded in
# g-code. # g-code.
@ -64,25 +67,33 @@ class GCodeWriter(MeshWriter):
# #
# \param settings A container stack to serialise. # \param settings A container stack to serialise.
# \return A serialised string of the settings. # \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 = ";SETTING_" + str(GCodeWriter.version) + " " # The prefix to put before each line.
prefix_length = len(prefix) prefix_length = len(prefix)
global_stack = Application.getInstance().getGlobalContainerStack() container_with_profile = stack.findContainer({"type": "quality"})
container_with_profile = global_stack.findContainer({"type": "quality"})
serialized = container_with_profile.serialize() 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. # Escape characters that have a special meaning in g-code comments.
pattern = re.compile("|".join(GCodeWriter.escape_characters.keys())) pattern = re.compile("|".join(GCodeWriter.escape_characters.keys()))
# Perform the replacement with a regular expression. # 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. # Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix.
result = "" result = ""
# Lines have 80 characters, so the payload of each line is 80 - prefix. # Lines have 80 characters, so the payload of each line is 80 - prefix.
for pos in range(0, len(serialized), 80 - prefix_length): for pos in range(0, len(escaped_string), 80 - prefix_length):
result += prefix + serialized[pos : pos + 80 - prefix_length] + "\n" result += prefix + escaped_string[pos : pos + 80 - prefix_length] + "\n"
serialized = result return result
return serialized