ExtruderStack.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. # Copyright (c) 2018 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from typing import Any, Dict, TYPE_CHECKING, Optional
  4. from PyQt5.QtCore import pyqtProperty, pyqtSignal
  5. from UM.Decorators import override
  6. from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
  7. from UM.Settings.ContainerStack import ContainerStack
  8. from UM.Settings.ContainerRegistry import ContainerRegistry
  9. from UM.Settings.Interfaces import ContainerInterface, PropertyEvaluationContext
  10. from UM.Util import parseBool
  11. import cura.CuraApplication
  12. from . import Exceptions
  13. from .CuraContainerStack import CuraContainerStack, _ContainerIndexes
  14. from .ExtruderManager import ExtruderManager
  15. if TYPE_CHECKING:
  16. from cura.Settings.GlobalStack import GlobalStack
  17. ## Represents an Extruder and its related containers.
  18. #
  19. #
  20. class ExtruderStack(CuraContainerStack):
  21. def __init__(self, container_id: str) -> None:
  22. super().__init__(container_id)
  23. self.setMetaDataEntry("type", "extruder_train") # For backward compatibility
  24. self.propertiesChanged.connect(self._onPropertiesChanged)
  25. enabledChanged = pyqtSignal()
  26. ## Overridden from ContainerStack
  27. #
  28. # This will set the next stack and ensure that we register this stack as an extruder.
  29. @override(ContainerStack)
  30. def setNextStack(self, stack: CuraContainerStack, connect_signals: bool = True) -> None:
  31. super().setNextStack(stack)
  32. stack.addExtruder(self)
  33. self.setMetaDataEntry("machine", stack.id)
  34. # For backward compatibility: Register the extruder with the Extruder Manager
  35. ExtruderManager.getInstance().registerExtruder(self, stack.id)
  36. @override(ContainerStack)
  37. def getNextStack(self) -> Optional["GlobalStack"]:
  38. return super().getNextStack()
  39. @pyqtProperty(int, constant = True)
  40. def position(self) -> int:
  41. return int(self.getMetaDataEntry("position"))
  42. def setEnabled(self, enabled: bool) -> None:
  43. if self.getMetaDataEntry("enabled", True) == enabled: # No change.
  44. return # Don't emit a signal then.
  45. self.setMetaDataEntry("enabled", str(enabled))
  46. self.enabledChanged.emit()
  47. @pyqtProperty(bool, notify = enabledChanged)
  48. def isEnabled(self) -> bool:
  49. return parseBool(self.getMetaDataEntry("enabled", "True"))
  50. @classmethod
  51. def getLoadingPriority(cls) -> int:
  52. return 3
  53. compatibleMaterialDiameterChanged = pyqtSignal()
  54. ## Return the filament diameter that the machine requires.
  55. #
  56. # If the machine has no requirement for the diameter, -1 is returned.
  57. # \return The filament diameter for the printer
  58. def getCompatibleMaterialDiameter(self) -> float:
  59. context = PropertyEvaluationContext(self)
  60. context.context["evaluate_from_container_index"] = _ContainerIndexes.Variant
  61. return float(self.getProperty("material_diameter", "value", context = context))
  62. def setCompatibleMaterialDiameter(self, value: float) -> None:
  63. old_approximate_diameter = self.getApproximateMaterialDiameter()
  64. if self.getCompatibleMaterialDiameter() != value:
  65. self.definitionChanges.setProperty("material_diameter", "value", value)
  66. self.compatibleMaterialDiameterChanged.emit()
  67. # Emit approximate diameter changed signal if needed
  68. if old_approximate_diameter != self.getApproximateMaterialDiameter():
  69. self.approximateMaterialDiameterChanged.emit()
  70. compatibleMaterialDiameter = pyqtProperty(float, fset = setCompatibleMaterialDiameter,
  71. fget = getCompatibleMaterialDiameter,
  72. notify = compatibleMaterialDiameterChanged)
  73. approximateMaterialDiameterChanged = pyqtSignal()
  74. ## Return the approximate filament diameter that the machine requires.
  75. #
  76. # The approximate material diameter is the material diameter rounded to
  77. # the nearest millimetre.
  78. #
  79. # If the machine has no requirement for the diameter, -1 is returned.
  80. #
  81. # \return The approximate filament diameter for the printer
  82. def getApproximateMaterialDiameter(self) -> float:
  83. return round(self.getCompatibleMaterialDiameter())
  84. approximateMaterialDiameter = pyqtProperty(float, fget = getApproximateMaterialDiameter,
  85. notify = approximateMaterialDiameterChanged)
  86. ## Overridden from ContainerStack
  87. #
  88. # It will perform a few extra checks when trying to get properties.
  89. #
  90. # The two extra checks it currently does is to ensure a next stack is set and to bypass
  91. # the extruder when the property is not settable per extruder.
  92. #
  93. # \throws Exceptions.NoGlobalStackError Raised when trying to get a property from an extruder without
  94. # having a next stack set.
  95. @override(ContainerStack)
  96. def getProperty(self, key: str, property_name: str, context: Optional[PropertyEvaluationContext] = None) -> Any:
  97. if not self._next_stack:
  98. raise Exceptions.NoGlobalStackError("Extruder {id} is missing the next stack!".format(id = self.id))
  99. if context is None:
  100. context = PropertyEvaluationContext()
  101. context.pushContainer(self)
  102. if not super().getProperty(key, "settable_per_extruder", context):
  103. result = self.getNextStack().getProperty(key, property_name, context)
  104. context.popContainer()
  105. return result
  106. limit_to_extruder = super().getProperty(key, "limit_to_extruder", context)
  107. if limit_to_extruder is not None:
  108. if limit_to_extruder == -1:
  109. limit_to_extruder = int(cura.CuraApplication.CuraApplication.getInstance().getMachineManager().defaultExtruderPosition)
  110. limit_to_extruder = str(limit_to_extruder)
  111. if (limit_to_extruder is not None and limit_to_extruder != "-1") and self.getMetaDataEntry("position") != str(limit_to_extruder):
  112. try:
  113. result = self.getNextStack().extruderList[int(limit_to_extruder)].getProperty(key, property_name, context)
  114. if result is not None:
  115. context.popContainer()
  116. return result
  117. except IndexError:
  118. pass
  119. result = super().getProperty(key, property_name, context)
  120. context.popContainer()
  121. return result
  122. @override(CuraContainerStack)
  123. def _getMachineDefinition(self) -> ContainerInterface:
  124. if not self.getNextStack():
  125. raise Exceptions.NoGlobalStackError("Extruder {id} is missing the next stack!".format(id = self.id))
  126. return self.getNextStack()._getMachineDefinition()
  127. @override(CuraContainerStack)
  128. def deserialize(self, contents: str, file_name: Optional[str] = None) -> None:
  129. super().deserialize(contents, file_name)
  130. if "enabled" not in self.getMetaData():
  131. self.setMetaDataEntry("enabled", "True")
  132. def _onPropertiesChanged(self, key: str, properties: Dict[str, Any]) -> None:
  133. # When there is a setting that is not settable per extruder that depends on a value from a setting that is,
  134. # we do not always get properly informed that we should re-evaluate the setting. So make sure to indicate
  135. # something changed for those settings.
  136. if not self.getNextStack():
  137. return #There are no global settings to depend on.
  138. definitions = self.getNextStack().definition.findDefinitions(key = key)
  139. if definitions:
  140. has_global_dependencies = False
  141. for relation in definitions[0].relations:
  142. if not getattr(relation.target, "settable_per_extruder", True):
  143. has_global_dependencies = True
  144. break
  145. if has_global_dependencies:
  146. self.getNextStack().propertiesChanged.emit(key, properties)
  147. extruder_stack_mime = MimeType(
  148. name = "application/x-cura-extruderstack",
  149. comment = "Cura Extruder Stack",
  150. suffixes = ["extruder.cfg"]
  151. )
  152. MimeTypeDatabase.addMimeType(extruder_stack_mime)
  153. ContainerRegistry.addContainerTypeByName(ExtruderStack, "extruder_stack", extruder_stack_mime.name)