Browse Source

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

Jack Ha 8 years ago
parent
commit
3e869048fd

+ 27 - 17
cura/CuraApplication.py

@@ -19,7 +19,7 @@ from UM.JobQueue import JobQueue
 from UM.SaveFile import SaveFile
 from UM.Scene.Selection import Selection
 from UM.Scene.GroupDecorator import GroupDecorator
-import UM.Settings.Validator
+from UM.Settings.Validator import Validator
 
 from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
 from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
@@ -99,7 +99,32 @@ class CuraApplication(QtApplication):
         SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True)
         SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True)
         SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True)
-        SettingDefinition.addSettingType("extruder", int, str, UM.Settings.Validator)
+        SettingDefinition.addSettingType("extruder", int, str, Validator)
+
+        ## Add the 4 types of profiles to storage.
+        Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")
+        Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants")
+        Resources.addStorageType(self.ResourceTypes.MaterialInstanceContainer, "materials")
+        Resources.addStorageType(self.ResourceTypes.UserInstanceContainer, "user")
+        Resources.addStorageType(self.ResourceTypes.ExtruderStack, "extruders")
+        Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances")
+
+        ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer)
+        ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer)
+        ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer)
+        ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer)
+        ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack)
+        ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack)
+
+        ##  Initialise the version upgrade manager with Cura's storage paths.
+        import UM.VersionUpgradeManager #Needs to be here to prevent circular dependencies.
+        self._version_upgrade_manager = UM.VersionUpgradeManager.VersionUpgradeManager(
+            {
+                ("quality", UM.Settings.InstanceContainer.Version):    (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"),
+                ("machine_stack", UM.Settings.ContainerStack.Version): (self.ResourceTypes.MachineStack, "application/x-uranium-containerstack"),
+                ("preferences", UM.Preferences.Version):               (Resources.Preferences, "application/x-uranium-preferences")
+            }
+        )
 
         self._machine_action_manager = MachineActionManager.MachineActionManager()
 
@@ -142,21 +167,6 @@ class CuraApplication(QtApplication):
 
         self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading machines..."))
 
-        ## Add the 4 types of profiles to storage.
-        Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")
-        Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants")
-        Resources.addStorageType(self.ResourceTypes.MaterialInstanceContainer, "materials")
-        Resources.addStorageType(self.ResourceTypes.UserInstanceContainer, "user")
-        Resources.addStorageType(self.ResourceTypes.ExtruderStack, "extruders")
-        Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances")
-
-        ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer)
-        ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer)
-        ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer)
-        ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer)
-        ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack)
-        ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack)
-
         # Add empty variant, material and quality containers.
         # Since they are empty, they should never be serialized and instead just programmatically created.
         # We need them to simplify the switching between materials.

+ 0 - 1
plugins/CuraEngineBackend/CuraEngineBackend.py

@@ -80,7 +80,6 @@ class CuraEngineBackend(Backend):
         self._message_handlers["cura.proto.GCodeLayer"] = self._onGCodeLayerMessage
         self._message_handlers["cura.proto.GCodePrefix"] = self._onGCodePrefixMessage
         self._message_handlers["cura.proto.PrintTimeMaterialEstimates"] = self._onPrintTimeMaterialEstimates
-        #self._message_handlers["cura.proto.ObjectPrintTime"] = self._onObjectPrintTimeMessage
         self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
 
         self._start_slice_job = None

+ 7 - 4
plugins/USBPrinting/USBPrinterOutputDevice.py

@@ -37,9 +37,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
         self._connect_thread = threading.Thread(target = self._connect)
         self._connect_thread.daemon = True
 
-        self._end_stop_thread = threading.Thread(target = self._pollEndStop)
-        self._end_stop_thread.daemon = True
-        self._poll_endstop = -1
+        self._end_stop_thread = None
+        self._poll_endstop = False
 
         # The baud checking is done by sending a number of m105 commands to the printer and waiting for a readable
         # response. If the baudrate is correct, this should make sense, else we get giberish.
@@ -221,13 +220,17 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
 
     @pyqtSlot()
     def startPollEndstop(self):
-        if self._poll_endstop == -1:
+        if not self._poll_endstop:
             self._poll_endstop = True
+            if self._end_stop_thread is None:
+                self._end_stop_thread = threading.Thread(target=self._pollEndStop)
+                self._end_stop_thread.daemon = True
             self._end_stop_thread.start()
 
     @pyqtSlot()
     def stopPollEndstop(self):
         self._poll_endstop = False
+        self._end_stop_thread = None
 
     def _pollEndStop(self):
         while self._connection_state == ConnectionState.connected and self._poll_endstop:

+ 11 - 1
plugins/UltimakerMachineActions/UMOCheckupMachineAction.py

@@ -157,9 +157,19 @@ class UMOCheckupMachineAction(MachineAction):
                 self._output_device.bedTemperatureChanged.connect(self._onBedTemperatureChanged)
                 self._output_device.hotendTemperaturesChanged.connect(self._onHotendTemperatureChanged)
                 self._output_device.endstopStateChanged.connect(self._onEndstopStateChanged)
-            except AttributeError:  # Connection is probably not a USB connection. Something went pretty wrong if this happens.
+            except AttributeError as e:  # Connection is probably not a USB connection. Something went pretty wrong if this happens.
                 pass
 
+    @pyqtSlot()
+    def cooldownHotend(self):
+        if self._output_device is not None:
+            self._output_device.setTargetHotendTemperature(0, 0)
+
+    @pyqtSlot()
+    def cooldownBed(self):
+        if self._output_device is not None:
+            self._output_device.setTargetBedTemperature(0)
+
     @pyqtSlot()
     def heatupHotend(self):
         if self._output_device is not None:

+ 25 - 4
plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml

@@ -15,6 +15,8 @@ Cura.MachineAction
         anchors.fill: parent;
         property int leftRow: checkupMachineAction.width * 0.40
         property int rightRow: checkupMachineAction.width * 0.60
+        property bool heatupHotendStarted: false
+        property bool heatupBedStarted: false
         UM.I18nCatalog { id: catalog; name:"cura"}
         Label
         {
@@ -51,6 +53,8 @@ Cura.MachineAction
                 text: catalog.i18nc("@action:button","Start Printer Check");
                 onClicked:
                 {
+                    checkupMachineAction.heatupHotendStarted = false
+                    checkupMachineAction.heatupBedStarted = false
                     manager.startCheck()
                 }
             }
@@ -181,10 +185,19 @@ Cura.MachineAction
                 anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
                 Button
                 {
-                    text: catalog.i18nc("@action:button","Start Heating")
+                    text: checkupMachineAction.heatupHotendStarted ? catalog.i18nc("@action:button","Stop Heating") : catalog.i18nc("@action:button","Start Heating")
+                    //
                     onClicked:
                     {
-                        manager.heatupHotend()
+                        if (checkupMachineAction.heatupHotendStarted)
+                        {
+                            manager.cooldownHotend()
+                            checkupMachineAction.heatupHotendStarted = false
+                        } else
+                        {
+                            manager.heatupHotend()
+                            checkupMachineAction.heatupHotendStarted = true
+                        }
                     }
                 }
             }
@@ -230,10 +243,18 @@ Cura.MachineAction
                 anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
                 Button
                 {
-                    text: catalog.i18nc("@action:button","Start Heating")
+                    text: checkupMachineAction.heatupBedStarted ?catalog.i18nc("@action:button","Stop Heating") : catalog.i18nc("@action:button","Start Heating")
                     onClicked:
                     {
-                        manager.heatupBed()
+                        if (checkupMachineAction.heatupBedStarted)
+                        {
+                            manager.cooldownBed()
+                            checkupMachineAction.heatupBedStarted = false
+                        } else
+                        {
+                            manager.heatupBed()
+                            checkupMachineAction.heatupBedStarted = true
+                        }
                     }
                 }
             }

+ 100 - 0
plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py

@@ -0,0 +1,100 @@
+# Copyright (c) 2016 Ultimaker B.V.
+# Cura is released under the terms of the AGPLv3 or higher.
+
+import UM.VersionUpgrade #To indicate that a file is of incorrect format.
+
+import configparser #To read config files.
+import io #To write config files to strings as if they were files.
+
+##  Creates a new machine instance instance by parsing a serialised machine
+#   instance in version 1 of the file format.
+#
+#   \param serialised The serialised form of a machine instance in version 1.
+#   \param filename The supposed file name of this machine instance, without
+#   extension.
+#   \return A machine instance instance, or None if the file format is
+#   incorrect.
+def importFrom(serialised, filename):
+    try:
+        return MachineInstance(serialised, filename)
+    except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException):
+        return None
+
+##  A representation of a machine instance used as intermediary form for
+#   conversion from one format to the other.
+class MachineInstance:
+    ##  Reads version 1 of the file format, storing it in memory.
+    #
+    #   \param serialised A string with the contents of a machine instance file,
+    #   without extension.
+    #   \param filename The supposed file name of this machine instance.
+    def __init__(self, serialised, filename):
+        self._filename = filename
+
+        config = configparser.ConfigParser(interpolation = None)
+        config.read_string(serialised) # Read the input string as config file.
+
+        # Checking file correctness.
+        if not config.has_section("general"):
+            raise UM.VersionUpgrade.FormatException("No \"general\" section.")
+        if not config.has_option("general", "version"):
+            raise UM.VersionUpgrade.FormatException("No \"version\" in \"general\" section.")
+        if not config.has_option("general", "name"):
+            raise UM.VersionUpgrade.FormatException("No \"name\" in \"general\" section.")
+        if not config.has_option("general", "type"):
+            raise UM.VersionUpgrade.FormatException("No \"type\" in \"general\" section.")
+        if int(config.get("general", "version")) != 1: # Explicitly hard-code version 1, since if this number changes the programmer MUST change this entire function.
+            raise UM.VersionUpgrade.InvalidVersionException("The version of this machine instance is wrong. It must be 1.")
+
+        self._type_name = config.get("general", "type")
+        self._variant_name = config.get("general", "variant", fallback = None)
+        self._name = config.get("general", "name")
+        self._key = config.get("general", "key", fallback = None)
+        self._active_profile_name = config.get("general", "active_profile", fallback = None)
+        self._active_material_name = config.get("general", "material", fallback = None)
+
+        self._machine_setting_overrides = {}
+        for key, value in config["machine_settings"].items():
+            self._machine_setting_overrides[key] = value
+
+    ##  Serialises this machine instance as file format version 2.
+    #
+    #   This is where the actual translation happens in this case.
+    #
+    #   \return A tuple containing the new filename and a serialised form of
+    #   this machine instance, serialised in version 2 of the file format.
+    def export(self):
+        config = configparser.ConfigParser(interpolation = None) # Build a config file in the form of version 2.
+
+        config.add_section("general")
+        config.set("general", "name", self._name)
+        config.set("general", "id", self._name)
+        config.set("general", "type", self._type_name)
+        config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function.
+
+        import VersionUpgrade21to22 # Import here to prevent circular dependencies.
+        type_name = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._type_name)
+        active_profile = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name)
+        active_material = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_material_name)
+        variant = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._variant_name, type_name)
+
+        containers = [
+            self._name + "_current_settings",
+            active_profile,
+            active_material,
+            variant,
+            type_name
+        ]
+        config.set("general", "containers", ",".join(containers))
+
+        config.add_section("metadata")
+        config.set("metadata", "type", "machine")
+
+        VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._machine_setting_overrides)
+        config.add_section("values")
+        for key, value in self._machine_setting_overrides.items():
+            config.set("values", key, str(value))
+
+        output = io.StringIO()
+        config.write(output)
+        return self._filename, output.getvalue()

+ 80 - 0
plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py

@@ -0,0 +1,80 @@
+# Copyright (c) 2016 Ultimaker B.V.
+# Cura is released under the terms of the AGPLv3 or higher.
+
+import configparser #To read config files.
+import io #To output config files to string.
+
+import UM.VersionUpgrade #To indicate that a file is of the wrong format.
+
+##  Creates a new preferences instance by parsing a serialised preferences file
+#   in version 1 of the file format.
+#
+#   \param serialised The serialised form of a preferences file in version 1.
+#   \param filename The supposed filename of the preferences file, without
+#   extension.
+#   \return A representation of those preferences, or None if the file format is
+#   incorrect.
+def importFrom(serialised, filename):
+    try:
+        return Preferences(serialised, filename)
+    except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException):
+        return None
+
+##  A representation of preferences files as intermediary form for conversion
+#   from one format to the other.
+class Preferences:
+    ##  Reads version 2 of the preferences file format, storing it in memory.
+    #
+    #   \param serialised A serialised version 2 preferences file.
+    #   \param filename The supposed filename of the preferences file, without
+    #   extension.
+    def __init__(self, serialised, filename):
+        self._filename = filename
+
+        self._config = configparser.ConfigParser(interpolation = None)
+        self._config.read_string(serialised)
+
+        #Checking file correctness.
+        if not self._config.has_section("general"):
+            raise UM.VersionUpgrade.FormatException("No \"general\" section.")
+        if not self._config.has_option("general", "version"):
+            raise UM.VersionUpgrade.FormatException("No \"version\" in \"general\" section.")
+        if int(self._config.get("general", "version")) != 2: # Explicitly hard-code version 2, since if this number changes the programmer MUST change this entire function.
+            raise UM.VersionUpgrade.InvalidVersionException("The version of this preferences file is wrong. It must be 2.")
+        if self._config.has_option("general", "name"): #This is probably a machine instance.
+            raise UM.VersionUpgrade.FormatException("There is a \"name\" field in this configuration file. I suspect it is not a preferences file.")
+
+    ##  Serialises these preferences as a preferences file of version 3.
+    #
+    #   This is where the actual translation happens.
+    #
+    #   \return A tuple containing the new filename and a serialised version of
+    #   a preferences file in version 3.
+    def export(self):
+        #Reset the cura/categories_expanded property since it works differently now.
+        if self._config.has_section("cura") and self._config.has_option("cura", "categories_expanded"):
+            self._config.remove_option("cura", "categories_expanded")
+
+        #Translate the setting names in the visible settings.
+        if self._config.has_section("machines") and self._config.has_option("machines", "setting_visibility"):
+            visible_settings = self._config.get("machines", "setting_visibility")
+            visible_settings = visible_settings.split(",")
+            import VersionUpgrade21to22 #Import here to prevent a circular dependency.
+            visible_settings = [VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingName(setting_name)
+                                for setting_name in visible_settings]
+            visible_settings = ",".join(visible_settings)
+            self._config.set("machines", "setting_visibility", value = visible_settings)
+
+        #Translate the active_instance key.
+        if self._config.has_section("machines") and self._config.has_option("machines", "active_instance"):
+            active_machine = self._config.get("machines", "active_instance")
+            self._config.remove_option("machines", "active_instance")
+            self._config.set("cura", "active_machine", active_machine)
+
+        #Update the version number itself.
+        self._config.set("general", "version", value = "3")
+
+        #Output the result as a string.
+        output = io.StringIO()
+        self._config.write(output)
+        return self._filename, output.getvalue()

+ 133 - 0
plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py

@@ -0,0 +1,133 @@
+# Copyright (c) 2016 Ultimaker B.V.
+# Cura is released under the terms of the AGPLv3 or higher.
+
+import configparser #To read config files.
+import io #To write config files to strings as if they were files.
+
+import UM.VersionUpgrade
+
+##  Creates a new profile instance by parsing a serialised profile in version 1
+#   of the file format.
+#
+#   \param serialised The serialised form of a profile in version 1.
+#   \param filename The supposed filename of the profile, without extension.
+#   \return A profile instance, or None if the file format is incorrect.
+def importFrom(serialised, filename):
+    try:
+        return Profile(serialised, filename)
+    except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException):
+        return None
+
+##  A representation of a profile used as intermediary form for conversion from
+#   one format to the other.
+class Profile:
+    ##  Reads version 1 of the file format, storing it in memory.
+    #
+    #   \param serialised A string with the contents of a profile.
+    #   \param filename The supposed filename of the profile, without extension.
+    def __init__(self, serialised, filename):
+        self._filename = filename
+
+        parser = configparser.ConfigParser(interpolation = None)
+        parser.read_string(serialised)
+
+        # Check correctness.
+        if not parser.has_section("general"):
+            raise UM.VersionUpgrade.FormatException("No \"general\" section.")
+        if not parser.has_option("general", "version"):
+            raise UM.VersionUpgrade.FormatException("No \"version\" in the \"general\" section.")
+        if int(parser.get("general", "version")) != 1: # Hard-coded profile version here. If this number changes the entire function needs to change.
+            raise UM.VersionUpgrade.InvalidVersionException("The version of this profile is wrong. It must be 1.")
+
+        # Parse the general section.
+        self._name = parser.get("general", "name")
+        self._type = parser.get("general", "type", fallback = None)
+        if "weight" in parser["general"]:
+            self._weight = int(parser.get("general", "weight"))
+        else:
+            self._weight = None
+        self._machine_type_id = parser.get("general", "machine_type", fallback = None)
+        self._machine_variant_name = parser.get("general", "machine_variant", fallback = None)
+        self._machine_instance_name = parser.get("general", "machine_instance", fallback = None)
+        if "material" in parser["general"]:
+            self._material_name = parser.get("general", "material")
+        elif self._type == "material":
+            self._material_name = parser.get("general", "name", fallback = None)
+        else:
+            self._material_name = None
+
+        # Parse the settings.
+        self._settings = {}
+        if parser.has_section("settings"):
+            for key, value in parser["settings"].items():
+                self._settings[key] = value
+
+        # Parse the defaults and the disabled defaults.
+        self._changed_settings_defaults = {}
+        if parser.has_section("defaults"):
+            for key, value in parser["defaults"].items():
+                self._changed_settings_defaults[key] = value
+        self._disabled_settings_defaults = []
+        if parser.has_section("disabled_defaults"):
+            disabled_defaults_string = parser.get("disabled_defaults", "values")
+            self._disabled_settings_defaults = [item for item in disabled_defaults_string.split(",") if item != ""] # Split by comma.
+
+    ##  Serialises this profile as file format version 2.
+    #
+    #   \return A tuple containing the new filename and a serialised form of
+    #   this profile, serialised in version 2 of the file format.
+    def export(self):
+        import VersionUpgrade21to22 # Import here to prevent circular dependencies.
+
+        if self._name == "Current settings":
+            self._filename += "_current_settings" #This resolves a duplicate ID arising from how Cura 2.1 stores its current settings.
+
+        config = configparser.ConfigParser(interpolation = None)
+
+        config.add_section("general")
+        config.set("general", "version", "2") #Hard-coded profile version 2.
+        config.set("general", "name", self._name)
+        if self._machine_type_id:
+            translated_machine = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._machine_type_id)
+            config.set("general", "definition", translated_machine)
+        else:
+            config.set("general", "definition", "fdmprinter")
+
+        config.add_section("metadata")
+        if self._type:
+            config.set("metadata", "type", self._type)
+        else:
+            config.set("metadata", "type", "quality")
+        if self._weight:
+            config.set("metadata", "weight", self._weight)
+        if self._machine_variant_name:
+            if self._machine_type_id:
+                config.set("metadata", "variant", VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._machine_variant_name, self._machine_type_id))
+            else:
+                config.set("metadata", "variant", self._machine_variant_name)
+        if self._material_name and self._type != "material":
+            config.set("metadata", "material", self._material_name)
+
+        if self._settings:
+            VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings)
+            config.add_section("values")
+            for key, value in self._settings.items():
+                config.set("values", key, str(value))
+
+        if self._changed_settings_defaults:
+            VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._changed_settings_defaults)
+            config.add_section("defaults")
+            for key, value in self._changed_settings_defaults.items():
+                config.set("defaults", key, str(value))
+
+        if self._disabled_settings_defaults:
+            disabled_settings_defaults = [VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingName(setting)
+                                          for setting in self._disabled_settings_defaults]
+            config.add_section("disabled_defaults")
+            disabled_defaults_string = str(disabled_settings_defaults[0]) #Must be at least 1 item, otherwise we wouldn't enter this if statement.
+            for item in disabled_settings_defaults[1:]:
+                disabled_defaults_string += "," + str(item)
+
+        output = io.StringIO()
+        config.write(output)
+        return self._filename, output.getvalue()

+ 181 - 0
plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py

@@ -0,0 +1,181 @@
+# Copyright (c) 2016 Ultimaker B.V.
+# Cura is released under the terms of the AGPLv3 or higher.
+
+import configparser #To get version numbers from config files.
+
+from UM.VersionUpgrade import VersionUpgrade # Superclass of the plugin.
+
+from . import MachineInstance # To upgrade machine instances.
+from . import Preferences #To upgrade preferences.
+from . import Profile # To upgrade profiles.
+
+##  How to translate printer names from the old version to the new.
+_printer_translations = {
+    "ultimaker2plus": "ultimaker2_plus"
+}
+
+##  How to translate profile names from the old version to the new.
+_profile_translations = {
+    "PLA": "generic_pla",
+    "ABS": "generic_abs",
+    "CPE": "generic_cpe"
+}
+
+##  How to translate setting names from the old version to the new.
+_setting_name_translations = {
+    "remove_overlapping_walls_0_enabled": "travel_compensate_overlapping_walls_0_enabled",
+    "remove_overlapping_walls_enabled": "travel_compensate_overlapping_walls_enabled",
+    "remove_overlapping_walls_x_enabled": "travel_compensate_overlapping_walls_x_enabled",
+    "retraction_hop": "retraction_hop_enabled",
+    "speed_support_lines": "speed_support_infill"
+}
+
+##  How to translate variants of specific machines from the old version to the
+#   new.
+_variant_translations = {
+    "ultimaker2_plus": {
+        "0.25 mm": "ultimaker2_plus_0.25",
+        "0.4 mm": "ultimaker2_plus_0.4",
+        "0.6 mm": "ultimaker2_plus_0.6",
+        "0.8 mm": "ultimaker2_plus_0.8"
+    },
+    "ultimaker2_extended_plus": {
+        "0.25 mm": "ultimaker2_extended_plus_0.25",
+        "0.4 mm": "ultimaker2_extended_plus_0.4",
+        "0.6 mm": "ultimaker2_extended_plus_0.6",
+        "0.8 mm": "ultimaker2_extended_plus_0.8"
+    }
+}
+
+##  Converts configuration from Cura 2.1's file formats to Cura 2.2's.
+#
+#   It converts the machine instances and profiles.
+class VersionUpgrade21to22(VersionUpgrade):
+    ##  Gets the version number from a config file.
+    #
+    #   In all config files that concern this version upgrade, the version
+    #   number is stored in general/version, so get the data from that key.
+    #
+    #   \param serialised The contents of a config file.
+    #   \return \type{int} The version number of that config file.
+    def getCfgVersion(self, serialised):
+        parser = configparser.ConfigParser(interpolation = None)
+        parser.read_string(serialised)
+        return int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
+
+    ##  Converts machine instances from format version 1 to version 2.
+    #
+    #   \param serialised The serialised machine instance in version 1.
+    #   \param filename The supposed file name of the machine instance, without
+    #   extension.
+    #   \return A tuple containing the new filename and the serialised machine
+    #   instance in version 2, or None if the input was not of the correct
+    #   format.
+    def upgradeMachineInstance(self, serialised, filename):
+        machine_instance = MachineInstance.importFrom(serialised, filename)
+        if not machine_instance: #Invalid file format.
+            return filename, None
+        return machine_instance.export()
+
+    ##  Converts preferences from format version 2 to version 3.
+    #
+    #   \param serialised The serialised preferences file in version 2.
+    #   \param filename THe supposed file name of the preferences file, without
+    #   extension.
+    #   \return A tuple containing the new filename and the serialised
+    #   preferences in version 3, or None if the input was not of the correct
+    #   format.
+    def upgradePreferences(self, serialised, filename):
+        preferences = Preferences.importFrom(serialised, filename)
+        if not preferences: #Invalid file format.
+            return filename, None
+        return preferences.export()
+
+    ##  Converts profiles from format version 1 to version 2.
+    #
+    #   \param serialised The serialised profile in version 1.
+    #   \param filename The supposed file name of the profile, without
+    #   extension.
+    #   \return A tuple containing the new filename and the serialised profile
+    #   in version 2, or None if the input was not of the correct format.
+    def upgradeProfile(self, serialised, filename):
+        profile = Profile.importFrom(serialised, filename)
+        if not profile: # Invalid file format.
+            return filename, None
+        return profile.export()
+
+    ##  Translates a printer name that might have changed since the last
+    #   version.
+    #
+    #   \param printer A printer name in Cura 2.1.
+    #   \return The name of the corresponding printer in Cura 2.2.
+    @staticmethod
+    def translatePrinter(printer):
+        if printer in _printer_translations:
+            return _printer_translations[printer]
+        return printer #Doesn't need to be translated.
+
+    ##  Translates a built-in profile name that might have changed since the
+    #   last version.
+    #
+    #   \param profile A profile name in the old version.
+    #   \return The corresponding profile name in the new version.
+    @staticmethod
+    def translateProfile(profile):
+        if profile in _profile_translations:
+            return _profile_translations[profile]
+        return profile #Doesn't need to be translated.
+
+    ##  Updates settings for the change from Cura 2.1 to 2.2.
+    #
+    #   The keys and values of settings are changed to what they should be in
+    #   the new version. Each setting is changed in-place in the provided
+    #   dictionary. This changes the input parameter.
+    #
+    #   \param settings A dictionary of settings (as key-value pairs) to update.
+    #   \return The same dictionary.
+    @staticmethod
+    def translateSettings(settings):
+        for key, value in settings.items():
+            if key == "fill_perimeter_gaps": #Setting is removed.
+                del settings[key]
+            elif key == "remove_overlapping_walls_0_enabled": #Setting is functionally replaced.
+                del settings[key]
+                settings["travel_compensate_overlapping_walls_0_enabled"] = value
+            elif key == "remove_overlapping_walls_enabled": #Setting is functionally replaced.
+                del settings[key]
+                settings["travel_compensate_overlapping_walls_enabled"] = value
+            elif key == "remove_overlapping_walls_x_enabled": #Setting is functionally replaced.
+                del settings[key]
+                settings["travel_compensate_overlapping_walls_x_enabled"] = value
+            elif key == "retraction_combing": #Combing was made into an enum instead of a boolean.
+                settings[key] = "off" if (value == "False") else "all"
+            elif key == "retraction_hop": #Setting key was changed.
+                del settings[key]
+                settings["retraction_hop_enabled"] = value
+            elif key == "speed_support_lines": #Setting key was changed.
+                del settings[key]
+                settings["speed_support_infill"] = value
+        return settings
+
+    ##  Translates a setting name for the change from Cura 2.1 to 2.2.
+    #
+    #   \param setting The name of a setting in Cura 2.1.
+    #   \return The name of the corresponding setting in Cura 2.2.
+    @staticmethod
+    def translateSettingName(setting):
+        if setting in _setting_name_translations:
+            return _setting_name_translations[setting]
+        return setting #Doesn't need to be translated.
+
+    ##  Translates a variant name for the change from Cura 2.1 to 2.2
+    #
+    #   \param variant The name of a variant in Cura 2.1.
+    #   \param machine The name of the machine this variant is part of in Cura
+    #   2.2's naming.
+    #   \return The name of the corresponding variant in Cura 2.2.
+    @staticmethod
+    def translateVariant(variant, machine):
+        if machine in _variant_translations and variant in _variant_translations[machine]:
+            return _variant_translations[machine][variant]
+        return variant

+ 43 - 0
plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py

@@ -0,0 +1,43 @@
+# Copyright (c) 2016 Ultimaker B.V.
+# Cura is released under the terms of the AGPLv3 or higher.
+
+from . import VersionUpgrade21to22
+
+from UM.i18n import i18nCatalog
+catalog = i18nCatalog("cura")
+
+upgrade = VersionUpgrade21to22.VersionUpgrade21to22()
+
+def getMetaData():
+    return {
+        "plugin": {
+            "name": catalog.i18nc("@label", "Version Upgrade 2.1 to 2.2"),
+            "author": "Ultimaker",
+            "version": "1.0",
+            "description": catalog.i18nc("@info:whatsthis", "Upgrades configurations from Cura 2.1 to Cura 2.2."),
+            "api": 3
+        },
+        "version_upgrade": {
+            # From                     To                 Upgrade function
+            ("profile", 1):          ("quality", 2,       upgrade.upgradeProfile),
+            ("machine_instance", 1): ("machine_stack", 2, upgrade.upgradeMachineInstance),
+            ("preferences", 2):      ("preferences", 3,   upgrade.upgradePreferences)
+        },
+        "sources": {
+            "profile": {
+                "get_version": upgrade.getCfgVersion,
+                "location": {"./profiles", "./instance_profiles"}
+            },
+            "machine_instance": {
+                "get_version": upgrade.getCfgVersion,
+                "location": {"./machine_instances"}
+            },
+            "preferences": {
+                "get_version": upgrade.getCfgVersion,
+                "location": {"."}
+            }
+        }
+    }
+
+def register(app):
+    return { "version_upgrade": upgrade }

Some files were not shown because too many files changed in this diff