Merge branch 'master' into CURA-5334_extruder_def_for_all

This commit is contained in:
Jack Ha 2018-07-02 14:11:24 +02:00
commit 3761123513
710 changed files with 272858 additions and 117101 deletions

View file

@ -313,7 +313,6 @@ class ContainerManager(QObject):
self._machine_manager.blurSettings.emit()
global_stack = self._machine_manager.activeMachine
current_quality_changes_name = global_stack.qualityChanges.getName()
current_quality_type = global_stack.quality.getMetaDataEntry("quality_type")
extruder_stacks = list(global_stack.extruders.values())
@ -324,6 +323,7 @@ class ContainerManager(QObject):
if quality_changes.getId() == "empty_quality_changes":
quality_changes = self._quality_manager._createQualityChanges(current_quality_type, current_quality_changes_name,
global_stack, stack)
self._container_registry.addContainer(quality_changes)
stack.qualityChanges = quality_changes
if not quality_changes or self._container_registry.isReadOnly(quality_changes.getId()):
@ -468,7 +468,7 @@ class ContainerManager(QObject):
container_list = [n.getContainer() for n in quality_changes_group.getAllNodes() if n.getContainer() is not None]
self._container_registry.exportQualityProfile(container_list, path, file_type)
__instance = None
__instance = None # type: ContainerManager
@classmethod
def getInstance(cls, *args, **kwargs) -> "ContainerManager":

View file

@ -5,7 +5,7 @@ import os
import re
import configparser
from typing import Optional
from typing import cast, Optional
from PyQt5.QtWidgets import QMessageBox
@ -26,7 +26,7 @@ from UM.Resources import Resources
from . import ExtruderStack
from . import GlobalStack
from cura.CuraApplication import CuraApplication
import cura.CuraApplication
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
from cura.ReaderWriters.ProfileReader import NoProfileException
@ -57,7 +57,7 @@ class CuraContainerRegistry(ContainerRegistry):
if isinstance(container, InstanceContainer) and type(container) != type(self.getEmptyInstanceContainer()):
# Check against setting version of the definition.
required_setting_version = CuraApplication.SettingVersion
required_setting_version = cura.CuraApplication.CuraApplication.SettingVersion
actual_setting_version = int(container.getMetaDataEntry("setting_version", default = 0))
if required_setting_version != actual_setting_version:
Logger.log("w", "Instance container {container_id} is outdated. Its setting version is {actual_setting_version} but it should be {required_setting_version}.".format(container_id = container.getId(), actual_setting_version = actual_setting_version, required_setting_version = required_setting_version))
@ -260,7 +260,7 @@ class CuraContainerRegistry(ContainerRegistry):
profile_id = ContainerRegistry.getInstance().uniqueName(global_stack.getId() + "_extruder_" + str(idx + 1))
profile = InstanceContainer(profile_id)
profile.setName(quality_name)
profile.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
profile.addMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.SettingVersion)
profile.addMetaDataEntry("type", "quality_changes")
profile.addMetaDataEntry("definition", expected_machine_definition)
profile.addMetaDataEntry("quality_type", quality_type)
@ -356,13 +356,15 @@ class CuraContainerRegistry(ContainerRegistry):
return catalog.i18nc("@info:status", "Profile is missing a quality type.")
global_stack = Application.getInstance().getGlobalContainerStack()
if global_stack is None:
return None
definition_id = getMachineDefinitionIDForQualitySearch(global_stack.definition)
profile.setDefinition(definition_id)
# Check to make sure the imported profile actually makes sense in context of the current configuration.
# This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as
# successfully imported but then fail to show up.
quality_manager = CuraApplication.getInstance()._quality_manager
quality_manager = cura.CuraApplication.CuraApplication.getInstance()._quality_manager
quality_group_dict = quality_manager.getQualityGroupsForMachineDefinition(global_stack)
if quality_type not in quality_group_dict:
return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type)
@ -465,7 +467,7 @@ class CuraContainerRegistry(ContainerRegistry):
def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id, new_global_quality_changes = None, create_new_ids = True):
new_extruder_id = extruder_id
application = CuraApplication.getInstance()
application = cura.CuraApplication.CuraApplication.getInstance()
extruder_definitions = self.findDefinitionContainers(id = new_extruder_id)
if not extruder_definitions:
@ -485,7 +487,7 @@ class CuraContainerRegistry(ContainerRegistry):
definition_changes_name = definition_changes_id
definition_changes = InstanceContainer(definition_changes_id, parent = application)
definition_changes.setName(definition_changes_name)
definition_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
definition_changes.addMetaDataEntry("setting_version", application.SettingVersion)
definition_changes.addMetaDataEntry("type", "definition_changes")
definition_changes.addMetaDataEntry("definition", extruder_definition.getId())
@ -514,7 +516,7 @@ class CuraContainerRegistry(ContainerRegistry):
user_container.setName(user_container_name)
user_container.addMetaDataEntry("type", "user")
user_container.addMetaDataEntry("machine", machine.getId())
user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
user_container.addMetaDataEntry("setting_version", application.SettingVersion)
user_container.setDefinition(machine.definition.getId())
user_container.setMetaDataEntry("position", extruder_stack.getMetaDataEntry("position"))
@ -587,7 +589,7 @@ class CuraContainerRegistry(ContainerRegistry):
extruder_quality_changes_container = InstanceContainer(container_id, parent = application)
extruder_quality_changes_container.setName(container_name)
extruder_quality_changes_container.addMetaDataEntry("type", "quality_changes")
extruder_quality_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
extruder_quality_changes_container.addMetaDataEntry("setting_version", application.SettingVersion)
extruder_quality_changes_container.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
extruder_quality_changes_container.addMetaDataEntry("quality_type", machine_quality_changes.getMetaDataEntry("quality_type"))
extruder_quality_changes_container.setDefinition(machine_quality_changes.getDefinition().getId())
@ -675,7 +677,7 @@ class CuraContainerRegistry(ContainerRegistry):
return extruder_stack
def _findQualityChangesContainerInCuraFolder(self, name):
quality_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.QualityChangesInstanceContainer)
quality_changes_dir = Resources.getPath(cura.CuraApplication.CuraApplication.ResourceTypes.QualityChangesInstanceContainer)
instance_container = None
@ -731,3 +733,9 @@ class CuraContainerRegistry(ContainerRegistry):
extruder_stack.setNextStack(machines[0])
else:
Logger.log("w", "Could not find machine {machine} for extruder {extruder}", machine = extruder_stack.getMetaDataEntry("machine"), extruder = extruder_stack.getId())
#Override just for the type.
@classmethod
@override(ContainerRegistry)
def getInstance(cls, *args, **kwargs) -> "CuraContainerRegistry":
return cast(CuraContainerRegistry, super().getInstance(*args, **kwargs))

View file

@ -1,15 +1,12 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os.path
from typing import Any, Optional
from typing import Any, cast, List, Optional, Union
from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject
from UM.FlameProfiler import pyqtSlot
from UM.Application import Application
from UM.Decorators import override
from UM.FlameProfiler import pyqtSlot
from UM.Logger import Logger
from UM.Settings.ContainerStack import ContainerStack, InvalidContainerStackError
from UM.Settings.InstanceContainer import InstanceContainer
@ -39,19 +36,19 @@ from . import Exceptions
# This also means that operations on the stack that modifies the container ordering is prohibited and
# will raise an exception.
class CuraContainerStack(ContainerStack):
def __init__(self, container_id: str):
def __init__(self, container_id: str) -> None:
super().__init__(container_id)
self._container_registry = ContainerRegistry.getInstance()
self._container_registry = ContainerRegistry.getInstance() #type: ContainerRegistry
self._empty_instance_container = self._container_registry.getEmptyInstanceContainer()
self._empty_instance_container = self._container_registry.getEmptyInstanceContainer() #type: InstanceContainer
self._empty_quality_changes = self._container_registry.findInstanceContainers(id = "empty_quality_changes")[0]
self._empty_quality = self._container_registry.findInstanceContainers(id = "empty_quality")[0]
self._empty_material = self._container_registry.findInstanceContainers(id = "empty_material")[0]
self._empty_variant = self._container_registry.findInstanceContainers(id = "empty_variant")[0]
self._empty_quality_changes = self._container_registry.findInstanceContainers(id = "empty_quality_changes")[0] #type: InstanceContainer
self._empty_quality = self._container_registry.findInstanceContainers(id = "empty_quality")[0] #type: InstanceContainer
self._empty_material = self._container_registry.findInstanceContainers(id = "empty_material")[0] #type: InstanceContainer
self._empty_variant = self._container_registry.findInstanceContainers(id = "empty_variant")[0] #type: InstanceContainer
self._containers = [self._empty_instance_container for i in range(len(_ContainerIndexes.IndexTypeMap))]
self._containers = [self._empty_instance_container for i in range(len(_ContainerIndexes.IndexTypeMap))] #type: List[Union[InstanceContainer, DefinitionContainer]]
self._containers[_ContainerIndexes.QualityChanges] = self._empty_quality_changes
self._containers[_ContainerIndexes.Quality] = self._empty_quality
self._containers[_ContainerIndexes.Material] = self._empty_material
@ -76,7 +73,7 @@ class CuraContainerStack(ContainerStack):
# \return The user changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setUserChanges, notify = pyqtContainersChanged)
def userChanges(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.UserChanges]
return cast(InstanceContainer, self._containers[_ContainerIndexes.UserChanges])
## Set the quality changes container.
#
@ -89,12 +86,12 @@ class CuraContainerStack(ContainerStack):
# \return The quality changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setQualityChanges, notify = pyqtContainersChanged)
def qualityChanges(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.QualityChanges]
return cast(InstanceContainer, self._containers[_ContainerIndexes.QualityChanges])
## Set the quality container.
#
# \param new_quality The new quality container. It is expected to have a "type" metadata entry with the value "quality".
def setQuality(self, new_quality: InstanceContainer, postpone_emit = False) -> None:
def setQuality(self, new_quality: InstanceContainer, postpone_emit: bool = False) -> None:
self.replaceContainer(_ContainerIndexes.Quality, new_quality, postpone_emit = postpone_emit)
## Get the quality container.
@ -102,12 +99,12 @@ class CuraContainerStack(ContainerStack):
# \return The quality container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setQuality, notify = pyqtContainersChanged)
def quality(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.Quality]
return cast(InstanceContainer, self._containers[_ContainerIndexes.Quality])
## Set the material container.
#
# \param new_material The new material container. It is expected to have a "type" metadata entry with the value "material".
def setMaterial(self, new_material: InstanceContainer, postpone_emit = False) -> None:
def setMaterial(self, new_material: InstanceContainer, postpone_emit: bool = False) -> None:
self.replaceContainer(_ContainerIndexes.Material, new_material, postpone_emit = postpone_emit)
## Get the material container.
@ -115,7 +112,7 @@ class CuraContainerStack(ContainerStack):
# \return The material container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setMaterial, notify = pyqtContainersChanged)
def material(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.Material]
return cast(InstanceContainer, self._containers[_ContainerIndexes.Material])
## Set the variant container.
#
@ -128,7 +125,7 @@ class CuraContainerStack(ContainerStack):
# \return The variant container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setVariant, notify = pyqtContainersChanged)
def variant(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.Variant]
return cast(InstanceContainer, self._containers[_ContainerIndexes.Variant])
## Set the definition changes container.
#
@ -141,7 +138,7 @@ class CuraContainerStack(ContainerStack):
# \return The definition changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setDefinitionChanges, notify = pyqtContainersChanged)
def definitionChanges(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.DefinitionChanges]
return cast(InstanceContainer, self._containers[_ContainerIndexes.DefinitionChanges])
## Set the definition container.
#
@ -154,7 +151,7 @@ class CuraContainerStack(ContainerStack):
# \return The definition container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(QObject, fset = setDefinition, notify = pyqtContainersChanged)
def definition(self) -> DefinitionContainer:
return self._containers[_ContainerIndexes.Definition]
return cast(DefinitionContainer, self._containers[_ContainerIndexes.Definition])
@override(ContainerStack)
def getBottom(self) -> "DefinitionContainer":
@ -189,13 +186,9 @@ class CuraContainerStack(ContainerStack):
# \param key The key of the setting to set.
# \param property_name The name of the property to set.
# \param new_value The new value to set the property to.
# \param target_container The type of the container to set the property of. Defaults to "user".
def setProperty(self, key: str, property_name: str, new_value: Any, target_container: str = "user") -> None:
container_index = _ContainerIndexes.TypeIndexMap.get(target_container, -1)
if container_index != -1:
self._containers[container_index].setProperty(key, property_name, new_value)
else:
raise IndexError("Invalid target container {type}".format(type = target_container))
def setProperty(self, key: str, property_name: str, property_value: Any, container: "ContainerInterface" = None, set_from_cache: bool = False) -> None:
container_index = _ContainerIndexes.UserChanges
self._containers[container_index].setProperty(key, property_name, property_value, container, set_from_cache)
## Overridden from ContainerStack
#
@ -310,15 +303,15 @@ class CuraContainerStack(ContainerStack):
#
# \return The ID of the definition container to use when searching for instance containers.
@classmethod
def _findInstanceContainerDefinitionId(cls, machine_definition: DefinitionContainer) -> str:
def _findInstanceContainerDefinitionId(cls, machine_definition: DefinitionContainerInterface) -> str:
quality_definition = machine_definition.getMetaDataEntry("quality_definition")
if not quality_definition:
return machine_definition.id
return machine_definition.id #type: ignore
definitions = ContainerRegistry.getInstance().findDefinitionContainers(id = quality_definition)
if not definitions:
Logger.log("w", "Unable to find parent definition {parent} for machine {machine}", parent = quality_definition, machine = machine_definition.id)
return machine_definition.id
Logger.log("w", "Unable to find parent definition {parent} for machine {machine}", parent = quality_definition, machine = machine_definition.id) #type: ignore
return machine_definition.id #type: ignore
return cls._findInstanceContainerDefinitionId(definitions[0])

View file

@ -1,10 +1,10 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant # For communicating data and events to Qt.
from UM.FlameProfiler import pyqtSlot
from UM.Application import Application # To get the global container stack to find the current machine.
import cura.CuraApplication #To get the global container stack to find the current machine.
from UM.Logger import Logger
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.SceneNode import SceneNode
@ -16,7 +16,7 @@ from UM.Settings.SettingInstance import SettingInstance
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext
from typing import Optional, List, TYPE_CHECKING, Union
from typing import Optional, List, TYPE_CHECKING, Union, Dict
if TYPE_CHECKING:
from cura.Settings.ExtruderStack import ExtruderStack
@ -36,14 +36,13 @@ class ExtruderManager(QObject):
super().__init__(parent)
self._application = Application.getInstance()
self._application = cura.CuraApplication.CuraApplication.getInstance()
self._extruder_trains = {} # Per machine, a dictionary of extruder container stack IDs. Only for separately defined extruders.
self._active_extruder_index = -1 # Indicates the index of the active extruder stack. -1 means no active extruder stack
self._selected_object_extruders = []
self._addCurrentMachineExtruders()
#Application.getInstance().globalContainerStackChanged.connect(self._globalContainerStackChanged)
Selection.selectionChanged.connect(self.resetSelectedObjectExtruders)
## Signal to notify other components when the list of extruders for a machine definition changes.
@ -60,42 +59,47 @@ class ExtruderManager(QObject):
# \return The unique ID of the currently active extruder stack.
@pyqtProperty(str, notify = activeExtruderChanged)
def activeExtruderStackId(self) -> Optional[str]:
if not Application.getInstance().getGlobalContainerStack():
if not self._application.getGlobalContainerStack():
return None # No active machine, so no active extruder.
try:
return self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()][str(self._active_extruder_index)].getId()
return self._extruder_trains[self._application.getGlobalContainerStack().getId()][str(self._active_extruder_index)].getId()
except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong.
return None
## Return extruder count according to extruder trains.
@pyqtProperty(int, notify = extrudersChanged)
def extruderCount(self):
if not Application.getInstance().getGlobalContainerStack():
if not self._application.getGlobalContainerStack():
return 0 # No active machine, so no extruders.
try:
return len(self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()])
return len(self._extruder_trains[self._application.getGlobalContainerStack().getId()])
except KeyError:
return 0
## Gets a dict with the extruder stack ids with the extruder number as the key.
@pyqtProperty("QVariantMap", notify = extrudersChanged)
def extruderIds(self):
def extruderIds(self) -> Dict[str, str]:
extruder_stack_ids = {}
global_stack_id = Application.getInstance().getGlobalContainerStack().getId()
global_container_stack = self._application.getGlobalContainerStack()
if global_container_stack:
global_stack_id = global_container_stack.getId()
if global_stack_id in self._extruder_trains:
for position in self._extruder_trains[global_stack_id]:
extruder_stack_ids[position] = self._extruder_trains[global_stack_id][position].getId()
if global_stack_id in self._extruder_trains:
for position in self._extruder_trains[global_stack_id]:
extruder_stack_ids[position] = self._extruder_trains[global_stack_id][position].getId()
return extruder_stack_ids
@pyqtSlot(str, result = str)
def getQualityChangesIdByExtruderStackId(self, extruder_stack_id: str) -> str:
for position in self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()]:
extruder = self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()][position]
if extruder.getId() == extruder_stack_id:
return extruder.qualityChanges.getId()
global_container_stack = self._application.getGlobalContainerStack()
if global_container_stack is not None:
for position in self._extruder_trains[global_container_stack.getId()]:
extruder = self._extruder_trains[global_container_stack.getId()][position]
if extruder.getId() == extruder_stack_id:
return extruder.qualityChanges.getId()
return ""
## Changes the active extruder by index.
#
@ -132,7 +136,7 @@ class ExtruderManager(QObject):
selected_nodes = []
for node in Selection.getAllSelectedObjects():
if node.callDecoration("isGroup"):
for grouped_node in BreadthFirstIterator(node):
for grouped_node in BreadthFirstIterator(node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if grouped_node.callDecoration("isGroup"):
continue
@ -141,7 +145,7 @@ class ExtruderManager(QObject):
selected_nodes.append(node)
# Then, figure out which nodes are used by those selected nodes.
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = self._application.getGlobalContainerStack()
current_extruder_trains = self._extruder_trains.get(global_stack.getId())
for node in selected_nodes:
extruder = node.callDecoration("getActiveExtruder")
@ -164,7 +168,7 @@ class ExtruderManager(QObject):
@pyqtSlot(result = QObject)
def getActiveExtruderStack(self) -> Optional["ExtruderStack"]:
global_container_stack = Application.getInstance().getGlobalContainerStack()
global_container_stack = self._application.getGlobalContainerStack()
if global_container_stack:
if global_container_stack.getId() in self._extruder_trains:
@ -175,7 +179,7 @@ class ExtruderManager(QObject):
## Get an extruder stack by index
def getExtruderStack(self, index) -> Optional["ExtruderStack"]:
global_container_stack = Application.getInstance().getGlobalContainerStack()
global_container_stack = self._application.getGlobalContainerStack()
if global_container_stack:
if global_container_stack.getId() in self._extruder_trains:
if str(index) in self._extruder_trains[global_container_stack.getId()]:
@ -186,7 +190,9 @@ class ExtruderManager(QObject):
def getExtruderStacks(self) -> List["ExtruderStack"]:
result = []
for i in range(self.extruderCount):
result.append(self.getExtruderStack(i))
stack = self.getExtruderStack(i)
if stack:
result.append(stack)
return result
def registerExtruder(self, extruder_train, machine_id):
@ -252,14 +258,14 @@ class ExtruderManager(QObject):
support_bottom_enabled = False
support_roof_enabled = False
scene_root = Application.getInstance().getController().getScene().getRoot()
scene_root = self._application.getController().getScene().getRoot()
# If no extruders are registered in the extruder manager yet, return an empty array
if len(self.extruderIds) == 0:
return []
# Get the extruders of all printable meshes in the scene
meshes = [node for node in DepthFirstIterator(scene_root) if isinstance(node, SceneNode) and node.isSelectable()]
meshes = [node for node in DepthFirstIterator(scene_root) if isinstance(node, SceneNode) and node.isSelectable()] #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
for mesh in meshes:
extruder_stack_id = mesh.callDecoration("getActiveExtruder")
if not extruder_stack_id:
@ -301,10 +307,10 @@ class ExtruderManager(QObject):
# The platform adhesion extruder. Not used if using none.
if global_stack.getProperty("adhesion_type", "value") != "none":
extruder_nr = str(global_stack.getProperty("adhesion_extruder_nr", "value"))
if extruder_nr == "-1":
extruder_nr = Application.getInstance().getMachineManager().defaultExtruderPosition
used_extruder_stack_ids.add(self.extruderIds[extruder_nr])
extruder_str_nr = str(global_stack.getProperty("adhesion_extruder_nr", "value"))
if extruder_str_nr == "-1":
extruder_str_nr = self._application.getMachineManager().defaultExtruderPosition
used_extruder_stack_ids.add(self.extruderIds[extruder_str_nr])
try:
return [container_registry.findContainerStacks(id = stack_id)[0] for stack_id in used_extruder_stack_ids]
@ -335,7 +341,7 @@ class ExtruderManager(QObject):
# The first element is the global container stack, followed by any extruder stacks.
# \return \type{List[ContainerStack]}
def getActiveGlobalAndExtruderStacks(self) -> Optional[List[Union["ExtruderStack", "GlobalStack"]]]:
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = self._application.getGlobalContainerStack()
if not global_stack:
return None
@ -347,7 +353,7 @@ class ExtruderManager(QObject):
#
# \return \type{List[ContainerStack]} a list of
def getActiveExtruderStacks(self) -> List["ExtruderStack"]:
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = self._application.getGlobalContainerStack()
if not global_stack:
return []
@ -420,7 +426,7 @@ class ExtruderManager(QObject):
# If no extruder has the value, the list will contain the global value.
@staticmethod
def getExtruderValues(key):
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
result = []
for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
@ -455,7 +461,7 @@ class ExtruderManager(QObject):
# If no extruder has the value, the list will contain the global value.
@staticmethod
def getDefaultExtruderValues(key):
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
context = PropertyEvaluationContext(global_stack)
context.context["evaluate_from_container_index"] = 1 # skip the user settings container
context.context["override_operators"] = {
@ -488,7 +494,7 @@ class ExtruderManager(QObject):
## Return the default extruder position from the machine manager
@staticmethod
def getDefaultExtruderPosition() -> str:
return Application.getInstance().getMachineManager().defaultExtruderPosition
return cura.CuraApplication.CuraApplication.getInstance().getMachineManager().defaultExtruderPosition
## Get all extruder values for a certain setting.
#
@ -513,7 +519,7 @@ class ExtruderManager(QObject):
@staticmethod
def getExtruderValue(extruder_index, key):
if extruder_index == -1:
extruder_index = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
extruder_index = int(cura.CuraApplication.CuraApplication.getInstance().getMachineManager().defaultExtruderPosition)
extruder = ExtruderManager.getInstance().getExtruderStack(extruder_index)
if extruder:
@ -522,7 +528,7 @@ class ExtruderManager(QObject):
value = value(extruder)
else:
# Just a value from global.
value = Application.getInstance().getGlobalContainerStack().getProperty(key, "value")
value = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack().getProperty(key, "value")
return value
@ -551,7 +557,7 @@ class ExtruderManager(QObject):
if isinstance(value, SettingFunction):
value = value(extruder, context = context)
else: # Just a value from global.
value = Application.getInstance().getGlobalContainerStack().getProperty(key, "value", context = context)
value = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack().getProperty(key, "value", context = context)
return value
@ -564,7 +570,7 @@ class ExtruderManager(QObject):
# \return The effective value
@staticmethod
def getResolveOrValue(key):
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
resolved_value = global_stack.getProperty(key, "value")
return resolved_value
@ -578,7 +584,7 @@ class ExtruderManager(QObject):
# \return The effective value
@staticmethod
def getDefaultResolveOrValue(key):
global_stack = Application.getInstance().getGlobalContainerStack()
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
context = PropertyEvaluationContext(global_stack)
context.context["evaluate_from_container_index"] = 1 # skip the user settings container
context.context["override_operators"] = {
@ -591,7 +597,7 @@ class ExtruderManager(QObject):
return resolved_value
__instance = None
__instance = None # type: ExtruderManager
@classmethod
def getInstance(cls, *args, **kwargs) -> "ExtruderManager":

View file

@ -1,11 +1,10 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Any, TYPE_CHECKING, Optional
from typing import Any, Dict, TYPE_CHECKING, Optional
from PyQt5.QtCore import pyqtProperty, pyqtSignal
from UM.Application import Application
from UM.Decorators import override
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
from UM.Settings.ContainerStack import ContainerStack
@ -13,6 +12,8 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.Interfaces import ContainerInterface, PropertyEvaluationContext
from UM.Util import parseBool
import cura.CuraApplication
from . import Exceptions
from .CuraContainerStack import CuraContainerStack, _ContainerIndexes
from .ExtruderManager import ExtruderManager
@ -25,7 +26,7 @@ if TYPE_CHECKING:
#
#
class ExtruderStack(CuraContainerStack):
def __init__(self, container_id: str):
def __init__(self, container_id: str) -> None:
super().__init__(container_id)
self.addMetaDataEntry("type", "extruder_train") # For backward compatibility
@ -50,14 +51,14 @@ class ExtruderStack(CuraContainerStack):
def getNextStack(self) -> Optional["GlobalStack"]:
return super().getNextStack()
def setEnabled(self, enabled):
def setEnabled(self, enabled: bool) -> None:
if "enabled" not in self._metadata:
self.addMetaDataEntry("enabled", "True")
self.setMetaDataEntry("enabled", str(enabled))
self.enabledChanged.emit()
@pyqtProperty(bool, notify = enabledChanged)
def isEnabled(self):
def isEnabled(self) -> bool:
return parseBool(self.getMetaDataEntry("enabled", "True"))
@classmethod
@ -113,7 +114,7 @@ class ExtruderStack(CuraContainerStack):
limit_to_extruder = super().getProperty(key, "limit_to_extruder", context)
if limit_to_extruder is not None:
if limit_to_extruder == -1:
limit_to_extruder = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
limit_to_extruder = int(cura.CuraApplication.CuraApplication.getInstance().getMachineManager().defaultExtruderPosition)
limit_to_extruder = str(limit_to_extruder)
if (limit_to_extruder is not None and limit_to_extruder != "-1") and self.getMetaDataEntry("position") != str(limit_to_extruder):
if str(limit_to_extruder) in self.getNextStack().extruders:
@ -142,7 +143,7 @@ class ExtruderStack(CuraContainerStack):
if stacks:
self.setNextStack(stacks[0])
def _onPropertiesChanged(self, key, properties):
def _onPropertiesChanged(self, key: str, properties: Dict[str, Any]) -> None:
# When there is a setting that is not settable per extruder that depends on a value from a setting that is,
# we do not always get properly informed that we should re-evaluate the setting. So make sure to indicate
# something changed for those settings.

View file

@ -1,29 +1,30 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from collections import defaultdict
import threading
from typing import Any, Dict, Optional
from typing import Any, Dict, Optional, Set, TYPE_CHECKING
from PyQt5.QtCore import pyqtProperty
from UM.Application import Application
from UM.Decorators import override
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.SettingInstance import InstanceState
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.Interfaces import PropertyEvaluationContext
from UM.Logger import Logger
import cura.CuraApplication
from . import Exceptions
from .CuraContainerStack import CuraContainerStack
if TYPE_CHECKING:
from cura.Settings.ExtruderStack import ExtruderStack
## Represents the Global or Machine stack and its related containers.
#
class GlobalStack(CuraContainerStack):
def __init__(self, container_id: str):
def __init__(self, container_id: str) -> None:
super().__init__(container_id)
self.addMetaDataEntry("type", "machine") # For backward compatibility
@ -34,7 +35,7 @@ class GlobalStack(CuraContainerStack):
# and if so, to bypass the resolve to prevent an infinite recursion that would occur
# if the resolve function tried to access the same property it is a resolve for.
# Per thread we have our own resolving_settings, or strange things sometimes occur.
self._resolving_settings = defaultdict(set) # keys are thread names
self._resolving_settings = defaultdict(set) #type: Dict[str, Set[str]] # keys are thread names
## Get the list of extruders of this stack.
#
@ -94,6 +95,7 @@ class GlobalStack(CuraContainerStack):
context.pushContainer(self)
# Handle the "resolve" property.
#TODO: Why the hell does this involve threading?
if self._shouldResolve(key, property_name, context):
current_thread = threading.current_thread()
self._resolving_settings[current_thread.name].add(key)
@ -106,7 +108,7 @@ class GlobalStack(CuraContainerStack):
limit_to_extruder = super().getProperty(key, "limit_to_extruder", context)
if limit_to_extruder is not None:
if limit_to_extruder == -1:
limit_to_extruder = int(Application.getInstance().getMachineManager().defaultExtruderPosition)
limit_to_extruder = int(cura.CuraApplication.CuraApplication.getInstance().getMachineManager().defaultExtruderPosition)
limit_to_extruder = str(limit_to_extruder)
if limit_to_extruder is not None and limit_to_extruder != "-1" and limit_to_extruder in self._extruders:
if super().getProperty(key, "settable_per_extruder", context):
@ -155,7 +157,7 @@ class GlobalStack(CuraContainerStack):
## Perform some sanity checks on the global stack
# Sanity check for extruders; they must have positions 0 and up to machine_extruder_count - 1
def isValid(self):
def isValid(self) -> bool:
container_registry = ContainerRegistry.getInstance()
extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = self.getId())

View file

@ -1,10 +1,9 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import collections
import time
#Type hinting.
from typing import List, Dict, TYPE_CHECKING, Optional
from typing import Any, Callable, List, Dict, TYPE_CHECKING, Optional
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@ -15,20 +14,22 @@ from UM.Signal import Signal
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer
from UM.FlameProfiler import pyqtSlot
from UM import Util
from UM.Application import Application
from UM.Logger import Logger
from UM.Message import Message
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.SettingFunction import SettingFunction
from UM.Signal import postponeSignals, CompressTechnique
import cura.CuraApplication
from cura.Machines.ContainerNode import ContainerNode #For typing.
from cura.Machines.QualityChangesGroup import QualityChangesGroup #For typing.
from cura.Machines.QualityGroup import QualityGroup #For typing.
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
from cura.PrinterOutputDevice import PrinterOutputDevice
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.ExtruderStack import ExtruderStack
@ -40,29 +41,31 @@ catalog = i18nCatalog("cura")
if TYPE_CHECKING:
from cura.Settings.CuraContainerStack import CuraContainerStack
from cura.Settings.GlobalStack import GlobalStack
from cura.Machines.MaterialManager import MaterialManager
from cura.Machines.QualityManager import QualityManager
from cura.Machines.VariantManager import VariantManager
class MachineManager(QObject):
def __init__(self, parent = None):
def __init__(self, parent: QObject = None) -> None:
super().__init__(parent)
self._active_container_stack = None # type: Optional[ExtruderManager]
self._global_container_stack = None # type: Optional[GlobalStack]
self._current_root_material_id = {} # type: Dict[str, str]
self._current_quality_group = None
self._current_quality_changes_group = None
self._current_quality_group = None # type: Optional[QualityGroup]
self._current_quality_changes_group = None # type: Optional[QualityChangesGroup]
self._default_extruder_position = "0" # to be updated when extruders are switched on and off
self.machine_extruder_material_update_dict = collections.defaultdict(list)
self.machine_extruder_material_update_dict = collections.defaultdict(list) #type: Dict[str, List[Callable[[], None]]]
self._instance_container_timer = QTimer()
self._instance_container_timer = QTimer() #type: QTimer
self._instance_container_timer.setInterval(250)
self._instance_container_timer.setSingleShot(True)
self._instance_container_timer.timeout.connect(self.__emitChangedSignals)
self._application = Application.getInstance()
self._application = cura.CuraApplication.CuraApplication.getInstance() #type: cura.CuraApplication.CuraApplication
self._application.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
self._application.getContainerRegistry().containerLoadComplete.connect(self._onContainersChanged)
@ -74,14 +77,14 @@ class MachineManager(QObject):
self.globalContainerChanged.connect(self.activeQualityChangesGroupChanged)
self.globalContainerChanged.connect(self.activeQualityGroupChanged)
self._stacks_have_errors = None # type:Optional[bool]
self._stacks_have_errors = None # type: Optional[bool]
self._empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
self._empty_definition_changes_container = ContainerRegistry.getInstance().findContainers(id = "empty_definition_changes")[0]
self._empty_variant_container = ContainerRegistry.getInstance().findContainers(id = "empty_variant")[0]
self._empty_material_container = ContainerRegistry.getInstance().findContainers(id = "empty_material")[0]
self._empty_quality_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0]
self._empty_quality_changes_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality_changes")[0]
self._empty_container = CuraContainerRegistry.getInstance().getEmptyInstanceContainer() #type: InstanceContainer
self._empty_definition_changes_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_definition_changes")[0] #type: InstanceContainer
self._empty_variant_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_variant")[0] #type: InstanceContainer
self._empty_material_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_material")[0] #type: InstanceContainer
self._empty_quality_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_quality")[0] #type: InstanceContainer
self._empty_quality_changes_container = CuraContainerRegistry.getInstance().findContainers(id = "empty_quality_changes")[0] #type: InstanceContainer
self._onGlobalContainerChanged()
@ -99,8 +102,6 @@ class MachineManager(QObject):
self._application.getPreferences().addPreference("cura/active_machine", "")
self._global_event_keys = set()
self._printer_output_devices = [] # type: List[PrinterOutputDevice]
self._application.getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
# There might already be some output devices by the time the signal is connected
@ -116,15 +117,15 @@ class MachineManager(QObject):
self._material_incompatible_message = Message(catalog.i18nc("@info:status",
"The selected material is incompatible with the selected machine or configuration."),
title = catalog.i18nc("@info:title", "Incompatible Material"))
title = catalog.i18nc("@info:title", "Incompatible Material")) #type: Message
containers = ContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId)
containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId) #type: List[InstanceContainer]
if containers:
containers[0].nameChanged.connect(self._onMaterialNameChanged)
self._material_manager = self._application.getMaterialManager()
self._variant_manager = self._application.getVariantManager()
self._quality_manager = self._application.getQualityManager()
self._material_manager = self._application.getMaterialManager() #type: MaterialManager
self._variant_manager = self._application.getVariantManager() #type: VariantManager
self._quality_manager = self._application.getQualityManager() #type: QualityManager
# When the materials lookup table gets updated, it can mean that a material has its name changed, which should
# be reflected on the GUI. This signal emission makes sure that it happens.
@ -164,7 +165,7 @@ class MachineManager(QObject):
def setInitialActiveMachine(self) -> None:
active_machine_id = self._application.getPreferences().getValue("cura/active_machine")
if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacksMetadata(id = active_machine_id):
if active_machine_id != "" and CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = active_machine_id):
# An active machine was saved, so restore it.
self.setActiveMachine(active_machine_id)
@ -215,7 +216,10 @@ class MachineManager(QObject):
@pyqtProperty(int, constant=True)
def totalNumberOfSettings(self) -> int:
return len(ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0].getAllKeys())
general_definition_containers = CuraContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")
if not general_definition_containers:
return 0
return len(general_definition_containers[0].getAllKeys())
def _onGlobalContainerChanged(self) -> None:
if self._global_container_stack:
@ -355,7 +359,7 @@ class MachineManager(QObject):
def setActiveMachine(self, stack_id: str) -> None:
self.blurSettings.emit() # Ensure no-one has focus.
container_registry = ContainerRegistry.getInstance()
container_registry = CuraContainerRegistry.getInstance()
containers = container_registry.findContainerStacks(id = stack_id)
if not containers:
@ -381,7 +385,7 @@ class MachineManager(QObject):
# \param metadata_filter \type{dict} list of metadata keys and values used for filtering
@staticmethod
def getMachine(definition_id: str, metadata_filter: Dict[str, str] = None) -> Optional["GlobalStack"]:
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
machines = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
for machine in machines:
if machine.definition.getId() == definition_id:
return machine
@ -625,11 +629,13 @@ class MachineManager(QObject):
## Check if a container is read_only
@pyqtSlot(str, result = bool)
def isReadOnly(self, container_id: str) -> bool:
return ContainerRegistry.getInstance().isReadOnly(container_id)
return CuraContainerRegistry.getInstance().isReadOnly(container_id)
## Copy the value of the setting of the current extruder to all other extruders as well as the global container.
@pyqtSlot(str)
def copyValueToExtruders(self, key: str) -> None:
if self._active_container_stack is None or self._global_container_stack is None:
return
new_value = self._active_container_stack.getProperty(key, "value")
extruder_stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())]
@ -641,6 +647,8 @@ class MachineManager(QObject):
## Copy the value of all manually changed settings of the current extruder to all other extruders.
@pyqtSlot()
def copyAllValuesToExtruders(self) -> None:
if self._active_container_stack is None or self._global_container_stack is None:
return
extruder_stacks = list(self._global_container_stack.extruders.values())
for extruder_stack in extruder_stacks:
if extruder_stack != self._active_container_stack:
@ -704,7 +712,7 @@ class MachineManager(QObject):
@pyqtSlot(str, str)
def renameMachine(self, machine_id: str, new_name: str) -> None:
container_registry = ContainerRegistry.getInstance()
container_registry = CuraContainerRegistry.getInstance()
machine_stack = container_registry.findContainerStacks(id = machine_id)
if machine_stack:
new_name = container_registry.createUniqueName("machine", machine_stack[0].getName(), new_name, machine_stack[0].definition.getName())
@ -718,23 +726,23 @@ class MachineManager(QObject):
# activate a new machine before removing a machine because this is safer
if activate_new_machine:
machine_stacks = ContainerRegistry.getInstance().findContainerStacksMetadata(type = "machine")
machine_stacks = CuraContainerRegistry.getInstance().findContainerStacksMetadata(type = "machine")
other_machine_stacks = [s for s in machine_stacks if s["id"] != machine_id]
if other_machine_stacks:
self.setActiveMachine(other_machine_stacks[0]["id"])
metadata = ContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)[0]
metadata = CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)[0]
network_key = metadata["um_network_key"] if "um_network_key" in metadata else None
ExtruderManager.getInstance().removeMachineExtruders(machine_id)
containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id)
containers = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id)
for container in containers:
ContainerRegistry.getInstance().removeContainer(container["id"])
ContainerRegistry.getInstance().removeContainer(machine_id)
CuraContainerRegistry.getInstance().removeContainer(container["id"])
CuraContainerRegistry.getInstance().removeContainer(machine_id)
# If the printer that is being removed is a network printer, the hidden printers have to be also removed
if network_key:
metadata_filter = {"um_network_key": network_key}
hidden_containers = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
hidden_containers = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
if hidden_containers:
# This reuses the method and remove all printers recursively
self.removeMachine(hidden_containers[0].getId())
@ -802,14 +810,17 @@ class MachineManager(QObject):
## Get the Definition ID of a machine (specified by ID)
# \param machine_id string machine id to get the definition ID of
# \returns DefinitionID (string) if found, None otherwise
# \returns DefinitionID if found, None otherwise
@pyqtSlot(str, result = str)
def getDefinitionByMachineId(self, machine_id: str) -> str:
containers = ContainerRegistry.getInstance().findContainerStacks(id = machine_id)
def getDefinitionByMachineId(self, machine_id: str) -> Optional[str]:
containers = CuraContainerRegistry.getInstance().findContainerStacks(id = machine_id)
if containers:
return containers[0].definition.getId()
return None
def getIncompatibleSettingsOnEnabledExtruders(self, container: InstanceContainer) -> List[str]:
if self._global_container_stack is None:
return []
extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
result = [] # type: List[str]
for setting_instance in container.findInstances():
@ -834,6 +845,8 @@ class MachineManager(QObject):
## Update extruder number to a valid value when the number of extruders are changed, or when an extruder is changed
def correctExtruderSettings(self) -> None:
if self._global_container_stack is None:
return
for setting_key in self.getIncompatibleSettingsOnEnabledExtruders(self._global_container_stack.userChanges):
self._global_container_stack.userChanges.removeInstance(setting_key)
add_user_changes = self.getIncompatibleSettingsOnEnabledExtruders(self._global_container_stack.qualityChanges)
@ -851,6 +864,8 @@ class MachineManager(QObject):
## Set the amount of extruders on the active machine (global stack)
# \param extruder_count int the number of extruders to set
def setActiveMachineExtruderCount(self, extruder_count: int) -> None:
if self._global_container_stack is None:
return
extruder_manager = self._application.getExtruderManager()
definition_changes_container = self._global_container_stack.definitionChanges
@ -869,7 +884,7 @@ class MachineManager(QObject):
# Check to see if any objects are set to print with an extruder that will no longer exist
root_node = self._application.getController().getScene().getRoot()
for node in DepthFirstIterator(root_node):
for node in DepthFirstIterator(root_node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if node.getMeshData():
extruder_nr = node.callDecoration("getActiveExtruderPosition")
@ -884,7 +899,7 @@ class MachineManager(QObject):
global_user_container = self._global_container_stack.userChanges
# Make sure extruder_stacks exists
extruder_stacks = []
extruder_stacks = [] #type: List[ExtruderStack]
if previous_extruder_count == 1:
extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
@ -912,6 +927,8 @@ class MachineManager(QObject):
return extruder
def updateDefaultExtruder(self) -> None:
if self._global_container_stack is None:
return
extruder_items = sorted(self._global_container_stack.extruders.items())
old_position = self._default_extruder_position
new_default_position = "0"
@ -924,6 +941,8 @@ class MachineManager(QObject):
self.extruderChanged.emit()
def updateNumberExtrudersEnabled(self) -> None:
if self._global_container_stack is None:
return
definition_changes_container = self._global_container_stack.definitionChanges
machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
extruder_count = 0
@ -936,6 +955,8 @@ class MachineManager(QObject):
@pyqtProperty(int, notify = numberExtrudersEnabledChanged)
def numberExtrudersEnabled(self) -> int:
if self._global_container_stack is None:
return 1
return self._global_container_stack.definitionChanges.getProperty("extruders_enabled_count", "value")
@pyqtProperty(str, notify = extruderChanged)
@ -945,6 +966,8 @@ class MachineManager(QObject):
## This will fire the propertiesChanged for all settings so they will be updated in the front-end
@pyqtSlot()
def forceUpdateAllSettings(self) -> None:
if self._global_container_stack is None:
return
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
property_names = ["value", "resolve", "validationState"]
for container in [self._global_container_stack] + list(self._global_container_stack.extruders.values()):
@ -954,8 +977,9 @@ class MachineManager(QObject):
@pyqtSlot(int, bool)
def setExtruderEnabled(self, position: int, enabled: bool) -> None:
extruder = self.getExtruder(position)
if not extruder:
if not extruder or self._global_container_stack is None:
Logger.log("w", "Could not find extruder on position %s", position)
return
extruder.setEnabled(enabled)
self.updateDefaultExtruder()
@ -991,6 +1015,8 @@ class MachineManager(QObject):
@pyqtSlot(str, str, str)
def setSettingForAllExtruders(self, setting_name: str, property_name: str, property_value: str) -> None:
if self._global_container_stack is None:
return
for key, extruder in self._global_container_stack.extruders.items():
container = extruder.userChanges
container.setProperty(setting_name, property_name, property_value)
@ -999,6 +1025,8 @@ class MachineManager(QObject):
# \param setting_name The ID of the setting to reset.
@pyqtSlot(str)
def resetSettingForAllExtruders(self, setting_name: str) -> None:
if self._global_container_stack is None:
return
for key, extruder in self._global_container_stack.extruders.items():
container = extruder.userChanges
container.removeInstance(setting_name)
@ -1041,6 +1069,8 @@ class MachineManager(QObject):
# for all stacks in the currently active machine.
#
def _setEmptyQuality(self) -> None:
if self._global_container_stack is None:
return
self._current_quality_group = None
self._current_quality_changes_group = None
self._global_container_stack.quality = self._empty_quality_container
@ -1052,12 +1082,14 @@ class MachineManager(QObject):
self.activeQualityGroupChanged.emit()
self.activeQualityChangesGroupChanged.emit()
def _setQualityGroup(self, quality_group, empty_quality_changes: bool = True) -> None:
def _setQualityGroup(self, quality_group: Optional[QualityGroup], empty_quality_changes: bool = True) -> None:
if self._global_container_stack is None:
return
if quality_group is None:
self._setEmptyQuality()
return
if quality_group.node_for_global.getContainer() is None:
if quality_group.node_for_global is None or quality_group.node_for_global.getContainer() is None:
return
for node in quality_group.nodes_for_extruders.values():
if node.getContainer() is None:
@ -1081,14 +1113,15 @@ class MachineManager(QObject):
self.activeQualityGroupChanged.emit()
self.activeQualityChangesGroupChanged.emit()
def _fixQualityChangesGroupToNotSupported(self, quality_changes_group):
def _fixQualityChangesGroupToNotSupported(self, quality_changes_group: QualityChangesGroup) -> None:
nodes = [quality_changes_group.node_for_global] + list(quality_changes_group.nodes_for_extruders.values())
containers = [n.getContainer() for n in nodes if n is not None]
for container in containers:
container.setMetaDataEntry("quality_type", "not_supported")
if container:
container.setMetaDataEntry("quality_type", "not_supported")
quality_changes_group.quality_type = "not_supported"
def _setQualityChangesGroup(self, quality_changes_group):
def _setQualityChangesGroup(self, quality_changes_group: QualityChangesGroup) -> None:
if self._global_container_stack is None:
return #Can't change that.
quality_type = quality_changes_group.quality_type
@ -1132,21 +1165,25 @@ class MachineManager(QObject):
self.activeQualityGroupChanged.emit()
self.activeQualityChangesGroupChanged.emit()
def _setVariantNode(self, position, container_node):
if container_node.getContainer() is None:
def _setVariantNode(self, position: str, container_node: ContainerNode) -> None:
if container_node.getContainer() is None or self._global_container_stack is None:
return
self._global_container_stack.extruders[position].variant = container_node.getContainer()
self.activeVariantChanged.emit()
def _setGlobalVariant(self, container_node):
def _setGlobalVariant(self, container_node: ContainerNode) -> None:
if self._global_container_stack is None:
return
self._global_container_stack.variant = container_node.getContainer()
if not self._global_container_stack.variant:
self._global_container_stack.variant = self._application.empty_variant_container
def _setMaterial(self, position, container_node = None):
def _setMaterial(self, position: str, container_node: ContainerNode = None) -> None:
if self._global_container_stack is None:
return
if container_node and container_node.getContainer():
self._global_container_stack.extruders[position].material = container_node.getContainer()
root_material_id = container_node.metadata["base_file"]
root_material_id = container_node.getMetaDataEntry("base_file", None)
else:
self._global_container_stack.extruders[position].material = self._empty_material_container
root_material_id = None
@ -1155,18 +1192,19 @@ class MachineManager(QObject):
self._current_root_material_id[position] = root_material_id
self.rootMaterialChanged.emit()
def activeMaterialsCompatible(self):
def activeMaterialsCompatible(self) -> bool:
# check material - variant compatibility
if Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False)):
for position, extruder in self._global_container_stack.extruders.items():
if extruder.isEnabled and not extruder.material.getMetaDataEntry("compatible"):
return False
if not extruder.material.getMetaDataEntry("compatible"):
return False
if self._global_container_stack is not None:
if Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False)):
for position, extruder in self._global_container_stack.extruders.items():
if extruder.isEnabled and not extruder.material.getMetaDataEntry("compatible"):
return False
if not extruder.material.getMetaDataEntry("compatible"):
return False
return True
## Update current quality type and machine after setting material
def _updateQualityWithMaterial(self, *args):
def _updateQualityWithMaterial(self, *args: Any) -> None:
if self._global_container_stack is None:
return
Logger.log("i", "Updating quality/quality_changes due to material change")
@ -1205,7 +1243,7 @@ class MachineManager(QObject):
current_quality_type, quality_type)
self._setQualityGroup(candidate_quality_groups[quality_type], empty_quality_changes = True)
def updateMaterialWithVariant(self, position: Optional[str]):
def updateMaterialWithVariant(self, position: Optional[str]) -> None:
if self._global_container_stack is None:
return
if position is None:
@ -1213,8 +1251,8 @@ class MachineManager(QObject):
else:
position_list = [position]
for position in position_list:
extruder = self._global_container_stack.extruders[position]
for position_item in position_list:
extruder = self._global_container_stack.extruders[position_item]
current_material_base_name = extruder.material.getMetaDataEntry("base_file")
current_variant_name = None
@ -1232,28 +1270,28 @@ class MachineManager(QObject):
material_diameter)
if not candidate_materials:
self._setMaterial(position, container_node = None)
self._setMaterial(position_item, container_node = None)
continue
if current_material_base_name in candidate_materials:
new_material = candidate_materials[current_material_base_name]
self._setMaterial(position, new_material)
self._setMaterial(position_item, new_material)
continue
# The current material is not available, find the preferred one
material_node = self._material_manager.getDefaultMaterial(self._global_container_stack, position, current_variant_name)
if material_node is not None:
self._setMaterial(position, material_node)
self._setMaterial(position_item, material_node)
## Given a printer definition name, select the right machine instance. In case it doesn't exist, create a new
# instance with the same network key.
@pyqtSlot(str)
def switchPrinterType(self, machine_name: str) -> None:
# Don't switch if the user tries to change to the same type of printer
if self.activeMachineDefinitionName == machine_name:
if self._global_container_stack is None or self.self.activeMachineDefinitionName == machine_name:
return
# Get the definition id corresponding to this machine name
machine_definition_id = ContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId()
machine_definition_id = CuraContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId()
# Try to find a machine with the same network key
new_machine = self.getMachine(machine_definition_id, metadata_filter = {"um_network_key": self.activeMachineNetworkKey})
# If there is no machine, then create a new one and set it to the non-hidden instance
@ -1275,6 +1313,8 @@ class MachineManager(QObject):
@pyqtSlot(QObject)
def applyRemoteConfiguration(self, configuration: ConfigurationModel) -> None:
if self._global_container_stack is None:
return
self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self.switchPrinterType(configuration.printerType)
@ -1309,7 +1349,7 @@ class MachineManager(QObject):
## Find all container stacks that has the pair 'key = value' in its metadata and replaces the value with 'new_value'
def replaceContainersMetadata(self, key: str, value: str, new_value: str) -> None:
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
machines = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine")
for machine in machines:
if machine.getMetaDataEntry(key) == value:
machine.setMetaDataEntry(key, new_value)
@ -1322,18 +1362,18 @@ class MachineManager(QObject):
# Check if the connect_group_name is correct. If not, update all the containers connected to the same printer
if self.activeMachineNetworkGroupName != group_name:
metadata_filter = {"um_network_key": self.activeMachineNetworkKey}
containers = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
containers = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
for container in containers:
container.setMetaDataEntry("connect_group_name", group_name)
## This method checks if there is an instance connected to the given network_key
def existNetworkInstances(self, network_key: str) -> bool:
metadata_filter = {"um_network_key": network_key}
containers = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
containers = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter)
return bool(containers)
@pyqtSlot("QVariant")
def setGlobalVariant(self, container_node):
def setGlobalVariant(self, container_node: ContainerNode) -> None:
self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self._setGlobalVariant(container_node)
@ -1341,7 +1381,9 @@ class MachineManager(QObject):
self._updateQualityWithMaterial()
@pyqtSlot(str, str)
def setMaterialById(self, position, root_material_id):
def setMaterialById(self, position: str, root_material_id: str) -> None:
if self._global_container_stack is None:
return
machine_definition_id = self._global_container_stack.definition.id
position = str(position)
extruder_stack = self._global_container_stack.extruders[position]
@ -1364,12 +1406,14 @@ class MachineManager(QObject):
@pyqtSlot(str, str)
def setVariantByName(self, position: str, variant_name: str) -> None:
if self._global_container_stack is None:
return
machine_definition_id = self._global_container_stack.definition.id
variant_node = self._variant_manager.getVariantNode(machine_definition_id, variant_name)
self.setVariant(position, variant_node)
@pyqtSlot(str, "QVariant")
def setVariant(self, position: str, container_node):
def setVariant(self, position: str, container_node: ContainerNode) -> None:
position = str(position)
self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
@ -1391,7 +1435,7 @@ class MachineManager(QObject):
self.setQualityGroup(quality_group)
@pyqtSlot(QObject)
def setQualityGroup(self, quality_group, no_dialog = False):
def setQualityGroup(self, quality_group: QualityGroup, no_dialog: bool = False) -> None:
self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self._setQualityGroup(quality_group)
@ -1401,11 +1445,11 @@ class MachineManager(QObject):
self._application.discardOrKeepProfileChanges()
@pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
def activeQualityGroup(self):
def activeQualityGroup(self) -> Optional[QualityGroup]:
return self._current_quality_group
@pyqtSlot(QObject)
def setQualityChangesGroup(self, quality_changes_group, no_dialog = False):
def setQualityChangesGroup(self, quality_changes_group: QualityChangesGroup, no_dialog: bool = False) -> None:
self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self._setQualityChangesGroup(quality_changes_group)
@ -1415,18 +1459,20 @@ class MachineManager(QObject):
self._application.discardOrKeepProfileChanges()
@pyqtSlot()
def resetToUseDefaultQuality(self):
def resetToUseDefaultQuality(self) -> None:
if self._global_container_stack is None:
return
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self._setQualityGroup(self._current_quality_group)
for stack in [self._global_container_stack] + list(self._global_container_stack.extruders.values()):
stack.userChanges.clear()
@pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged)
def activeQualityChangesGroup(self):
def activeQualityChangesGroup(self) -> Optional[QualityChangesGroup]:
return self._current_quality_changes_group
@pyqtProperty(str, notify = activeQualityGroupChanged)
def activeQualityOrQualityChangesName(self):
def activeQualityOrQualityChangesName(self) -> str:
name = self._empty_quality_container.getName()
if self._current_quality_changes_group:
name = self._current_quality_changes_group.name

View file

@ -1,3 +1,6 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Any, Optional
from UM.Application import Application
@ -9,7 +12,6 @@ from .CuraContainerStack import CuraContainerStack
class PerObjectContainerStack(CuraContainerStack):
@override(CuraContainerStack)
def getProperty(self, key: str, property_name: str, context: Optional[PropertyEvaluationContext] = None) -> Any:
if context is None:
@ -20,7 +22,7 @@ class PerObjectContainerStack(CuraContainerStack):
# Return the user defined value if present, otherwise, evaluate the value according to the default routine.
if self.getContainer(0).hasProperty(key, property_name):
if self.getContainer(0)._instances[key].state == InstanceState.User:
if self.getContainer(0).getProperty(key, "state") == InstanceState.User:
result = super().getProperty(key, property_name, context)
context.popContainer()
return result
@ -53,13 +55,13 @@ class PerObjectContainerStack(CuraContainerStack):
return result
@override(CuraContainerStack)
def setNextStack(self, stack: CuraContainerStack):
def setNextStack(self, stack: CuraContainerStack) -> None:
super().setNextStack(stack)
# trigger signal to re-evaluate all default settings
for key, instance in self.getContainer(0)._instances.items():
for key in self.getContainer(0).getAllKeys():
# only evaluate default settings
if instance.state != InstanceState.Default:
if self.getContainer(0).getProperty(key, "state") != InstanceState.Default:
continue
self._collectPropertyChanges(key, "value")

View file

@ -1,5 +1,6 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import List
from PyQt5.QtCore import QObject, QTimer, pyqtProperty, pyqtSignal
from UM.FlameProfiler import pyqtSlot
@ -13,6 +14,7 @@ from UM.Logger import Logger
# speed settings. If all the children of print_speed have a single value override, changing the speed won't
# actually do anything, as only the 'leaf' settings are used by the engine.
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.Interfaces import ContainerInterface
from UM.Settings.SettingFunction import SettingFunction
from UM.Settings.SettingInstance import InstanceState
@ -157,7 +159,7 @@ class SettingInheritanceManager(QObject):
stack = self._active_container_stack
if not stack: #No active container stack yet!
return False
containers = []
containers = [] # type: List[ContainerInterface]
## Check if the setting has a user state. If not, it is never overwritten.
has_user_state = stack.getProperty(key, "state") == InstanceState.User

View file

@ -9,7 +9,6 @@ from UM.Signal import Signal, signalemitter
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Logger import Logger
from UM.Util import parseBool
from UM.Application import Application
@ -40,7 +39,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
user_container = InstanceContainer(container_id = self._generateUniqueName())
user_container.addMetaDataEntry("type", "user")
self._stack.userChanges = user_container
self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0)
self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId()
self._is_non_printing_mesh = False
self._is_non_thumbnail_visible_mesh = False
@ -49,25 +48,13 @@ class SettingOverrideDecorator(SceneNodeDecorator):
Application.getInstance().getContainerRegistry().addContainer(self._stack)
Application.getInstance().globalContainerStackChanged.connect(self._onNumberOfExtrudersEnabledChanged)
Application.getInstance().getMachineManager().numberExtrudersEnabledChanged.connect(self._onNumberOfExtrudersEnabledChanged)
Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack)
self.activeExtruderChanged.connect(self._updateNextStack)
self._updateNextStack()
def _generateUniqueName(self):
return "SettingOverrideInstanceContainer-%s" % uuid.uuid1()
def _onNumberOfExtrudersEnabledChanged(self, *args, **kwargs):
if not parseBool(self._extruder_stack.getMetaDataEntry("enabled", "True")):
# switch to the first extruder that's available
global_stack = Application.getInstance().getMachineManager().activeMachine
for _, extruder in sorted(list(global_stack.extruders.items())):
if parseBool(extruder.getMetaDataEntry("enabled", "True")):
self._extruder_stack = extruder
self._updateNextStack()
break
def __deepcopy__(self, memo):
## Create a fresh decorator object
deep_copy = SettingOverrideDecorator()
@ -82,7 +69,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
deep_copy._stack.replaceContainer(0, instance_container)
# Properly set the right extruder on the copy
deep_copy.setActiveExtruder(self._extruder_stack.getId())
deep_copy.setActiveExtruder(self._extruder_stack)
# use value from the stack because there can be a delay in signal triggering and "_is_non_printing_mesh"
# has not been updated yet.
@ -95,7 +82,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
#
# \return An extruder's container stack.
def getActiveExtruder(self):
return self._extruder_stack.getId()
return self._extruder_stack
## Gets the signal that emits if the active extruder changed.
#
@ -137,16 +124,20 @@ class SettingOverrideDecorator(SceneNodeDecorator):
# kept up to date.
def _updateNextStack(self):
if self._extruder_stack:
if self._stack.getNextStack():
old_extruder_stack_id = self._stack.getNextStack().getId()
else:
old_extruder_stack_id = ""
extruder_stack = ContainerRegistry.getInstance().findContainerStacks(id = self._extruder_stack)
if extruder_stack:
if self._stack.getNextStack():
old_extruder_stack_id = self._stack.getNextStack().getId()
else:
old_extruder_stack_id = ""
self._stack.setNextStack(self._extruder_stack)
# Trigger slice/need slicing if the extruder changed.
if self._stack.getNextStack().getId() != old_extruder_stack_id:
Application.getInstance().getBackend().needsSlicing()
Application.getInstance().getBackend().tickle()
self._stack.setNextStack(extruder_stack[0])
# Trigger slice/need slicing if the extruder changed.
if self._stack.getNextStack().getId() != old_extruder_stack_id:
Application.getInstance().getBackend().needsSlicing()
Application.getInstance().getBackend().tickle()
else:
Logger.log("e", "Extruder stack %s below per-object settings does not exist.", self._extruder_stack)
else:
self._stack.setNextStack(Application.getInstance().getGlobalContainerStack())
@ -154,14 +145,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
#
# \param extruder_stack_id The new extruder stack to print with.
def setActiveExtruder(self, extruder_stack_id):
if self._extruder_stack.getId() == extruder_stack_id:
return
global_stack = Application.getInstance().getMachineManager().activeMachine
for extruder in global_stack.extruders.values():
if extruder.getId() == extruder_stack_id:
self._extruder_stack = extruder
break
self._extruder_stack = extruder_stack_id
self._updateNextStack()
ExtruderManager.getInstance().resetSelectedObjectExtruders()
self.activeExtruderChanged.emit()

View file

@ -39,12 +39,12 @@ class SimpleModeSettingsManager(QObject):
global_stack = self._machine_manager.activeMachine
# check user settings in the global stack
user_setting_keys.update(set(global_stack.userChanges.getAllKeys()))
user_setting_keys.update(global_stack.userChanges.getAllKeys())
# check user settings in the extruder stacks
if global_stack.extruders:
for extruder_stack in global_stack.extruders.values():
user_setting_keys.update(set(extruder_stack.userChanges.getAllKeys()))
user_setting_keys.update(extruder_stack.userChanges.getAllKeys())
# remove settings that are visible in recommended (we don't show the reset button for those)
for skip_key in self.__ignored_custom_setting_keys:
@ -70,12 +70,12 @@ class SimpleModeSettingsManager(QObject):
global_stack = self._machine_manager.activeMachine
# check quality changes settings in the global stack
quality_changes_keys.update(set(global_stack.qualityChanges.getAllKeys()))
quality_changes_keys.update(global_stack.qualityChanges.getAllKeys())
# check quality changes settings in the extruder stacks
if global_stack.extruders:
for extruder_stack in global_stack.extruders.values():
quality_changes_keys.update(set(extruder_stack.qualityChanges.getAllKeys()))
quality_changes_keys.update(extruder_stack.qualityChanges.getAllKeys())
# check if the qualityChanges container is not empty (meaning it is a user created profile)
has_quality_changes = len(quality_changes_keys) > 0