CuraContainerStack.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. # Copyright (c) 2017 Ultimaker B.V.
  2. # Cura is released under the terms of the AGPLv3 or higher.
  3. from typing import Any
  4. from PyQt5.QtCore import pyqtProperty, pyqtSlot, pyqtSignal
  5. from UM.Decorators import override
  6. from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
  7. from UM.Settings.ContainerStack import ContainerStack, InvalidContainerStackError
  8. from UM.Settings.InstanceContainer import InstanceContainer
  9. from UM.Settings.DefinitionContainer import DefinitionContainer
  10. from UM.Settings.ContainerRegistry import ContainerRegistry
  11. from UM.Settings.Interfaces import ContainerInterface
  12. from . import Exceptions
  13. class CuraContainerStack(ContainerStack):
  14. def __init__(self, container_id: str, *args, **kwargs):
  15. super().__init__(container_id, *args, **kwargs)
  16. self._empty_instance_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
  17. self._containers = [self._empty_instance_container for i in range(len(_ContainerIndexes.IndexTypeMap))]
  18. self.containersChanged.connect(self._onContainersChanged)
  19. pyqtContainersChanged = pyqtSignal()
  20. def setUserChanges(self, new_user_changes: InstanceContainer) -> None:
  21. self.replaceContainer(_ContainerIndexes.UserChanges, new_user_changes)
  22. @pyqtProperty(InstanceContainer, fset = setUserChanges, notify = pyqtContainersChanged)
  23. def userChanges(self) -> InstanceContainer:
  24. return self._containers[_ContainerIndexes.UserChanges]
  25. def setQualityChanges(self, new_quality_changes: InstanceContainer) -> None:
  26. self.replaceContainer(_ContainerIndexes.QualityChanges, new_quality_changes)
  27. def setQualityChangesById(self, new_quality_changes_id: str) -> None:
  28. quality_changes = ContainerRegistry.getInstance().findInstanceContainers(id = new_quality_changes_id)
  29. if quality_changes:
  30. self.setQualityChanges(quality_changes[0])
  31. else:
  32. raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_quality_changes_id))
  33. @pyqtProperty(InstanceContainer, fset = setQualityChanges, notify = pyqtContainersChanged)
  34. def qualityChanges(self) -> InstanceContainer:
  35. return self._containers[_ContainerIndexes.QualityChanges]
  36. def setQuality(self, new_quality: InstanceContainer) -> None:
  37. self.replaceContainer(_ContainerIndexes.Quality, new_quality)
  38. def setQualityById(self, new_quality_id: str) -> None:
  39. quality = ContainerRegistry.getInstance().findInstanceContainers(id = new_quality_id)
  40. if quality:
  41. self.setQuality(quality[0])
  42. else:
  43. raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_quality_id))
  44. @pyqtProperty(InstanceContainer, fset = setQuality, notify = pyqtContainersChanged)
  45. def quality(self) -> InstanceContainer:
  46. return self._containers[_ContainerIndexes.Quality]
  47. def setMaterial(self, new_material: InstanceContainer) -> None:
  48. self.replaceContainer(_ContainerIndexes.Material, new_material)
  49. def setMaterialById(self, new_material_id: str) -> None:
  50. material = ContainerRegistry.getInstance().findInstanceContainers(id = new_material_id)
  51. if material:
  52. self.setMaterial(material[0])
  53. else:
  54. raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_material_id))
  55. @pyqtProperty(InstanceContainer, fset = setMaterial, notify = pyqtContainersChanged)
  56. def material(self) -> InstanceContainer:
  57. return self._containers[_ContainerIndexes.Material]
  58. def setVariant(self, new_variant: InstanceContainer) -> None:
  59. self.replaceContainer(_ContainerIndexes.Variant, new_variant)
  60. def setVariantById(self, new_variant_id: str) -> None:
  61. variant = ContainerRegistry.getInstance().findInstanceContainers(id = new_variant_id)
  62. if variant:
  63. self.setVariant(variant[0])
  64. else:
  65. raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_variant_id))
  66. @pyqtProperty(InstanceContainer, fset = setVariant, notify = pyqtContainersChanged)
  67. def variant(self) -> InstanceContainer:
  68. return self._containers[_ContainerIndexes.Variant]
  69. def setDefinitionChanges(self, new_definition_changes: InstanceContainer) -> None:
  70. self.replaceContainer(_ContainerIndexes.DefinitionChanges, new_definition_changes)
  71. def setDefinitionChangesById(self, new_definition_changes_id: str) -> None:
  72. new_definition_changes = ContainerRegistry.getInstance().findInstanceContainers(id = new_definition_changes_id)
  73. if new_definition_changes:
  74. self.setDefinitionChanges(new_definition_changes[0])
  75. else:
  76. raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_definition_changes_id))
  77. @pyqtProperty(InstanceContainer, fset = setDefinitionChanges, notify = pyqtContainersChanged)
  78. def definitionChanges(self) -> InstanceContainer:
  79. return self._containers[_ContainerIndexes.DefinitionChanges]
  80. def setDefinition(self, new_definition: DefinitionContainer) -> None:
  81. self.replaceContainer(_ContainerIndexes.Definition, new_definition)
  82. def setDefinitionById(self, new_definition_id: str) -> None:
  83. new_definition = ContainerRegistry.getInstance().findDefinitionContainers(id = new_definition_id)
  84. if new_definition:
  85. self.setDefinition(new_definition[0])
  86. else:
  87. raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_definition_id))
  88. @pyqtProperty(DefinitionContainer, fset = setDefinition, notify = pyqtContainersChanged)
  89. def definition(self) -> DefinitionContainer:
  90. return self._containers[_ContainerIndexes.Definition]
  91. ## Check whether the specified setting has a 'user' value.
  92. #
  93. # A user value here is defined as the setting having a value in either
  94. # the UserChanges or QualityChanges container.
  95. #
  96. # \return True if the setting has a user value, False if not.
  97. @pyqtSlot(str, result = bool)
  98. def hasUserValue(self, key: str) -> bool:
  99. if self._containers[_ContainerIndexes.UserChanges].hasProperty(key, "value"):
  100. return True
  101. if self._containers[_ContainerIndexes.QualityChanges].hasProperty(key, "value"):
  102. return True
  103. return False
  104. def setProperty(self, key: str, property_name: str, new_value: Any, target_container: str = "user") -> None:
  105. container_index = _ContainerIndexes.indexForType(target_container)
  106. if container_index != -1:
  107. self._containers[container_index].setProperty(key, property_name, new_value)
  108. else:
  109. raise IndexError("Invalid target container {type}".format(type = target_container))
  110. ## Overridden from ContainerStack
  111. #
  112. # Since we have a fixed order of containers in the stack, we want to enforce this.
  113. @override(ContainerStack)
  114. def addContainer(self, container: ContainerInterface) -> None:
  115. raise Exceptions.InvalidOperationError("Cannot add a container to Global stack")
  116. ## Overridden from ContainerStack
  117. @override(ContainerStack)
  118. def insertContainer(self, index: int, container: ContainerInterface) -> None:
  119. raise Exceptions.InvalidOperationError("Cannot insert a container into Global stack")
  120. ## Overridden from ContainerStack
  121. @override(ContainerStack)
  122. def removeContainer(self, index: int) -> None:
  123. raise Exceptions.InvalidOperationError("Cannot remove a container from Global stack")
  124. ## Overridden from ContainerStack
  125. @override(ContainerStack)
  126. def replaceContainer(self, index: int, container: ContainerInterface, postpone_emit: bool = False) -> None:
  127. expected_type = _ContainerIndexes.IndexTypeMap[index]
  128. if expected_type == "definition":
  129. if not isinstance(container, DefinitionContainer):
  130. raise Exceptions.InvalidContainerError("Cannot replace container at index {index} with a container that is not a DefinitionContainer".format(index = index))
  131. elif container != self._empty_instance_container and container.getMetaDataEntry("type") != expected_type:
  132. raise Exceptions.InvalidContainerError("Cannot replace container at index {index} with a container that is not of {type} type, but {actual_type} type.".format(index = index, type = expected_type, actual_type = container.getMetaDataEntry("type")))
  133. super().replaceContainer(index, container, postpone_emit)
  134. ## Overridden from ContainerStack
  135. @override(ContainerStack)
  136. def deserialize(self, contents: str) -> None:
  137. super().deserialize(contents)
  138. new_containers = self._containers.copy()
  139. while(len(new_containers) < len(_ContainerIndexes.IndexTypeMap)):
  140. new_containers.append(self._empty_instance_container)
  141. # Validate and ensure the list of containers matches with what we expect
  142. for index, type_name in _ContainerIndexes.IndexTypeMap.items():
  143. try:
  144. container = new_containers[index]
  145. except IndexError:
  146. container = None
  147. if type_name == "definition":
  148. if not container or not isinstance(container, DefinitionContainer):
  149. definition = self.findContainer(container_type = DefinitionContainer, category = "*")
  150. if not definition:
  151. raise InvalidContainerStackError("Stack {id} does not have a definition!".format(id = self._id))
  152. new_containers[index] = definition
  153. continue
  154. if not container or container.getMetaDataEntry("type") != type_name:
  155. actual_container = self.findContainer(type = type_name)
  156. if actual_container:
  157. new_containers[index] = actual_container
  158. else:
  159. new_containers[index] = self._empty_instance_container
  160. self._containers = new_containers
  161. def _onContainersChanged(self, container):
  162. self.pyqtContainersChanged.emit()
  163. ## private:
  164. class _ContainerIndexes:
  165. UserChanges = 0
  166. QualityChanges = 1
  167. Quality = 2
  168. Material = 3
  169. Variant = 4
  170. DefinitionChanges = 5
  171. Definition = 6
  172. # Simple hash map to map from index to "type" metadata entry
  173. IndexTypeMap = {
  174. UserChanges: "user",
  175. QualityChanges: "quality_changes",
  176. Quality: "quality",
  177. Material: "material",
  178. Variant: "variant",
  179. DefinitionChanges: "definition_changes",
  180. Definition: "definition",
  181. }
  182. @classmethod
  183. def indexForType(cls, type_name: str) -> int:
  184. for key, value in cls.IndexTypeMap.items():
  185. if value == type_name:
  186. return key
  187. return -1