ActiveIntentQualitiesModel.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. # Copyright (c) 2022 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from typing import Optional, Set, Dict, List, Any
  4. from PyQt6.QtCore import Qt, QObject, QTimer
  5. import cura.CuraApplication
  6. from UM.Logger import Logger
  7. from UM.Qt.ListModel import ListModel
  8. from UM.Settings.ContainerStack import ContainerStack
  9. from cura.Machines.ContainerTree import ContainerTree
  10. from cura.Machines.Models.MachineModelUtils import fetchLayerHeight
  11. from cura.Machines.MaterialNode import MaterialNode
  12. from cura.Machines.QualityGroup import QualityGroup
  13. from cura.Settings.IntentManager import IntentManager
  14. class ActiveIntentQualitiesModel(ListModel):
  15. NameRole = Qt.ItemDataRole.UserRole + 1
  16. DisplayTextRole = Qt.ItemDataRole.UserRole + 2
  17. QualityTypeRole = Qt.ItemDataRole.UserRole + 3
  18. LayerHeightRole = Qt.ItemDataRole.UserRole + 4
  19. IntentCategeoryRole = Qt.ItemDataRole.UserRole + 5
  20. def __init__(self, parent: Optional[QObject] = None) -> None:
  21. super().__init__(parent)
  22. self.addRoleName(self.NameRole, "name")
  23. self.addRoleName(self.QualityTypeRole, "quality_type")
  24. self.addRoleName(self.LayerHeightRole, "layer_height")
  25. self.addRoleName(self.DisplayTextRole, "display_text")
  26. self.addRoleName(self.IntentCategeoryRole, "intent_category")
  27. self._intent_category = ""
  28. IntentManager.intentCategoryChangedSignal.connect(self._update)
  29. machine_manager = cura.CuraApplication.CuraApplication.getInstance().getMachineManager()
  30. machine_manager.activeQualityGroupChanged.connect(self._update)
  31. machine_manager.globalContainerChanged.connect(self._updateDelayed)
  32. machine_manager.extruderChanged.connect(self._updateDelayed) # We also need to update if an extruder gets disabled
  33. self._update_timer = QTimer()
  34. self._update_timer.setInterval(100)
  35. self._update_timer.setSingleShot(True)
  36. self._update_timer.timeout.connect(self._update)
  37. self._update()
  38. def _updateDelayed(self):
  39. self._update_timer.start()
  40. def _onChanged(self, container: ContainerStack) -> None:
  41. if container.getMetaDataEntry("type") == "intent":
  42. self._updateDelayed()
  43. def _update(self):
  44. self._intent_category = IntentManager.getInstance().currentIntentCategory
  45. new_items: List[Dict[str, Any]] = []
  46. global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
  47. if not global_stack:
  48. self.setItems(new_items)
  49. return
  50. quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
  51. material_nodes = self._getActiveMaterials()
  52. added_quality_type_set: Set[str] = set()
  53. for material_node in material_nodes:
  54. intents = self._getIntentsForMaterial(material_node, quality_groups)
  55. for intent in intents:
  56. if intent["quality_type"] not in added_quality_type_set:
  57. new_items.append(intent)
  58. added_quality_type_set.add(intent["quality_type"])
  59. new_items = sorted(new_items, key=lambda x: x["layer_height"])
  60. self.setItems(new_items)
  61. def _getActiveMaterials(self) -> Set["MaterialNode"]:
  62. """Get the active materials for all extruders. No duplicates will be returned"""
  63. global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
  64. if global_stack is None:
  65. return set()
  66. container_tree = ContainerTree.getInstance()
  67. machine_node = container_tree.machines[global_stack.definition.getId()]
  68. nodes: Set[MaterialNode] = set()
  69. for extruder in global_stack.extruderList:
  70. active_variant_name = extruder.variant.getMetaDataEntry("name")
  71. if active_variant_name not in machine_node.variants:
  72. Logger.log("w", "Could not find the variant %s", active_variant_name)
  73. continue
  74. active_variant_node = machine_node.variants[active_variant_name]
  75. active_material_node = active_variant_node.materials.get(extruder.material.getMetaDataEntry("base_file"))
  76. if active_material_node is None:
  77. Logger.log("w", "Could not find the material %s", extruder.material.getMetaDataEntry("base_file"))
  78. continue
  79. nodes.add(active_material_node)
  80. return nodes
  81. def _getIntentsForMaterial(self, active_material_node: "MaterialNode", quality_groups: Dict[str, "QualityGroup"]) -> List[Dict[str, Any]]:
  82. extruder_intents: List[Dict[str, Any]] = []
  83. for quality_id, quality_node in active_material_node.qualities.items():
  84. if quality_node.quality_type not in quality_groups: # Don't add the empty quality type (or anything else that would crash, defensively).
  85. continue
  86. quality_group = quality_groups[quality_node.quality_type]
  87. if not quality_group.is_available:
  88. continue
  89. layer_height = fetchLayerHeight(quality_group)
  90. for intent_id, intent_node in quality_node.intents.items():
  91. if intent_node.intent_category != self._intent_category:
  92. continue
  93. extruder_intents.append({"name": quality_group.name,
  94. "display_text": f"<b>{quality_group.name}</b> - {layer_height}mm",
  95. "quality_type": quality_group.quality_type,
  96. "layer_height": layer_height,
  97. "intent_category": self._intent_category
  98. })
  99. return extruder_intents