mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 14:37:29 -06:00
Merge branch 'cura_containerstack'
Contributes to issue CURA-3497.
This commit is contained in:
commit
945486ade9
39 changed files with 2388 additions and 157 deletions
|
@ -69,6 +69,8 @@ from cura.Settings.ContainerSettingsModel import ContainerSettingsModel
|
|||
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
||||
from cura.Settings.QualitySettingsModel import QualitySettingsModel
|
||||
from cura.Settings.ContainerManager import ContainerManager
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
from cura.Settings.ExtruderStack import ExtruderStack
|
||||
|
||||
from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
|
@ -439,16 +441,18 @@ class CuraApplication(QtApplication):
|
|||
|
||||
mime_type = ContainerRegistry.getMimeTypeForContainer(type(stack))
|
||||
file_name = urllib.parse.quote_plus(stack.getId()) + "." + mime_type.preferredSuffix
|
||||
stack_type = stack.getMetaDataEntry("type", None)
|
||||
|
||||
path = None
|
||||
if not stack_type or stack_type == "machine":
|
||||
if isinstance(stack, GlobalStack):
|
||||
path = Resources.getStoragePath(self.ResourceTypes.MachineStack, file_name)
|
||||
elif stack_type == "extruder_train":
|
||||
elif isinstance(stack, ExtruderStack):
|
||||
path = Resources.getStoragePath(self.ResourceTypes.ExtruderStack, file_name)
|
||||
if path:
|
||||
stack.setPath(path)
|
||||
with SaveFile(path, "wt") as f:
|
||||
f.write(data)
|
||||
else:
|
||||
path = Resources.getStoragePath(Resources.ContainerStacks, file_name)
|
||||
|
||||
stack.setPath(path)
|
||||
with SaveFile(path, "wt") as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
@pyqtSlot(str, result = QUrl)
|
||||
|
|
|
@ -183,7 +183,10 @@ class PrintInformation(QObject):
|
|||
|
||||
def _onActiveMaterialChanged(self):
|
||||
if self._active_material_container:
|
||||
self._active_material_container.metaDataChanged.disconnect(self._onMaterialMetaDataChanged)
|
||||
try:
|
||||
self._active_material_container.metaDataChanged.disconnect(self._onMaterialMetaDataChanged)
|
||||
except TypeError: #pyQtSignal gives a TypeError when disconnecting from something that is already disconnected.
|
||||
pass
|
||||
|
||||
active_material_id = Application.getInstance().getMachineManager().activeMaterialId
|
||||
active_material_containers = ContainerRegistry.getInstance().findInstanceContainers(id=active_material_id)
|
||||
|
|
|
@ -429,7 +429,7 @@ class ContainerManager(QObject):
|
|||
|
||||
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
|
||||
# Find the quality_changes container for this stack and merge the contents of the top container into it.
|
||||
quality_changes = stack.findContainer(type = "quality_changes")
|
||||
quality_changes = stack.qualityChanges
|
||||
if not quality_changes or quality_changes.isReadOnly():
|
||||
Logger.log("e", "Could not update quality of a nonexistant or read only quality profile in stack %s", stack.getId())
|
||||
continue
|
||||
|
@ -482,8 +482,8 @@ class ContainerManager(QObject):
|
|||
# Go through the active stacks and create quality_changes containers from the user containers.
|
||||
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
|
||||
user_container = stack.getTop()
|
||||
quality_container = stack.findContainer(type = "quality")
|
||||
quality_changes_container = stack.findContainer(type = "quality_changes")
|
||||
quality_container = stack.quality
|
||||
quality_changes_container = stack.qualityChanges
|
||||
if not quality_container or not quality_changes_container:
|
||||
Logger.log("w", "No quality or quality changes container found in stack %s, ignoring it", stack.getId())
|
||||
continue
|
||||
|
@ -604,7 +604,7 @@ class ContainerManager(QObject):
|
|||
machine_definition = global_stack.getBottom()
|
||||
|
||||
active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()
|
||||
material_containers = [stack.findContainer(type="material") for stack in active_stacks]
|
||||
material_containers = [stack.material for stack in active_stacks]
|
||||
|
||||
result = self._duplicateQualityOrQualityChangesForMachineType(quality_name, base_name,
|
||||
QualityManager.getInstance().getParentMachineDefinition(machine_definition),
|
||||
|
|
|
@ -6,6 +6,7 @@ import os.path
|
|||
import re
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from UM.Decorators import override
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
|
@ -16,8 +17,8 @@ from UM.Platform import Platform
|
|||
from UM.PluginRegistry import PluginRegistry #For getting the possible profile writers to write with.
|
||||
from UM.Util import parseBool
|
||||
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from cura.Settings.ContainerManager import ContainerManager
|
||||
from . import ExtruderStack
|
||||
from . import GlobalStack
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
@ -26,6 +27,20 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
## Overridden from ContainerRegistry
|
||||
#
|
||||
# Adds a container to the registry.
|
||||
#
|
||||
# This will also try to convert a ContainerStack to either Extruder or
|
||||
# Global stack based on metadata information.
|
||||
@override(ContainerRegistry)
|
||||
def addContainer(self, container):
|
||||
# Note: Intentional check with type() because we want to ignore subclasses
|
||||
if type(container) == ContainerStack:
|
||||
container = self._convertContainerStack(container)
|
||||
|
||||
super().addContainer(container)
|
||||
|
||||
## Create a name that is not empty and unique
|
||||
# \param container_type \type{string} Type of the container (machine, quality, ...)
|
||||
# \param current_name \type{} Current name of the container, which may be an acceptable option
|
||||
|
@ -284,3 +299,27 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
if global_container_stack:
|
||||
return parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", False))
|
||||
return False
|
||||
|
||||
## Convert an "old-style" pure ContainerStack to either an Extruder or Global stack.
|
||||
def _convertContainerStack(self, container):
|
||||
assert type(container) == ContainerStack
|
||||
|
||||
container_type = container.getMetaDataEntry("type")
|
||||
if container_type not in ("extruder_train", "machine"):
|
||||
# It is not an extruder or machine, so do nothing with the stack
|
||||
return container
|
||||
|
||||
new_stack = None
|
||||
if container_type == "extruder_train":
|
||||
new_stack = ExtruderStack.ExtruderStack(container.getId())
|
||||
else:
|
||||
new_stack = GlobalStack.GlobalStack(container.getId())
|
||||
|
||||
container_contents = container.serialize()
|
||||
new_stack.deserialize(container_contents)
|
||||
|
||||
# Delete the old configuration file so we do not get double stacks
|
||||
if os.path.isfile(container.getPath()):
|
||||
os.remove(container.getPath())
|
||||
|
||||
return new_stack
|
||||
|
|
607
cura/Settings/CuraContainerStack.py
Executable file
607
cura/Settings/CuraContainerStack.py
Executable file
|
@ -0,0 +1,607 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import os.path
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSlot, pyqtSignal
|
||||
|
||||
from UM.Decorators import override
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.ContainerStack import ContainerStack, InvalidContainerStackError
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.Interfaces import ContainerInterface
|
||||
|
||||
from . import Exceptions
|
||||
|
||||
|
||||
## Base class for Cura related stacks that want to enforce certain containers are available.
|
||||
#
|
||||
# This class makes sure that the stack has the following containers set: user changes, quality
|
||||
# changes, quality, material, variant, definition changes and finally definition. Initially,
|
||||
# these will be equal to the empty instance container.
|
||||
#
|
||||
# The container types are determined based on the following criteria:
|
||||
# - user: An InstanceContainer with the metadata entry "type" set to "user".
|
||||
# - quality changes: An InstanceContainer with the metadata entry "type" set to "quality_changes".
|
||||
# - quality: An InstanceContainer with the metadata entry "type" set to "quality".
|
||||
# - material: An InstanceContainer with the metadata entry "type" set to "material".
|
||||
# - variant: An InstanceContainer with the metadata entry "type" set to "variant".
|
||||
# - definition changes: An InstanceContainer with the metadata entry "type" set to "definition_changes".
|
||||
# - definition: A DefinitionContainer.
|
||||
#
|
||||
# Internally, this class ensures the mentioned containers are always there and kept in a specific order.
|
||||
# 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, *args, **kwargs):
|
||||
super().__init__(container_id, *args, **kwargs)
|
||||
|
||||
self._empty_instance_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
|
||||
self._containers = [self._empty_instance_container for i in range(len(_ContainerIndexes.IndexTypeMap))]
|
||||
|
||||
self.containersChanged.connect(self._onContainersChanged)
|
||||
|
||||
# This is emitted whenever the containersChanged signal from the ContainerStack base class is emitted.
|
||||
pyqtContainersChanged = pyqtSignal()
|
||||
|
||||
## Set the user changes container.
|
||||
#
|
||||
# \param new_user_changes The new user changes container. It is expected to have a "type" metadata entry with the value "user".
|
||||
def setUserChanges(self, new_user_changes: InstanceContainer) -> None:
|
||||
self.replaceContainer(_ContainerIndexes.UserChanges, new_user_changes)
|
||||
|
||||
## Get the user changes container.
|
||||
#
|
||||
# \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]
|
||||
|
||||
## Set the quality changes container.
|
||||
#
|
||||
# \param new_quality_changes The new quality changes container. It is expected to have a "type" metadata entry with the value "quality_changes".
|
||||
def setQualityChanges(self, new_quality_changes: InstanceContainer) -> None:
|
||||
self.replaceContainer(_ContainerIndexes.QualityChanges, new_quality_changes)
|
||||
|
||||
## Set the quality changes container by an ID.
|
||||
#
|
||||
# This will search for the specified container and set it. If no container was found, an error will be raised.
|
||||
#
|
||||
# \param new_quality_changes_id The ID of the new quality changes container.
|
||||
#
|
||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||
def setQualityChangesById(self, new_quality_changes_id: str) -> None:
|
||||
quality_changes = ContainerRegistry.getInstance().findInstanceContainers(id = new_quality_changes_id)
|
||||
if quality_changes:
|
||||
self.setQualityChanges(quality_changes[0])
|
||||
else:
|
||||
raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_quality_changes_id))
|
||||
|
||||
## Get the quality changes container.
|
||||
#
|
||||
# \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]
|
||||
|
||||
## 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) -> None:
|
||||
self.replaceContainer(_ContainerIndexes.Quality, new_quality)
|
||||
|
||||
## Set the quality container by an ID.
|
||||
#
|
||||
# This will search for the specified container and set it. If no container was found, an error will be raised.
|
||||
# There is a special value for ID, which is "default". The "default" value indicates the quality should be set
|
||||
# to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultQuality
|
||||
# for details.
|
||||
#
|
||||
# \param new_quality_id The ID of the new quality container.
|
||||
#
|
||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||
def setQualityById(self, new_quality_id: str) -> None:
|
||||
quality = self._empty_instance_container
|
||||
if new_quality_id == "default":
|
||||
new_quality = self.findDefaultQuality()
|
||||
if new_quality:
|
||||
quality = new_quality
|
||||
else:
|
||||
qualities = ContainerRegistry.getInstance().findInstanceContainers(id = new_quality_id)
|
||||
if qualities:
|
||||
quality = qualities[0]
|
||||
else:
|
||||
raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_quality_id))
|
||||
|
||||
self.setQuality(quality)
|
||||
|
||||
## Get the quality container.
|
||||
#
|
||||
# \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]
|
||||
|
||||
## Set the material container.
|
||||
#
|
||||
# \param new_quality_changes The new material container. It is expected to have a "type" metadata entry with the value "quality_changes".
|
||||
def setMaterial(self, new_material: InstanceContainer) -> None:
|
||||
self.replaceContainer(_ContainerIndexes.Material, new_material)
|
||||
|
||||
## Set the material container by an ID.
|
||||
#
|
||||
# This will search for the specified container and set it. If no container was found, an error will be raised.
|
||||
# There is a special value for ID, which is "default". The "default" value indicates the quality should be set
|
||||
# to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultMaterial
|
||||
# for details.
|
||||
#
|
||||
# \param new_quality_changes_id The ID of the new material container.
|
||||
#
|
||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||
def setMaterialById(self, new_material_id: str) -> None:
|
||||
material = self._empty_instance_container
|
||||
if new_material_id == "default":
|
||||
new_material = self.findDefaultMaterial()
|
||||
if new_material:
|
||||
material = new_material
|
||||
else:
|
||||
materials = ContainerRegistry.getInstance().findInstanceContainers(id = new_material_id)
|
||||
if materials:
|
||||
material = materials[0]
|
||||
else:
|
||||
raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_material_id))
|
||||
|
||||
self.setMaterial(material)
|
||||
|
||||
## Get the material container.
|
||||
#
|
||||
# \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]
|
||||
|
||||
## Set the variant container.
|
||||
#
|
||||
# \param new_quality_changes The new variant container. It is expected to have a "type" metadata entry with the value "quality_changes".
|
||||
def setVariant(self, new_variant: InstanceContainer) -> None:
|
||||
self.replaceContainer(_ContainerIndexes.Variant, new_variant)
|
||||
|
||||
## Set the variant container by an ID.
|
||||
#
|
||||
# This will search for the specified container and set it. If no container was found, an error will be raised.
|
||||
# There is a special value for ID, which is "default". The "default" value indicates the quality should be set
|
||||
# to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultVariant
|
||||
# for details.
|
||||
#
|
||||
# \param new_quality_changes_id The ID of the new variant container.
|
||||
#
|
||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||
def setVariantById(self, new_variant_id: str) -> None:
|
||||
variant = self._empty_instance_container
|
||||
if new_variant_id == "default":
|
||||
new_variant = self.findDefaultVariant()
|
||||
if new_variant:
|
||||
variant = new_variant
|
||||
else:
|
||||
variants = ContainerRegistry.getInstance().findInstanceContainers(id = new_variant_id)
|
||||
if variants:
|
||||
variant = variants[0]
|
||||
else:
|
||||
raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_variant_id))
|
||||
|
||||
self.setVariant(variant)
|
||||
|
||||
## Get the variant container.
|
||||
#
|
||||
# \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]
|
||||
|
||||
## Set the definition changes container.
|
||||
#
|
||||
# \param new_quality_changes The new definition changes container. It is expected to have a "type" metadata entry with the value "quality_changes".
|
||||
def setDefinitionChanges(self, new_definition_changes: InstanceContainer) -> None:
|
||||
self.replaceContainer(_ContainerIndexes.DefinitionChanges, new_definition_changes)
|
||||
|
||||
## Set the definition changes container by an ID.
|
||||
#
|
||||
# \param new_quality_changes_id The ID of the new definition changes container.
|
||||
#
|
||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||
def setDefinitionChangesById(self, new_definition_changes_id: str) -> None:
|
||||
new_definition_changes = ContainerRegistry.getInstance().findInstanceContainers(id = new_definition_changes_id)
|
||||
if new_definition_changes:
|
||||
self.setDefinitionChanges(new_definition_changes[0])
|
||||
else:
|
||||
raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_definition_changes_id))
|
||||
|
||||
## Get the definition changes container.
|
||||
#
|
||||
# \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]
|
||||
|
||||
## Set the definition container.
|
||||
#
|
||||
# \param new_quality_changes The new definition container. It is expected to have a "type" metadata entry with the value "quality_changes".
|
||||
def setDefinition(self, new_definition: DefinitionContainer) -> None:
|
||||
self.replaceContainer(_ContainerIndexes.Definition, new_definition)
|
||||
|
||||
## Set the definition container by an ID.
|
||||
#
|
||||
# \param new_quality_changes_id The ID of the new definition container.
|
||||
#
|
||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||
def setDefinitionById(self, new_definition_id: str) -> None:
|
||||
new_definition = ContainerRegistry.getInstance().findDefinitionContainers(id = new_definition_id)
|
||||
if new_definition:
|
||||
self.setDefinition(new_definition[0])
|
||||
else:
|
||||
raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_definition_id))
|
||||
|
||||
## Get the definition container.
|
||||
#
|
||||
# \return The definition container. Should always be a valid container, but can be equal to the empty InstanceContainer.
|
||||
@pyqtProperty(DefinitionContainer, fset = setDefinition, notify = pyqtContainersChanged)
|
||||
def definition(self) -> DefinitionContainer:
|
||||
return self._containers[_ContainerIndexes.Definition]
|
||||
|
||||
## Check whether the specified setting has a 'user' value.
|
||||
#
|
||||
# A user value here is defined as the setting having a value in either
|
||||
# the UserChanges or QualityChanges container.
|
||||
#
|
||||
# \return True if the setting has a user value, False if not.
|
||||
@pyqtSlot(str, result = bool)
|
||||
def hasUserValue(self, key: str) -> bool:
|
||||
if self._containers[_ContainerIndexes.UserChanges].hasProperty(key, "value"):
|
||||
return True
|
||||
|
||||
if self._containers[_ContainerIndexes.QualityChanges].hasProperty(key, "value"):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
## Set a property of a setting.
|
||||
#
|
||||
# This will set a property of a specified setting. Since the container stack does not contain
|
||||
# any settings itself, it is required to specify a container to set the property on. The target
|
||||
# container is matched by container type.
|
||||
#
|
||||
# \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))
|
||||
|
||||
## Overridden from ContainerStack
|
||||
#
|
||||
# Since we have a fixed order of containers in the stack and this method would modify the container
|
||||
# ordering, we disallow this operation.
|
||||
@override(ContainerStack)
|
||||
def addContainer(self, container: ContainerInterface) -> None:
|
||||
raise Exceptions.InvalidOperationError("Cannot add a container to Global stack")
|
||||
|
||||
## Overridden from ContainerStack
|
||||
#
|
||||
# Since we have a fixed order of containers in the stack and this method would modify the container
|
||||
# ordering, we disallow this operation.
|
||||
@override(ContainerStack)
|
||||
def insertContainer(self, index: int, container: ContainerInterface) -> None:
|
||||
raise Exceptions.InvalidOperationError("Cannot insert a container into Global stack")
|
||||
|
||||
## Overridden from ContainerStack
|
||||
#
|
||||
# Since we have a fixed order of containers in the stack and this method would modify the container
|
||||
# ordering, we disallow this operation.
|
||||
@override(ContainerStack)
|
||||
def removeContainer(self, index: int = 0) -> None:
|
||||
raise Exceptions.InvalidOperationError("Cannot remove a container from Global stack")
|
||||
|
||||
## Overridden from ContainerStack
|
||||
#
|
||||
# Replaces the container at the specified index with another container.
|
||||
# This version performs checks to make sure the new container has the expected metadata and type.
|
||||
#
|
||||
# \throws Exception.InvalidContainerError Raised when trying to replace a container with a container that has an incorrect type.
|
||||
@override(ContainerStack)
|
||||
def replaceContainer(self, index: int, container: ContainerInterface, postpone_emit: bool = False) -> None:
|
||||
expected_type = _ContainerIndexes.IndexTypeMap[index]
|
||||
if expected_type == "definition":
|
||||
if not isinstance(container, DefinitionContainer):
|
||||
raise Exceptions.InvalidContainerError("Cannot replace container at index {index} with a container that is not a DefinitionContainer".format(index = index))
|
||||
elif container != self._empty_instance_container and container.getMetaDataEntry("type") != expected_type:
|
||||
raise Exceptions.InvalidContainerError("Cannot replace container at index {index} with a container that is not of {type} type, but {actual_type} type.".format(index = index, type = expected_type, actual_type = container.getMetaDataEntry("type")))
|
||||
|
||||
super().replaceContainer(index, container, postpone_emit)
|
||||
|
||||
## Overridden from ContainerStack
|
||||
#
|
||||
# This deserialize will make sure the internal list of containers matches with what we expect.
|
||||
# It will first check to see if the container at a certain index already matches with what we
|
||||
# expect. If it does not, it will search for a matching container with the correct type. Should
|
||||
# no container with the correct type be found, it will use the empty container.
|
||||
#
|
||||
# \throws InvalidContainerStackError Raised when no definition can be found for the stack.
|
||||
@override(ContainerStack)
|
||||
def deserialize(self, contents: str) -> None:
|
||||
super().deserialize(contents)
|
||||
|
||||
new_containers = self._containers.copy()
|
||||
while len(new_containers) < len(_ContainerIndexes.IndexTypeMap):
|
||||
new_containers.append(self._empty_instance_container)
|
||||
|
||||
# Validate and ensure the list of containers matches with what we expect
|
||||
for index, type_name in _ContainerIndexes.IndexTypeMap.items():
|
||||
try:
|
||||
container = new_containers[index]
|
||||
except IndexError:
|
||||
container = None
|
||||
|
||||
if type_name == "definition":
|
||||
if not container or not isinstance(container, DefinitionContainer):
|
||||
definition = self.findContainer(container_type = DefinitionContainer)
|
||||
if not definition:
|
||||
raise InvalidContainerStackError("Stack {id} does not have a definition!".format(id = self._id))
|
||||
|
||||
new_containers[index] = definition
|
||||
continue
|
||||
|
||||
if not container or container.getMetaDataEntry("type") != type_name:
|
||||
actual_container = self.findContainer(type = type_name)
|
||||
if actual_container:
|
||||
new_containers[index] = actual_container
|
||||
else:
|
||||
new_containers[index] = self._empty_instance_container
|
||||
|
||||
self._containers = new_containers
|
||||
|
||||
## Find the variant that should be used as "default" variant.
|
||||
#
|
||||
# This will search for variants that match the current definition and pick the preferred one,
|
||||
# if specified by the machine definition.
|
||||
#
|
||||
# The following criteria are used to find the default variant:
|
||||
# - If the machine definition does not have a metadata entry "has_variants" set to True, return None
|
||||
# - The definition of the variant should be the same as the machine definition for this stack.
|
||||
# - The container should have a metadata entry "type" with value "variant".
|
||||
# - If the machine definition has a metadata entry "preferred_variant", filter the variant IDs based on that.
|
||||
#
|
||||
# \return The container that should be used as default, or None if nothing was found or the machine does not use variants.
|
||||
#
|
||||
# \note This method assumes the stack has a valid machine definition.
|
||||
def findDefaultVariant(self) -> Optional[ContainerInterface]:
|
||||
definition = self._getMachineDefinition()
|
||||
if not definition.getMetaDataEntry("has_variants"):
|
||||
# If the machine does not use variants, we should never set a variant.
|
||||
return None
|
||||
|
||||
# First add any variant. Later, overwrite with preference if the preference is valid.
|
||||
variant = None
|
||||
definition_id = self._findInstanceContainerDefinitionId(definition)
|
||||
variants = ContainerRegistry.getInstance().findInstanceContainers(definition = definition_id, type = "variant")
|
||||
if variants:
|
||||
variant = variants[0]
|
||||
|
||||
preferred_variant_id = definition.getMetaDataEntry("preferred_variant")
|
||||
if preferred_variant_id:
|
||||
preferred_variants = ContainerRegistry.getInstance().findInstanceContainers(id = preferred_variant_id, definition = definition_id, type = "variant")
|
||||
if preferred_variants:
|
||||
variant = preferred_variants[0]
|
||||
else:
|
||||
Logger.log("w", "The preferred variant \"{variant}\" of stack {stack} does not exist or is not a variant.", variant = preferred_variant_id, stack = self.id)
|
||||
# And leave it at the default variant.
|
||||
|
||||
if variant:
|
||||
return variant
|
||||
|
||||
Logger.log("w", "Could not find a valid default variant for stack {stack}", stack = self.id)
|
||||
return None
|
||||
|
||||
## Find the material that should be used as "default" material.
|
||||
#
|
||||
# This will search for materials that match the current definition and pick the preferred one,
|
||||
# if specified by the machine definition.
|
||||
#
|
||||
# The following criteria are used to find the default material:
|
||||
# - If the machine definition does not have a metadata entry "has_materials" set to True, return None
|
||||
# - If the machine definition has a metadata entry "has_machine_materials", the definition of the material should
|
||||
# be the same as the machine definition for this stack. Otherwise, the definition should be "fdmprinter".
|
||||
# - The container should have a metadata entry "type" with value "material".
|
||||
# - If the machine definition has a metadata entry "has_variants" and set to True, the "variant" metadata entry of
|
||||
# the material should be the same as the ID of the variant in the stack. Only applies if "has_machine_materials" is also True.
|
||||
# - If the stack currently has a material set, try to find a material that matches the current material by name.
|
||||
# - Otherwise, if the machine definition has a metadata entry "preferred_material", try to find a material that matches the specified ID.
|
||||
#
|
||||
# \return The container that should be used as default, or None if nothing was found or the machine does not use materials.
|
||||
def findDefaultMaterial(self) -> Optional[ContainerInterface]:
|
||||
definition = self._getMachineDefinition()
|
||||
if not definition.getMetaDataEntry("has_materials"):
|
||||
# Machine does not use materials, never try to set it.
|
||||
return None
|
||||
|
||||
search_criteria = {"type": "material"}
|
||||
if definition.getMetaDataEntry("has_machine_materials"):
|
||||
search_criteria["definition"] = self._findInstanceContainerDefinitionId(definition)
|
||||
|
||||
if definition.getMetaDataEntry("has_variants"):
|
||||
search_criteria["variant"] = self.variant.id
|
||||
else:
|
||||
search_criteria["definition"] = "fdmprinter"
|
||||
|
||||
if self.material != self._empty_instance_container:
|
||||
search_criteria["name"] = self.material.name
|
||||
else:
|
||||
preferred_material = definition.getMetaDataEntry("preferred_material")
|
||||
if preferred_material:
|
||||
search_criteria["id"] = preferred_material
|
||||
|
||||
materials = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
|
||||
if not materials:
|
||||
Logger.log("w", "The preferred material \"{material}\" could not be found for stack {stack}", material = preferred_material, stack = self.id)
|
||||
# We failed to find any materials matching the specified criteria, drop some specific criteria and try to find
|
||||
# a material that sort-of matches what we want.
|
||||
search_criteria.pop("variant", None)
|
||||
search_criteria.pop("id", None)
|
||||
search_criteria.pop("name", None)
|
||||
materials = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
|
||||
|
||||
if materials:
|
||||
return materials[0]
|
||||
|
||||
Logger.log("w", "Could not find a valid material for stack {stack}", stack = self.id)
|
||||
return None
|
||||
|
||||
## Find the quality that should be used as "default" quality.
|
||||
#
|
||||
# This will search for qualities that match the current definition and pick the preferred one,
|
||||
# if specified by the machine definition.
|
||||
#
|
||||
# \return The container that should be used as default, or None if nothing was found.
|
||||
def findDefaultQuality(self) -> Optional[ContainerInterface]:
|
||||
definition = self._getMachineDefinition()
|
||||
registry = ContainerRegistry.getInstance()
|
||||
material_container = self.material if self.material != self._empty_instance_container else None
|
||||
|
||||
search_criteria = {"type": "quality"}
|
||||
|
||||
if definition.getMetaDataEntry("has_machine_quality"):
|
||||
search_criteria["definition"] = self._findInstanceContainerDefinitionId(definition)
|
||||
|
||||
if definition.getMetaDataEntry("has_materials") and material_container:
|
||||
search_criteria["material"] = material_container.id
|
||||
else:
|
||||
search_criteria["definition"] = "fdmprinter"
|
||||
|
||||
if self.quality != self._empty_instance_container:
|
||||
search_criteria["name"] = self.quality.name
|
||||
else:
|
||||
preferred_quality = definition.getMetaDataEntry("preferred_quality")
|
||||
if preferred_quality:
|
||||
search_criteria["id"] = preferred_quality
|
||||
|
||||
containers = registry.findInstanceContainers(**search_criteria)
|
||||
if containers:
|
||||
return containers[0]
|
||||
|
||||
if "material" in search_criteria:
|
||||
# First check if we can solve our material not found problem by checking if we can find quality containers
|
||||
# that are assigned to the parents of this material profile.
|
||||
try:
|
||||
inherited_files = material_container.getInheritedFiles()
|
||||
except AttributeError: # Material_container does not support inheritance.
|
||||
inherited_files = []
|
||||
|
||||
if inherited_files:
|
||||
for inherited_file in inherited_files:
|
||||
# Extract the ID from the path we used to load the file.
|
||||
search_criteria["material"] = os.path.basename(inherited_file).split(".")[0]
|
||||
containers = registry.findInstanceContainers(**search_criteria)
|
||||
if containers:
|
||||
return containers[0]
|
||||
|
||||
# We still weren't able to find a quality for this specific material.
|
||||
# Try to find qualities for a generic version of the material.
|
||||
material_search_criteria = {"type": "material", "material": material_container.getMetaDataEntry("material"), "color_name": "Generic"}
|
||||
if definition.getMetaDataEntry("has_machine_quality"):
|
||||
if self.material != self._empty_instance_container:
|
||||
material_search_criteria["definition"] = material_container.getDefinition().id
|
||||
|
||||
if definition.getMetaDataEntry("has_variants"):
|
||||
material_search_criteria["variant"] = material_container.getMetaDataEntry("variant")
|
||||
else:
|
||||
material_search_criteria["definition"] = self._findInstanceContainerDefinitionId(definition)
|
||||
|
||||
if definition.getMetaDataEntry("has_variants") and self.variant != self._empty_instance_container:
|
||||
material_search_criteria["variant"] = self.variant.id
|
||||
else:
|
||||
material_search_criteria["definition"] = "fdmprinter"
|
||||
material_containers = registry.findInstanceContainers(**material_search_criteria)
|
||||
# Try all materials to see if there is a quality profile available.
|
||||
for material_container in material_containers:
|
||||
search_criteria["material"] = material_container.getId()
|
||||
|
||||
containers = registry.findInstanceContainers(**search_criteria)
|
||||
if containers:
|
||||
return containers[0]
|
||||
|
||||
if "name" in search_criteria or "id" in search_criteria:
|
||||
# If a quality by this name can not be found, try a wider set of search criteria
|
||||
search_criteria.pop("name", None)
|
||||
search_criteria.pop("id", None)
|
||||
|
||||
containers = registry.findInstanceContainers(**search_criteria)
|
||||
if containers:
|
||||
return containers[0]
|
||||
|
||||
return None
|
||||
|
||||
## protected:
|
||||
|
||||
# Helper to make sure we emit a PyQt signal on container changes.
|
||||
def _onContainersChanged(self, container: Any) -> None:
|
||||
self.pyqtContainersChanged.emit()
|
||||
|
||||
# Helper that can be overridden to get the "machine" definition, that is, the definition that defines the machine
|
||||
# and its properties rather than, for example, the extruder. Defaults to simply returning the definition property.
|
||||
def _getMachineDefinition(self) -> DefinitionContainer:
|
||||
return self.definition
|
||||
|
||||
## Find the ID that should be used when searching for instance containers for a specified definition.
|
||||
#
|
||||
# This handles the situation where the definition specifies we should use a different definition when
|
||||
# searching for instance containers.
|
||||
#
|
||||
# \param machine_definition The definition to find the "quality definition" for.
|
||||
#
|
||||
# \return The ID of the definition container to use when searching for instance containers.
|
||||
@classmethod
|
||||
def _findInstanceContainerDefinitionId(cls, machine_definition: DefinitionContainer) -> str:
|
||||
quality_definition = machine_definition.getMetaDataEntry("quality_definition")
|
||||
if not quality_definition:
|
||||
return machine_definition.id
|
||||
|
||||
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
|
||||
|
||||
return cls._findInstanceContainerDefinitionId(definitions[0])
|
||||
|
||||
## private:
|
||||
|
||||
# Private helper class to keep track of container positions and their types.
|
||||
class _ContainerIndexes:
|
||||
UserChanges = 0
|
||||
QualityChanges = 1
|
||||
Quality = 2
|
||||
Material = 3
|
||||
Variant = 4
|
||||
DefinitionChanges = 5
|
||||
Definition = 6
|
||||
|
||||
# Simple hash map to map from index to "type" metadata entry
|
||||
IndexTypeMap = {
|
||||
UserChanges: "user",
|
||||
QualityChanges: "quality_changes",
|
||||
Quality: "quality",
|
||||
Material: "material",
|
||||
Variant: "variant",
|
||||
DefinitionChanges: "definition_changes",
|
||||
Definition: "definition",
|
||||
}
|
||||
|
||||
# Reverse lookup: type -> index
|
||||
TypeIndexMap = dict([(v, k) for k, v in IndexTypeMap.items()])
|
152
cura/Settings/CuraStackBuilder.py
Normal file
152
cura/Settings/CuraStackBuilder.py
Normal file
|
@ -0,0 +1,152 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Logger import Logger
|
||||
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
from .GlobalStack import GlobalStack
|
||||
from .ExtruderStack import ExtruderStack
|
||||
from .CuraContainerStack import CuraContainerStack
|
||||
from typing import Optional
|
||||
|
||||
|
||||
## Contains helper functions to create new machines.
|
||||
class CuraStackBuilder:
|
||||
## Create a new instance of a machine.
|
||||
#
|
||||
# \param name The name of the new machine.
|
||||
# \param definition_id The ID of the machine definition to use.
|
||||
#
|
||||
# \return The new global stack or None if an error occurred.
|
||||
@classmethod
|
||||
def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]:
|
||||
registry = ContainerRegistry.getInstance()
|
||||
definitions = registry.findDefinitionContainers(id = definition_id)
|
||||
if not definitions:
|
||||
Logger.log("w", "Definition {definition} was not found!", definition = definition_id)
|
||||
return None
|
||||
|
||||
machine_definition = definitions[0]
|
||||
name = registry.createUniqueName("machine", "", name, machine_definition.name)
|
||||
|
||||
new_global_stack = cls.createGlobalStack(
|
||||
new_stack_id = name,
|
||||
definition = machine_definition,
|
||||
quality = "default",
|
||||
material = "default",
|
||||
variant = "default",
|
||||
)
|
||||
|
||||
for extruder_definition in registry.findDefinitionContainers(machine = machine_definition.id):
|
||||
position = extruder_definition.getMetaDataEntry("position", None)
|
||||
if not position:
|
||||
Logger.log("w", "Extruder definition %s specifies no position metadata entry.", extruder_definition.id)
|
||||
|
||||
new_extruder_id = registry.uniqueName(extruder_definition.id)
|
||||
new_extruder = cls.createExtruderStack(
|
||||
new_extruder_id,
|
||||
definition = extruder_definition,
|
||||
machine_definition = machine_definition,
|
||||
quality = "default",
|
||||
material = "default",
|
||||
variant = "default",
|
||||
next_stack = new_global_stack
|
||||
)
|
||||
|
||||
return new_global_stack
|
||||
|
||||
## Create a new Extruder stack
|
||||
#
|
||||
# \param new_stack_id The ID of the new stack.
|
||||
# \param definition The definition to base the new stack on.
|
||||
# \param machine_definition The machine definition to use for the user container.
|
||||
# \param kwargs You can add keyword arguments to specify IDs of containers to use for a specific type, for example "variant": "0.4mm"
|
||||
#
|
||||
# \return A new Global stack instance with the specified parameters.
|
||||
@classmethod
|
||||
def createExtruderStack(cls, new_stack_id: str, definition: DefinitionContainer, machine_definition: DefinitionContainer, **kwargs) -> ExtruderStack:
|
||||
stack = ExtruderStack(new_stack_id)
|
||||
stack.setName(definition.getName())
|
||||
stack.setDefinition(definition)
|
||||
stack.addMetaDataEntry("position", definition.getMetaDataEntry("position"))
|
||||
|
||||
user_container = InstanceContainer(new_stack_id + "_user")
|
||||
user_container.addMetaDataEntry("type", "user")
|
||||
user_container.addMetaDataEntry("extruder", new_stack_id)
|
||||
user_container.setDefinition(machine_definition)
|
||||
|
||||
stack.setUserChanges(user_container)
|
||||
|
||||
if "next_stack" in kwargs:
|
||||
stack.setNextStack(kwargs["next_stack"])
|
||||
|
||||
# Important! The order here matters, because that allows the stack to
|
||||
# assume the material and variant have already been set.
|
||||
if "definition_changes" in kwargs:
|
||||
stack.setDefinitionChangesById(kwargs["definition_changes"])
|
||||
|
||||
if "variant" in kwargs:
|
||||
stack.setVariantById(kwargs["variant"])
|
||||
|
||||
if "material" in kwargs:
|
||||
stack.setMaterialById(kwargs["material"])
|
||||
|
||||
if "quality" in kwargs:
|
||||
stack.setQualityById(kwargs["quality"])
|
||||
|
||||
if "quality_changes" in kwargs:
|
||||
stack.setQualityChangesById(kwargs["quality_changes"])
|
||||
|
||||
# Only add the created containers to the registry after we have set all the other
|
||||
# properties. This makes the create operation more transactional, since any problems
|
||||
# setting properties will not result in incomplete containers being added.
|
||||
registry = ContainerRegistry.getInstance()
|
||||
registry.addContainer(stack)
|
||||
registry.addContainer(user_container)
|
||||
|
||||
return stack
|
||||
|
||||
## Create a new Global stack
|
||||
#
|
||||
# \param new_stack_id The ID of the new stack.
|
||||
# \param definition The definition to base the new stack on.
|
||||
# \param kwargs You can add keyword arguments to specify IDs of containers to use for a specific type, for example "variant": "0.4mm"
|
||||
#
|
||||
# \return A new Global stack instance with the specified parameters.
|
||||
@classmethod
|
||||
def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainer, **kwargs) -> GlobalStack:
|
||||
stack = GlobalStack(new_stack_id)
|
||||
stack.setDefinition(definition)
|
||||
|
||||
user_container = InstanceContainer(new_stack_id + "_user")
|
||||
user_container.addMetaDataEntry("type", "user")
|
||||
user_container.addMetaDataEntry("machine", new_stack_id)
|
||||
user_container.setDefinition(definition)
|
||||
|
||||
stack.setUserChanges(user_container)
|
||||
|
||||
# Important! The order here matters, because that allows the stack to
|
||||
# assume the material and variant have already been set.
|
||||
if "definition_changes" in kwargs:
|
||||
stack.setDefinitionChangesById(kwargs["definition_changes"])
|
||||
|
||||
if "variant" in kwargs:
|
||||
stack.setVariantById(kwargs["variant"])
|
||||
|
||||
if "material" in kwargs:
|
||||
stack.setMaterialById(kwargs["material"])
|
||||
|
||||
if "quality" in kwargs:
|
||||
stack.setQualityById(kwargs["quality"])
|
||||
|
||||
if "quality_changes" in kwargs:
|
||||
stack.setQualityChangesById(kwargs["quality_changes"])
|
||||
|
||||
registry = ContainerRegistry.getInstance()
|
||||
registry.addContainer(stack)
|
||||
registry.addContainer(user_container)
|
||||
|
||||
return stack
|
22
cura/Settings/Exceptions.py
Normal file
22
cura/Settings/Exceptions.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
|
||||
## Raised when trying to perform an operation like add on a stack that does not allow that.
|
||||
class InvalidOperationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
## Raised when trying to replace a container with a container that does not have the expected type.
|
||||
class InvalidContainerError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
## Raised when trying to add an extruder to a Global stack that already has the maximum number of extruders.
|
||||
class TooManyExtrudersError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
## Raised when an extruder has no next stack set.
|
||||
class NoGlobalStackError(Exception):
|
||||
pass
|
|
@ -6,6 +6,7 @@ from UM.FlameProfiler import pyqtSlot
|
|||
|
||||
from UM.Application import Application #To get the global container stack to find the current machine.
|
||||
from UM.Logger import Logger
|
||||
from UM.Decorators import deprecated
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Selection import Selection
|
||||
|
@ -194,6 +195,7 @@ class ExtruderManager(QObject):
|
|||
#
|
||||
# \param machine_definition The machine definition to add the extruders for.
|
||||
# \param machine_id The machine_id to add the extruders for.
|
||||
@deprecated("Use CuraStackBuilder", "2.6")
|
||||
def addMachineExtruders(self, machine_definition: DefinitionContainer, machine_id: str) -> None:
|
||||
changed = False
|
||||
machine_definition_id = machine_definition.getId()
|
||||
|
@ -246,6 +248,7 @@ class ExtruderManager(QObject):
|
|||
# \param machine_definition The machine that the extruder train belongs to.
|
||||
# \param position The position of this extruder train in the extruder slots of the machine.
|
||||
# \param machine_id The id of the "global" stack this extruder is linked to.
|
||||
@deprecated("Use CuraStackBuilder::createExtruderStack", "2.6")
|
||||
def createExtruderTrain(self, extruder_definition: DefinitionContainer, machine_definition: DefinitionContainer,
|
||||
position, machine_id: str) -> None:
|
||||
# Cache some things.
|
||||
|
@ -459,7 +462,6 @@ class ExtruderManager(QObject):
|
|||
# \param machine_id The machine to get the extruders of.
|
||||
def getMachineExtruders(self, machine_id):
|
||||
if machine_id not in self._extruder_trains:
|
||||
Logger.log("w", "Tried to get the extruder trains for machine %s, which doesn't exist.", machine_id)
|
||||
return []
|
||||
return [self._extruder_trains[machine_id][name] for name in self._extruder_trains[machine_id]]
|
||||
|
||||
|
@ -483,13 +485,12 @@ class ExtruderManager(QObject):
|
|||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
|
||||
result = []
|
||||
if global_stack:
|
||||
if global_stack and global_stack.getId() in self._extruder_trains:
|
||||
for extruder in sorted(self._extruder_trains[global_stack.getId()]):
|
||||
result.append(self._extruder_trains[global_stack.getId()][extruder])
|
||||
return result
|
||||
|
||||
def __globalContainerStackChanged(self) -> None:
|
||||
self._addCurrentMachineExtruders()
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack and global_container_stack.getBottom() and global_container_stack.getBottom().getId() != self._global_container_stack_definition_id:
|
||||
self._global_container_stack_definition_id = global_container_stack.getBottom().getId()
|
||||
|
@ -576,18 +577,6 @@ class ExtruderManager(QObject):
|
|||
@staticmethod
|
||||
def getResolveOrValue(key):
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
resolved_value = global_stack.getProperty(key, "value")
|
||||
|
||||
resolved_value = global_stack.getProperty(key, "resolve")
|
||||
if resolved_value is not None:
|
||||
user_container = global_stack.findContainer({"type": "user"})
|
||||
quality_changes_container = global_stack.findContainer({"type": "quality_changes"})
|
||||
if user_container.hasProperty(key, "value") or quality_changes_container.hasProperty(key, "value"):
|
||||
# Normal case
|
||||
value = global_stack.getProperty(key, "value")
|
||||
else:
|
||||
# We have a resolved value and we're using it because of no user and quality_changes value
|
||||
value = resolved_value
|
||||
else:
|
||||
value = global_stack.getRawProperty(key, "value")
|
||||
|
||||
return value
|
||||
return resolved_value
|
||||
|
|
85
cura/Settings/ExtruderStack.py
Normal file
85
cura/Settings/ExtruderStack.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from typing import Any
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot
|
||||
|
||||
from UM.Decorators import override
|
||||
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
|
||||
from UM.Settings.ContainerStack import ContainerStack, InvalidContainerStackError
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.Interfaces import ContainerInterface
|
||||
|
||||
from . import Exceptions
|
||||
from .CuraContainerStack import CuraContainerStack
|
||||
from .ExtruderManager import ExtruderManager
|
||||
|
||||
## Represents an Extruder and its related containers.
|
||||
#
|
||||
#
|
||||
class ExtruderStack(CuraContainerStack):
|
||||
def __init__(self, container_id, *args, **kwargs):
|
||||
super().__init__(container_id, *args, **kwargs)
|
||||
|
||||
self.addMetaDataEntry("type", "extruder_train") # For backward compatibility
|
||||
|
||||
## Overridden from ContainerStack
|
||||
#
|
||||
# This will set the next stack and ensure that we register this stack as an extruder.
|
||||
@override(ContainerStack)
|
||||
def setNextStack(self, stack: ContainerStack) -> None:
|
||||
super().setNextStack(stack)
|
||||
stack.addExtruder(self)
|
||||
self.addMetaDataEntry("machine", stack.id)
|
||||
|
||||
# For backward compatibility: Register the extruder with the Extruder Manager
|
||||
ExtruderManager.getInstance().registerExtruder(self, stack.id)
|
||||
|
||||
@classmethod
|
||||
def getLoadingPriority(cls) -> int:
|
||||
return 3
|
||||
|
||||
## Overridden from ContainerStack
|
||||
#
|
||||
# It will perform a few extra checks when trying to get properties.
|
||||
#
|
||||
# The two extra checks it currently does is to ensure a next stack is set and to bypass
|
||||
# the extruder when the property is not settable per extruder.
|
||||
#
|
||||
# \throws Exceptions.NoGlobalStackError Raised when trying to get a property from an extruder without
|
||||
# having a next stack set.
|
||||
@override(ContainerStack)
|
||||
def getProperty(self, key: str, property_name: str) -> Any:
|
||||
if not self._next_stack:
|
||||
raise Exceptions.NoGlobalStackError("Extruder {id} is missing the next stack!".format(id = self.id))
|
||||
|
||||
if not super().getProperty(key, "settable_per_extruder"):
|
||||
return self.getNextStack().getProperty(key, property_name)
|
||||
|
||||
return super().getProperty(key, property_name)
|
||||
|
||||
@override(CuraContainerStack)
|
||||
def _getMachineDefinition(self) -> ContainerInterface:
|
||||
if not self.getNextStack():
|
||||
raise Exceptions.NoGlobalStackError("Extruder {id} is missing the next stack!".format(id = self.id))
|
||||
|
||||
return self.getNextStack()._getMachineDefinition()
|
||||
|
||||
@override(CuraContainerStack)
|
||||
def deserialize(self, contents: str) -> None:
|
||||
super().deserialize(contents)
|
||||
stacks = ContainerRegistry.getInstance().findContainerStacks(id=self.getMetaDataEntry("machine", ""))
|
||||
if stacks:
|
||||
self.setNextStack(stacks[0])
|
||||
|
||||
extruder_stack_mime = MimeType(
|
||||
name = "application/x-cura-extruderstack",
|
||||
comment = "Cura Extruder Stack",
|
||||
suffixes = ["extruder.cfg"]
|
||||
)
|
||||
|
||||
MimeTypeDatabase.addMimeType(extruder_stack_mime)
|
||||
ContainerRegistry.addContainerTypeByName(ExtruderStack, "extruder_stack", extruder_stack_mime.name)
|
125
cura/Settings/GlobalStack.py
Normal file
125
cura/Settings/GlobalStack.py
Normal file
|
@ -0,0 +1,125 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from typing import Any
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSlot, pyqtSignal
|
||||
|
||||
from UM.Decorators import override
|
||||
|
||||
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
|
||||
from UM.Settings.ContainerStack import ContainerStack, InvalidContainerStackError
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.SettingInstance import InstanceState
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.Interfaces import ContainerInterface
|
||||
|
||||
from . import Exceptions
|
||||
from .CuraContainerStack import CuraContainerStack
|
||||
|
||||
## Represents the Global or Machine stack and its related containers.
|
||||
#
|
||||
class GlobalStack(CuraContainerStack):
|
||||
def __init__(self, container_id: str, *args, **kwargs):
|
||||
super().__init__(container_id, *args, **kwargs)
|
||||
|
||||
self.addMetaDataEntry("type", "machine") # For backward compatibility
|
||||
|
||||
self._extruders = []
|
||||
|
||||
# This property is used to track which settings we are calculating the "resolve" for
|
||||
# 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.
|
||||
self._resolving_settings = set()
|
||||
|
||||
## Get the list of extruders of this stack.
|
||||
#
|
||||
# \return The extruders registered with this stack.
|
||||
@pyqtProperty("QVariantList")
|
||||
def extruders(self) -> list:
|
||||
return self._extruders
|
||||
|
||||
@classmethod
|
||||
def getLoadingPriority(cls) -> int:
|
||||
return 2
|
||||
|
||||
## Add an extruder to the list of extruders of this stack.
|
||||
#
|
||||
# \param extruder The extruder to add.
|
||||
#
|
||||
# \throws Exceptions.TooManyExtrudersError Raised when trying to add an extruder while we
|
||||
# already have the maximum number of extruders.
|
||||
def addExtruder(self, extruder: ContainerStack) -> None:
|
||||
extruder_count = self.getProperty("machine_extruder_count", "value")
|
||||
if extruder_count and len(self._extruders) + 1 > extruder_count:
|
||||
raise Exceptions.TooManyExtrudersError("Tried to add extruder to {id} but its extruder count is {count}".format(id = self.id, count = extruder_count))
|
||||
|
||||
self._extruders.append(extruder)
|
||||
|
||||
## Overridden from ContainerStack
|
||||
#
|
||||
# This will return the value of the specified property for the specified setting,
|
||||
# unless the property is "value" and that setting has a "resolve" function set.
|
||||
# When a resolve is set, it will instead try and execute the resolve first and
|
||||
# then fall back to the normal "value" property.
|
||||
#
|
||||
# \param key The setting key to get the property of.
|
||||
# \param property_name The property to get the value of.
|
||||
#
|
||||
# \return The value of the property for the specified setting, or None if not found.
|
||||
@override(ContainerStack)
|
||||
def getProperty(self, key: str, property_name: str) -> Any:
|
||||
if not self.definition.findDefinitions(key = key):
|
||||
return None
|
||||
|
||||
if self._shouldResolve(key, property_name):
|
||||
self._resolving_settings.add(key)
|
||||
resolve = super().getProperty(key, "resolve")
|
||||
self._resolving_settings.remove(key)
|
||||
if resolve is not None:
|
||||
return resolve
|
||||
|
||||
return super().getProperty(key, property_name)
|
||||
|
||||
## Overridden from ContainerStack
|
||||
#
|
||||
# This will simply raise an exception since the Global stack cannot have a next stack.
|
||||
@override(ContainerStack)
|
||||
def setNextStack(self, next_stack: ContainerStack) -> None:
|
||||
raise Exceptions.InvalidOperationError("Global stack cannot have a next stack!")
|
||||
|
||||
# protected:
|
||||
|
||||
# Determine whether or not we should try to get the "resolve" property instead of the
|
||||
# requested property.
|
||||
def _shouldResolve(self, key: str, property_name: str) -> bool:
|
||||
if property_name is not "value":
|
||||
# Do not try to resolve anything but the "value" property
|
||||
return False
|
||||
|
||||
if key in self._resolving_settings:
|
||||
# To prevent infinite recursion, if getProperty is called with the same key as
|
||||
# we are already trying to resolve, we should not try to resolve again. Since
|
||||
# this can happen multiple times when trying to resolve a value, we need to
|
||||
# track all settings that are being resolved.
|
||||
return False
|
||||
|
||||
setting_state = super().getProperty(key, "state")
|
||||
if setting_state is not None and setting_state != InstanceState.Default:
|
||||
# When the user has explicitly set a value, we should ignore any resolve and
|
||||
# just return that value.
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
## private:
|
||||
global_stack_mime = MimeType(
|
||||
name = "application/x-cura-globalstack",
|
||||
comment = "Cura Global Stack",
|
||||
suffixes = ["global.cfg"]
|
||||
)
|
||||
|
||||
MimeTypeDatabase.addMimeType(global_stack_mime)
|
||||
ContainerRegistry.addContainerTypeByName(GlobalStack, "global_stack", global_stack_mime.name)
|
|
@ -11,17 +11,23 @@ from UM.Application import Application
|
|||
from UM.Preferences import Preferences
|
||||
from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
from UM.Decorators import deprecated
|
||||
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.SettingDefinition import SettingDefinition
|
||||
from UM.Settings.SettingFunction import SettingFunction
|
||||
from UM.Settings.Validator import ValidatorState
|
||||
from UM.Signal import postponeSignals
|
||||
|
||||
from cura.QualityManager import QualityManager
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
||||
from .GlobalStack import GlobalStack
|
||||
from .CuraStackBuilder import CuraStackBuilder
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
@ -46,10 +52,12 @@ class MachineManager(QObject):
|
|||
self.globalContainerChanged.connect(self.activeQualityChanged)
|
||||
|
||||
self._stacks_have_errors = None
|
||||
self._empty_variant_container = ContainerRegistry.getInstance().findInstanceContainers(id="empty_variant")[0]
|
||||
self._empty_material_container = ContainerRegistry.getInstance().findInstanceContainers(id="empty_material")[0]
|
||||
self._empty_quality_container = ContainerRegistry.getInstance().findInstanceContainers(id="empty_quality")[0]
|
||||
self._empty_quality_changes_container = ContainerRegistry.getInstance().findInstanceContainers(id="empty_quality_changes")[0]
|
||||
|
||||
self._empty_variant_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
self._empty_material_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
self._empty_quality_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
self._empty_quality_changes_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
|
||||
self._onGlobalContainerChanged()
|
||||
|
||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
|
||||
|
@ -226,14 +234,22 @@ class MachineManager(QObject):
|
|||
|
||||
def _onGlobalContainerChanged(self):
|
||||
if self._global_container_stack:
|
||||
self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged)
|
||||
self._global_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
|
||||
self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
|
||||
|
||||
material = self._global_container_stack.findContainer({"type": "material"})
|
||||
try:
|
||||
self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged)
|
||||
except TypeError: #pyQtSignal gives a TypeError when disconnecting from something that was already disconnected.
|
||||
pass
|
||||
try:
|
||||
self._global_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
|
||||
except TypeError:
|
||||
pass
|
||||
try:
|
||||
self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
|
||||
except TypeError:
|
||||
pass
|
||||
material = self._global_container_stack.material
|
||||
material.nameChanged.disconnect(self._onMaterialNameChanged)
|
||||
|
||||
quality = self._global_container_stack.findContainer({"type": "quality"})
|
||||
quality = self._global_container_stack.quality
|
||||
quality.nameChanged.disconnect(self._onQualityNameChanged)
|
||||
|
||||
if self._global_container_stack.getProperty("machine_extruder_count", "value") > 1:
|
||||
|
@ -256,23 +272,23 @@ class MachineManager(QObject):
|
|||
# For multi-extrusion machines, we do not want variant or material profiles in the stack,
|
||||
# because these are extruder specific and may cause wrong values to be used for extruders
|
||||
# that did not specify a value in the extruder.
|
||||
global_variant = self._global_container_stack.findContainer(type = "variant")
|
||||
global_variant = self._global_container_stack.variant
|
||||
if global_variant != self._empty_variant_container:
|
||||
self._global_container_stack.replaceContainer(self._global_container_stack.getContainerIndex(global_variant), self._empty_variant_container)
|
||||
self._global_container_stack.setVariant(self._empty_variant_container)
|
||||
|
||||
global_material = self._global_container_stack.findContainer(type = "material")
|
||||
global_material = self._global_container_stack.material
|
||||
if global_material != self._empty_material_container:
|
||||
self._global_container_stack.replaceContainer(self._global_container_stack.getContainerIndex(global_material), self._empty_material_container)
|
||||
self._global_container_stack.setMaterial(self._empty_material_container)
|
||||
|
||||
for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks(): #Listen for changes on all extruder stacks.
|
||||
extruder_stack.propertyChanged.connect(self._onPropertyChanged)
|
||||
extruder_stack.containersChanged.connect(self._onInstanceContainersChanged)
|
||||
|
||||
else:
|
||||
material = self._global_container_stack.findContainer({"type": "material"})
|
||||
material = self._global_container_stack.material
|
||||
material.nameChanged.connect(self._onMaterialNameChanged)
|
||||
|
||||
quality = self._global_container_stack.findContainer({"type": "quality"})
|
||||
quality = self._global_container_stack.quality
|
||||
quality.nameChanged.connect(self._onQualityNameChanged)
|
||||
|
||||
self._updateStacksHaveErrors()
|
||||
|
@ -325,41 +341,11 @@ class MachineManager(QObject):
|
|||
|
||||
@pyqtSlot(str, str)
|
||||
def addMachine(self, name: str, definition_id: str) -> None:
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
definitions = container_registry.findDefinitionContainers(id = definition_id)
|
||||
if definitions:
|
||||
definition = definitions[0]
|
||||
name = self._createUniqueName("machine", "", name, definition.getName())
|
||||
new_global_stack = ContainerStack(name)
|
||||
new_global_stack.addMetaDataEntry("type", "machine")
|
||||
new_global_stack.addContainer(definition)
|
||||
container_registry.addContainer(new_global_stack)
|
||||
|
||||
variant_instance_container = self._updateVariantContainer(definition)
|
||||
material_instance_container = self._updateMaterialContainer(definition, new_global_stack, variant_instance_container)
|
||||
quality_instance_container = self._updateQualityContainer(definition, variant_instance_container, material_instance_container)
|
||||
|
||||
current_settings_instance_container = InstanceContainer(name + "_current_settings")
|
||||
current_settings_instance_container.addMetaDataEntry("machine", name)
|
||||
current_settings_instance_container.addMetaDataEntry("type", "user")
|
||||
current_settings_instance_container.setDefinition(definitions[0])
|
||||
container_registry.addContainer(current_settings_instance_container)
|
||||
|
||||
|
||||
if variant_instance_container:
|
||||
new_global_stack.addContainer(variant_instance_container)
|
||||
if material_instance_container:
|
||||
new_global_stack.addContainer(material_instance_container)
|
||||
if quality_instance_container:
|
||||
new_global_stack.addContainer(quality_instance_container)
|
||||
|
||||
new_global_stack.addContainer(self._empty_quality_changes_container)
|
||||
new_global_stack.addContainer(current_settings_instance_container)
|
||||
|
||||
ExtruderManager.getInstance().addMachineExtruders(definition, new_global_stack.getId())
|
||||
|
||||
Application.getInstance().setGlobalContainerStack(new_global_stack)
|
||||
|
||||
new_stack = CuraStackBuilder.createMachine(name, definition_id)
|
||||
if new_stack:
|
||||
Application.getInstance().setGlobalContainerStack(new_stack)
|
||||
else:
|
||||
Logger.log("w", "Failed creating a new machine!")
|
||||
|
||||
## Create a name that is not empty and unique
|
||||
# \param container_type \type{string} Type of the container (machine, quality, ...)
|
||||
|
@ -478,6 +464,10 @@ class MachineManager(QObject):
|
|||
|
||||
return ""
|
||||
|
||||
@pyqtProperty("QObject", notify = globalContainerChanged)
|
||||
def activeMachine(self) -> GlobalStack:
|
||||
return self._global_container_stack
|
||||
|
||||
@pyqtProperty(str, notify = activeStackChanged)
|
||||
def activeStackId(self) -> str:
|
||||
if self._active_container_stack:
|
||||
|
@ -488,7 +478,7 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify = activeMaterialChanged)
|
||||
def activeMaterialName(self) -> str:
|
||||
if self._active_container_stack:
|
||||
material = self._active_container_stack.findContainer({"type":"material"})
|
||||
material = self._active_container_stack.material
|
||||
if material:
|
||||
return material.getName()
|
||||
|
||||
|
@ -499,7 +489,7 @@ class MachineManager(QObject):
|
|||
result = []
|
||||
if ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() is not None:
|
||||
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
|
||||
variant_container = stack.findContainer({"type": "variant"})
|
||||
variant_container = stack.variant
|
||||
if variant_container and variant_container != self._empty_variant_container:
|
||||
result.append(variant_container.getName())
|
||||
|
||||
|
@ -521,7 +511,7 @@ class MachineManager(QObject):
|
|||
result = []
|
||||
if ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() is not None:
|
||||
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
|
||||
material_container = stack.findContainer(type="material")
|
||||
material_container = stack.material
|
||||
if material_container and material_container != self._empty_material_container:
|
||||
result.append(material_container.getName())
|
||||
return result
|
||||
|
@ -529,7 +519,7 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify=activeMaterialChanged)
|
||||
def activeMaterialId(self) -> str:
|
||||
if self._active_container_stack:
|
||||
material = self._active_container_stack.findContainer({"type": "material"})
|
||||
material = self._active_container_stack.material
|
||||
if material:
|
||||
return material.getId()
|
||||
|
||||
|
@ -543,7 +533,7 @@ class MachineManager(QObject):
|
|||
result = {}
|
||||
|
||||
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
|
||||
material_container = stack.findContainer(type = "material")
|
||||
material_container = stack.material
|
||||
if not material_container:
|
||||
continue
|
||||
|
||||
|
@ -562,13 +552,13 @@ class MachineManager(QObject):
|
|||
if not self._global_container_stack:
|
||||
return 0
|
||||
|
||||
quality_changes = self._global_container_stack.findContainer({"type": "quality_changes"})
|
||||
quality_changes = self._global_container_stack.qualityChanges
|
||||
if quality_changes:
|
||||
value = self._global_container_stack.getRawProperty("layer_height", "value", skip_until_container = quality_changes.getId())
|
||||
if isinstance(value, SettingFunction):
|
||||
value = value(self._global_container_stack)
|
||||
return value
|
||||
quality = self._global_container_stack.findContainer({"type": "quality"})
|
||||
quality = self._global_container_stack.quality
|
||||
if quality:
|
||||
value = self._global_container_stack.getRawProperty("layer_height", "value", skip_until_container = quality.getId())
|
||||
if isinstance(value, SettingFunction):
|
||||
|
@ -582,7 +572,7 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify=activeQualityChanged)
|
||||
def activeQualityMaterialId(self) -> str:
|
||||
if self._active_container_stack:
|
||||
quality = self._active_container_stack.findContainer({"type": "quality"})
|
||||
quality = self._active_container_stack.quality
|
||||
if quality:
|
||||
material_id = quality.getMetaDataEntry("material")
|
||||
if material_id:
|
||||
|
@ -599,10 +589,10 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify=activeQualityChanged)
|
||||
def activeQualityName(self):
|
||||
if self._active_container_stack and self._global_container_stack:
|
||||
quality = self._global_container_stack.findContainer({"type": "quality_changes"})
|
||||
if quality and quality != self._empty_quality_changes_container:
|
||||
quality = self._global_container_stack.qualityChanges
|
||||
if quality and not isinstance(quality, type(self._empty_quality_changes_container)):
|
||||
return quality.getName()
|
||||
quality = self._active_container_stack.findContainer({"type": "quality"})
|
||||
quality = self._active_container_stack.quality
|
||||
if quality:
|
||||
return quality.getName()
|
||||
return ""
|
||||
|
@ -610,10 +600,10 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify=activeQualityChanged)
|
||||
def activeQualityId(self):
|
||||
if self._active_container_stack:
|
||||
quality = self._active_container_stack.findContainer({"type": "quality_changes"})
|
||||
if quality and quality != self._empty_quality_changes_container:
|
||||
quality = self._active_container_stack.qualityChanges
|
||||
if quality and not isinstance(quality, type(self._empty_quality_changes_container)):
|
||||
return quality.getId()
|
||||
quality = self._active_container_stack.findContainer({"type": "quality"})
|
||||
quality = self._active_container_stack.quality
|
||||
if quality:
|
||||
return quality.getId()
|
||||
return ""
|
||||
|
@ -621,10 +611,10 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify=activeQualityChanged)
|
||||
def globalQualityId(self):
|
||||
if self._global_container_stack:
|
||||
quality = self._global_container_stack.findContainer({"type": "quality_changes"})
|
||||
if quality and quality != self._empty_quality_changes_container:
|
||||
quality = self._global_container_stack.qualityChanges
|
||||
if quality and not isinstance(quality, type(self._empty_quality_changes_container)):
|
||||
return quality.getId()
|
||||
quality = self._global_container_stack.findContainer({"type": "quality"})
|
||||
quality = self._global_container_stack.quality
|
||||
if quality:
|
||||
return quality.getId()
|
||||
return ""
|
||||
|
@ -632,7 +622,7 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify = activeQualityChanged)
|
||||
def activeQualityType(self):
|
||||
if self._active_container_stack:
|
||||
quality = self._active_container_stack.findContainer(type = "quality")
|
||||
quality = self._active_container_stack.quality
|
||||
if quality:
|
||||
return quality.getMetaDataEntry("quality_type")
|
||||
return ""
|
||||
|
@ -640,7 +630,7 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(bool, notify = activeQualityChanged)
|
||||
def isActiveQualitySupported(self):
|
||||
if self._active_container_stack:
|
||||
quality = self._active_container_stack.findContainer(type = "quality")
|
||||
quality = self._active_container_stack.quality
|
||||
if quality:
|
||||
return Util.parseBool(quality.getMetaDataEntry("supported", True))
|
||||
return False
|
||||
|
@ -655,7 +645,7 @@ class MachineManager(QObject):
|
|||
def activeQualityContainerId(self):
|
||||
# We're using the active stack instead of the global stack in case the list of qualities differs per extruder
|
||||
if self._global_container_stack:
|
||||
quality = self._active_container_stack.findContainer(type = "quality")
|
||||
quality = self._active_container_stack.quality
|
||||
if quality:
|
||||
return quality.getId()
|
||||
return ""
|
||||
|
@ -663,8 +653,8 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify = activeQualityChanged)
|
||||
def activeQualityChangesId(self):
|
||||
if self._active_container_stack:
|
||||
changes = self._active_container_stack.findContainer(type = "quality_changes")
|
||||
if changes:
|
||||
changes = self._active_container_stack.qualityChanges
|
||||
if changes and changes.getId() != "empty":
|
||||
return changes.getId()
|
||||
return ""
|
||||
|
||||
|
@ -701,21 +691,20 @@ class MachineManager(QObject):
|
|||
|
||||
Logger.log("d", "Attempting to change the active material to %s", material_id)
|
||||
|
||||
old_material = self._active_container_stack.findContainer({"type": "material"})
|
||||
old_quality = self._active_container_stack.findContainer({"type": "quality"})
|
||||
old_quality_changes = self._active_container_stack.findContainer({"type": "quality_changes"})
|
||||
old_material = self._active_container_stack.material
|
||||
old_quality = self._active_container_stack.quality
|
||||
old_quality_changes = self._active_container_stack.qualityChanges
|
||||
if not old_material:
|
||||
Logger.log("w", "While trying to set the active material, no material was found to replace it.")
|
||||
return
|
||||
|
||||
if old_quality_changes.getId() == "empty_quality_changes":
|
||||
if old_quality_changes and old_quality_changes.getId() == "empty_quality_changes":
|
||||
old_quality_changes = None
|
||||
|
||||
self.blurSettings.emit()
|
||||
old_material.nameChanged.disconnect(self._onMaterialNameChanged)
|
||||
|
||||
material_index = self._active_container_stack.getContainerIndex(old_material)
|
||||
self._active_container_stack.replaceContainer(material_index, material_container)
|
||||
self._active_container_stack.material = material_container
|
||||
Logger.log("d", "Active material changed")
|
||||
|
||||
material_container.nameChanged.connect(self._onMaterialNameChanged)
|
||||
|
@ -764,8 +753,8 @@ class MachineManager(QObject):
|
|||
if not containers or not self._active_container_stack:
|
||||
return
|
||||
Logger.log("d", "Attempting to change the active variant to %s", variant_id)
|
||||
old_variant = self._active_container_stack.findContainer({"type": "variant"})
|
||||
old_material = self._active_container_stack.findContainer({"type": "material"})
|
||||
old_variant = self._active_container_stack.variant
|
||||
old_material = self._active_container_stack.material
|
||||
if old_variant:
|
||||
self.blurSettings.emit()
|
||||
variant_index = self._active_container_stack.getContainerIndex(old_variant)
|
||||
|
@ -856,7 +845,7 @@ class MachineManager(QObject):
|
|||
stacks = [global_container_stack]
|
||||
|
||||
for stack in stacks:
|
||||
material = stack.findContainer(type="material")
|
||||
material = stack.material
|
||||
quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material])
|
||||
if not quality: #No quality profile is found for this quality type.
|
||||
quality = self._empty_quality_container
|
||||
|
@ -893,7 +882,7 @@ class MachineManager(QObject):
|
|||
else:
|
||||
Logger.log("e", "Could not find the global quality changes container with name %s", quality_changes_name)
|
||||
return None
|
||||
material = global_container_stack.findContainer(type="material")
|
||||
material = global_container_stack.material
|
||||
|
||||
# For the global stack, find a quality which matches the quality_type in
|
||||
# the quality changes profile and also satisfies any material constraints.
|
||||
|
@ -916,7 +905,7 @@ class MachineManager(QObject):
|
|||
else:
|
||||
quality_changes = global_quality_changes
|
||||
|
||||
material = stack.findContainer(type="material")
|
||||
material = stack.material
|
||||
quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material])
|
||||
if not quality: #No quality profile found for this quality type.
|
||||
quality = self._empty_quality_container
|
||||
|
@ -935,18 +924,18 @@ class MachineManager(QObject):
|
|||
|
||||
def _replaceQualityOrQualityChangesInStack(self, stack, container, postpone_emit = False):
|
||||
# Disconnect the signal handling from the old container.
|
||||
old_container = stack.findContainer(type=container.getMetaDataEntry("type"))
|
||||
if old_container:
|
||||
old_container.nameChanged.disconnect(self._onQualityNameChanged)
|
||||
else:
|
||||
Logger.log("e", "Could not find container of type %s in stack %s while replacing quality (changes) with container %s", container.getMetaDataEntry("type"), stack.getId(), container.getId())
|
||||
return
|
||||
|
||||
# Swap in the new container into the stack.
|
||||
stack.replaceContainer(stack.getContainerIndex(old_container), container, postpone_emit = postpone_emit)
|
||||
|
||||
# Attach the needed signal handling.
|
||||
container.nameChanged.connect(self._onQualityNameChanged)
|
||||
container_type = container.getMetaDataEntry("type")
|
||||
if container_type == "quality":
|
||||
stack.quality.nameChanged.disconnect(self._onQualityNameChanged)
|
||||
stack.setQuality(container)
|
||||
stack.qualityChanges.nameChanged.connect(self._onQualityNameChanged)
|
||||
elif container_type == "quality_changes" or container_type is None:
|
||||
# If the container is an empty container, we need to change the quality_changes.
|
||||
# Quality can never be set to empty.
|
||||
stack.qualityChanges.nameChanged.disconnect(self._onQualityNameChanged)
|
||||
stack.setQualityChanges(container)
|
||||
stack.qualityChanges.nameChanged.connect(self._onQualityNameChanged)
|
||||
self._onQualityNameChanged()
|
||||
|
||||
def _askUserToKeepOrClearCurrentSettings(self):
|
||||
Application.getInstance().discardOrKeepProfileChanges()
|
||||
|
@ -954,7 +943,7 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify = activeVariantChanged)
|
||||
def activeVariantName(self):
|
||||
if self._active_container_stack:
|
||||
variant = self._active_container_stack.findContainer({"type": "variant"})
|
||||
variant = self._active_container_stack.variant
|
||||
if variant:
|
||||
return variant.getName()
|
||||
|
||||
|
@ -963,7 +952,7 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify = activeVariantChanged)
|
||||
def activeVariantId(self):
|
||||
if self._active_container_stack:
|
||||
variant = self._active_container_stack.findContainer({"type": "variant"})
|
||||
variant = self._active_container_stack.variant
|
||||
if variant:
|
||||
return variant.getId()
|
||||
|
||||
|
@ -1009,7 +998,7 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify = activeVariantChanged)
|
||||
def activeQualityVariantId(self):
|
||||
if self._active_container_stack:
|
||||
variant = self._active_container_stack.findContainer({"type": "variant"})
|
||||
variant = self._active_container_stack.variant
|
||||
if variant:
|
||||
return self.getQualityVariantId(self._global_container_stack.getBottom(), variant)
|
||||
return ""
|
||||
|
|
|
@ -231,20 +231,7 @@ class StartSliceJob(Job):
|
|||
keys = stack.getAllKeys()
|
||||
settings = {}
|
||||
for key in keys:
|
||||
# Use resolvement value if available, or take the value
|
||||
resolved_value = stack.getProperty(key, "resolve")
|
||||
if resolved_value is not None:
|
||||
# There is a resolvement value. Check if we need to use it.
|
||||
user_container = stack.findContainer({"type": "user"})
|
||||
quality_changes_container = stack.findContainer({"type": "quality_changes"})
|
||||
if user_container.hasProperty(key,"value") or quality_changes_container.hasProperty(key,"value"):
|
||||
# Normal case
|
||||
settings[key] = stack.getProperty(key, "value")
|
||||
else:
|
||||
settings[key] = resolved_value
|
||||
else:
|
||||
# Normal case
|
||||
settings[key] = stack.getProperty(key, "value")
|
||||
settings[key] = stack.getProperty(key, "value")
|
||||
Job.yieldThread()
|
||||
|
||||
start_gcode = settings["machine_start_gcode"]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal
|
||||
|
@ -101,8 +101,7 @@ class MachineSettingsAction(MachineAction):
|
|||
definition_changes_container.addMetaDataEntry("type", "definition_changes")
|
||||
|
||||
self._container_registry.addContainer(definition_changes_container)
|
||||
# Insert definition_changes between the definition and the variant
|
||||
container_stack.insertContainer(-1, definition_changes_container)
|
||||
container_stack.definitionChanges = definition_changes_container
|
||||
|
||||
return definition_changes_container
|
||||
|
||||
|
@ -152,9 +151,9 @@ class MachineSettingsAction(MachineAction):
|
|||
if extruder_count == 1:
|
||||
# Get the material and variant of the first extruder before setting the number extruders to 1
|
||||
if machine_manager.hasMaterials:
|
||||
extruder_material_id = machine_manager.allActiveMaterialIds[extruder_manager.extruderIds["0"]]
|
||||
extruder_material_id = machine_manager.allActiveMaterialIds[extruder_manager.extruderIds["0"]]
|
||||
if machine_manager.hasVariants:
|
||||
extruder_variant_id = machine_manager.activeVariantIds[0]
|
||||
extruder_variant_id = machine_manager.activeVariantIds[0]
|
||||
|
||||
# Copy any settable_per_extruder setting value from the extruders to the global stack
|
||||
extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
|
||||
|
@ -168,7 +167,7 @@ class MachineSettingsAction(MachineAction):
|
|||
setting_key = setting_instance.definition.key
|
||||
settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder")
|
||||
if settable_per_extruder:
|
||||
limit_to_extruder = self._global_container_stack.getProperty(setting_key, "limit_to_extruder")
|
||||
limit_to_extruder = self._global_container_stack.getProperty(setting_key, "limit_to_extruder")
|
||||
|
||||
if limit_to_extruder == "-1" or limit_to_extruder == extruder_index:
|
||||
global_user_container.setProperty(setting_key, "value", extruder_user_container.getProperty(setting_key, "value"))
|
||||
|
@ -176,9 +175,9 @@ class MachineSettingsAction(MachineAction):
|
|||
|
||||
# Check to see if any features are set to print with an extruder that will no longer exist
|
||||
for setting_key in ["adhesion_extruder_nr", "support_extruder_nr", "support_extruder_nr_layer_0", "support_infill_extruder_nr", "support_interface_extruder_nr"]:
|
||||
if int(self._global_container_stack.getProperty(setting_key, "value")) > extruder_count -1:
|
||||
if int(self._global_container_stack.getProperty(setting_key, "value")) > extruder_count - 1:
|
||||
Logger.log("i", "Lowering %s setting to match number of extruders", setting_key)
|
||||
self._global_container_stack.getTop().setProperty(setting_key, "value", extruder_count -1)
|
||||
self._global_container_stack.getTop().setProperty(setting_key, "value", extruder_count - 1)
|
||||
|
||||
# Check to see if any objects are set to print with an extruder that will no longer exist
|
||||
root_node = Application.getInstance().getController().getScene().getRoot()
|
||||
|
@ -217,7 +216,7 @@ class MachineSettingsAction(MachineAction):
|
|||
|
||||
# Make sure the machine stack is active
|
||||
if extruder_manager.activeExtruderIndex > -1:
|
||||
extruder_manager.setActiveExtruderIndex(-1);
|
||||
extruder_manager.setActiveExtruderIndex(-1)
|
||||
|
||||
# Restore material and variant on global stack
|
||||
# MachineManager._onGlobalContainerChanged removes the global material and variant of multiextruder machines
|
||||
|
@ -229,9 +228,9 @@ class MachineSettingsAction(MachineAction):
|
|||
preferences.setValue("cura/choice_on_profile_override", "always_keep")
|
||||
|
||||
if extruder_material_id:
|
||||
machine_manager.setActiveMaterial(extruder_material_id);
|
||||
machine_manager.setActiveMaterial(extruder_material_id)
|
||||
if extruder_variant_id:
|
||||
machine_manager.setActiveVariant(extruder_variant_id);
|
||||
machine_manager.setActiveVariant(extruder_variant_id)
|
||||
|
||||
preferences.setValue("cura/choice_on_profile_override", choice_on_profile_override)
|
||||
|
||||
|
@ -263,7 +262,7 @@ class MachineSettingsAction(MachineAction):
|
|||
|
||||
# Set the material container to a sane default
|
||||
if material_container.getId() == "empty_material":
|
||||
search_criteria = { "type": "material", "definition": "fdmprinter", "id": "*pla*" }
|
||||
search_criteria = { "type": "material", "definition": "fdmprinter", "id": "*pla*"}
|
||||
containers = self._container_registry.findInstanceContainers(**search_criteria)
|
||||
if containers:
|
||||
self._global_container_stack.replaceContainer(material_index, containers[0])
|
||||
|
|
|
@ -375,6 +375,10 @@ Cura.MachineAction
|
|||
}
|
||||
}
|
||||
currentIndex: machineExtruderCountProvider.properties.value - 1
|
||||
Component.onCompleted:
|
||||
{
|
||||
manager.setMachineExtruderCount(1);
|
||||
}
|
||||
onActivated:
|
||||
{
|
||||
manager.setMachineExtruderCount(index + 1);
|
||||
|
|
|
@ -22,5 +22,12 @@
|
|||
"7": "custom_extruder_8"
|
||||
},
|
||||
"first_start_actions": ["MachineSettingsAction"]
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"machine_extruder_count":
|
||||
{
|
||||
"default_value": 8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ Menu
|
|||
{
|
||||
text: model.name + " - " + model.layer_height
|
||||
checkable: true
|
||||
checked: Cura.MachineManager.activeQualityChangesId == "empty_quality_changes" && Cura.MachineManager.activeQualityType == model.metadata.quality_type
|
||||
checked: Cura.MachineManager.activeQualityChangesId == "" && Cura.MachineManager.activeQualityType == model.metadata.quality_type
|
||||
exclusiveGroup: group
|
||||
onTriggered: Cura.MachineManager.setActiveQuality(model.id)
|
||||
}
|
||||
|
|
96
tests/Settings/TestCuraContainerRegistry.py
Normal file
96
tests/Settings/TestCuraContainerRegistry.py
Normal file
|
@ -0,0 +1,96 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import os #To find the directory with test files and find the test files.
|
||||
import pytest #This module contains unit tests.
|
||||
import shutil #To copy files to make a temporary file.
|
||||
import unittest.mock #To mock and monkeypatch stuff.
|
||||
import urllib.parse
|
||||
|
||||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry #The class we're testing.
|
||||
from cura.Settings.ExtruderStack import ExtruderStack #Testing for returning the correct types of stacks.
|
||||
from cura.Settings.GlobalStack import GlobalStack #Testing for returning the correct types of stacks.
|
||||
from UM.Resources import Resources #Mocking some functions of this.
|
||||
import UM.Settings.ContainerRegistry #Making empty container stacks.
|
||||
import UM.Settings.ContainerStack #Setting the container registry here properly.
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
|
||||
## Gives a fresh CuraContainerRegistry instance.
|
||||
@pytest.fixture()
|
||||
def container_registry():
|
||||
return CuraContainerRegistry()
|
||||
|
||||
def teardown():
|
||||
#If the temporary file for the legacy file rename test still exists, remove it.
|
||||
temporary_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "stacks", "temporary.stack.cfg")
|
||||
if os.path.isfile(temporary_file):
|
||||
os.remove(temporary_file)
|
||||
|
||||
## Tests whether loading gives objects of the correct type.
|
||||
@pytest.mark.parametrize("filename, output_class", [
|
||||
("ExtruderLegacy.stack.cfg", ExtruderStack),
|
||||
("MachineLegacy.stack.cfg", GlobalStack),
|
||||
("Left.extruder.cfg", ExtruderStack),
|
||||
("Global.global.cfg", GlobalStack),
|
||||
("Global.stack.cfg", GlobalStack)
|
||||
])
|
||||
def test_loadTypes(filename, output_class, container_registry):
|
||||
#Mock some dependencies.
|
||||
UM.Settings.ContainerStack.setContainerRegistry(container_registry)
|
||||
Resources.getAllResourcesOfType = unittest.mock.MagicMock(return_value = [os.path.join(os.path.dirname(os.path.abspath(__file__)), "stacks", filename)]) #Return just this tested file.
|
||||
|
||||
def findContainers(container_type = 0, id = None):
|
||||
if id == "some_instance":
|
||||
return [UM.Settings.ContainerRegistry._EmptyInstanceContainer(id)]
|
||||
elif id == "some_definition":
|
||||
return [DefinitionContainer(container_id = id)]
|
||||
else:
|
||||
return []
|
||||
|
||||
container_registry.findContainers = findContainers
|
||||
|
||||
with unittest.mock.patch("cura.Settings.GlobalStack.GlobalStack.findContainer"):
|
||||
with unittest.mock.patch("os.remove"):
|
||||
container_registry.load()
|
||||
|
||||
#Check whether the resulting type was correct.
|
||||
stack_id = filename.split(".")[0]
|
||||
for container in container_registry._containers: #Stupid ContainerRegistry class doesn't expose any way of getting at this except by prodding the privates.
|
||||
if container.getId() == stack_id: #This is the one we're testing.
|
||||
assert type(container) == output_class
|
||||
break
|
||||
else:
|
||||
assert False #Container stack with specified ID was not loaded.
|
||||
|
||||
## Tests whether loading a legacy file moves the upgraded file properly.
|
||||
def test_loadLegacyFileRenamed(container_registry):
|
||||
#Create a temporary file for the registry to load.
|
||||
stacks_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), "stacks")
|
||||
temp_file = os.path.join(stacks_folder, "temporary.stack.cfg")
|
||||
temp_file_source = os.path.join(stacks_folder, "MachineLegacy.stack.cfg")
|
||||
shutil.copyfile(temp_file_source, temp_file)
|
||||
|
||||
#Mock some dependencies.
|
||||
UM.Settings.ContainerStack.setContainerRegistry(container_registry)
|
||||
Resources.getAllResourcesOfType = unittest.mock.MagicMock(return_value = [temp_file]) #Return a temporary file that we'll make for this test.
|
||||
|
||||
def findContainers(container_type = 0, id = None):
|
||||
if id == "MachineLegacy":
|
||||
return None
|
||||
return [UM.Settings.ContainerRegistry._EmptyInstanceContainer(id)]
|
||||
|
||||
old_find_containers = container_registry.findContainers
|
||||
container_registry.findContainers = findContainers
|
||||
|
||||
with unittest.mock.patch("cura.Settings.GlobalStack.GlobalStack.findContainer"):
|
||||
container_registry.load()
|
||||
|
||||
container_registry.findContainers = old_find_containers
|
||||
|
||||
container_registry.saveAll()
|
||||
print("all containers in registry", container_registry._containers)
|
||||
assert not os.path.isfile(temp_file)
|
||||
mime_type = container_registry.getMimeTypeForContainer(GlobalStack)
|
||||
file_name = urllib.parse.quote_plus("MachineLegacy") + "." + mime_type.preferredSuffix
|
||||
path = Resources.getStoragePath(Resources.ContainerStacks, file_name)
|
||||
assert os.path.isfile(path)
|
389
tests/Settings/TestExtruderStack.py
Normal file
389
tests/Settings/TestExtruderStack.py
Normal file
|
@ -0,0 +1,389 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import pytest #This module contains automated tests.
|
||||
import unittest.mock #For the mocking and monkeypatching functionality.
|
||||
|
||||
import UM.Settings.ContainerRegistry #To create empty instance containers.
|
||||
import UM.Settings.ContainerStack #To set the container registry the container stacks use.
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer #To check against the class of DefinitionContainer.
|
||||
from UM.Settings.InstanceContainer import InstanceContainer #To check against the class of InstanceContainer.
|
||||
import cura.Settings.ExtruderStack #The module we're testing.
|
||||
from cura.Settings.Exceptions import InvalidContainerError, InvalidOperationError #To check whether the correct exceptions are raised.
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
## Fake container registry that always provides all containers you ask of.
|
||||
@pytest.yield_fixture()
|
||||
def container_registry():
|
||||
registry = unittest.mock.MagicMock()
|
||||
registry.return_value = unittest.mock.NonCallableMagicMock()
|
||||
registry.findInstanceContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
|
||||
registry.findDefinitionContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
|
||||
|
||||
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = registry
|
||||
UM.Settings.ContainerStack._containerRegistry = registry
|
||||
|
||||
yield registry
|
||||
|
||||
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = None
|
||||
UM.Settings.ContainerStack._containerRegistry = None
|
||||
|
||||
## An empty extruder stack to test with.
|
||||
@pytest.fixture()
|
||||
def extruder_stack() -> cura.Settings.ExtruderStack.ExtruderStack:
|
||||
return cura.Settings.ExtruderStack.ExtruderStack("TestStack")
|
||||
|
||||
## Gets an instance container with a specified container type.
|
||||
#
|
||||
# \param container_type The type metadata for the instance container.
|
||||
# \return An instance container instance.
|
||||
def getInstanceContainer(container_type) -> InstanceContainer:
|
||||
container = InstanceContainer(container_id = "InstanceContainer")
|
||||
container.addMetaDataEntry("type", container_type)
|
||||
return container
|
||||
|
||||
class DefinitionContainerSubClass(DefinitionContainer):
|
||||
def __init__(self):
|
||||
super().__init__(container_id = "SubDefinitionContainer")
|
||||
|
||||
class InstanceContainerSubClass(InstanceContainer):
|
||||
def __init__(self, container_type):
|
||||
super().__init__(container_id = "SubInstanceContainer")
|
||||
self.addMetaDataEntry("type", container_type)
|
||||
|
||||
#############################START OF TEST CASES################################
|
||||
|
||||
## Tests whether adding a container is properly forbidden.
|
||||
def test_addContainer(extruder_stack):
|
||||
with pytest.raises(InvalidOperationError):
|
||||
extruder_stack.addContainer(unittest.mock.MagicMock())
|
||||
|
||||
#Tests setting user changes profiles to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong container type"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
def test_constrainUserChangesInvalid(container, extruder_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
extruder_stack.userChanges = container
|
||||
|
||||
#Tests setting user changes profiles.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "user"),
|
||||
InstanceContainerSubClass(container_type = "user")
|
||||
])
|
||||
def test_constrainUserChangesValid(container, extruder_stack):
|
||||
extruder_stack.userChanges = container #Should not give an error.
|
||||
|
||||
#Tests setting quality changes profiles to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong container type"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
def test_constrainQualityChangesInvalid(container, extruder_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
extruder_stack.qualityChanges = container
|
||||
|
||||
#Test setting quality changes profiles.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "quality_changes"),
|
||||
InstanceContainerSubClass(container_type = "quality_changes")
|
||||
])
|
||||
def test_constrainQualityChangesValid(container, extruder_stack):
|
||||
extruder_stack.qualityChanges = container #Should not give an error.
|
||||
|
||||
#Tests setting quality profiles to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong container type"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
def test_constrainQualityInvalid(container, extruder_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
extruder_stack.quality = container
|
||||
|
||||
#Test setting quality profiles.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "quality"),
|
||||
InstanceContainerSubClass(container_type = "quality")
|
||||
])
|
||||
def test_constrainQualityValid(container, extruder_stack):
|
||||
extruder_stack.quality = container #Should not give an error.
|
||||
|
||||
#Tests setting materials to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong container type"),
|
||||
getInstanceContainer(container_type = "quality"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
def test_constrainMaterialInvalid(container, extruder_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
extruder_stack.material = container
|
||||
|
||||
#Test setting materials.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "material"),
|
||||
InstanceContainerSubClass(container_type = "material")
|
||||
])
|
||||
def test_constrainMaterialValid(container, extruder_stack):
|
||||
extruder_stack.material = container #Should not give an error.
|
||||
|
||||
#Tests setting variants to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong container type"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
def test_constrainVariantInvalid(container, extruder_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
extruder_stack.variant = container
|
||||
|
||||
#Test setting variants.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "variant"),
|
||||
InstanceContainerSubClass(container_type = "variant")
|
||||
])
|
||||
def test_constrainVariantValid(container, extruder_stack):
|
||||
extruder_stack.variant = container #Should not give an error.
|
||||
|
||||
#Tests setting definitions to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong class"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong class.
|
||||
])
|
||||
def test_constrainVariantInvalid(container, extruder_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
extruder_stack.definition = container
|
||||
|
||||
#Test setting definitions.
|
||||
@pytest.mark.parametrize("container", [
|
||||
DefinitionContainer(container_id = "DefinitionContainer"),
|
||||
DefinitionContainerSubClass()
|
||||
])
|
||||
def test_constrainDefinitionValid(container, extruder_stack):
|
||||
extruder_stack.definition = container #Should not give an error.
|
||||
|
||||
## Tests whether deserialising completes the missing containers with empty
|
||||
# ones.
|
||||
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
|
||||
def test_deserializeCompletesEmptyContainers(extruder_stack: cura.Settings.ExtruderStack):
|
||||
extruder_stack._containers = [DefinitionContainer(container_id = "definition")] #Set the internal state of this stack manually.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
assert len(extruder_stack.getContainers()) == len(cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap) #Needs a slot for every type.
|
||||
for container_type_index in cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap:
|
||||
if container_type_index == cura.Settings.CuraContainerStack._ContainerIndexes.Definition: #We're not checking the definition.
|
||||
continue
|
||||
assert extruder_stack.getContainer(container_type_index).getId() == "empty" #All others need to be empty.
|
||||
|
||||
## Tests whether an instance container with the wrong type gets removed when
|
||||
# deserialising.
|
||||
def test_deserializeRemovesWrongInstanceContainer(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type")
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
assert extruder_stack.quality == extruder_stack._empty_instance_container #Replaced with empty.
|
||||
|
||||
## Tests whether a container with the wrong class gets removed when
|
||||
# deserialising.
|
||||
def test_deserializeRemovesWrongContainerClass(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class")
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
assert extruder_stack.quality == extruder_stack._empty_instance_container #Replaced with empty.
|
||||
|
||||
## Tests whether an instance container in the definition spot results in an
|
||||
# error.
|
||||
def test_deserializeWrongDefinitionClass(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
## Tests whether an instance container with the wrong type is moved into the
|
||||
# correct slot by deserialising.
|
||||
def test_deserializeMoveInstanceContainer(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot.
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
assert extruder_stack.quality.getId() == "empty"
|
||||
assert extruder_stack.material.getId() != "empty"
|
||||
|
||||
## Tests whether a definition container in the wrong spot is moved into the
|
||||
# correct spot by deserialising.
|
||||
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
|
||||
def test_deserializeMoveDefinitionContainer(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
assert extruder_stack.material.getId() == "empty"
|
||||
assert extruder_stack.definition.getId() != "empty"
|
||||
|
||||
UM.Settings.ContainerStack._containerRegistry = None
|
||||
|
||||
## Tests whether getProperty properly applies the stack-like behaviour on its
|
||||
# containers.
|
||||
def test_getPropertyFallThrough(extruder_stack):
|
||||
CuraApplication.getInstance() # To ensure that we have the right Application
|
||||
#A few instance container mocks to put in the stack.
|
||||
mock_layer_heights = {} #For each container type, a mock container that defines layer height to something unique.
|
||||
mock_no_settings = {} #For each container type, a mock container that has no settings at all.
|
||||
container_indices = cura.Settings.CuraContainerStack._ContainerIndexes #Cache.
|
||||
for type_id, type_name in container_indices.IndexTypeMap.items():
|
||||
container = unittest.mock.MagicMock()
|
||||
container.getProperty = lambda key, property, type_id = type_id: type_id if (key == "layer_height" and property == "value") else None #Returns the container type ID as layer height, in order to identify it.
|
||||
container.hasProperty = lambda key, property: key == "layer_height"
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = type_name)
|
||||
mock_layer_heights[type_id] = container
|
||||
|
||||
container = unittest.mock.MagicMock()
|
||||
container.getProperty = unittest.mock.MagicMock(return_value = None) #Has no settings at all.
|
||||
container.hasProperty = unittest.mock.MagicMock(return_value = False)
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = type_name)
|
||||
mock_no_settings[type_id] = container
|
||||
|
||||
extruder_stack.userChanges = mock_no_settings[container_indices.UserChanges]
|
||||
extruder_stack.qualityChanges = mock_no_settings[container_indices.QualityChanges]
|
||||
extruder_stack.quality = mock_no_settings[container_indices.Quality]
|
||||
extruder_stack.material = mock_no_settings[container_indices.Material]
|
||||
extruder_stack.variant = mock_no_settings[container_indices.Variant]
|
||||
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock): #To guard against the type checking.
|
||||
extruder_stack.definition = mock_layer_heights[container_indices.Definition] #There's a layer height in here!
|
||||
extruder_stack.setNextStack(unittest.mock.MagicMock())
|
||||
|
||||
assert extruder_stack.getProperty("layer_height", "value") == container_indices.Definition
|
||||
extruder_stack.variant = mock_layer_heights[container_indices.Variant]
|
||||
assert extruder_stack.getProperty("layer_height", "value") == container_indices.Variant
|
||||
extruder_stack.material = mock_layer_heights[container_indices.Material]
|
||||
assert extruder_stack.getProperty("layer_height", "value") == container_indices.Material
|
||||
extruder_stack.quality = mock_layer_heights[container_indices.Quality]
|
||||
assert extruder_stack.getProperty("layer_height", "value") == container_indices.Quality
|
||||
extruder_stack.qualityChanges = mock_layer_heights[container_indices.QualityChanges]
|
||||
assert extruder_stack.getProperty("layer_height", "value") == container_indices.QualityChanges
|
||||
extruder_stack.userChanges = mock_layer_heights[container_indices.UserChanges]
|
||||
assert extruder_stack.getProperty("layer_height", "value") == container_indices.UserChanges
|
||||
|
||||
## Tests whether inserting a container is properly forbidden.
|
||||
def test_insertContainer(extruder_stack):
|
||||
with pytest.raises(InvalidOperationError):
|
||||
extruder_stack.insertContainer(0, unittest.mock.MagicMock())
|
||||
|
||||
## Tests whether removing a container is properly forbidden.
|
||||
def test_removeContainer(extruder_stack):
|
||||
with pytest.raises(InvalidOperationError):
|
||||
extruder_stack.removeContainer(unittest.mock.MagicMock())
|
||||
|
||||
## Tests setting definitions by specifying an ID of a definition that exists.
|
||||
def test_setDefinitionByIdExists(extruder_stack, container_registry):
|
||||
container_registry.return_value = DefinitionContainer(container_id = "some_definition")
|
||||
extruder_stack.setDefinitionById("some_definition")
|
||||
assert extruder_stack.definition.getId() == "some_definition"
|
||||
|
||||
## Tests setting definitions by specifying an ID of a definition that doesn't
|
||||
# exist.
|
||||
def test_setDefinitionByIdDoesntExist(extruder_stack):
|
||||
with pytest.raises(InvalidContainerError):
|
||||
extruder_stack.setDefinitionById("some_definition") #Container registry is empty now.
|
||||
|
||||
## Tests setting materials by specifying an ID of a material that exists.
|
||||
def test_setMaterialByIdExists(extruder_stack, container_registry):
|
||||
container_registry.return_value = getInstanceContainer(container_type = "material")
|
||||
extruder_stack.setMaterialById("InstanceContainer")
|
||||
assert extruder_stack.material.getId() == "InstanceContainer"
|
||||
|
||||
## Tests setting materials by specifying an ID of a material that doesn't
|
||||
# exist.
|
||||
def test_setMaterialByIdDoesntExist(extruder_stack):
|
||||
with pytest.raises(InvalidContainerError):
|
||||
extruder_stack.setMaterialById("some_material") #Container registry is empty now.
|
||||
|
||||
## Tests setting properties directly on the extruder stack.
|
||||
@pytest.mark.parametrize("key, property, value", [
|
||||
("layer_height", "value", 0.1337),
|
||||
("foo", "value", 100),
|
||||
("support_enabled", "value", True),
|
||||
("layer_height", "default_value", 0.1337),
|
||||
("layer_height", "is_bright_pink", "of course")
|
||||
])
|
||||
def test_setPropertyUser(key, property, value, extruder_stack):
|
||||
user_changes = unittest.mock.MagicMock()
|
||||
user_changes.getMetaDataEntry = unittest.mock.MagicMock(return_value = "user")
|
||||
extruder_stack.userChanges = user_changes
|
||||
|
||||
extruder_stack.setProperty(key, property, value) #The actual test.
|
||||
|
||||
extruder_stack.userChanges.setProperty.assert_called_once_with(key, property, value) #Make sure that the user container gets a setProperty call.
|
||||
|
||||
## Tests setting properties on specific containers on the global stack.
|
||||
@pytest.mark.parametrize("target_container, stack_variable", [
|
||||
("user", "userChanges"),
|
||||
("quality_changes", "qualityChanges"),
|
||||
("quality", "quality"),
|
||||
("material", "material"),
|
||||
("variant", "variant")
|
||||
])
|
||||
def test_setPropertyOtherContainers(target_container, stack_variable, extruder_stack):
|
||||
#Other parameters that don't need to be varied.
|
||||
key = "layer_height"
|
||||
property = "value"
|
||||
value = 0.1337
|
||||
#A mock container in the right spot.
|
||||
container = unittest.mock.MagicMock()
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = target_container)
|
||||
setattr(extruder_stack, stack_variable, container) #For instance, set global_stack.qualityChanges = container.
|
||||
|
||||
extruder_stack.setProperty(key, property, value, target_container = target_container) #The actual test.
|
||||
|
||||
getattr(extruder_stack, stack_variable).setProperty.assert_called_once_with(key, property, value) #Make sure that the proper container gets a setProperty call.
|
||||
|
||||
## Tests setting qualities by specifying an ID of a quality that exists.
|
||||
def test_setQualityByIdExists(extruder_stack, container_registry):
|
||||
container_registry.return_value = getInstanceContainer(container_type = "quality")
|
||||
extruder_stack.setQualityById("InstanceContainer")
|
||||
assert extruder_stack.quality.getId() == "InstanceContainer"
|
||||
|
||||
## Tests setting qualities by specifying an ID of a quality that doesn't exist.
|
||||
def test_setQualityByIdDoesntExist(extruder_stack):
|
||||
with pytest.raises(InvalidContainerError):
|
||||
extruder_stack.setQualityById("some_quality") #Container registry is empty now.
|
||||
|
||||
## Tests setting quality changes by specifying an ID of a quality change that
|
||||
# exists.
|
||||
def test_setQualityChangesByIdExists(extruder_stack, container_registry):
|
||||
container_registry.return_value = getInstanceContainer(container_type = "quality_changes")
|
||||
extruder_stack.setQualityChangesById("InstanceContainer")
|
||||
assert extruder_stack.qualityChanges.getId() == "InstanceContainer"
|
||||
|
||||
## Tests setting quality changes by specifying an ID of a quality change that
|
||||
# doesn't exist.
|
||||
def test_setQualityChangesByIdDoesntExist(extruder_stack):
|
||||
with pytest.raises(InvalidContainerError):
|
||||
extruder_stack.setQualityChangesById("some_quality_changes") #Container registry is empty now.
|
||||
|
||||
## Tests setting variants by specifying an ID of a variant that exists.
|
||||
def test_setVariantByIdExists(extruder_stack, container_registry):
|
||||
container_registry.return_value = getInstanceContainer(container_type = "variant")
|
||||
extruder_stack.setVariantById("InstanceContainer")
|
||||
assert extruder_stack.variant.getId() == "InstanceContainer"
|
||||
|
||||
## Tests setting variants by specifying an ID of a variant that doesn't exist.
|
||||
def test_setVariantByIdDoesntExist(extruder_stack):
|
||||
with pytest.raises(InvalidContainerError):
|
||||
extruder_stack.setVariantById("some_variant") #Container registry is empty now.
|
558
tests/Settings/TestGlobalStack.py
Normal file
558
tests/Settings/TestGlobalStack.py
Normal file
|
@ -0,0 +1,558 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import pytest #This module contains unit tests.
|
||||
import unittest.mock #To monkeypatch some mocks in place of dependencies.
|
||||
|
||||
import cura.Settings.GlobalStack #The module we're testing.
|
||||
import cura.Settings.CuraContainerStack #To get the list of container types.
|
||||
from cura.Settings.Exceptions import TooManyExtrudersError, InvalidContainerError, InvalidOperationError #To test raising these errors.
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer #To test against the class DefinitionContainer.
|
||||
from UM.Settings.InstanceContainer import InstanceContainer #To test against the class InstanceContainer.
|
||||
from UM.Settings.SettingInstance import InstanceState
|
||||
import UM.Settings.ContainerRegistry
|
||||
import UM.Settings.ContainerStack
|
||||
|
||||
## Fake container registry that always provides all containers you ask of.
|
||||
@pytest.yield_fixture()
|
||||
def container_registry():
|
||||
registry = unittest.mock.MagicMock()
|
||||
registry.return_value = unittest.mock.NonCallableMagicMock()
|
||||
registry.findInstanceContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
|
||||
registry.findDefinitionContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
|
||||
|
||||
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = registry
|
||||
UM.Settings.ContainerStack._containerRegistry = registry
|
||||
|
||||
yield registry
|
||||
|
||||
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = None
|
||||
UM.Settings.ContainerStack._containerRegistry = None
|
||||
|
||||
#An empty global stack to test with.
|
||||
@pytest.fixture()
|
||||
def global_stack() -> cura.Settings.GlobalStack.GlobalStack:
|
||||
return cura.Settings.GlobalStack.GlobalStack("TestStack")
|
||||
|
||||
## Gets an instance container with a specified container type.
|
||||
#
|
||||
# \param container_type The type metadata for the instance container.
|
||||
# \return An instance container instance.
|
||||
def getInstanceContainer(container_type) -> InstanceContainer:
|
||||
container = InstanceContainer(container_id = "InstanceContainer")
|
||||
container.addMetaDataEntry("type", container_type)
|
||||
return container
|
||||
|
||||
class DefinitionContainerSubClass(DefinitionContainer):
|
||||
def __init__(self):
|
||||
super().__init__(container_id = "SubDefinitionContainer")
|
||||
|
||||
class InstanceContainerSubClass(InstanceContainer):
|
||||
def __init__(self, container_type):
|
||||
super().__init__(container_id = "SubInstanceContainer")
|
||||
self.addMetaDataEntry("type", container_type)
|
||||
|
||||
#############################START OF TEST CASES################################
|
||||
|
||||
## Tests whether adding a container is properly forbidden.
|
||||
def test_addContainer(global_stack):
|
||||
with pytest.raises(InvalidOperationError):
|
||||
global_stack.addContainer(unittest.mock.MagicMock())
|
||||
|
||||
## Tests adding extruders to the global stack.
|
||||
def test_addExtruder(global_stack):
|
||||
mock_definition = unittest.mock.MagicMock()
|
||||
mock_definition.getProperty = lambda key, property: 2 if key == "machine_extruder_count" and property == "value" else None
|
||||
|
||||
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock):
|
||||
global_stack.definition = mock_definition
|
||||
|
||||
assert len(global_stack.extruders) == 0
|
||||
first_extruder = unittest.mock.MagicMock()
|
||||
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock):
|
||||
global_stack.addExtruder(first_extruder)
|
||||
assert len(global_stack.extruders) == 1
|
||||
assert global_stack.extruders[0] == first_extruder
|
||||
second_extruder = unittest.mock.MagicMock()
|
||||
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock):
|
||||
global_stack.addExtruder(second_extruder)
|
||||
assert len(global_stack.extruders) == 2
|
||||
assert global_stack.extruders[1] == second_extruder
|
||||
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock):
|
||||
with pytest.raises(TooManyExtrudersError): #Should be limited to 2 extruders because of machine_extruder_count.
|
||||
global_stack.addExtruder(unittest.mock.MagicMock())
|
||||
assert len(global_stack.extruders) == 2 #Didn't add the faulty extruder.
|
||||
|
||||
#Tests setting user changes profiles to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong container type"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
def test_constrainUserChangesInvalid(container, global_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
global_stack.userChanges = container
|
||||
|
||||
#Tests setting user changes profiles.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "user"),
|
||||
InstanceContainerSubClass(container_type = "user")
|
||||
])
|
||||
def test_constrainUserChangesValid(container, global_stack):
|
||||
global_stack.userChanges = container #Should not give an error.
|
||||
|
||||
#Tests setting quality changes profiles to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong container type"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
def test_constrainQualityChangesInvalid(container, global_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
global_stack.qualityChanges = container
|
||||
|
||||
#Test setting quality changes profiles.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "quality_changes"),
|
||||
InstanceContainerSubClass(container_type = "quality_changes")
|
||||
])
|
||||
def test_constrainQualityChangesValid(container, global_stack):
|
||||
global_stack.qualityChanges = container #Should not give an error.
|
||||
|
||||
#Tests setting quality profiles to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong container type"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
def test_constrainQualityInvalid(container, global_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
global_stack.quality = container
|
||||
|
||||
#Test setting quality profiles.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "quality"),
|
||||
InstanceContainerSubClass(container_type = "quality")
|
||||
])
|
||||
def test_constrainQualityValid(container, global_stack):
|
||||
global_stack.quality = container #Should not give an error.
|
||||
|
||||
#Tests setting materials to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong container type"),
|
||||
getInstanceContainer(container_type = "quality"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
def test_constrainMaterialInvalid(container, global_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
global_stack.material = container
|
||||
|
||||
#Test setting materials.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "material"),
|
||||
InstanceContainerSubClass(container_type = "material")
|
||||
])
|
||||
def test_constrainMaterialValid(container, global_stack):
|
||||
global_stack.material = container #Should not give an error.
|
||||
|
||||
#Tests setting variants to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong container type"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
def test_constrainVariantInvalid(container, global_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
global_stack.variant = container
|
||||
|
||||
#Test setting variants.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "variant"),
|
||||
InstanceContainerSubClass(container_type = "variant")
|
||||
])
|
||||
def test_constrainVariantValid(container, global_stack):
|
||||
global_stack.variant = container #Should not give an error.
|
||||
|
||||
#Tests setting definition changes profiles to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong container type"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
def test_constrainDefinitionChangesInvalid(container, global_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
global_stack.definitionChanges = container
|
||||
|
||||
#Test setting definition changes profiles.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "definition_changes"),
|
||||
InstanceContainerSubClass(container_type = "definition_changes")
|
||||
])
|
||||
def test_constrainDefinitionChangesValid(container, global_stack):
|
||||
global_stack.definitionChanges = container #Should not give an error.
|
||||
|
||||
#Tests setting definitions to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong class"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong class.
|
||||
])
|
||||
def test_constrainVariantInvalid(container, global_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
global_stack.definition = container
|
||||
|
||||
#Test setting definitions.
|
||||
@pytest.mark.parametrize("container", [
|
||||
DefinitionContainer(container_id = "DefinitionContainer"),
|
||||
DefinitionContainerSubClass()
|
||||
])
|
||||
def test_constrainDefinitionValid(container, global_stack):
|
||||
global_stack.definition = container #Should not give an error.
|
||||
|
||||
## Tests whether deserialising completes the missing containers with empty
|
||||
# ones.
|
||||
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
|
||||
def test_deserializeCompletesEmptyContainers(global_stack: cura.Settings.GlobalStack):
|
||||
global_stack._containers = [DefinitionContainer(container_id = "definition")] #Set the internal state of this stack manually.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
global_stack.deserialize("")
|
||||
|
||||
assert len(global_stack.getContainers()) == len(cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap) #Needs a slot for every type.
|
||||
for container_type_index in cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap:
|
||||
if container_type_index == cura.Settings.CuraContainerStack._ContainerIndexes.Definition: #We're not checking the definition.
|
||||
continue
|
||||
assert global_stack.getContainer(container_type_index).getId() == "empty" #All others need to be empty.
|
||||
|
||||
## Tests whether an instance container with the wrong type gets removed when
|
||||
# deserialising.
|
||||
def test_deserializeRemovesWrongInstanceContainer(global_stack):
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type")
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
global_stack.deserialize("")
|
||||
|
||||
assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty.
|
||||
|
||||
## Tests whether a container with the wrong class gets removed when
|
||||
# deserialising.
|
||||
def test_deserializeRemovesWrongContainerClass(global_stack):
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class")
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
global_stack.deserialize("")
|
||||
|
||||
assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty.
|
||||
|
||||
## Tests whether an instance container in the definition spot results in an
|
||||
# error.
|
||||
def test_deserializeWrongDefinitionClass(global_stack):
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container.
|
||||
global_stack.deserialize("")
|
||||
|
||||
## Tests whether an instance container with the wrong type is moved into the
|
||||
# correct slot by deserialising.
|
||||
def test_deserializeMoveInstanceContainer(global_stack):
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot.
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
global_stack.deserialize("")
|
||||
|
||||
assert global_stack.quality.getId() == "empty"
|
||||
assert global_stack.material.getId() != "empty"
|
||||
|
||||
## Tests whether a definition container in the wrong spot is moved into the
|
||||
# correct spot by deserialising.
|
||||
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
|
||||
def test_deserializeMoveDefinitionContainer(global_stack):
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
global_stack.deserialize("")
|
||||
|
||||
assert global_stack.material.getId() == "empty"
|
||||
assert global_stack.definition.getId() != "empty"
|
||||
|
||||
UM.Settings.ContainerStack._containerRegistry = None
|
||||
|
||||
## Tests whether getProperty properly applies the stack-like behaviour on its
|
||||
# containers.
|
||||
def test_getPropertyFallThrough(global_stack):
|
||||
#A few instance container mocks to put in the stack.
|
||||
mock_layer_heights = {} #For each container type, a mock container that defines layer height to something unique.
|
||||
mock_no_settings = {} #For each container type, a mock container that has no settings at all.
|
||||
container_indexes = cura.Settings.CuraContainerStack._ContainerIndexes #Cache.
|
||||
for type_id, type_name in container_indexes.IndexTypeMap.items():
|
||||
container = unittest.mock.MagicMock()
|
||||
container.getProperty = lambda key, property, type_id = type_id: type_id if (key == "layer_height" and property == "value") else None #Returns the container type ID as layer height, in order to identify it.
|
||||
container.hasProperty = lambda key, property: key == "layer_height"
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = type_name)
|
||||
mock_layer_heights[type_id] = container
|
||||
|
||||
container = unittest.mock.MagicMock()
|
||||
container.getProperty = unittest.mock.MagicMock(return_value = None) #Has no settings at all.
|
||||
container.hasProperty = unittest.mock.MagicMock(return_value = False)
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = type_name)
|
||||
mock_no_settings[type_id] = container
|
||||
|
||||
global_stack.userChanges = mock_no_settings[container_indexes.UserChanges]
|
||||
global_stack.qualityChanges = mock_no_settings[container_indexes.QualityChanges]
|
||||
global_stack.quality = mock_no_settings[container_indexes.Quality]
|
||||
global_stack.material = mock_no_settings[container_indexes.Material]
|
||||
global_stack.variant = mock_no_settings[container_indexes.Variant]
|
||||
global_stack.definitionChanges = mock_no_settings[container_indexes.DefinitionChanges]
|
||||
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock): #To guard against the type checking.
|
||||
global_stack.definition = mock_layer_heights[container_indexes.Definition] #There's a layer height in here!
|
||||
|
||||
assert global_stack.getProperty("layer_height", "value") == container_indexes.Definition
|
||||
global_stack.definitionChanges = mock_layer_heights[container_indexes.DefinitionChanges]
|
||||
assert global_stack.getProperty("layer_height", "value") == container_indexes.DefinitionChanges
|
||||
global_stack.variant = mock_layer_heights[container_indexes.Variant]
|
||||
assert global_stack.getProperty("layer_height", "value") == container_indexes.Variant
|
||||
global_stack.material = mock_layer_heights[container_indexes.Material]
|
||||
assert global_stack.getProperty("layer_height", "value") == container_indexes.Material
|
||||
global_stack.quality = mock_layer_heights[container_indexes.Quality]
|
||||
assert global_stack.getProperty("layer_height", "value") == container_indexes.Quality
|
||||
global_stack.qualityChanges = mock_layer_heights[container_indexes.QualityChanges]
|
||||
assert global_stack.getProperty("layer_height", "value") == container_indexes.QualityChanges
|
||||
global_stack.userChanges = mock_layer_heights[container_indexes.UserChanges]
|
||||
assert global_stack.getProperty("layer_height", "value") == container_indexes.UserChanges
|
||||
|
||||
## In definitions, test whether having no resolve allows us to find the value.
|
||||
def test_getPropertyNoResolveInDefinition(global_stack):
|
||||
value = unittest.mock.MagicMock() #Just sets the value for bed temperature.
|
||||
value.getProperty = lambda key, property: 10 if (key == "material_bed_temperature" and property == "value") else None
|
||||
|
||||
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock): #To guard against the type checking.
|
||||
global_stack.definition = value
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 10 #No resolve, so fall through to value.
|
||||
|
||||
## In definitions, when the value is asked and there is a resolve function, it
|
||||
# must get the resolve first.
|
||||
def test_getPropertyResolveInDefinition(global_stack):
|
||||
resolve_and_value = unittest.mock.MagicMock() #Sets the resolve and value for bed temperature.
|
||||
resolve_and_value.getProperty = lambda key, property: (7.5 if property == "resolve" else 5) if (key == "material_bed_temperature" and property in ("resolve", "value")) else None #7.5 resolve, 5 value.
|
||||
|
||||
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock): #To guard against the type checking.
|
||||
global_stack.definition = resolve_and_value
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 7.5 #Resolve wins in the definition.
|
||||
|
||||
## In instance containers, when the value is asked and there is a resolve
|
||||
# function, it must get the value first.
|
||||
def test_getPropertyResolveInInstance(global_stack):
|
||||
container_indices = cura.Settings.CuraContainerStack._ContainerIndexes
|
||||
instance_containers = {}
|
||||
for container_type in container_indices.IndexTypeMap:
|
||||
instance_containers[container_type] = unittest.mock.MagicMock() #Sets the resolve and value for bed temperature.
|
||||
instance_containers[container_type].getProperty = lambda key, property: (7.5 if property == "resolve" else (InstanceState.User if property == "state" else 5)) if (key == "material_bed_temperature") else None #7.5 resolve, 5 value.
|
||||
instance_containers[container_type].getMetaDataEntry = unittest.mock.MagicMock(return_value = container_indices.IndexTypeMap[container_type]) #Make queries for the type return the desired type.
|
||||
instance_containers[container_indices.Definition].getProperty = lambda key, property: 10 if (key == "material_bed_temperature" and property == "value") else None #Definition only has value.
|
||||
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock): #To guard against the type checking.
|
||||
global_stack.definition = instance_containers[container_indices.Definition] #Stack must have a definition.
|
||||
|
||||
#For all instance container slots, the value reigns over resolve.
|
||||
global_stack.definitionChanges = instance_containers[container_indices.DefinitionChanges]
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 5
|
||||
global_stack.variant = instance_containers[container_indices.Variant]
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 5
|
||||
global_stack.material = instance_containers[container_indices.Material]
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 5
|
||||
global_stack.quality = instance_containers[container_indices.Quality]
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 5
|
||||
global_stack.qualityChanges = instance_containers[container_indices.QualityChanges]
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 5
|
||||
global_stack.userChanges = instance_containers[container_indices.UserChanges]
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 5
|
||||
|
||||
## Tests whether the value in instances gets evaluated before the resolve in
|
||||
# definitions.
|
||||
def test_getPropertyInstancesBeforeResolve(global_stack):
|
||||
value = unittest.mock.MagicMock() #Sets just the value.
|
||||
value.getProperty = lambda key, property: (10 if property == "value" else InstanceState.User) if key == "material_bed_temperature" else None
|
||||
value.getMetaDataEntry = unittest.mock.MagicMock(return_value = "quality")
|
||||
resolve = unittest.mock.MagicMock() #Sets just the resolve.
|
||||
resolve.getProperty = lambda key, property: 7.5 if (key == "material_bed_temperature" and property == "resolve") else None
|
||||
|
||||
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock): #To guard against the type checking.
|
||||
global_stack.definition = resolve
|
||||
global_stack.quality = value
|
||||
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 10
|
||||
|
||||
## Tests whether the hasUserValue returns true for settings that are changed in
|
||||
# the user-changes container.
|
||||
def test_hasUserValueUserChanges(global_stack):
|
||||
container = unittest.mock.MagicMock()
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "user")
|
||||
container.hasProperty = lambda key, property: key == "layer_height" #Only have the layer_height property set.
|
||||
global_stack.userChanges = container
|
||||
|
||||
assert global_stack.hasUserValue("layer_height")
|
||||
assert not global_stack.hasUserValue("infill_sparse_density")
|
||||
assert not global_stack.hasUserValue("")
|
||||
|
||||
## Tests whether the hasUserValue returns true for settings that are changed in
|
||||
# the quality-changes container.
|
||||
def test_hasUserValueQualityChanges(global_stack):
|
||||
container = unittest.mock.MagicMock()
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "quality_changes")
|
||||
container.hasProperty = lambda key, property: key == "layer_height" #Only have the layer_height property set.
|
||||
global_stack.qualityChanges = container
|
||||
|
||||
assert global_stack.hasUserValue("layer_height")
|
||||
assert not global_stack.hasUserValue("infill_sparse_density")
|
||||
assert not global_stack.hasUserValue("")
|
||||
|
||||
## Tests whether a container in some other place on the stack is correctly not
|
||||
# recognised as user value.
|
||||
def test_hasNoUserValue(global_stack):
|
||||
container = unittest.mock.MagicMock()
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "quality")
|
||||
container.hasProperty = lambda key, property: key == "layer_height" #Only have the layer_height property set.
|
||||
global_stack.quality = container
|
||||
|
||||
assert not global_stack.hasUserValue("layer_height") #However this container is quality, so it's not a user value.
|
||||
|
||||
## Tests whether inserting a container is properly forbidden.
|
||||
def test_insertContainer(global_stack):
|
||||
with pytest.raises(InvalidOperationError):
|
||||
global_stack.insertContainer(0, unittest.mock.MagicMock())
|
||||
|
||||
## Tests whether removing a container is properly forbidden.
|
||||
def test_removeContainer(global_stack):
|
||||
with pytest.raises(InvalidOperationError):
|
||||
global_stack.removeContainer(unittest.mock.MagicMock())
|
||||
|
||||
## Tests setting definitions by specifying an ID of a definition that exists.
|
||||
def test_setDefinitionByIdExists(global_stack, container_registry):
|
||||
container_registry.return_value = DefinitionContainer(container_id = "some_definition")
|
||||
global_stack.setDefinitionById("some_definition")
|
||||
assert global_stack.definition.getId() == "some_definition"
|
||||
|
||||
## Tests setting definitions by specifying an ID of a definition that doesn't
|
||||
# exist.
|
||||
def test_setDefinitionByIdDoesntExist(global_stack):
|
||||
with pytest.raises(InvalidContainerError):
|
||||
global_stack.setDefinitionById("some_definition") #Container registry is empty now.
|
||||
|
||||
## Tests setting definition changes by specifying an ID of a container that
|
||||
# exists.
|
||||
def test_setDefinitionChangesByIdExists(global_stack, container_registry):
|
||||
container_registry.return_value = getInstanceContainer(container_type = "definition_changes")
|
||||
global_stack.setDefinitionChangesById("InstanceContainer")
|
||||
assert global_stack.definitionChanges.getId() == "InstanceContainer"
|
||||
|
||||
## Tests setting definition changes by specifying an ID of a container that
|
||||
# doesn't exist.
|
||||
def test_setDefinitionChangesByIdDoesntExist(global_stack):
|
||||
with pytest.raises(InvalidContainerError):
|
||||
global_stack.setDefinitionChangesById("some_definition_changes") #Container registry is empty now.
|
||||
|
||||
## Tests setting materials by specifying an ID of a material that exists.
|
||||
def test_setMaterialByIdExists(global_stack, container_registry):
|
||||
container_registry.return_value = getInstanceContainer(container_type = "material")
|
||||
global_stack.setMaterialById("InstanceContainer")
|
||||
assert global_stack.material.getId() == "InstanceContainer"
|
||||
|
||||
## Tests setting materials by specifying an ID of a material that doesn't
|
||||
# exist.
|
||||
def test_setMaterialByIdDoesntExist(global_stack):
|
||||
with pytest.raises(InvalidContainerError):
|
||||
global_stack.setMaterialById("some_material") #Container registry is empty now.
|
||||
|
||||
## Tests whether changing the next stack is properly forbidden.
|
||||
def test_setNextStack(global_stack):
|
||||
with pytest.raises(InvalidOperationError):
|
||||
global_stack.setNextStack(unittest.mock.MagicMock())
|
||||
|
||||
## Tests setting properties directly on the global stack.
|
||||
@pytest.mark.parametrize("key, property, value", [
|
||||
("layer_height", "value", 0.1337),
|
||||
("foo", "value", 100),
|
||||
("support_enabled", "value", True),
|
||||
("layer_height", "default_value", 0.1337),
|
||||
("layer_height", "is_bright_pink", "of course")
|
||||
])
|
||||
def test_setPropertyUser(key, property, value, global_stack):
|
||||
user_changes = unittest.mock.MagicMock()
|
||||
user_changes.getMetaDataEntry = unittest.mock.MagicMock(return_value = "user")
|
||||
global_stack.userChanges = user_changes
|
||||
|
||||
global_stack.setProperty(key, property, value) #The actual test.
|
||||
|
||||
global_stack.userChanges.setProperty.assert_called_once_with(key, property, value) #Make sure that the user container gets a setProperty call.
|
||||
|
||||
## Tests setting properties on specific containers on the global stack.
|
||||
@pytest.mark.parametrize("target_container, stack_variable", [
|
||||
("user", "userChanges"),
|
||||
("quality_changes", "qualityChanges"),
|
||||
("quality", "quality"),
|
||||
("material", "material"),
|
||||
("variant", "variant"),
|
||||
("definition_changes", "definitionChanges")
|
||||
])
|
||||
def test_setPropertyOtherContainers(target_container, stack_variable, global_stack):
|
||||
#Other parameters that don't need to be varied.
|
||||
key = "layer_height"
|
||||
property = "value"
|
||||
value = 0.1337
|
||||
#A mock container in the right spot.
|
||||
container = unittest.mock.MagicMock()
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = target_container)
|
||||
setattr(global_stack, stack_variable, container) #For instance, set global_stack.qualityChanges = container.
|
||||
|
||||
global_stack.setProperty(key, property, value, target_container = target_container) #The actual test.
|
||||
|
||||
getattr(global_stack, stack_variable).setProperty.assert_called_once_with(key, property, value) #Make sure that the proper container gets a setProperty call.
|
||||
|
||||
## Tests setting qualities by specifying an ID of a quality that exists.
|
||||
def test_setQualityByIdExists(global_stack, container_registry):
|
||||
container_registry.return_value = getInstanceContainer(container_type = "quality")
|
||||
global_stack.setQualityById("InstanceContainer")
|
||||
assert global_stack.quality.getId() == "InstanceContainer"
|
||||
|
||||
## Tests setting qualities by specifying an ID of a quality that doesn't exist.
|
||||
def test_setQualityByIdDoesntExist(global_stack):
|
||||
with pytest.raises(InvalidContainerError):
|
||||
global_stack.setQualityById("some_quality") #Container registry is empty now.
|
||||
|
||||
## Tests setting quality changes by specifying an ID of a quality change that
|
||||
# exists.
|
||||
def test_setQualityChangesByIdExists(global_stack, container_registry):
|
||||
container_registry.return_value = getInstanceContainer(container_type = "quality_changes")
|
||||
global_stack.setQualityChangesById("InstanceContainer")
|
||||
assert global_stack.qualityChanges.getId() == "InstanceContainer"
|
||||
|
||||
## Tests setting quality changes by specifying an ID of a quality change that
|
||||
# doesn't exist.
|
||||
def test_setQualityChangesByIdDoesntExist(global_stack):
|
||||
with pytest.raises(InvalidContainerError):
|
||||
global_stack.setQualityChangesById("some_quality_changes") #Container registry is empty now.
|
||||
|
||||
## Tests setting variants by specifying an ID of a variant that exists.
|
||||
def test_setVariantByIdExists(global_stack, container_registry):
|
||||
container_registry.return_value = getInstanceContainer(container_type = "variant")
|
||||
global_stack.setVariantById("InstanceContainer")
|
||||
assert global_stack.variant.getId() == "InstanceContainer"
|
||||
|
||||
## Tests setting variants by specifying an ID of a variant that doesn't exist.
|
||||
def test_setVariantByIdDoesntExist(global_stack):
|
||||
with pytest.raises(InvalidContainerError):
|
||||
global_stack.setVariantById("some_variant") #Container registry is empty now.
|
||||
|
||||
## Smoke test for findDefaultVariant
|
||||
def test_smoke_findDefaultVariant(global_stack):
|
||||
global_stack.findDefaultVariant()
|
||||
|
||||
## Smoke test for findDefaultMaterial
|
||||
def test_smoke_findDefaultMaterial(global_stack):
|
||||
global_stack.findDefaultMaterial()
|
||||
|
||||
## Smoke test for findDefaultQuality
|
||||
def test_smoke_findDefaultQuality(global_stack):
|
||||
global_stack.findDefaultQuality()
|
12
tests/Settings/stacks/Complete.extruder.cfg
Normal file
12
tests/Settings/stacks/Complete.extruder.cfg
Normal file
|
@ -0,0 +1,12 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Complete
|
||||
id = Complete
|
||||
|
||||
[containers]
|
||||
0 = some_user_changes
|
||||
1 = some_quality_changes
|
||||
2 = some_quality
|
||||
3 = some_material
|
||||
4 = some_variant
|
||||
5 = some_definition
|
13
tests/Settings/stacks/Complete.global.cfg
Normal file
13
tests/Settings/stacks/Complete.global.cfg
Normal file
|
@ -0,0 +1,13 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Complete
|
||||
id = Complete
|
||||
|
||||
[containers]
|
||||
0 = some_user_changes
|
||||
1 = some_quality_changes
|
||||
2 = some_quality
|
||||
3 = some_material
|
||||
4 = some_variant
|
||||
5 = some_definition_changes
|
||||
6 = some_definition
|
11
tests/Settings/stacks/ExtruderLegacy.stack.cfg
Normal file
11
tests/Settings/stacks/ExtruderLegacy.stack.cfg
Normal file
|
@ -0,0 +1,11 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Legacy Extruder Stack
|
||||
id = ExtruderLegacy
|
||||
|
||||
[metadata]
|
||||
type = extruder_train
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
5 = some_definition
|
8
tests/Settings/stacks/Global.global.cfg
Normal file
8
tests/Settings/stacks/Global.global.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Global
|
||||
id = Global
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
6 = some_definition
|
11
tests/Settings/stacks/Global.stack.cfg
Normal file
11
tests/Settings/stacks/Global.stack.cfg
Normal file
|
@ -0,0 +1,11 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Global
|
||||
id = Global
|
||||
|
||||
[metadata]
|
||||
type = machine
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
6 = some_definition
|
8
tests/Settings/stacks/Left.extruder.cfg
Normal file
8
tests/Settings/stacks/Left.extruder.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Left
|
||||
id = Left
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
5 = some_definition
|
11
tests/Settings/stacks/MachineLegacy.stack.cfg
Normal file
11
tests/Settings/stacks/MachineLegacy.stack.cfg
Normal file
|
@ -0,0 +1,11 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Legacy Global Stack
|
||||
id = MachineLegacy
|
||||
|
||||
[metadata]
|
||||
type = machine
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
6 = some_definition
|
7
tests/Settings/stacks/OnlyDefinition.extruder.cfg
Normal file
7
tests/Settings/stacks/OnlyDefinition.extruder.cfg
Normal file
|
@ -0,0 +1,7 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Definition
|
||||
id = OnlyDefinition
|
||||
|
||||
[containers]
|
||||
5 = some_definition
|
7
tests/Settings/stacks/OnlyDefinition.global.cfg
Normal file
7
tests/Settings/stacks/OnlyDefinition.global.cfg
Normal file
|
@ -0,0 +1,7 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Definition
|
||||
id = OnlyDefinition
|
||||
|
||||
[containers]
|
||||
6 = some_definition
|
8
tests/Settings/stacks/OnlyDefinitionChanges.global.cfg
Normal file
8
tests/Settings/stacks/OnlyDefinitionChanges.global.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Definition Changes
|
||||
id = OnlyDefinitionChanges
|
||||
|
||||
[containers]
|
||||
5 = some_instance
|
||||
6 = some_definition
|
8
tests/Settings/stacks/OnlyMaterial.extruder.cfg
Normal file
8
tests/Settings/stacks/OnlyMaterial.extruder.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Material
|
||||
id = OnlyMaterial
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
5 = some_definition
|
8
tests/Settings/stacks/OnlyMaterial.global.cfg
Normal file
8
tests/Settings/stacks/OnlyMaterial.global.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Material
|
||||
id = OnlyMaterial
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
6 = some_definition
|
8
tests/Settings/stacks/OnlyQuality.extruder.cfg
Normal file
8
tests/Settings/stacks/OnlyQuality.extruder.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Quality
|
||||
id = OnlyQuality
|
||||
|
||||
[containers]
|
||||
2 = some_instance
|
||||
5 = some_definition
|
8
tests/Settings/stacks/OnlyQuality.global.cfg
Normal file
8
tests/Settings/stacks/OnlyQuality.global.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Quality
|
||||
id = OnlyQuality
|
||||
|
||||
[containers]
|
||||
2 = some_instance
|
||||
6 = some_definition
|
8
tests/Settings/stacks/OnlyQualityChanges.extruder.cfg
Normal file
8
tests/Settings/stacks/OnlyQualityChanges.extruder.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Quality Changes
|
||||
id = OnlyQualityChanges
|
||||
|
||||
[containers]
|
||||
1 = some_instance
|
||||
5 = some_definition
|
8
tests/Settings/stacks/OnlyQualityChanges.global.cfg
Normal file
8
tests/Settings/stacks/OnlyQualityChanges.global.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Quality Changes
|
||||
id = OnlyQualityChanges
|
||||
|
||||
[containers]
|
||||
1 = some_instance
|
||||
6 = some_definition
|
8
tests/Settings/stacks/OnlyUser.extruder.cfg
Normal file
8
tests/Settings/stacks/OnlyUser.extruder.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only User
|
||||
id = OnlyUser
|
||||
|
||||
[containers]
|
||||
0 = some_instance
|
||||
5 = some_definition
|
8
tests/Settings/stacks/OnlyUser.global.cfg
Normal file
8
tests/Settings/stacks/OnlyUser.global.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only User
|
||||
id = OnlyUser
|
||||
|
||||
[containers]
|
||||
0 = some_instance
|
||||
6 = some_definition
|
8
tests/Settings/stacks/OnlyVariant.extruder.cfg
Normal file
8
tests/Settings/stacks/OnlyVariant.extruder.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Variant
|
||||
id = OnlyVariant
|
||||
|
||||
[containers]
|
||||
4 = some_instance
|
||||
5 = some_definition
|
8
tests/Settings/stacks/OnlyVariant.global.cfg
Normal file
8
tests/Settings/stacks/OnlyVariant.global.cfg
Normal file
|
@ -0,0 +1,8 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Variant
|
||||
id = OnlyVariant
|
||||
|
||||
[containers]
|
||||
4 = some_instance
|
||||
6 = some_definition
|
Loading…
Add table
Add a link
Reference in a new issue