ExtruderStack.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. # Copyright (c) 2017 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from typing import Any, TYPE_CHECKING, Optional
  4. from UM.Decorators import override
  5. from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
  6. from UM.Settings.ContainerStack import ContainerStack
  7. from UM.Settings.ContainerRegistry import ContainerRegistry
  8. from UM.Settings.Interfaces import ContainerInterface, PropertyEvaluationContext
  9. from UM.Settings.SettingInstance import SettingInstance
  10. from . import Exceptions
  11. from .CuraContainerStack import CuraContainerStack
  12. from .ExtruderManager import ExtruderManager
  13. if TYPE_CHECKING:
  14. from cura.Settings.GlobalStack import GlobalStack
  15. _EXTRUDER_SPECIFIC_DEFINITION_CHANGES_SETTINGS = ["machine_nozzle_size",
  16. "material_diameter"]
  17. ## Represents an Extruder and its related containers.
  18. #
  19. #
  20. class ExtruderStack(CuraContainerStack):
  21. def __init__(self, container_id: str, *args, **kwargs):
  22. super().__init__(container_id, *args, **kwargs)
  23. self.addMetaDataEntry("type", "extruder_train") # For backward compatibility
  24. self.propertiesChanged.connect(self._onPropertiesChanged)
  25. ## Overridden from ContainerStack
  26. #
  27. # This will set the next stack and ensure that we register this stack as an extruder.
  28. @override(ContainerStack)
  29. def setNextStack(self, stack: ContainerStack) -> None:
  30. super().setNextStack(stack)
  31. stack.addExtruder(self)
  32. self.addMetaDataEntry("machine", stack.id)
  33. # For backward compatibility: Register the extruder with the Extruder Manager
  34. ExtruderManager.getInstance().registerExtruder(self, stack.id)
  35. # Now each machine will have at least one extruder stack. If this is the first extruder, the extruder-specific
  36. # settings such as nozzle size and material diameter should be moved from the machine's definition_changes to
  37. # the this extruder's definition_changes.
  38. #
  39. # We do this here because it is tooooo expansive to do it in the version upgrade: During the version upgrade,
  40. # when we are upgrading a definition_changes container file, there is NO guarantee that other files such as
  41. # machine an extruder stack files are upgraded before this, so we cannot read those files assuming they are in
  42. # the latest format.
  43. if self.getMetaDataEntry("position") == "0":
  44. for key in _EXTRUDER_SPECIFIC_DEFINITION_CHANGES_SETTINGS:
  45. setting_value = stack.definitionChanges.getProperty(key, "value")
  46. if setting_value is None:
  47. continue
  48. setting_definition = stack.getSettingDefinition(key)
  49. new_instance = SettingInstance(setting_definition, self.definitionChanges)
  50. new_instance.setProperty("value", setting_value)
  51. new_instance.resetState() # Ensure that the state is not seen as a user state.
  52. self.definitionChanges.addInstance(new_instance)
  53. self.definitionChanges.setDirty(True)
  54. stack.definitionChanges.removeInstance(key, postpone_emit = True)
  55. @override(ContainerStack)
  56. def getNextStack(self) -> Optional["GlobalStack"]:
  57. return super().getNextStack()
  58. @classmethod
  59. def getLoadingPriority(cls) -> int:
  60. return 3
  61. ## Overridden from ContainerStack
  62. #
  63. # It will perform a few extra checks when trying to get properties.
  64. #
  65. # The two extra checks it currently does is to ensure a next stack is set and to bypass
  66. # the extruder when the property is not settable per extruder.
  67. #
  68. # \throws Exceptions.NoGlobalStackError Raised when trying to get a property from an extruder without
  69. # having a next stack set.
  70. @override(ContainerStack)
  71. def getProperty(self, key: str, property_name: str, context: Optional[PropertyEvaluationContext] = None) -> Any:
  72. if not self._next_stack:
  73. raise Exceptions.NoGlobalStackError("Extruder {id} is missing the next stack!".format(id = self.id))
  74. if context is None:
  75. context = PropertyEvaluationContext()
  76. context.pushContainer(self)
  77. if not super().getProperty(key, "settable_per_extruder", context):
  78. result = self.getNextStack().getProperty(key, property_name, context)
  79. context.popContainer()
  80. return result
  81. limit_to_extruder = super().getProperty(key, "limit_to_extruder", context)
  82. if limit_to_extruder is not None:
  83. limit_to_extruder = str(limit_to_extruder)
  84. if (limit_to_extruder is not None and limit_to_extruder != "-1") and self.getMetaDataEntry("position") != str(limit_to_extruder):
  85. if str(limit_to_extruder) in self.getNextStack().extruders:
  86. result = self.getNextStack().extruders[str(limit_to_extruder)].getProperty(key, property_name, context)
  87. if result is not None:
  88. context.popContainer()
  89. return result
  90. result = super().getProperty(key, property_name, context)
  91. context.popContainer()
  92. return result
  93. @override(CuraContainerStack)
  94. def _getMachineDefinition(self) -> ContainerInterface:
  95. if not self.getNextStack():
  96. raise Exceptions.NoGlobalStackError("Extruder {id} is missing the next stack!".format(id = self.id))
  97. return self.getNextStack()._getMachineDefinition()
  98. @override(CuraContainerStack)
  99. def deserialize(self, contents: str, file_name: Optional[str] = None) -> None:
  100. super().deserialize(contents, file_name)
  101. stacks = ContainerRegistry.getInstance().findContainerStacks(id=self.getMetaDataEntry("machine", ""))
  102. if stacks:
  103. self.setNextStack(stacks[0])
  104. def _onPropertiesChanged(self, key, properties):
  105. # When there is a setting that is not settable per extruder that depends on a value from a setting that is,
  106. # we do not always get properly informed that we should re-evaluate the setting. So make sure to indicate
  107. # something changed for those settings.
  108. if not self.getNextStack():
  109. return #There are no global settings to depend on.
  110. definitions = self.getNextStack().definition.findDefinitions(key = key)
  111. if definitions:
  112. has_global_dependencies = False
  113. for relation in definitions[0].relations:
  114. if not getattr(relation.target, "settable_per_extruder", True):
  115. has_global_dependencies = True
  116. break
  117. if has_global_dependencies:
  118. self.getNextStack().propertiesChanged.emit(key, properties)
  119. def findDefaultVariant(self):
  120. # The default variant is defined in the machine stack and/or definition, so use the machine stack to find
  121. # the default variant.
  122. return self.getNextStack().findDefaultVariant()
  123. extruder_stack_mime = MimeType(
  124. name = "application/x-cura-extruderstack",
  125. comment = "Cura Extruder Stack",
  126. suffixes = ["extruder.cfg"]
  127. )
  128. MimeTypeDatabase.addMimeType(extruder_stack_mime)
  129. ContainerRegistry.addContainerTypeByName(ExtruderStack, "extruder_stack", extruder_stack_mime.name)