ExtruderManager.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. # Copyright (c) 2016 Ultimaker B.V.
  2. # Cura is released under the terms of the AGPLv3 or higher.
  3. from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot, QObject, QVariant #For communicating data and events to Qt.
  4. import UM.Application #To get the global container stack to find the current machine.
  5. import UM.Logger
  6. import UM.Settings.ContainerRegistry #Finding containers by ID.
  7. ## Manages all existing extruder stacks.
  8. #
  9. # This keeps a list of extruder stacks for each machine.
  10. class ExtruderManager(QObject):
  11. ## Signal to notify other components when the list of extruders changes.
  12. extrudersChanged = pyqtSignal(QVariant)
  13. ## Notify when the user switches the currently active extruder.
  14. activeExtruderChanged = pyqtSignal()
  15. ## Registers listeners and such to listen to changes to the extruders.
  16. def __init__(self, parent = None):
  17. super().__init__(parent)
  18. self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs.
  19. self._active_extruder_index = 0
  20. UM.Application.getInstance().globalContainerStackChanged.connect(self._addCurrentMachineExtruders)
  21. ## Gets the unique identifier of the currently active extruder stack.
  22. #
  23. # The currently active extruder stack is the stack that is currently being
  24. # edited.
  25. #
  26. # \return The unique ID of the currently active extruder stack.
  27. @pyqtProperty(str, notify = activeExtruderChanged)
  28. def activeExtruderStackId(self):
  29. if not UM.Application.getInstance().getGlobalContainerStack():
  30. return None #No active machine, so no active extruder.
  31. try:
  32. return self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getBottom().getId()][str(self._active_extruder_index)]
  33. except KeyError: #Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong.
  34. return None
  35. ## The instance of the singleton pattern.
  36. #
  37. # It's None if the extruder manager hasn't been created yet.
  38. __instance = None
  39. ## Gets an instance of the extruder manager, or creates one if no instance
  40. # exists yet.
  41. #
  42. # This is an implementation of singleton. If an extruder manager already
  43. # exists, it is re-used.
  44. #
  45. # \return The extruder manager.
  46. @classmethod
  47. def getInstance(cls):
  48. if not cls.__instance:
  49. cls.__instance = ExtruderManager()
  50. return cls.__instance
  51. @pyqtSlot(int)
  52. def setActiveExtruderIndex(self, index):
  53. self._active_extruder_index = index
  54. self.activeExtruderChanged.emit()
  55. ## Adds all extruders of a specific machine definition to the extruder
  56. # manager.
  57. #
  58. # \param machine_definition The machine to add the extruders for.
  59. def addMachineExtruders(self, machine_definition):
  60. machine_id = machine_definition.getId()
  61. if machine_id not in self._extruder_trains:
  62. self._extruder_trains[machine_id] = { }
  63. container_registry = UM.Settings.ContainerRegistry.getInstance()
  64. if not container_registry: #Then we shouldn't have any machine definition either. In any case, there are no extruder trains then so bye bye.
  65. return
  66. #Add the extruder trains that don't exist yet.
  67. for extruder_definition in container_registry.findDefinitionContainers(machine = machine_definition.getId()):
  68. position = extruder_definition.getMetaDataEntry("position", None)
  69. if not position:
  70. UM.Logger.log("w", "Extruder definition %s specifies no position metadata entry.", extruder_definition.getId())
  71. if not container_registry.findContainerStacks(machine = machine_id, position = position): #Doesn't exist yet.
  72. self.createExtruderTrain(extruder_definition, machine_definition, position)
  73. #Gets the extruder trains that we just created as well as any that still existed.
  74. extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = machine_definition.getId())
  75. for extruder_train in extruder_trains:
  76. self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train.getId()
  77. if extruder_trains:
  78. self.extrudersChanged.emit(machine_definition)
  79. ## Creates a container stack for an extruder train.
  80. #
  81. # The container stack has an extruder definition at the bottom, which is
  82. # linked to a machine definition. Then it has a nozzle profile, a material
  83. # profile, a quality profile and a user profile, in that order.
  84. #
  85. # The resulting container stack is added to the registry.
  86. #
  87. # \param extruder_definition The extruder to create the extruder train
  88. # for.
  89. # \param machine_definition The machine that the extruder train belongs
  90. # to.
  91. # \param position The position of this extruder train in the extruder
  92. # slots of the machine.
  93. def createExtruderTrain(self, extruder_definition, machine_definition, position):
  94. #Cache some things.
  95. container_registry = UM.Settings.ContainerRegistry.getInstance()
  96. machine_id = machine_definition.getId()
  97. #Create a container stack for this extruder.
  98. extruder_stack_id = container_registry.uniqueName(extruder_definition.getId())
  99. container_stack = UM.Settings.ContainerStack(extruder_stack_id)
  100. container_stack.setName(extruder_definition.getName()) #Take over the display name to display the stack with.
  101. container_stack.addMetaDataEntry("type", "extruder_train")
  102. container_stack.addMetaDataEntry("machine", machine_definition.getId())
  103. container_stack.addMetaDataEntry("position", position)
  104. container_stack.addContainer(extruder_definition)
  105. #Find the nozzle to use for this extruder.
  106. nozzle = container_registry.getEmptyInstanceContainer()
  107. if machine_definition.getMetaDataEntry("has_nozzles", default = "False") == "True":
  108. #First add any nozzle. Later, overwrite with preference if the preference is valid.
  109. nozzles = container_registry.findInstanceContainers(machine = machine_id, type = "nozzle")
  110. if len(nozzles) >= 1:
  111. nozzle = nozzles[0]
  112. preferred_nozzle_id = machine_definition.getMetaDataEntry("preferred_nozzle")
  113. if preferred_nozzle_id:
  114. preferred_nozzles = container_registry.findInstanceContainers(id = preferred_nozzle_id, type = "nozzle")
  115. if len(preferred_nozzles) >= 1:
  116. nozzle = preferred_nozzles[0]
  117. else:
  118. UM.Logger.log("w", "The preferred nozzle \"%s\" of machine %s doesn't exist or is not a nozzle profile.", preferred_nozzle_id, machine_id)
  119. #And leave it at the default nozzle.
  120. container_stack.addContainer(nozzle)
  121. #Find a material to use for this nozzle.
  122. material = container_registry.getEmptyInstanceContainer()
  123. if machine_definition.getMetaDataEntry("has_materials", default = "False") == "True":
  124. #First add any material. Later, overwrite with preference if the preference is valid.
  125. if machine_definition.getMetaDataEntry("has_nozzle_materials", default = "False") == "True":
  126. materials = container_registry.findInstanceContainers(type = "material", machine = machine_id, nozzle = nozzle.getId())
  127. else:
  128. materials = container_registry.findInstanceContainers(type = "material", machine = machine_id)
  129. if len(materials) >= 1:
  130. material = materials[0]
  131. preferred_material_id = machine_definition.getMetaDataEntry("preferred_material")
  132. if preferred_material_id:
  133. preferred_materials = container_registry.findInstanceContainers(id = preferred_material_id, type = "material")
  134. if len(preferred_materials) >= 1:
  135. material = preferred_materials[0]
  136. else:
  137. UM.Logger.log("w", "The preferred material \"%s\" of machine %s doesn't exist or is not a material profile.", preferred_material_id, machine_id)
  138. #And leave it at the default material.
  139. container_stack.addContainer(material)
  140. #Find a quality to use for this extruder.
  141. quality = container_registry.getEmptyInstanceContainer()
  142. if machine_definition.getMetaDataEntry("has_machine_quality"):
  143. #First add any quality. Later, overwrite with preference if the preference is valid.
  144. qualities = container_registry.findInstanceContainers(type = "quality")
  145. if len(qualities) >= 1:
  146. quality = qualities[0]
  147. preferred_quality_id = machine_definition.getMetaDataEntry("preferred_quality")
  148. if preferred_quality_id:
  149. preferred_quality = container_registry.findInstanceContainers(id = preferred_quality_id.lower(), type = "quality")
  150. if len(preferred_quality) >= 1:
  151. quality = preferred_quality[0]
  152. else:
  153. UM.Logger.log("w", "The preferred quality \"%s\" of machine %s doesn't exist or is not a quality profile.", preferred_quality_id, machine_id)
  154. #And leave it at the default quality.
  155. container_stack.addContainer(quality)
  156. user_profile = container_registry.findInstanceContainers(id = extruder_stack_id + "_current_settings")
  157. if user_profile: #There was already a user profile, loaded from settings.
  158. user_profile = user_profile[0]
  159. else:
  160. user_profile = UM.Settings.InstanceContainer(extruder_stack_id + "_current_settings") #Add an empty user profile.
  161. user_profile.addMetaDataEntry("type", "user")
  162. user_profile.setDefinition(machine_definition)
  163. container_registry.addContainer(user_profile)
  164. container_stack.addContainer(user_profile)
  165. container_stack.setNextStack(UM.Application.getInstance().getGlobalContainerStack())
  166. container_registry.addContainer(container_stack)
  167. ## Gets extruders for a specific machine.
  168. def getMachineExtruders(self, machine_definition_id):
  169. container_registry = UM.Settings.ContainerRegistry.getInstance()
  170. if not machine_definition_id in self._extruder_trains:
  171. UM.Logger.log("w", "Tried to get the extruder trains for machine %s, which doesn't exist.", machine_definition_id)
  172. return
  173. for _,extruder_train_id in self._extruder_trains[machine_definition_id].items():
  174. extruder_train = container_registry.findContainerStacks(id = extruder_train_id)
  175. if extruder_train:
  176. yield extruder_train[0]
  177. else:
  178. UM.Logger.log("w", "Machine %s refers to an extruder train with ID %s, which doesn't exist.", machine_definition_id, extruder_train_id)
  179. ## Adds the extruders of the currently active machine.
  180. def _addCurrentMachineExtruders(self):
  181. global_stack = UM.Application.getInstance().getGlobalContainerStack()
  182. if global_stack and global_stack.getBottom():
  183. self.addMachineExtruders(global_stack.getBottom())