PerObjectSettingsTool.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. # Copyright (c) 2020 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from UM.Logger import Logger
  4. from UM.Tool import Tool
  5. from UM.Scene.Selection import Selection
  6. from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
  7. from UM.Application import Application
  8. from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
  9. from cura.Settings.ExtruderManager import ExtruderManager
  10. from UM.Settings.SettingInstance import SettingInstance
  11. from UM.Event import Event
  12. class PerObjectSettingsTool(Tool):
  13. """This tool allows the user to add & change settings per node in the scene.
  14. The settings per object are kept in a ContainerStack, which is linked to a node by decorator.
  15. """
  16. def __init__(self):
  17. super().__init__()
  18. self._model = None
  19. self.setExposedProperties("SelectedObjectId", "ContainerID", "SelectedActiveExtruder", "MeshType")
  20. self._multi_extrusion = False
  21. self._single_model_selected = False
  22. self.visibility_handler = None
  23. Selection.selectionChanged.connect(self.propertyChanged)
  24. Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
  25. self._onGlobalContainerChanged()
  26. Selection.selectionChanged.connect(self._updateEnabled)
  27. def event(self, event):
  28. super().event(event)
  29. if event.type == Event.MousePressEvent and self._controller.getToolsEnabled():
  30. self.operationStopped.emit(self)
  31. return False
  32. def getSelectedObjectId(self):
  33. selected_object = Selection.getSelectedObject(0)
  34. selected_object_id = id(selected_object)
  35. return selected_object_id
  36. def getContainerID(self):
  37. selected_object = Selection.getSelectedObject(0)
  38. try:
  39. return selected_object.callDecoration("getStack").getId()
  40. except AttributeError:
  41. return ""
  42. def getSelectedActiveExtruder(self):
  43. """Gets the active extruder of the currently selected object.
  44. :return: The active extruder of the currently selected object.
  45. """
  46. selected_object = Selection.getSelectedObject(0)
  47. return selected_object.callDecoration("getActiveExtruder")
  48. def setSelectedActiveExtruder(self, extruder_stack_id):
  49. """Changes the active extruder of the currently selected object.
  50. :param extruder_stack_id: The ID of the extruder to print the currently
  51. selected object with.
  52. """
  53. selected_object = Selection.getSelectedObject(0)
  54. stack = selected_object.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway.
  55. if not stack:
  56. selected_object.addDecorator(SettingOverrideDecorator())
  57. selected_object.callDecoration("setActiveExtruder", extruder_stack_id)
  58. def setMeshType(self, mesh_type: str) -> bool:
  59. """Returns True when the mesh_type was changed, False when current mesh_type == mesh_type"""
  60. old_mesh_type = self.getMeshType()
  61. if old_mesh_type == mesh_type:
  62. return False
  63. selected_object = Selection.getSelectedObject(0)
  64. if selected_object is None:
  65. Logger.log("w", "Tried setting the mesh type of the selected object, but no object was selected")
  66. return False
  67. stack = selected_object.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway.
  68. if not stack:
  69. selected_object.addDecorator(SettingOverrideDecorator())
  70. stack = selected_object.callDecoration("getStack")
  71. settings_visibility_changed = False
  72. settings = stack.getTop()
  73. for property_key in ["infill_mesh", "cutting_mesh", "support_mesh", "anti_overhang_mesh"]:
  74. if property_key != mesh_type:
  75. if settings.getInstance(property_key):
  76. settings.removeInstance(property_key)
  77. else:
  78. if not (settings.getInstance(property_key) and settings.getProperty(property_key, "value")):
  79. definition = stack.getSettingDefinition(property_key)
  80. new_instance = SettingInstance(definition, settings)
  81. new_instance.setProperty("value", True)
  82. new_instance.resetState() # Ensure that the state is not seen as a user state.
  83. settings.addInstance(new_instance)
  84. for property_key in ["top_bottom_thickness", "wall_thickness", "wall_line_count"]:
  85. if mesh_type == "infill_mesh":
  86. if settings.getInstance(property_key) is None:
  87. definition = stack.getSettingDefinition(property_key)
  88. new_instance = SettingInstance(definition, settings)
  89. # We just want the wall_line count to be there in case it was overriden in the global stack.
  90. # as such, we don't need to set a value.
  91. if property_key != "wall_line_count":
  92. new_instance.setProperty("value", 0)
  93. new_instance.resetState() # Ensure that the state is not seen as a user state.
  94. settings.addInstance(new_instance)
  95. settings_visibility_changed = True
  96. elif old_mesh_type == "infill_mesh" and settings.getInstance(property_key) and (settings.getProperty(property_key, "value") == 0 or property_key == "wall_line_count"):
  97. settings.removeInstance(property_key)
  98. settings_visibility_changed = True
  99. if settings_visibility_changed:
  100. self.visibility_handler.forceVisibilityChanged()
  101. self.propertyChanged.emit()
  102. return True
  103. def getMeshType(self):
  104. selected_object = Selection.getSelectedObject(0)
  105. stack = selected_object.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway.
  106. if not stack:
  107. return ""
  108. settings = stack.getTop()
  109. for property_key in ["infill_mesh", "cutting_mesh", "support_mesh", "anti_overhang_mesh"]:
  110. if settings.getInstance(property_key) and settings.getProperty(property_key, "value"):
  111. return property_key
  112. return ""
  113. def _onGlobalContainerChanged(self):
  114. global_container_stack = Application.getInstance().getGlobalContainerStack()
  115. if global_container_stack:
  116. # used for enabling or disabling per extruder settings per object
  117. self._multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
  118. extruder_stack = ExtruderManager.getInstance().getExtruderStack(0)
  119. if extruder_stack:
  120. root_node = Application.getInstance().getController().getScene().getRoot()
  121. for node in DepthFirstIterator(root_node):
  122. new_stack_id = extruder_stack.getId()
  123. # Get position of old extruder stack for this node
  124. old_extruder_pos = node.callDecoration("getActiveExtruderPosition")
  125. if old_extruder_pos is not None:
  126. # Fetch current (new) extruder stack at position
  127. new_stack = ExtruderManager.getInstance().getExtruderStack(old_extruder_pos)
  128. if new_stack:
  129. new_stack_id = new_stack.getId()
  130. node.callDecoration("setActiveExtruder", new_stack_id)
  131. self._updateEnabled()
  132. def _updateEnabled(self):
  133. selected_objects = Selection.getAllSelectedObjects()
  134. if len(selected_objects)> 1:
  135. self._single_model_selected = False
  136. elif len(selected_objects) == 1 and selected_objects[0].callDecoration("isGroup"):
  137. self._single_model_selected = False # Group is selected, so tool needs to be disabled
  138. else:
  139. self._single_model_selected = True
  140. Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, self._single_model_selected)