MachineSettingsAction.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. # Copyright (c) 2016 Ultimaker B.V.
  2. # Cura is released under the terms of the AGPLv3 or higher.
  3. from PyQt5.QtCore import pyqtProperty, pyqtSignal
  4. from UM.FlameProfiler import pyqtSlot
  5. from cura.MachineAction import MachineAction
  6. from UM.Application import Application
  7. from UM.Settings.InstanceContainer import InstanceContainer
  8. from UM.Settings.ContainerRegistry import ContainerRegistry
  9. from UM.Settings.DefinitionContainer import DefinitionContainer
  10. from UM.Logger import Logger
  11. from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
  12. from cura.Settings.ExtruderManager import ExtruderManager
  13. import UM.i18n
  14. catalog = UM.i18n.i18nCatalog("cura")
  15. ## This action allows for certain settings that are "machine only") to be modified.
  16. # It automatically detects machine definitions that it knows how to change and attaches itself to those.
  17. class MachineSettingsAction(MachineAction):
  18. def __init__(self, parent = None):
  19. super().__init__("MachineSettingsAction", catalog.i18nc("@action", "Machine Settings"))
  20. self._qml_url = "MachineSettingsAction.qml"
  21. self._global_container_stack = None
  22. self._container_index = 0
  23. self._extruder_container_index = 0
  24. self._container_registry = ContainerRegistry.getInstance()
  25. self._container_registry.containerAdded.connect(self._onContainerAdded)
  26. Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
  27. ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
  28. self._backend = Application.getInstance().getBackend()
  29. def _reset(self):
  30. if not self._global_container_stack:
  31. return
  32. # Make sure there is a definition_changes container to store the machine settings
  33. definition_changes_container = self._global_container_stack.findContainer({"type": "definition_changes"})
  34. if not definition_changes_container:
  35. definition_changes_container = self._createDefinitionChangesContainer(self._global_container_stack, self._global_container_stack.getName() + "_settings")
  36. # Notify the UI in which container to store the machine settings data
  37. container_index = self._global_container_stack.getContainerIndex(definition_changes_container)
  38. if container_index != self._container_index:
  39. self._container_index = container_index
  40. self.containerIndexChanged.emit()
  41. # Disable autoslicing while the machineaction is showing
  42. self._backend.disableTimer()
  43. @pyqtSlot()
  44. def onFinishAction(self):
  45. # Restore autoslicing when the machineaction is dismissed
  46. if self._backend.determineAutoSlicing():
  47. self._backend.tickle()
  48. def _onActiveExtruderStackChanged(self):
  49. extruder_container_stack = ExtruderManager.getInstance().getActiveExtruderStack()
  50. if not self._global_container_stack or not extruder_container_stack:
  51. return
  52. # Make sure there is a definition_changes container to store the machine settings
  53. definition_changes_container = extruder_container_stack.findContainer({"type": "definition_changes"})
  54. if not definition_changes_container:
  55. definition_changes_container = self._createDefinitionChangesContainer(extruder_container_stack, extruder_container_stack.getId() + "_settings")
  56. # Notify the UI in which container to store the machine settings data
  57. container_index = extruder_container_stack.getContainerIndex(definition_changes_container)
  58. if container_index != self._extruder_container_index:
  59. self._extruder_container_index = container_index
  60. self.extruderContainerIndexChanged.emit()
  61. def _createDefinitionChangesContainer(self, container_stack, container_name, container_index = None):
  62. definition_changes_container = InstanceContainer(container_name)
  63. definition = container_stack.getBottom()
  64. definition_changes_container.setDefinition(definition)
  65. definition_changes_container.addMetaDataEntry("type", "definition_changes")
  66. self._container_registry.addContainer(definition_changes_container)
  67. # Insert definition_changes between the definition and the variant
  68. container_stack.insertContainer(-1, definition_changes_container)
  69. return definition_changes_container
  70. containerIndexChanged = pyqtSignal()
  71. @pyqtProperty(int, notify = containerIndexChanged)
  72. def containerIndex(self):
  73. return self._container_index
  74. extruderContainerIndexChanged = pyqtSignal()
  75. @pyqtProperty(int, notify = extruderContainerIndexChanged)
  76. def extruderContainerIndex(self):
  77. return self._extruder_container_index
  78. def _onContainerAdded(self, container):
  79. # Add this action as a supported action to all machine definitions
  80. if isinstance(container, DefinitionContainer) and container.getMetaDataEntry("type") == "machine":
  81. Application.getInstance().getMachineActionManager().addSupportedAction(container.getId(), self.getKey())
  82. def _onGlobalContainerChanged(self):
  83. self._global_container_stack = Application.getInstance().getGlobalContainerStack()
  84. # This additional emit is needed because we cannot connect a UM.Signal directly to a pyqtSignal
  85. self.globalContainerChanged.emit()
  86. globalContainerChanged = pyqtSignal()
  87. @pyqtProperty(int, notify = globalContainerChanged)
  88. def definedExtruderCount(self):
  89. if not self._global_container_stack:
  90. return 0
  91. return len(self._global_container_stack.getMetaDataEntry("machine_extruder_trains"))
  92. @pyqtSlot(int)
  93. def setMachineExtruderCount(self, extruder_count):
  94. machine_manager = Application.getInstance().getMachineManager()
  95. extruder_manager = ExtruderManager.getInstance()
  96. definition_changes_container = self._global_container_stack.findContainer({"type": "definition_changes"})
  97. if not self._global_container_stack or not definition_changes_container:
  98. return
  99. if extruder_count == self._global_container_stack.getProperty("machine_extruder_count", "value"):
  100. return
  101. extruder_material = None
  102. if extruder_count == 1 and machine_manager.hasMaterials:
  103. extruder_material = machine_manager.allActiveMaterialIds[machine_manager.activeStackId]
  104. definition_changes_container.setProperty("machine_extruder_count", "value", extruder_count)
  105. self.forceUpdate()
  106. if extruder_count > 1:
  107. # multiextrusion; make sure one of these extruder stacks is active
  108. if extruder_manager.activeExtruderIndex == -1:
  109. extruder_manager.setActiveExtruderIndex(0)
  110. else:
  111. # single extrusion; make sure the machine stack is active
  112. if extruder_manager.activeExtruderIndex > -1:
  113. extruder_manager.setActiveExtruderIndex(-1);
  114. if extruder_material:
  115. # restore material on global stack
  116. # MachineManager._onGlobalContainerChanged removes the global material of multiextruder machines
  117. machine_manager.setActiveMaterial(extruder_material);
  118. @pyqtSlot()
  119. def forceUpdate(self):
  120. # Force rebuilding the build volume by reloading the global container stack.
  121. # This is a bit of a hack, but it seems quick enough.
  122. Application.getInstance().globalContainerStackChanged.emit()
  123. @pyqtSlot()
  124. def updateHasMaterialsMetadata(self):
  125. # Updates the has_materials metadata flag after switching gcode flavor
  126. if not self._global_container_stack:
  127. return
  128. definition = self._global_container_stack.getBottom()
  129. if definition.getProperty("machine_gcode_flavor", "value") == "UltiGCode" and not definition.getMetaDataEntry("has_materials", False):
  130. has_materials = self._global_container_stack.getProperty("machine_gcode_flavor", "value") != "UltiGCode"
  131. material_container = self._global_container_stack.findContainer({"type": "material"})
  132. material_index = self._global_container_stack.getContainerIndex(material_container)
  133. if has_materials:
  134. if "has_materials" in self._global_container_stack.getMetaData():
  135. self._global_container_stack.setMetaDataEntry("has_materials", True)
  136. else:
  137. self._global_container_stack.addMetaDataEntry("has_materials", True)
  138. # Set the material container to a sane default
  139. if material_container.getId() == "empty_material":
  140. search_criteria = { "type": "material", "definition": "fdmprinter", "id": "*pla*" }
  141. containers = self._container_registry.findInstanceContainers(**search_criteria)
  142. if containers:
  143. self._global_container_stack.replaceContainer(material_index, containers[0])
  144. else:
  145. # The metadata entry is stored in an ini, and ini files are parsed as strings only.
  146. # Because any non-empty string evaluates to a boolean True, we have to remove the entry to make it False.
  147. if "has_materials" in self._global_container_stack.getMetaData():
  148. self._global_container_stack.removeMetaDataEntry("has_materials")
  149. empty_material = self._container_registry.findInstanceContainers(id = "empty_material")[0]
  150. self._global_container_stack.replaceContainer(material_index, empty_material)
  151. Application.getInstance().globalContainerStackChanged.emit()