BaseMaterialsModel.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. # Copyright (c) 2019 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from typing import Optional, Dict, Set
  4. from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty
  5. from UM.Qt.ListModel import ListModel
  6. import cura.CuraApplication # Imported like this to prevent a circular reference.
  7. from cura.Machines.ContainerTree import ContainerTree
  8. from cura.Machines.MaterialNode import MaterialNode
  9. from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
  10. ## This is the base model class for GenericMaterialsModel and MaterialBrandsModel.
  11. # Those 2 models are used by the material drop down menu to show generic materials and branded materials separately.
  12. # The extruder position defined here is being used to bound a menu to the correct extruder. This is used in the top
  13. # bar menu "Settings" -> "Extruder nr" -> "Material" -> this menu
  14. class BaseMaterialsModel(ListModel):
  15. extruderPositionChanged = pyqtSignal()
  16. enabledChanged = pyqtSignal()
  17. def __init__(self, parent = None):
  18. super().__init__(parent)
  19. from cura.CuraApplication import CuraApplication
  20. self._application = CuraApplication.getInstance()
  21. # Make these managers available to all material models
  22. self._container_registry = self._application.getInstance().getContainerRegistry()
  23. self._machine_manager = self._application.getMachineManager()
  24. self._extruder_position = 0
  25. self._extruder_stack = None
  26. self._enabled = True
  27. # Update the stack and the model data when the machine changes
  28. self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack)
  29. self._updateExtruderStack()
  30. # Update this model when switching machines, when adding materials or changing their metadata.
  31. self._machine_manager.activeStackChanged.connect(self._update)
  32. ContainerTree.getInstance().materialsChanged.connect(self._materialsListChanged)
  33. self.addRoleName(Qt.UserRole + 1, "root_material_id")
  34. self.addRoleName(Qt.UserRole + 2, "id")
  35. self.addRoleName(Qt.UserRole + 3, "GUID")
  36. self.addRoleName(Qt.UserRole + 4, "name")
  37. self.addRoleName(Qt.UserRole + 5, "brand")
  38. self.addRoleName(Qt.UserRole + 6, "description")
  39. self.addRoleName(Qt.UserRole + 7, "material")
  40. self.addRoleName(Qt.UserRole + 8, "color_name")
  41. self.addRoleName(Qt.UserRole + 9, "color_code")
  42. self.addRoleName(Qt.UserRole + 10, "density")
  43. self.addRoleName(Qt.UserRole + 11, "diameter")
  44. self.addRoleName(Qt.UserRole + 12, "approximate_diameter")
  45. self.addRoleName(Qt.UserRole + 13, "adhesion_info")
  46. self.addRoleName(Qt.UserRole + 14, "is_read_only")
  47. self.addRoleName(Qt.UserRole + 15, "container_node")
  48. self.addRoleName(Qt.UserRole + 16, "is_favorite")
  49. self._available_materials = None # type: Optional[Dict[str, MaterialNode]]
  50. self._favorite_ids = set() # type: Set[str]
  51. def _updateExtruderStack(self):
  52. global_stack = self._machine_manager.activeMachine
  53. if global_stack is None:
  54. return
  55. if self._extruder_stack is not None:
  56. self._extruder_stack.pyqtContainersChanged.disconnect(self._update)
  57. self._extruder_stack.approximateMaterialDiameterChanged.disconnect(self._update)
  58. self._extruder_stack = global_stack.extruders.get(str(self._extruder_position))
  59. if self._extruder_stack is not None:
  60. self._extruder_stack.pyqtContainersChanged.connect(self._update)
  61. self._extruder_stack.approximateMaterialDiameterChanged.connect(self._update)
  62. # Force update the model when the extruder stack changes
  63. self._update()
  64. def setExtruderPosition(self, position: int):
  65. if self._extruder_stack is None or self._extruder_position != position:
  66. self._extruder_position = position
  67. self._updateExtruderStack()
  68. self.extruderPositionChanged.emit()
  69. @pyqtProperty(int, fset = setExtruderPosition, notify = extruderPositionChanged)
  70. def extruderPosition(self) -> int:
  71. return self._extruder_position
  72. def setEnabled(self, enabled):
  73. if self._enabled != enabled:
  74. self._enabled = enabled
  75. if self._enabled:
  76. # ensure the data is there again.
  77. self._update()
  78. self.enabledChanged.emit()
  79. @pyqtProperty(bool, fset = setEnabled, notify = enabledChanged)
  80. def enabled(self):
  81. return self._enabled
  82. ## Triggered when a list of materials changed somewhere in the container
  83. # tree. This change may trigger an _update() call when the materials
  84. # changed for the configuration that this model is looking for.
  85. def _materialsListChanged(self, material: MaterialNode) -> None:
  86. if self._extruder_stack is None:
  87. return
  88. if material.variant.container_id != self._extruder_stack.variant.getId():
  89. return
  90. global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
  91. if not global_stack:
  92. return
  93. if material.variant.machine.container_id != global_stack.definition.getId():
  94. return
  95. self._update()
  96. ## This is an abstract method that needs to be implemented by the specific
  97. # models themselves.
  98. def _update(self):
  99. self._favorite_ids = set(cura.CuraApplication.CuraApplication.getInstance().getPreferences().getValue("cura/favorite_materials").split(";"))
  100. # Update the available materials (ContainerNode) for the current active machine and extruder setup.
  101. global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
  102. extruder_stack = global_stack.extruders.get(str(self._extruder_position))
  103. if not extruder_stack:
  104. return
  105. nozzle_name = extruder_stack.variant.getName()
  106. materials = ContainerTree.getInstance().machines[global_stack.definition.getId()].variants[nozzle_name].materials
  107. compatible_material_diameter = str(round(extruder_stack.getCompatibleMaterialDiameter()))
  108. self._available_materials = {key: material for key, material in materials.items() if material.container.getMetaDataEntry("approximate_diameter") == compatible_material_diameter}
  109. ## This method is used by all material models in the beginning of the
  110. # _update() method in order to prevent errors. It's the same in all models
  111. # so it's placed here for easy access.
  112. def _canUpdate(self):
  113. global_stack = self._machine_manager.activeMachine
  114. if global_stack is None or not self._enabled:
  115. return False
  116. extruder_position = str(self._extruder_position)
  117. if extruder_position not in global_stack.extruders:
  118. return False
  119. return True
  120. ## This is another convenience function which is shared by all material
  121. # models so it's put here to avoid having so much duplicated code.
  122. def _createMaterialItem(self, root_material_id, container_node):
  123. metadata_list = CuraContainerRegistry.getInstance().findContainersMetadata(id = container_node.container_id)
  124. if not metadata_list:
  125. return None
  126. metadata = metadata_list[0]
  127. item = {
  128. "root_material_id": root_material_id,
  129. "id": metadata["id"],
  130. "container_id": metadata["id"], # TODO: Remove duplicate in material manager qml
  131. "GUID": metadata["GUID"],
  132. "name": metadata["name"],
  133. "brand": metadata["brand"],
  134. "description": metadata["description"],
  135. "material": metadata["material"],
  136. "color_name": metadata["color_name"],
  137. "color_code": metadata.get("color_code", ""),
  138. "density": metadata.get("properties", {}).get("density", ""),
  139. "diameter": metadata.get("properties", {}).get("diameter", ""),
  140. "approximate_diameter": metadata["approximate_diameter"],
  141. "adhesion_info": metadata["adhesion_info"],
  142. "is_read_only": self._container_registry.isReadOnly(metadata["id"]),
  143. "container_node": container_node,
  144. "is_favorite": root_material_id in self._favorite_ids
  145. }
  146. return item