ObjectsModel.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. # Copyright (c) 2018 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from collections import defaultdict
  4. from typing import Dict
  5. from PyQt5.QtCore import QTimer
  6. from UM.Application import Application
  7. from UM.Qt.ListModel import ListModel
  8. from UM.Scene.Camera import Camera
  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.i18n import i18nCatalog
  13. catalog = i18nCatalog("cura")
  14. ## Keep track of all objects in the project
  15. class ObjectsModel(ListModel):
  16. def __init__(self) -> None:
  17. super().__init__()
  18. Application.getInstance().getController().getScene().sceneChanged.connect(self._updateSceneDelayed)
  19. Application.getInstance().getPreferences().preferenceChanged.connect(self._updateDelayed)
  20. self._update_timer = QTimer()
  21. self._update_timer.setInterval(200)
  22. self._update_timer.setSingleShot(True)
  23. self._update_timer.timeout.connect(self._update)
  24. self._build_plate_number = -1
  25. def setActiveBuildPlate(self, nr: int) -> None:
  26. if self._build_plate_number != nr:
  27. self._build_plate_number = nr
  28. self._update()
  29. def _updateSceneDelayed(self, source) -> None:
  30. if not isinstance(source, Camera):
  31. self._update_timer.start()
  32. def _updateDelayed(self, *args) -> None:
  33. self._update_timer.start()
  34. def _update(self, *args) -> None:
  35. nodes = []
  36. filter_current_build_plate = Application.getInstance().getPreferences().getValue("view/filter_current_build_plate")
  37. active_build_plate_number = self._build_plate_number
  38. group_nr = 1
  39. name_count_dict = defaultdict(int) # type: Dict[str, int]
  40. for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
  41. if not isinstance(node, SceneNode):
  42. continue
  43. if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
  44. continue
  45. if node.getParent() and node.getParent().callDecoration("isGroup"):
  46. continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
  47. if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"):
  48. continue
  49. node_build_plate_number = node.callDecoration("getBuildPlateNumber")
  50. if filter_current_build_plate and node_build_plate_number != active_build_plate_number:
  51. continue
  52. if not node.callDecoration("isGroup"):
  53. name = node.getName()
  54. else:
  55. name = catalog.i18nc("@label", "Group #{group_nr}").format(group_nr = str(group_nr))
  56. group_nr += 1
  57. if hasattr(node, "isOutsideBuildArea"):
  58. is_outside_build_area = node.isOutsideBuildArea()
  59. else:
  60. is_outside_build_area = False
  61. #check if we already have an instance of the object based on name
  62. name_count_dict[name] += 1
  63. name_count = name_count_dict[name]
  64. if name_count > 1:
  65. name = "{0}({1})".format(name, name_count-1)
  66. node.setName(name)
  67. nodes.append({
  68. "name": name,
  69. "isSelected": Selection.isSelected(node),
  70. "isOutsideBuildArea": is_outside_build_area,
  71. "buildPlateNumber": node_build_plate_number,
  72. "node": node
  73. })
  74. nodes = sorted(nodes, key=lambda n: n["name"])
  75. self.setItems(nodes)
  76. self.itemsChanged.emit()
  77. @staticmethod
  78. def createObjectsModel():
  79. return ObjectsModel()