Browse Source

Merge pull request #846 from Ultimaker/feature_material_editing

Material Editing Support
awhiemstra 8 years ago
parent
commit
90af9e3986

+ 16 - 12
cura/CuraApplication.py

@@ -32,8 +32,6 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
 
 from UM.i18n import i18nCatalog
 
-from . import ExtruderManager
-from . import ExtrudersModel
 from . import PlatformPhysics
 from . import BuildVolume
 from . import CameraAnimation
@@ -42,11 +40,11 @@ from . import CuraActions
 from . import MultiMaterialDecorator
 from . import ZOffsetDecorator
 from . import CuraSplashScreen
-from . import MachineManagerModel
-from . import ContainerSettingsModel
 from . import CameraImageProvider
 from . import MachineActionManager
 
+import cura.Settings
+
 from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
 from PyQt5.QtGui import QColor, QIcon
 from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
@@ -273,7 +271,8 @@ class CuraApplication(QtApplication):
                 Logger.logException("e", "An exception occurred when serializing container %s", instance.getId())
                 continue
 
-            file_name = urllib.parse.quote_plus(instance.getId()) + ".inst.cfg"
+            mime_type = ContainerRegistry.getMimeTypeForContainer(type(instance))
+            file_name = urllib.parse.quote_plus(instance.getId()) + "." + mime_type.preferredSuffix
             instance_type = instance.getMetaDataEntry("type")
             path = None
             if instance_type == "material":
@@ -301,7 +300,8 @@ class CuraApplication(QtApplication):
                 Logger.logException("e", "An exception occurred when serializing container %s", instance.getId())
                 continue
 
-            file_name = urllib.parse.quote_plus(stack.getId()) + ".stack.cfg"
+            mime_type = ContainerRegistry.getMimeTypeForContainer(type(stack))
+            file_name = urllib.parse.quote_plus(stack.getId()) + "." + mime_type.preferredSuffix
             stack_type = stack.getMetaDataEntry("type", None)
             path = None
             if not stack_type or stack_type == "machine":
@@ -378,9 +378,9 @@ class CuraApplication(QtApplication):
         self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface..."))
 
         # Initialise extruder so as to listen to global container stack changes before the first global container stack is set.
-        ExtruderManager.ExtruderManager.getInstance()
-        qmlRegisterSingletonType(MachineManagerModel.MachineManagerModel, "Cura", 1, 0, "MachineManager",
-                                 MachineManagerModel.createMachineManagerModel)
+        cura.Settings.ExtruderManager.getInstance()
+        qmlRegisterSingletonType(cura.Settings.MachineManager, "Cura", 1, 0, "MachineManager",
+                                 cura.Settings.MachineManager.createMachineManager)
 
         qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
         self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
@@ -424,6 +424,7 @@ class CuraApplication(QtApplication):
     #   \param engine The QML engine.
     def registerObjects(self, engine):
         engine.rootContext().setContextProperty("Printer", self)
+        engine.rootContext().setContextProperty("CuraApplication", self)
         self._print_information = PrintInformation.PrintInformation()
         engine.rootContext().setContextProperty("PrintInformation", self._print_information)
         self._cura_actions = CuraActions.CuraActions(self)
@@ -431,13 +432,16 @@ class CuraApplication(QtApplication):
 
         qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
 
-        qmlRegisterType(ExtrudersModel.ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
+        qmlRegisterType(cura.Settings.ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
+
+        qmlRegisterType(cura.Settings.ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel")
+        qmlRegisterType(cura.Settings.MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler")
 
-        qmlRegisterType(ContainerSettingsModel.ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel")
+        qmlRegisterSingletonType(cura.Settings.ContainerManager, "Cura", 1, 0, "ContainerManager", cura.Settings.ContainerManager.createContainerManager)
 
         qmlRegisterSingletonType(QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")), "Cura", 1, 0, "Actions")
 
-        engine.rootContext().setContextProperty("ExtruderManager", ExtruderManager.ExtruderManager.getInstance())
+        engine.rootContext().setContextProperty("ExtruderManager", cura.Settings.ExtruderManager.getInstance())
 
         for path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.QmlFiles):
             type_name = os.path.splitext(os.path.basename(path))[0]

+ 383 - 0
cura/Settings/ContainerManager.py

@@ -0,0 +1,383 @@
+# Copyright (c) 2016 Ultimaker B.V.
+# Cura is released under the terms of the AGPLv3 or higher.
+
+import os.path
+import urllib
+
+from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal, QUrl
+from PyQt5.QtWidgets import QMessageBox
+
+import UM.PluginRegistry
+import UM.Settings
+import UM.SaveFile
+import UM.Platform
+import UM.MimeTypeDatabase
+import UM.Logger
+
+from UM.MimeTypeDatabase import MimeTypeNotFoundError
+
+from UM.i18n import i18nCatalog
+catalog = i18nCatalog("cura")
+
+##  Manager class that contains common actions to deal with containers in Cura.
+#
+#   This is primarily intended as a class to be able to perform certain actions
+#   from within QML. We want to be able to trigger things like removing a container
+#   when a certain action happens. This can be done through this class.
+class ContainerManager(QObject):
+    def __init__(self, parent = None):
+        super().__init__(parent)
+
+        self._registry = UM.Settings.ContainerRegistry.getInstance()
+        self._container_name_filters = {}
+
+    ##  Create a duplicate of the specified container
+    #
+    #   This will create and add a duplicate of the container corresponding
+    #   to the container ID.
+    #
+    #   \param container_id \type{str} The ID of the container to duplicate.
+    #
+    #   \return The ID of the new container, or an empty string if duplication failed.
+    @pyqtSlot(str, result = str)
+    def duplicateContainer(self, container_id):
+        containers = self._registry.findContainers(None, id = container_id)
+        if not containers:
+            UM.Logger.log("w", "Could duplicate container %s because it was not found.", container_id)
+            return ""
+
+        container = containers[0]
+
+        new_container = None
+        new_name = self._registry.uniqueName(container.getName())
+        # Only InstanceContainer has a duplicate method at the moment.
+        # So fall back to serialize/deserialize when no duplicate method exists.
+        if hasattr(container, "duplicate"):
+            new_container = container.duplicate(new_name)
+        else:
+            new_container = container.__class__(new_name)
+            new_container.deserialize(container.serialize())
+            new_container.setName(new_name)
+
+        if new_container:
+            self._registry.addContainer(new_container)
+
+        return new_container.getId()
+
+    ##  Change the name of a specified container to a new name.
+    #
+    #   \param container_id \type{str} The ID of the container to change the name of.
+    #   \param new_id \type{str} The new ID of the container.
+    #   \param new_name \type{str} The new name of the specified container.
+    #
+    #   \return True if successful, False if not.
+    @pyqtSlot(str, str, str, result = bool)
+    def renameContainer(self, container_id, new_id, new_name):
+        containers = self._registry.findContainers(None, id = container_id)
+        if not containers:
+            UM.Logger.log("w", "Could rename container %s because it was not found.", container_id)
+            return False
+
+        container = containers[0]
+        # First, remove the container from the registry. This will clean up any files related to the container.
+        self._registry.removeContainer(container)
+
+        # Ensure we have a unique name for the container
+        new_name = self._registry.uniqueName(new_name)
+
+        # Then, update the name and ID of the container
+        container.setName(new_name)
+        container._id = new_id # TODO: Find a nicer way to set a new, unique ID
+
+        # Finally, re-add the container so it will be properly serialized again.
+        self._registry.addContainer(container)
+
+        return True
+
+    ##  Remove the specified container.
+    #
+    #   \param container_id \type{str} The ID of the container to remove.
+    #
+    #   \return True if the container was successfully removed, False if not.
+    @pyqtSlot(str, result = bool)
+    def removeContainer(self, container_id):
+        containers = self._registry.findContainers(None, id = container_id)
+        if not containers:
+            UM.Logger.log("w", "Could remove container %s because it was not found.", container_id)
+            return False
+
+        self._registry.removeContainer(containers[0].getId())
+
+        return True
+
+    ##  Merge a container with another.
+    #
+    #   This will try to merge one container into the other, by going through the container
+    #   and setting the right properties on the other container.
+    #
+    #   \param merge_into_id \type{str} The ID of the container to merge into.
+    #   \param merge_id \type{str} The ID of the container to merge.
+    #
+    #   \return True if successfully merged, False if not.
+    @pyqtSlot(str, result = bool)
+    def mergeContainers(self, merge_into_id, merge_id):
+        containers = self._registry.findContainers(None, id = merge_into_id)
+        if not containers:
+            UM.Logger.log("w", "Could merge into container %s because it was not found.", merge_into_id)
+            return False
+
+        merge_into = containers[0]
+
+        containers = self._registry.findContainers(None, id = merge_id)
+        if not containers:
+            UM.Logger.log("w", "Could not merge container %s because it was not found", merge_id)
+            return False
+
+        merge = containers[0]
+
+        if type(merge) != type(merge_into):
+            UM.Logger.log("w", "Cannot merge two containers of different types")
+            return False
+
+        for key in merge.getAllKeys():
+            merge_into.setProperty(key, "value", merge.getProperty(key, "value"))
+
+        return True
+
+    ##  Clear the contents of a container.
+    #
+    #   \param container_id \type{str} The ID of the container to clear.
+    #
+    #   \return True if successful, False if not.
+    @pyqtSlot(str, result = bool)
+    def clearContainer(self, container_id):
+        containers = self._registry.findContainers(None, id = container_id)
+        if not containers:
+            UM.Logger.log("w", "Could clear container %s because it was not found.", container_id)
+            return False
+
+        if containers[0].isReadOnly():
+            UM.Logger.log("w", "Cannot clear read-only container %s", container_id)
+            return False
+
+        containers[0].clear()
+
+        return True
+
+    ##  Set a metadata entry of the specified container.
+    #
+    #   This will set the specified entry of the container's metadata to the specified
+    #   value. Note that entries containing dictionaries can have their entries changed
+    #   by using "/" as a separator. For example, to change an entry "foo" in a
+    #   dictionary entry "bar", you can specify "bar/foo" as entry name.
+    #
+    #   \param container_id \type{str} The ID of the container to change.
+    #   \param entry_name \type{str} The name of the metadata entry to change.
+    #   \param entry_value The new value of the entry.
+    #
+    #   \return True if successful, False if not.
+    @pyqtSlot(str, str, str, result = bool)
+    def setContainerMetaDataEntry(self, container_id, entry_name, entry_value):
+        containers = UM.Settings.ContainerRegistry.getInstance().findContainers(None, id = container_id)
+        if not containers:
+            UM.Logger.log("w", "Could set metadata of container %s because it was not found.", container_id)
+            return False
+
+        container = containers[0]
+
+        if container.isReadOnly():
+            UM.Logger.log("w", "Cannot set metadata of read-only container %s.", container_id)
+            return False
+
+        entries = entry_name.split("/")
+        entry_name = entries.pop()
+
+        if entries:
+            root_name = entries.pop(0)
+            root = container.getMetaDataEntry(root_name)
+
+            item = root
+            for entry in entries:
+                item = item.get(entries.pop(0), { })
+
+            item[entry_name] = entry_value
+
+            entry_name = root_name
+            entry_value = root
+
+        container.setMetaDataEntry(entry_name, entry_value)
+
+        return True
+
+    ##  Find instance containers matching certain criteria.
+    #
+    #   This effectively forwards to ContainerRegistry::findInstanceContainers.
+    #
+    #   \param criteria A dict of key - value pairs to search for.
+    #
+    #   \return A list of container IDs that match the given criteria.
+    @pyqtSlot("QVariantMap", result = "QVariantList")
+    def findInstanceContainers(self, criteria):
+        result = []
+        for entry in self._registry.findInstanceContainers(**criteria):
+            result.append(entry.getId())
+
+        return result
+
+    ##  Get a list of string that can be used as name filters for a Qt File Dialog
+    #
+    #   This will go through the list of available container types and generate a list of strings
+    #   out of that. The strings are formatted as "description (*.extension)" and can be directly
+    #   passed to a nameFilters property of a Qt File Dialog.
+    #
+    #   \param type_name Which types of containers to list. These types correspond to the "type"
+    #                    key of the plugin metadata.
+    #
+    #   \return A string list with name filters.
+    @pyqtSlot(str, result = "QStringList")
+    def getContainerNameFilters(self, type_name):
+        if not self._container_name_filters:
+            self._updateContainerNameFilters()
+
+        filters = []
+        for filter_string, entry in self._container_name_filters.items():
+            if not type_name or entry["type"] == type_name:
+                filters.append(filter_string)
+
+        return filters
+
+    ##  Export a container to a file
+    #
+    #   \param container_id The ID of the container to export
+    #   \param file_type The type of file to save as. Should be in the form of "description (*.extension, *.ext)"
+    #   \param file_url The URL where to save the file.
+    #
+    #   \return A dictionary containing a key "status" with a status code and a key "message" with a message
+    #           explaining the status.
+    #           The status code can be one of "error", "cancelled", "success"
+    @pyqtSlot(str, str, QUrl, result = "QVariantMap")
+    def exportContainer(self, container_id, file_type, file_url):
+        if not container_id or not file_type or not file_url:
+            return { "status": "error", "message": "Invalid arguments"}
+
+        if isinstance(file_url, QUrl):
+            file_url = file_url.toLocalFile()
+
+        if not file_url:
+            return { "status": "error", "message": "Invalid path"}
+
+        mime_type = None
+        if not file_type in self._container_name_filters:
+            try:
+                mime_type = UM.MimeTypeDatabase.getMimeTypeForFile(file_url)
+            except MimeTypeNotFoundError:
+                return { "status": "error", "message": "Unknown File Type" }
+        else:
+            mime_type = self._container_name_filters[file_type]["mime"]
+
+        containers = UM.Settings.ContainerRegistry.getInstance().findContainers(None, id = container_id)
+        if not containers:
+            return { "status": "error", "message": "Container not found"}
+        container = containers[0]
+
+        for suffix in mime_type.suffixes:
+            if file_url.endswith(suffix):
+                break
+        else:
+            file_url += "." + mime_type.preferredSuffix
+
+        if not UM.Platform.isWindows():
+            if os.path.exists(file_url):
+                result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"),
+                                              catalog.i18nc("@label", "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?").format(file_url))
+                if result == QMessageBox.No:
+                    return { "status": "cancelled", "message": "User cancelled"}
+
+        try:
+            contents = container.serialize()
+        except NotImplementedError:
+            return { "status": "error", "message": "Unable to serialize container"}
+
+        with UM.SaveFile(file_url, "w") as f:
+            f.write(contents)
+
+        return { "status": "success", "message": "Succesfully exported container"}
+
+    ##  Imports a profile from a file
+    #
+    #   \param file_url A URL that points to the file to import.
+    #
+    #   \return \type{Dict} dict with a 'status' key containing the string 'success' or 'error', and a 'message' key
+    #       containing a message for the user
+    @pyqtSlot(QUrl, result = "QVariantMap")
+    def importContainer(self, file_url):
+        if not file_url:
+            return { "status": "error", "message": "Invalid path"}
+
+        if isinstance(file_url, QUrl):
+            file_url = file_url.toLocalFile()
+
+        if not file_url or not os.path.exists(file_url):
+            return { "status": "error", "message": "Invalid path" }
+
+        try:
+            mime_type = UM.MimeTypeDatabase.getMimeTypeForFile(file_url)
+        except MimeTypeNotFoundError:
+            return { "status": "error", "message": "Could not determine mime type of file" }
+
+        container_type = UM.Settings.ContainerRegistry.getContainerForMimeType(mime_type)
+        if not container_type:
+            return { "status": "error", "message": "Could not find a container to handle the specified file."}
+
+        container_id = urllib.parse.unquote_plus(mime_type.stripExtension(os.path.basename(file_url)))
+        container_id = UM.Settings.ContainerRegistry.getInstance().uniqueName(container_id)
+
+        container = container_type(container_id)
+
+        try:
+            with open(file_url, "rt") as f:
+                container.deserialize(f.read())
+        except PermissionError:
+            return { "status": "error", "message": "Permission denied when trying to read the file"}
+
+        container.setName(container_id)
+
+        UM.Settings.ContainerRegistry.getInstance().addContainer(container)
+
+        return { "status": "success", "message": "Successfully imported container {0}".format(container.getName()) }
+
+    def _updateContainerNameFilters(self):
+        self._container_name_filters = {}
+        for plugin_id, container_type in UM.Settings.ContainerRegistry.getContainerTypes():
+            serialize_type = ""
+            try:
+                plugin_metadata = UM.PluginRegistry.getInstance().getMetaData(plugin_id)
+                if plugin_metadata:
+                    serialize_type = plugin_metadata["settings_container"]["type"]
+                else:
+                    continue
+            except KeyError as e:
+                continue
+
+            mime_type = UM.Settings.ContainerRegistry.getMimeTypeForContainer(container_type)
+
+            entry = {
+                "type": serialize_type,
+                "mime": mime_type,
+                "container": container_type
+            }
+
+            suffix_list = "*." + mime_type.preferredSuffix
+            for suffix in mime_type.suffixes:
+                if suffix == mime_type.preferredSuffix:
+                    continue
+
+                suffix_list += ", *." + suffix
+
+            name_filter = "{0} ({1})".format(mime_type.comment, suffix_list)
+            self._container_name_filters[name_filter] = entry
+
+    # Factory function, used by QML
+    @staticmethod
+    def createContainerManager(engine, js_engine):
+        return ContainerManager()

+ 1 - 1
cura/ContainerSettingsModel.py → cura/Settings/ContainerSettingsModel.py

@@ -90,4 +90,4 @@ class ContainerSettingsModel(ListModel):
     containersChanged = pyqtSignal()
     @pyqtProperty("QVariantList", fset = setContainers, notify = containersChanged)
     def containers(self):
-        return self.container_ids
+        return self.container_ids

+ 0 - 0
cura/CuraContainerRegistry.py → cura/Settings/CuraContainerRegistry.py


+ 0 - 0
cura/ExtruderManager.py → cura/Settings/ExtruderManager.py


+ 5 - 4
cura/ExtrudersModel.py → cura/Settings/ExtrudersModel.py

@@ -3,9 +3,10 @@
 
 from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty
 
-import cura.ExtruderManager
 import UM.Qt.ListModel
 
+from . import ExtruderManager
+
 ##  Model that holds extruders.
 #
 #   This model is designed for use by any list of extruders, but specifically
@@ -49,7 +50,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
         self._active_extruder_stack = None
 
         #Listen to changes.
-        manager = cura.ExtruderManager.ExtruderManager.getInstance()
+        manager = ExtruderManager.getInstance()
         manager.extrudersChanged.connect(self._updateExtruders) #When the list of extruders changes in general.
         UM.Application.getInstance().globalContainerStackChanged.connect(self._updateExtruders) #When the current machine changes.
         self._updateExtruders()
@@ -69,7 +70,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
         return self._add_global
 
     def _onActiveExtruderChanged(self):
-        manager = cura.ExtruderManager.ExtruderManager.getInstance()
+        manager = ExtruderManager.getInstance()
         active_extruder_stack = manager.getActiveExtruderStack()
         if self._active_extruder_stack != active_extruder_stack:
             if self._active_extruder_stack:
@@ -93,7 +94,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
     #   This should be called whenever the list of extruders changes.
     def _updateExtruders(self):
         self.clear()
-        manager = cura.ExtruderManager.ExtruderManager.getInstance()
+        manager = ExtruderManager.getInstance()
         global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
         if not global_container_stack:
             return #There is no machine to get the extruders of.

+ 17 - 17
cura/MachineManagerModel.py → cura/Settings/MachineManager.py

@@ -2,22 +2,21 @@
 # Cura is released under the terms of the AGPLv3 or higher.
 
 from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
+
 from UM.Application import Application
 from UM.Preferences import Preferences
 from UM.Logger import Logger
 
 import UM.Settings
-from UM.Settings.Validator import ValidatorState
-from UM.Settings.InstanceContainer import InstanceContainer
 
 from cura.PrinterOutputDevice import PrinterOutputDevice
-from UM.Settings.ContainerStack import ContainerStack
+
 from . import ExtruderManager
+
 from UM.i18n import i18nCatalog
 catalog = i18nCatalog("cura")
 
-
-class MachineManagerModel(QObject):
+class MachineManager(QObject):
     def __init__(self, parent = None):
         super().__init__(parent)
 
@@ -28,7 +27,7 @@ class MachineManagerModel(QObject):
         self._global_stack_valid = None
         self._onGlobalContainerChanged()
 
-        ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
+        ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
         self.globalContainerChanged.connect(self._onActiveExtruderStackChanged)
         self._onActiveExtruderStackChanged()
 
@@ -36,13 +35,13 @@ class MachineManagerModel(QObject):
         self.globalContainerChanged.connect(self.activeMaterialChanged)
         self.globalContainerChanged.connect(self.activeVariantChanged)
         self.globalContainerChanged.connect(self.activeQualityChanged)
-        ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeMaterialChanged)
-        ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeVariantChanged)
-        ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeQualityChanged)
+        ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeMaterialChanged)
+        ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeVariantChanged)
+        ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeQualityChanged)
 
         self.globalContainerChanged.connect(self.activeStackChanged)
         self.globalValueChanged.connect(self.activeStackChanged)
-        ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeStackChanged)
+        ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeStackChanged)
 
         self._empty_variant_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_variant")[0]
         self._empty_material_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_material")[0]
@@ -126,7 +125,7 @@ class MachineManagerModel(QObject):
         if property_name == "validationState":
             if self._global_stack_valid:
                 changed_validation_state = self._active_container_stack.getProperty(key, property_name)
-                if changed_validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
+                if changed_validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError):
                     self._global_stack_valid = False
                     self.globalValidationChanged.emit()
             else:
@@ -155,7 +154,7 @@ class MachineManagerModel(QObject):
             self._active_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
             self._active_container_stack.propertyChanged.disconnect(self._onGlobalPropertyChanged)
 
-        self._active_container_stack = ExtruderManager.ExtruderManager.getInstance().getActiveExtruderStack()
+        self._active_container_stack = ExtruderManager.getInstance().getActiveExtruderStack()
         if self._active_container_stack:
             self._active_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
             self._active_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
@@ -207,7 +206,7 @@ class MachineManagerModel(QObject):
                 new_global_stack.addContainer(quality_instance_container)
             new_global_stack.addContainer(current_settings_instance_container)
 
-            ExtruderManager.ExtruderManager.getInstance().addMachineExtruders(definition)
+            ExtruderManager.getInstance().addMachineExtruders(definition)
 
             Application.getInstance().setGlobalContainerStack(new_global_stack)
 
@@ -228,7 +227,7 @@ class MachineManagerModel(QObject):
 
         for key in stack.getAllKeys():
             validation_state = stack.getProperty(key, "validationState")
-            if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
+            if validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError):
                 return True
         return False
 
@@ -552,6 +551,10 @@ class MachineManagerModel(QObject):
         if containers:
             return containers[0].getBottom().getId()
 
+    @staticmethod
+    def createMachineManager(engine, script_engine):
+        return MachineManager()
+
     def _updateVariantContainer(self, definition):
         if not definition.getMetaDataEntry("has_variants"):
             return self._empty_variant_container
@@ -637,6 +640,3 @@ class MachineManagerModel(QObject):
                 return containers[0]
 
         return self._empty_quality_container
-
-def createMachineManagerModel(engine, script_engine):
-    return MachineManagerModel()

+ 19 - 0
cura/Settings/MaterialSettingsVisibilityHandler.py

@@ -0,0 +1,19 @@
+# Copyright (c) 2016 Ultimaker B.V.
+# Uranium is released under the terms of the AGPLv3 or higher.
+
+import UM.Settings.Models
+
+class MaterialSettingsVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler):
+    def __init__(self, parent = None, *args, **kwargs):
+        super().__init__(parent = parent, *args, **kwargs)
+
+        material_settings = set([
+            "material_print_temperature",
+            "material_bed_temperature",
+            "material_standby_temperature",
+            "cool_fan_speed",
+            "retraction_amount",
+            "retraction_speed",
+        ])
+
+        self.setVisible(material_settings)

+ 0 - 0
cura/SettingOverrideDecorator.py → cura/Settings/SettingOverrideDecorator.py


+ 12 - 0
cura/Settings/__init__.py

@@ -0,0 +1,12 @@
+# Copyright (c) 2016 Ultimaker B.V.
+# Cura is released under the terms of the AGPLv3 or higher.
+
+from .MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
+from .ContainerManager import ContainerManager
+from .ContainerSettingsModel import ContainerSettingsModel
+from .CuraContainerRegistry import CuraContainerRegistry
+from .ExtruderManager import ExtruderManager
+from .ExtrudersModel import ExtrudersModel
+from .MachineManager import MachineManager
+from .MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
+from .SettingOverrideDecorator import SettingOverrideDecorator

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