QualitySettingsModel.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. # Copyright (c) 2022 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from PyQt6.QtCore import pyqtProperty, pyqtSignal, Qt
  4. from typing import Set
  5. import cura.CuraApplication
  6. from UM import i18nCatalog
  7. from UM.Logger import Logger
  8. from UM.Qt.ListModel import ListModel
  9. from UM.Settings.ContainerRegistry import ContainerRegistry
  10. from UM.Settings.SettingFunction import SettingFunction # To format setting functions differently.
  11. import os
  12. class QualitySettingsModel(ListModel):
  13. """This model is used to show details settings of the selected quality in the quality management page."""
  14. KeyRole = Qt.ItemDataRole.UserRole + 1
  15. LabelRole = Qt.ItemDataRole.UserRole + 2
  16. UnitRole = Qt.ItemDataRole.UserRole + 3
  17. ProfileValueRole = Qt.ItemDataRole.UserRole + 4
  18. ProfileValueSourceRole = Qt.ItemDataRole.UserRole + 5
  19. UserValueRole = Qt.ItemDataRole.UserRole + 6
  20. CategoryRole = Qt.ItemDataRole.UserRole + 7
  21. GLOBAL_STACK_POSITION = -1
  22. def __init__(self, parent = None) -> None:
  23. super().__init__(parent = parent)
  24. self.addRoleName(self.KeyRole, "key")
  25. self.addRoleName(self.LabelRole, "label")
  26. self.addRoleName(self.UnitRole, "unit")
  27. self.addRoleName(self.ProfileValueRole, "profile_value")
  28. self.addRoleName(self.ProfileValueSourceRole, "profile_value_source")
  29. self.addRoleName(self.UserValueRole, "user_value")
  30. self.addRoleName(self.CategoryRole, "category")
  31. self._container_registry = ContainerRegistry.getInstance()
  32. self._application = cura.CuraApplication.CuraApplication.getInstance()
  33. self._application.getMachineManager().activeStackChanged.connect(self._update)
  34. # Must be either GLOBAL_STACK_POSITION or an extruder position (0, 1, etc.)
  35. self._selected_position = self.GLOBAL_STACK_POSITION
  36. self._selected_quality_item = None # The selected quality in the quality management page
  37. self._i18n_catalog = None
  38. self._update()
  39. selectedPositionChanged = pyqtSignal()
  40. selectedQualityItemChanged = pyqtSignal()
  41. def setSelectedPosition(self, selected_position: int) -> None:
  42. if selected_position != self._selected_position:
  43. self._selected_position = selected_position
  44. self.selectedPositionChanged.emit()
  45. self._update()
  46. @pyqtProperty(int, fset = setSelectedPosition, notify = selectedPositionChanged)
  47. def selectedPosition(self) -> int:
  48. return self._selected_position
  49. def setSelectedQualityItem(self, selected_quality_item):
  50. if selected_quality_item != self._selected_quality_item:
  51. self._selected_quality_item = selected_quality_item
  52. self.selectedQualityItemChanged.emit()
  53. self._update()
  54. @pyqtProperty("QVariantMap", fset = setSelectedQualityItem, notify = selectedQualityItemChanged)
  55. def selectedQualityItem(self):
  56. return self._selected_quality_item
  57. def _update(self) -> None:
  58. Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
  59. if not self._selected_quality_item:
  60. self.setItems([])
  61. return
  62. items = []
  63. global_container_stack = self._application.getGlobalContainerStack()
  64. definition_container = global_container_stack.definition
  65. # Try and find a translation catalog for the definition
  66. for file_name in definition_container.getInheritedFiles():
  67. catalog = i18nCatalog(os.path.basename(file_name))
  68. if catalog.hasTranslationLoaded():
  69. self._i18n_catalog = catalog
  70. quality_group = self._selected_quality_item["quality_group"]
  71. quality_changes_group = self._selected_quality_item["quality_changes_group"]
  72. quality_node = None
  73. settings_keys = set() # type: Set[str]
  74. if quality_group:
  75. if self._selected_position == self.GLOBAL_STACK_POSITION:
  76. quality_node = quality_group.node_for_global
  77. else:
  78. quality_node = quality_group.nodes_for_extruders.get(self._selected_position)
  79. settings_keys = quality_group.getAllKeys()
  80. quality_containers = []
  81. if quality_node is not None and quality_node.container is not None:
  82. quality_containers.append(quality_node.container)
  83. # Here, if the user has selected a quality changes, then "quality_changes_group" will not be None, and we fetch
  84. # the settings in that quality_changes_group.
  85. if quality_changes_group is not None:
  86. container_registry = ContainerRegistry.getInstance()
  87. metadata_for_global = quality_changes_group.metadata_for_global
  88. global_containers = container_registry.findContainers(id = metadata_for_global["id"])
  89. global_container = None if len(global_containers) == 0 else global_containers[0]
  90. extruders_containers = {pos: container_registry.findContainers(id = quality_changes_group.metadata_per_extruder[pos]["id"]) for pos in quality_changes_group.metadata_per_extruder}
  91. extruders_container = {pos: None if not containers else containers[0] for pos, containers in extruders_containers.items()}
  92. quality_changes_metadata = None
  93. if self._selected_position == self.GLOBAL_STACK_POSITION and global_container:
  94. quality_changes_metadata = global_container.getMetaData()
  95. else:
  96. extruder = extruders_container.get(self._selected_position)
  97. if extruder:
  98. quality_changes_metadata = extruder.getMetaData()
  99. if quality_changes_metadata is not None: # It can be None if number of extruders are changed during runtime.
  100. container = container_registry.findContainers(id = quality_changes_metadata["id"])
  101. if container:
  102. quality_containers.insert(0, container[0])
  103. if global_container:
  104. settings_keys.update(global_container.getAllKeys())
  105. for container in extruders_container.values():
  106. if container:
  107. settings_keys.update(container.getAllKeys())
  108. # We iterate over all definitions instead of settings in a quality/quality_changes group is because in the GUI,
  109. # the settings are grouped together by categories, and we had to go over all the definitions to figure out
  110. # which setting belongs in which category.
  111. current_category = ""
  112. for definition in definition_container.findDefinitions():
  113. if definition.type == "category":
  114. current_category = definition.label
  115. if self._i18n_catalog:
  116. current_category = self._i18n_catalog.i18nc(definition.key + " label", definition.label)
  117. continue
  118. profile_value = None
  119. profile_value_source = ""
  120. for quality_container in quality_containers:
  121. new_value = quality_container.getProperty(definition.key, "value")
  122. if new_value is not None:
  123. profile_value_source = quality_container.getMetaDataEntry("type")
  124. profile_value = new_value
  125. # Global tab should use resolve (if there is one)
  126. if self._selected_position == self.GLOBAL_STACK_POSITION:
  127. resolve_value = global_container_stack.getProperty(definition.key, "resolve")
  128. if resolve_value is not None and definition.key in settings_keys:
  129. profile_value = resolve_value
  130. if profile_value is not None:
  131. break
  132. if self._selected_position == self.GLOBAL_STACK_POSITION:
  133. user_value = global_container_stack.userChanges.getProperty(definition.key, "value")
  134. else:
  135. extruder_stack = global_container_stack.extruderList[self._selected_position]
  136. user_value = extruder_stack.userChanges.getProperty(definition.key, "value")
  137. if profile_value is None and user_value is None:
  138. continue
  139. label = definition.label
  140. if self._i18n_catalog:
  141. label = self._i18n_catalog.i18nc(definition.key + " label", label)
  142. if profile_value_source == "quality_changes":
  143. label = f"<i>{label}</i>" # Make setting name italic if it's derived from the quality-changes profile.
  144. if isinstance(profile_value, SettingFunction):
  145. if self._i18n_catalog:
  146. profile_value_display = self._i18n_catalog.i18nc("@info:status", "Calculated")
  147. else:
  148. profile_value_display = "Calculated"
  149. else:
  150. profile_value_display = "" if profile_value is None else str(profile_value)
  151. items.append({
  152. "key": definition.key,
  153. "label": label,
  154. "unit": definition.unit,
  155. "profile_value": profile_value_display,
  156. "profile_value_source": profile_value_source,
  157. "user_value": "" if user_value is None else str(user_value),
  158. "category": current_category
  159. })
  160. self.setItems(items)