Browse Source

WIP: Create VariantManager

Lipu Fei 7 years ago
parent
commit
55bdc0c853

+ 11 - 0
cura/CuraApplication.py

@@ -59,6 +59,8 @@ from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
 from cura.Settings.UserProfilesModel import UserProfilesModel
 from cura.Settings.UserProfilesModel import UserProfilesModel
 from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
 from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
 
 
+from cura.Machines.VariantManager import VariantManager
+
 
 
 from . import PlatformPhysics
 from . import PlatformPhysics
 from . import BuildVolume
 from . import BuildVolume
@@ -233,6 +235,9 @@ class CuraApplication(QtApplication):
         if kwargs["parsed_command_line"].get("trigger_early_crash", False):
         if kwargs["parsed_command_line"].get("trigger_early_crash", False):
             assert not "This crash is triggered by the trigger_early_crash command line argument."
             assert not "This crash is triggered by the trigger_early_crash command line argument."
 
 
+        # new stuff
+        self._variant_manager = VariantManager(ContainerRegistry.getInstance())
+
         self.default_theme = "cura-light"
         self.default_theme = "cura-light"
 
 
         self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
         self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
@@ -707,6 +712,9 @@ class CuraApplication(QtApplication):
                 return False
                 return False
         return True
         return True
 
 
+    def getVariantManager(self):
+        return self._variant_manager
+
     def preRun(self):
     def preRun(self):
         # Last check for unknown commandline arguments
         # Last check for unknown commandline arguments
         parser = self.getCommandlineParser()
         parser = self.getCommandlineParser()
@@ -723,6 +731,9 @@ class CuraApplication(QtApplication):
     def run(self):
     def run(self):
         self.preRun()
         self.preRun()
 
 
+        container_registry = ContainerRegistry.getInstance()
+        self._variant_manager.initialize()
+
         # Check if we should run as single instance or not
         # Check if we should run as single instance or not
         self._setUpSingleInstanceServer()
         self._setUpSingleInstanceServer()
 
 

+ 34 - 0
cura/Machines/ContainerNode.py

@@ -0,0 +1,34 @@
+from typing import Optional
+
+from collections import OrderedDict
+
+from UM.Logger import Logger
+
+
+##  A metadata / container combination. Use getContainer to get the container corresponding to the metadata
+class ContainerNode:
+    def __init__(self, metadata = None):
+        self.metadata = metadata
+        self.container = None
+        self.children_map = OrderedDict()
+
+    def getChildNode(self, child_key: str) -> Optional["QualityNode"]:
+        return self.children_map.get(child_key)
+
+    def getContainer(self) -> "InstanceContainer":
+        if self.metadata is None:
+            raise RuntimeError("Cannot get container for a QualityNode without metadata")
+
+        if self.container is None:
+            container_id = self.metadata["id"]
+            Logger.log("d", "Lazy-loading container [%s]", container_id)
+            from UM.Settings.ContainerRegistry import ContainerRegistry
+            container_list = ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
+            if not container_list:
+                raise RuntimeError("Failed to lazy-load container [%s], cannot find it" % container_id)
+            self.container = container_list[0]
+
+        return self.container
+
+    def __str__(self):
+        return "ContainerNode[%s]" % self.metadata.get("id")

+ 81 - 0
cura/Machines/VariantManager.py

@@ -0,0 +1,81 @@
+from typing import Optional
+
+from UM.Logger import Logger
+from UM.Settings.ContainerRegistry import ContainerRegistry
+from UM.Settings.InstanceContainer import InstanceContainer
+
+from cura.Machines.ContainerNode import ContainerNode
+from cura.Settings.GlobalStack import GlobalStack
+
+
+class VariantType:
+    BUILD_PLATE = "buildplate"
+    NOZZLE = "nozzle"
+
+
+ALL_VARIANT_TYPES = (VariantType.BUILD_PLATE, VariantType.NOZZLE)
+
+
+#
+# VariantManager is THE place to look for a specific variant. It maintains a variant lookup table with the following
+# structure:
+#
+#   [machine_definition_id] ->  [variant_type]  -> [variant_name]   -> ContainerNode(metadata / container)
+# Example:   "ultimaker3"   ->    "buildplate"  ->   "Glass" (if present)  -> ContainerNode
+#                                               ->    ...
+#                           ->    "nozzle"      ->   "AA 0.4"
+#                                               ->   "BB 0.8"
+#                                               ->    ...
+#
+# Note that the "container" field is not loaded in the beginning because it would defeat the purpose of lazy-loading.
+# A container is loaded when getVariant() is called to load a variant InstanceContainer.
+#
+class VariantManager:
+
+    def __init__(self, container_registry):
+        self._container_registry = container_registry  # type: ContainerRegistry
+
+        self._machine_to_variant_dict_map = {}  # <machine_type> -> <variant_dict>
+
+        self._exclude_variant_id_list = ["empty_variant"]
+
+    #
+    # Initializes the VariantManager including:
+    #  - initializing the variant lookup table based on the metadata in ContainerRegistry.
+    #
+    def initialize(self):
+        # Cache all variants from the container registry to a variant map for better searching and organization.
+        variant_metadata_list = self._container_registry.findContainersMetadata(type = "variant")
+        for variant_metadata in variant_metadata_list:
+            if variant_metadata["id"] in self._exclude_variant_id_list:
+                Logger.log("d", "Exclude variant [%s]", variant_metadata["id"])
+                continue
+
+            variant_name = variant_metadata["name"]
+            variant_definition = variant_metadata["definition"]
+            if variant_definition not in self._machine_to_variant_dict_map:
+                self._machine_to_variant_dict_map[variant_definition] = {}
+                #for variant_type in ALL_VARIANT_TYPES:
+                #    self._machine_to_variant_dict_map[variant_definition][variant_type] = {}
+
+            variant_type = variant_metadata["hardware_type"]
+            #variant_dict = self._machine_to_variant_dict_map[variant_definition][variant_type]
+            variant_dict = self._machine_to_variant_dict_map[variant_definition]
+            if variant_name in variant_dict:
+                # ERROR: duplicated variant name.
+                raise RuntimeError("Found duplicated variant name [%s], type [%s] for machine [%s]" %
+                                   (variant_name, variant_type, variant_definition))
+
+            variant_dict[variant_name] = ContainerNode(metadata = variant_metadata)
+
+    #
+    # Gets the variant InstanceContainer with the given information.
+    # Almost the same as getVariantMetadata() except that this returns an InstanceContainer if present.
+    #
+    def getVariant(self, machine_type_name: str, variant_name: str,
+                   variant_type: Optional[str] = None) -> Optional["InstanceContainer"]:
+        return self._machine_to_variant_dict_map[machine_type_name].get(variant_name)
+
+    def getVariantNodes(self, machine: "GlobalStack"):
+        machine_type_name = machine.definition.getId()
+        return self._machine_to_variant_dict_map.get(machine_type_name)

+ 0 - 0
cura/Machines/__init__.py


+ 20 - 0
cura/Settings/MachineManager.py

@@ -1490,3 +1490,23 @@ class MachineManager(QObject):
         stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
         stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
         stacks.append(self._global_container_stack)
         stacks.append(self._global_container_stack)
         return [ s.containersChanged for s in stacks ]
         return [ s.containersChanged for s in stacks ]
+
+    # New
+    @pyqtSlot(str, "QVariant")
+    def setVariantGroup(self, position, container_node):
+        Logger.log("d", "----------------  container = [%s]", container_node)
+        position = str(position)
+        self.blurSettings.emit()
+        with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
+            self._global_container_stack.extruders[position].variant = container_node.getContainer()
+
+    @pyqtSlot("QVariant")
+    def handleQualityGroup(self, quality_group):
+        Logger.log("d", "----------------  qg = [%s]", quality_group.name)
+        self.blurSettings.emit()
+        with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
+            self._global_container_stack.quality = quality_group.node_for_global.getContainer()
+            self._global_container_stack.qualityChanges = self._empty_quality_changes_container
+            for position, node in quality_group.nodes_for_extruders.items():
+                self._global_container_stack.extruders[position].quality = node.getContainer()
+                self._global_container_stack.extruders[position].qualityChanges = self._empty_quality_changes_container

+ 47 - 0
cura/Settings/NozzleModel.py

@@ -0,0 +1,47 @@
+from PyQt5.QtCore import Qt
+
+from UM.Application import Application
+from UM.Qt.ListModel import ListModel
+
+
+class NozzleModel(ListModel):
+    IdRole = Qt.UserRole + 1
+    HotendNameRole = Qt.UserRole + 2
+    ContainerNodeRole = Qt.UserRole + 3
+
+    def __init__(self, parent = None):
+        super().__init__(parent)
+
+        self.addRoleName(self.IdRole, "id")
+        self.addRoleName(self.HotendNameRole, "hotend_name")
+        self.addRoleName(self.ContainerNodeRole, "container_node")
+
+        Application.getInstance().globalContainerStackChanged.connect(self._update)
+        Application.getInstance().getMachineManager().activeVariantChanged.connect(self._update)
+        Application.getInstance().getMachineManager().activeStackChanged.connect(self._update)
+        Application.getInstance().getMachineManager().activeMaterialChanged.connect(self._update)
+
+    def _update(self):
+        self.items.clear()
+
+        variant_manager = Application.getInstance()._variant_manager
+        active_global_stack = Application.getInstance().getMachineManager()._global_container_stack
+        if active_global_stack is None:
+            self.setItems([])
+            return
+
+        variant_group_dict = variant_manager.getVariantNodes(active_global_stack)
+        if not variant_group_dict:
+            self.setItems([])
+            return
+
+        item_list = []
+        for hotend_name, container_node in sorted(variant_group_dict.items(), key = lambda i: i[0]):
+            item = {"id": hotend_name,
+                    "hotend_name": hotend_name,
+                    "container_node": container_node
+                    }
+
+            item_list.append(item)
+
+        self.setItems(item_list)

+ 12 - 8
plugins/XmlMaterialProfile/XmlMaterialProfile.py

@@ -590,15 +590,14 @@ class XmlMaterialProfile(InstanceContainer):
                         if buildplate_id is None:
                         if buildplate_id is None:
                             continue
                             continue
 
 
-                        variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(
-                            id = buildplate_id)
-                        if not variant_containers:
-                            # It is not really properly defined what "ID" is so also search for variants by name.
-                            variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(
-                                definition = machine_id, name = buildplate_id)
-
-                        if not variant_containers:
+                        from cura.Machines.VariantManager import VariantType
+                        variant_manager = CuraApplication.getInstance().getVariantManager()
+                        variant_node = variant_manager.getVariant(machine_id, VariantType.BUILD_PLATE, buildplate_id)
+                        if not variant_node:
                             continue
                             continue
+                        variant_metadata = variant_node.metadata
+
+                        # TODO: check if build plate variant exists
 
 
                         buildplate_compatibility = machine_compatibility
                         buildplate_compatibility = machine_compatibility
                         buildplate_recommended = machine_compatibility
                         buildplate_recommended = machine_compatibility
@@ -623,6 +622,11 @@ class XmlMaterialProfile(InstanceContainer):
                         if hotend_name is None:
                         if hotend_name is None:
                             continue
                             continue
 
 
+                        variant_manager = CuraApplication.getInstance().getVariantManager()
+                        variant_node = variant_manager.getVariant(machine_id, hotend_name)
+                        if not variant_node:
+                            continue
+
                         hotend_compatibility = machine_compatibility
                         hotend_compatibility = machine_compatibility
                         hotend_setting_values = {}
                         hotend_setting_values = {}
                         settings = hotend.iterfind("./um:setting", self.__namespaces)
                         settings = hotend.iterfind("./um:setting", self.__namespaces)

+ 22 - 27
resources/qml/Menus/NozzleMenu.qml

@@ -29,38 +29,33 @@ Menu
         return true;
         return true;
     }
     }
 
 
-    MenuItem
+    // TODO: single instance??
+    Cura.NozzleModel
     {
     {
-        id: automaticNozzle
-        text:
-        {
-            if(visible)
-            {
-                var nozzleName = Cura.MachineManager.printerOutputDevices[0].hotendIds[extruderIndex];
-                return catalog.i18nc("@title:menuitem %1 is the nozzle currently loaded in the printer", "Automatic: %1").arg(nozzleName);
-            }
-            return "";
-        }
-        visible: printerConnected && Cura.MachineManager.printerOutputDevices[0].hotendIds != undefined && Cura.MachineManager.printerOutputDevices[0].hotendIds.length > extruderIndex && !isClusterPrinter
-        onTriggered:
+        id: nozzleModel
+    }
+
+    Instantiator
+    {
+        model: nozzleModel
+
+        MenuItem
         {
         {
-            var activeExtruderIndex = Cura.ExtruderManager.activeExtruderIndex;
-            Cura.ExtruderManager.setActiveExtruderIndex(extruderIndex);
-            var hotendId = Cura.MachineManager.printerOutputDevices[0].hotendIds[extruderIndex];
-            var itemIndex = nozzleInstantiator.model.find("name", hotendId);
-            if(itemIndex > -1)
-            {
-                Cura.MachineManager.setActiveVariant(nozzleInstantiator.model.getItem(itemIndex).id);
+            text: model.hotend_name
+            checkable: true
+            checked: Cura.MachineManager.activeVariantId == model.hotend_name
+            exclusiveGroup: group
+            onTriggered: {
+                var position = Cura.ExtruderManager.activeExtruderIndex;
+                Cura.MachineManager.setVariantGroup(position, model.container_node);
             }
             }
-            Cura.ExtruderManager.setActiveExtruderIndex(activeExtruderIndex);
+            visible: true
         }
         }
-    }
 
 
-    MenuSeparator
-    {
-        visible: automaticNozzle.visible
+        onObjectAdded: menu.insertItem(index, object);
+        onObjectRemoved: menu.removeItem(object);
     }
     }
-
+    /*
     Instantiator
     Instantiator
     {
     {
         id: nozzleInstantiator
         id: nozzleInstantiator
@@ -96,7 +91,7 @@ Menu
         }
         }
         onObjectAdded: menu.insertItem(index, object)
         onObjectAdded: menu.insertItem(index, object)
         onObjectRemoved: menu.removeItem(object)
         onObjectRemoved: menu.removeItem(object)
-    }
+    } */
 
 
     ExclusiveGroup { id: group }
     ExclusiveGroup { id: group }
 }
 }