CuraSceneController.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. from UM.Logger import Logger
  2. from PyQt6.QtCore import Qt, pyqtSlot, QObject, QTimer
  3. from PyQt6.QtWidgets import QApplication
  4. from UM.Scene.Camera import Camera
  5. from cura.UI.ObjectsModel import ObjectsModel
  6. from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel
  7. from cura.Scene.CuraSceneNode import CuraSceneNode
  8. from UM.Application import Application
  9. from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
  10. from UM.Scene.SceneNode import SceneNode
  11. from UM.Scene.Selection import Selection
  12. from UM.Signal import Signal
  13. class CuraSceneController(QObject):
  14. activeBuildPlateChanged = Signal()
  15. def __init__(self, objects_model: ObjectsModel, multi_build_plate_model: MultiBuildPlateModel) -> None:
  16. super().__init__()
  17. self._objects_model = objects_model
  18. self._multi_build_plate_model = multi_build_plate_model
  19. self._active_build_plate = -1
  20. self._last_selected_index = 0
  21. self._max_build_plate = 1 # default
  22. self._change_timer = QTimer()
  23. self._change_timer.setInterval(100)
  24. self._change_timer.setSingleShot(True)
  25. self._change_timer.timeout.connect(self.updateMaxBuildPlate)
  26. Application.getInstance().getController().getScene().sceneChanged.connect(self.updateMaxBuildPlateDelayed)
  27. def updateMaxBuildPlateDelayed(self, *args):
  28. if args:
  29. source = args[0]
  30. else:
  31. source = None
  32. if not isinstance(source, SceneNode) or isinstance(source, Camera):
  33. return
  34. self._change_timer.start()
  35. def updateMaxBuildPlate(self, *args):
  36. global_stack = Application.getInstance().getGlobalContainerStack()
  37. if global_stack:
  38. scene_has_support_meshes = self._sceneHasSupportMeshes() # TODO: see if this can be cached
  39. if scene_has_support_meshes != global_stack.getProperty("support_meshes_present", "value"):
  40. # Adjust the setting without having the setting value in an InstanceContainer
  41. setting_definitions = global_stack.definition.findDefinitions(key="support_meshes_present")
  42. if setting_definitions:
  43. # Recreate the setting definition because the default_value is readonly
  44. definition_dict = setting_definitions[0].serialize_to_dict()
  45. definition_dict["enabled"] = False # The enabled property has a value that would need to be evaluated
  46. definition_dict["default_value"] = scene_has_support_meshes
  47. relations = setting_definitions[0].relations # Relations are wiped when deserializing from a dict
  48. setting_definitions[0].deserialize(definition_dict)
  49. # Restore relations and notify them that the setting has changed
  50. for relation in relations:
  51. setting_definitions[0].relations.append(relation)
  52. global_stack.propertyChanged.emit(relation.target.key, "enabled")
  53. max_build_plate = self._calcMaxBuildPlate()
  54. changed = False
  55. if max_build_plate != self._max_build_plate:
  56. self._max_build_plate = max_build_plate
  57. changed = True
  58. if changed:
  59. self._multi_build_plate_model.setMaxBuildPlate(self._max_build_plate)
  60. build_plates = [{"name": "Build Plate %d" % (i + 1), "buildPlateNumber": i} for i in range(self._max_build_plate + 1)]
  61. self._multi_build_plate_model.setItems(build_plates)
  62. if self._active_build_plate > self._max_build_plate:
  63. build_plate_number = 0
  64. if self._last_selected_index >= 0: # go to the buildplate of the item you last selected
  65. item = self._objects_model.getItem(self._last_selected_index)
  66. if "node" in item:
  67. node = item["node"]
  68. build_plate_number = node.callDecoration("getBuildPlateNumber")
  69. self.setActiveBuildPlate(build_plate_number)
  70. # self.buildPlateItemsChanged.emit() # TODO: necessary after setItems?
  71. def _calcMaxBuildPlate(self):
  72. max_build_plate = 0
  73. for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
  74. if node.callDecoration("isSliceable"):
  75. build_plate_number = node.callDecoration("getBuildPlateNumber")
  76. if build_plate_number is None:
  77. build_plate_number = 0
  78. max_build_plate = max(build_plate_number, max_build_plate)
  79. return max_build_plate
  80. def _sceneHasSupportMeshes(self):
  81. root = Application.getInstance().getController().getScene().getRoot()
  82. for node in root.getAllChildren():
  83. if isinstance(node, CuraSceneNode):
  84. per_mesh_stack = node.callDecoration("getStack")
  85. if per_mesh_stack and per_mesh_stack.getProperty("support_mesh", "value"):
  86. return True
  87. return False
  88. @pyqtSlot(int)
  89. def changeSelection(self, index):
  90. """Either select or deselect an item"""
  91. modifiers = QApplication.keyboardModifiers()
  92. ctrl_is_active = modifiers & Qt.KeyboardModifier.ControlModifier
  93. shift_is_active = modifiers & Qt.KeyboardModifier.ShiftModifier
  94. if ctrl_is_active:
  95. item = self._objects_model.getItem(index)
  96. node = item["node"]
  97. if Selection.isSelected(node):
  98. Selection.remove(node)
  99. else:
  100. Selection.add(node)
  101. elif shift_is_active:
  102. polarity = 1 if index + 1 > self._last_selected_index else -1
  103. for i in range(self._last_selected_index, index + polarity, polarity):
  104. item = self._objects_model.getItem(i)
  105. node = item["node"]
  106. Selection.add(node)
  107. else:
  108. # Single select
  109. item = self._objects_model.getItem(index)
  110. node = item["node"]
  111. build_plate_number = node.callDecoration("getBuildPlateNumber")
  112. if build_plate_number is not None and build_plate_number != -1:
  113. self.setActiveBuildPlate(build_plate_number)
  114. Selection.clear()
  115. Selection.add(node)
  116. self._last_selected_index = index
  117. @pyqtSlot(int)
  118. def setActiveBuildPlate(self, nr):
  119. if nr == self._active_build_plate:
  120. return
  121. Logger.log("d", "Select build plate: %s" % nr)
  122. self._active_build_plate = nr
  123. Selection.clear()
  124. self._multi_build_plate_model.setActiveBuildPlate(nr)
  125. self._objects_model.setActiveBuildPlate(nr)
  126. self.activeBuildPlateChanged.emit()
  127. @staticmethod
  128. def createCuraSceneController():
  129. objects_model = Application.getInstance().getObjectsModel()
  130. multi_build_plate_model = Application.getInstance().getMultiBuildPlateModel()
  131. return CuraSceneController(objects_model = objects_model, multi_build_plate_model = multi_build_plate_model)