SettingOverrideDecorator.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. # Copyright (c) 2016 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. import copy
  4. import uuid
  5. from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
  6. from UM.Signal import Signal, signalemitter
  7. from UM.Settings.InstanceContainer import InstanceContainer
  8. from UM.Settings.ContainerRegistry import ContainerRegistry
  9. from UM.Logger import Logger
  10. from UM.Util import parseBool
  11. from UM.Application import Application
  12. from cura.Settings.PerObjectContainerStack import PerObjectContainerStack
  13. from cura.Settings.ExtruderManager import ExtruderManager
  14. ## A decorator that adds a container stack to a Node. This stack should be queried for all settings regarding
  15. # the linked node. The Stack in question will refer to the global stack (so that settings that are not defined by
  16. # this stack still resolve.
  17. @signalemitter
  18. class SettingOverrideDecorator(SceneNodeDecorator):
  19. ## Event indicating that the user selected a different extruder.
  20. activeExtruderChanged = Signal()
  21. ## Non-printing meshes
  22. #
  23. # If these settings are True for any mesh, the mesh does not need a convex hull,
  24. # and is sent to the slicer regardless of whether it fits inside the build volume.
  25. # Note that Support Mesh is not in here because it actually generates
  26. # g-code in the volume of the mesh.
  27. _non_printing_mesh_settings = {"anti_overhang_mesh", "infill_mesh", "cutting_mesh"}
  28. _non_thumbnail_visible_settings = {"anti_overhang_mesh", "infill_mesh", "cutting_mesh", "support_mesh"}
  29. def __init__(self):
  30. super().__init__()
  31. self._stack = PerObjectContainerStack(container_id = "per_object_stack_" + str(id(self)))
  32. self._stack.setDirty(False) # This stack does not need to be saved.
  33. user_container = InstanceContainer(container_id = self._generateUniqueName())
  34. user_container.addMetaDataEntry("type", "user")
  35. self._stack.userChanges = user_container
  36. self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0)
  37. self._is_non_printing_mesh = False
  38. self._is_non_thumbnail_visible_mesh = False
  39. self._stack.propertyChanged.connect(self._onSettingChanged)
  40. Application.getInstance().getContainerRegistry().addContainer(self._stack)
  41. Application.getInstance().globalContainerStackChanged.connect(self._onNumberOfExtrudersEnabledChanged)
  42. Application.getInstance().getMachineManager().numberExtrudersEnabledChanged.connect(self._onNumberOfExtrudersEnabledChanged)
  43. self.activeExtruderChanged.connect(self._updateNextStack)
  44. self._updateNextStack()
  45. def _generateUniqueName(self):
  46. return "SettingOverrideInstanceContainer-%s" % uuid.uuid1()
  47. def _onNumberOfExtrudersEnabledChanged(self, *args, **kwargs):
  48. if not parseBool(self._extruder_stack.getMetaDataEntry("enabled", "True")):
  49. # switch to the first extruder that's available
  50. global_stack = Application.getInstance().getMachineManager().activeMachine
  51. for _, extruder in sorted(list(global_stack.extruders.items())):
  52. if parseBool(extruder.getMetaDataEntry("enabled", "True")):
  53. self._extruder_stack = extruder
  54. self._updateNextStack()
  55. break
  56. def __deepcopy__(self, memo):
  57. ## Create a fresh decorator object
  58. deep_copy = SettingOverrideDecorator()
  59. ## Copy the instance
  60. instance_container = copy.deepcopy(self._stack.getContainer(0), memo)
  61. # A unique name must be added, or replaceContainer will not replace it
  62. instance_container.setMetaDataEntry("id", self._generateUniqueName())
  63. ## Set the copied instance as the first (and only) instance container of the stack.
  64. deep_copy._stack.replaceContainer(0, instance_container)
  65. # Properly set the right extruder on the copy
  66. deep_copy.setActiveExtruder(self._extruder_stack.getId())
  67. # use value from the stack because there can be a delay in signal triggering and "_is_non_printing_mesh"
  68. # has not been updated yet.
  69. deep_copy._is_non_printing_mesh = self.evaluateIsNonPrintingMesh()
  70. deep_copy._is_non_thumbnail_visible_mesh = self.evaluateIsNonThumbnailVisibleMesh()
  71. return deep_copy
  72. ## Gets the currently active extruder to print this object with.
  73. #
  74. # \return An extruder's container stack.
  75. def getActiveExtruder(self):
  76. return None if self._extruder_stack is None else self._extruder_stack.getId()
  77. ## Gets the signal that emits if the active extruder changed.
  78. #
  79. # This can then be accessed via a decorator.
  80. def getActiveExtruderChangedSignal(self):
  81. return self.activeExtruderChanged
  82. ## Gets the currently active extruders position
  83. #
  84. # \return An extruder's position, or None if no position info is available.
  85. def getActiveExtruderPosition(self):
  86. containers = ContainerRegistry.getInstance().findContainers(id = self.getActiveExtruder())
  87. if containers:
  88. container_stack = containers[0]
  89. return container_stack.getMetaDataEntry("position", default=None)
  90. def isNonPrintingMesh(self):
  91. return self._is_non_printing_mesh
  92. def evaluateIsNonPrintingMesh(self):
  93. return any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_printing_mesh_settings)
  94. def isNonThumbnailVisibleMesh(self):
  95. return self._is_non_thumbnail_visible_mesh
  96. def evaluateIsNonThumbnailVisibleMesh(self):
  97. return any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_thumbnail_visible_settings)
  98. def _onSettingChanged(self, instance, property_name): # Reminder: 'property' is a built-in function
  99. if property_name == "value":
  100. # Trigger slice/need slicing if the value has changed.
  101. self._is_non_printing_mesh = self.evaluateIsNonPrintingMesh()
  102. self._is_non_thumbnail_visible_mesh = self.evaluateIsNonThumbnailVisibleMesh()
  103. Application.getInstance().getBackend().needsSlicing()
  104. Application.getInstance().getBackend().tickle()
  105. ## Makes sure that the stack upon which the container stack is placed is
  106. # kept up to date.
  107. def _updateNextStack(self):
  108. if self._extruder_stack:
  109. if self._stack.getNextStack():
  110. old_extruder_stack_id = self._stack.getNextStack().getId()
  111. else:
  112. old_extruder_stack_id = ""
  113. self._stack.setNextStack(self._extruder_stack)
  114. # Trigger slice/need slicing if the extruder changed.
  115. if self._stack.getNextStack().getId() != old_extruder_stack_id:
  116. Application.getInstance().getBackend().needsSlicing()
  117. Application.getInstance().getBackend().tickle()
  118. else:
  119. self._stack.setNextStack(Application.getInstance().getGlobalContainerStack())
  120. ## Changes the extruder with which to print this node.
  121. #
  122. # \param extruder_stack_id The new extruder stack to print with.
  123. def setActiveExtruder(self, extruder_stack_id):
  124. if self._extruder_stack is None or self._extruder_stack.getId() == extruder_stack_id:
  125. return
  126. global_stack = Application.getInstance().getMachineManager().activeMachine
  127. if global_stack is None:
  128. return
  129. for extruder in global_stack.extruders.values():
  130. if extruder.getId() == extruder_stack_id:
  131. self._extruder_stack = extruder
  132. break
  133. self._updateNextStack()
  134. ExtruderManager.getInstance().resetSelectedObjectExtruders()
  135. self.activeExtruderChanged.emit()
  136. def getStack(self):
  137. return self._stack