WIP: Create VariantManager

This commit is contained in:
Lipu Fei 2018-02-06 16:27:01 +01:00
parent 86eb9b925f
commit 55bdc0c853
8 changed files with 228 additions and 36 deletions

View File

@ -59,6 +59,8 @@ from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
from cura.Settings.UserProfilesModel import UserProfilesModel
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
from cura.Machines.VariantManager import VariantManager
from . import PlatformPhysics
from . import BuildVolume
@ -233,6 +235,9 @@ class CuraApplication(QtApplication):
if kwargs["parsed_command_line"].get("trigger_early_crash", False):
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.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
@ -707,6 +712,9 @@ class CuraApplication(QtApplication):
return False
return True
def getVariantManager(self):
return self._variant_manager
def preRun(self):
# Last check for unknown commandline arguments
parser = self.getCommandlineParser()
@ -723,6 +731,9 @@ class CuraApplication(QtApplication):
def run(self):
self.preRun()
container_registry = ContainerRegistry.getInstance()
self._variant_manager.initialize()
# Check if we should run as single instance or not
self._setUpSingleInstanceServer()

View File

@ -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")

View File

@ -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)

View File

View File

@ -1490,3 +1490,23 @@ class MachineManager(QObject):
stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
stacks.append(self._global_container_stack)
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

View File

@ -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)

View File

@ -590,15 +590,14 @@ class XmlMaterialProfile(InstanceContainer):
if buildplate_id is None:
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
variant_metadata = variant_node.metadata
# TODO: check if build plate variant exists
buildplate_compatibility = machine_compatibility
buildplate_recommended = machine_compatibility
@ -623,6 +622,11 @@ class XmlMaterialProfile(InstanceContainer):
if hotend_name is None:
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_setting_values = {}
settings = hotend.iterfind("./um:setting", self.__namespaces)

View File

@ -29,38 +29,33 @@ Menu
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:
{
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);
}
Cura.ExtruderManager.setActiveExtruderIndex(activeExtruderIndex);
}
id: nozzleModel
}
MenuSeparator
Instantiator
{
visible: automaticNozzle.visible
}
model: nozzleModel
MenuItem
{
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);
}
visible: true
}
onObjectAdded: menu.insertItem(index, object);
onObjectRemoved: menu.removeItem(object);
}
/*
Instantiator
{
id: nozzleInstantiator
@ -96,7 +91,7 @@ Menu
}
onObjectAdded: menu.insertItem(index, object)
onObjectRemoved: menu.removeItem(object)
}
} */
ExclusiveGroup { id: group }
}