ExtruderManager.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. # Copyright (c) 2016 Ultimaker B.V.
  2. # Cura is released under the terms of the AGPLv3 or higher.
  3. from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant #For communicating data and events to Qt.
  4. from UM.FlameProfiler import pyqtSlot
  5. from UM.Application import Application #To get the global container stack to find the current machine.
  6. from UM.Logger import Logger
  7. from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
  8. from UM.Scene.SceneNode import SceneNode
  9. from UM.Settings.ContainerRegistry import ContainerRegistry #Finding containers by ID.
  10. from UM.Settings.InstanceContainer import InstanceContainer
  11. from UM.Settings.SettingFunction import SettingFunction
  12. from UM.Settings.ContainerStack import ContainerStack
  13. from UM.Settings.DefinitionContainer import DefinitionContainer
  14. from typing import Optional
  15. ## Manages all existing extruder stacks.
  16. #
  17. # This keeps a list of extruder stacks for each machine.
  18. class ExtruderManager(QObject):
  19. ## Signal to notify other components when the list of extruders for a machine definition changes.
  20. extrudersChanged = pyqtSignal(QVariant)
  21. ## Signal to notify other components when the global container stack is switched to a definition
  22. # that has different extruders than the previous global container stack
  23. globalContainerStackDefinitionChanged = pyqtSignal()
  24. ## Notify when the user switches the currently active extruder.
  25. activeExtruderChanged = pyqtSignal()
  26. ## Registers listeners and such to listen to changes to the extruders.
  27. def __init__(self, parent = None):
  28. super().__init__(parent)
  29. self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs.
  30. self._active_extruder_index = 0
  31. Application.getInstance().globalContainerStackChanged.connect(self.__globalContainerStackChanged)
  32. self._global_container_stack_definition_id = None
  33. self._addCurrentMachineExtruders()
  34. ## Gets the unique identifier of the currently active extruder stack.
  35. #
  36. # The currently active extruder stack is the stack that is currently being
  37. # edited.
  38. #
  39. # \return The unique ID of the currently active extruder stack.
  40. @pyqtProperty(str, notify = activeExtruderChanged)
  41. def activeExtruderStackId(self) -> Optional[str]:
  42. if not Application.getInstance().getGlobalContainerStack():
  43. return None # No active machine, so no active extruder.
  44. try:
  45. return self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()][str(self._active_extruder_index)].getId()
  46. except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong.
  47. return None
  48. ## Return extruder count according to extruder trains.
  49. @pyqtProperty(int, notify = extrudersChanged)
  50. def extruderCount(self):
  51. if not Application.getInstance().getGlobalContainerStack():
  52. return 0 # No active machine, so no extruders.
  53. try:
  54. return len(self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()])
  55. except KeyError:
  56. return 0
  57. @pyqtProperty("QVariantMap", notify=extrudersChanged)
  58. def extruderIds(self):
  59. map = {}
  60. for position in self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()]:
  61. map[position] = self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()][position].getId()
  62. return map
  63. @pyqtSlot(str, result = str)
  64. def getQualityChangesIdByExtruderStackId(self, id: str) -> str:
  65. for position in self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()]:
  66. extruder = self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()][position]
  67. if extruder.getId() == id:
  68. return extruder.findContainer(type = "quality_changes").getId()
  69. ## The instance of the singleton pattern.
  70. #
  71. # It's None if the extruder manager hasn't been created yet.
  72. __instance = None
  73. ## Gets an instance of the extruder manager, or creates one if no instance
  74. # exists yet.
  75. #
  76. # This is an implementation of singleton. If an extruder manager already
  77. # exists, it is re-used.
  78. #
  79. # \return The extruder manager.
  80. @classmethod
  81. def getInstance(cls) -> "ExtruderManager":
  82. if not cls.__instance:
  83. cls.__instance = ExtruderManager()
  84. return cls.__instance
  85. ## Changes the active extruder by index.
  86. #
  87. # \param index The index of the new active extruder.
  88. @pyqtSlot(int)
  89. def setActiveExtruderIndex(self, index: int) -> None:
  90. self._active_extruder_index = index
  91. self.activeExtruderChanged.emit()
  92. @pyqtProperty(int, notify = activeExtruderChanged)
  93. def activeExtruderIndex(self) -> int:
  94. return self._active_extruder_index
  95. ## Gets the extruder name of an extruder of the currently active machine.
  96. #
  97. # \param index The index of the extruder whose name to get.
  98. @pyqtSlot(int, result = str)
  99. def getExtruderName(self, index):
  100. try:
  101. return list(self.getActiveExtruderStacks())[index].getName()
  102. except IndexError:
  103. return ""
  104. def getActiveExtruderStack(self) -> ContainerStack:
  105. global_container_stack = Application.getInstance().getGlobalContainerStack()
  106. if global_container_stack:
  107. if global_container_stack.getId() in self._extruder_trains:
  108. if str(self._active_extruder_index) in self._extruder_trains[global_container_stack.getId()]:
  109. return self._extruder_trains[global_container_stack.getId()][str(self._active_extruder_index)]
  110. return None
  111. ## Get an extruder stack by index
  112. def getExtruderStack(self, index):
  113. global_container_stack = Application.getInstance().getGlobalContainerStack()
  114. if global_container_stack:
  115. if global_container_stack.getId() in self._extruder_trains:
  116. if str(index) in self._extruder_trains[global_container_stack.getId()]:
  117. return self._extruder_trains[global_container_stack.getId()][str(index)]
  118. return None
  119. ## Get all extruder stacks
  120. def getExtruderStacks(self):
  121. result = []
  122. for i in range(self.extruderCount):
  123. result.append(self.getExtruderStack(i))
  124. return result
  125. ## Adds all extruders of a specific machine definition to the extruder
  126. # manager.
  127. #
  128. # \param machine_definition The machine definition to add the extruders for.
  129. # \param machine_id The machine_id to add the extruders for.
  130. def addMachineExtruders(self, machine_definition: DefinitionContainer, machine_id: str) -> None:
  131. changed = False
  132. machine_definition_id = machine_definition.getId()
  133. if machine_id not in self._extruder_trains:
  134. self._extruder_trains[machine_id] = { }
  135. changed = True
  136. container_registry = ContainerRegistry.getInstance()
  137. if container_registry:
  138. # Add the extruder trains that don't exist yet.
  139. for extruder_definition in container_registry.findDefinitionContainers(machine = machine_definition_id):
  140. position = extruder_definition.getMetaDataEntry("position", None)
  141. if not position:
  142. Logger.log("w", "Extruder definition %s specifies no position metadata entry.", extruder_definition.getId())
  143. if not container_registry.findContainerStacks(machine = machine_id, position = position): # Doesn't exist yet.
  144. self.createExtruderTrain(extruder_definition, machine_definition, position, machine_id)
  145. changed = True
  146. # Gets the extruder trains that we just created as well as any that still existed.
  147. extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = machine_id)
  148. for extruder_train in extruder_trains:
  149. self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train
  150. # regardless of what the next stack is, we have to set it again, because of signal routing.
  151. extruder_train.setNextStack(Application.getInstance().getGlobalContainerStack())
  152. changed = True
  153. if changed:
  154. self.extrudersChanged.emit(machine_id)
  155. def registerExtruder(self, extruder_train, machine_id):
  156. changed = False
  157. if machine_id not in self._extruder_trains:
  158. self._extruder_trains[machine_id] = {}
  159. changed = True
  160. if extruder_train:
  161. self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train
  162. changed = True
  163. if changed:
  164. self.extrudersChanged.emit(machine_id)
  165. ## Creates a container stack for an extruder train.
  166. #
  167. # The container stack has an extruder definition at the bottom, which is
  168. # linked to a machine definition. Then it has a variant profile, a material
  169. # profile, a quality profile and a user profile, in that order.
  170. #
  171. # The resulting container stack is added to the registry.
  172. #
  173. # \param extruder_definition The extruder to create the extruder train for.
  174. # \param machine_definition The machine that the extruder train belongs to.
  175. # \param position The position of this extruder train in the extruder slots of the machine.
  176. # \param machine_id The id of the "global" stack this extruder is linked to.
  177. def createExtruderTrain(self, extruder_definition: DefinitionContainer, machine_definition: DefinitionContainer,
  178. position, machine_id: str) -> None:
  179. # Cache some things.
  180. container_registry = ContainerRegistry.getInstance()
  181. machine_definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(machine_definition)
  182. # Create a container stack for this extruder.
  183. extruder_stack_id = container_registry.uniqueName(extruder_definition.getId())
  184. container_stack = ContainerStack(extruder_stack_id)
  185. container_stack.setName(extruder_definition.getName()) # Take over the display name to display the stack with.
  186. container_stack.addMetaDataEntry("type", "extruder_train")
  187. container_stack.addMetaDataEntry("machine", machine_id)
  188. container_stack.addMetaDataEntry("position", position)
  189. container_stack.addContainer(extruder_definition)
  190. # Find the variant to use for this extruder.
  191. variant = container_registry.findInstanceContainers(id = "empty_variant")[0]
  192. if machine_definition.getMetaDataEntry("has_variants"):
  193. # First add any variant. Later, overwrite with preference if the preference is valid.
  194. variants = container_registry.findInstanceContainers(definition = machine_definition_id, type = "variant")
  195. if len(variants) >= 1:
  196. variant = variants[0]
  197. preferred_variant_id = machine_definition.getMetaDataEntry("preferred_variant")
  198. if preferred_variant_id:
  199. preferred_variants = container_registry.findInstanceContainers(id = preferred_variant_id, definition = machine_definition_id, type = "variant")
  200. if len(preferred_variants) >= 1:
  201. variant = preferred_variants[0]
  202. else:
  203. Logger.log("w", "The preferred variant \"%s\" of machine %s doesn't exist or is not a variant profile.", preferred_variant_id, machine_id)
  204. # And leave it at the default variant.
  205. container_stack.addContainer(variant)
  206. # Find a material to use for this variant.
  207. material = container_registry.findInstanceContainers(id = "empty_material")[0]
  208. if machine_definition.getMetaDataEntry("has_materials"):
  209. # First add any material. Later, overwrite with preference if the preference is valid.
  210. machine_has_variant_materials = machine_definition.getMetaDataEntry("has_variant_materials", default = False)
  211. if machine_has_variant_materials or machine_has_variant_materials == "True":
  212. materials = container_registry.findInstanceContainers(type = "material", definition = machine_definition_id, variant = variant.getId())
  213. else:
  214. materials = container_registry.findInstanceContainers(type = "material", definition = machine_definition_id)
  215. if len(materials) >= 1:
  216. material = materials[0]
  217. preferred_material_id = machine_definition.getMetaDataEntry("preferred_material")
  218. if preferred_material_id:
  219. search_criteria = { "type": "material", "id": preferred_material_id}
  220. if machine_definition.getMetaDataEntry("has_machine_materials"):
  221. search_criteria["definition"] = machine_definition_id
  222. if machine_definition.getMetaDataEntry("has_variants") and variant:
  223. search_criteria["variant"] = variant.id
  224. else:
  225. search_criteria["definition"] = "fdmprinter"
  226. preferred_materials = container_registry.findInstanceContainers(**search_criteria)
  227. if len(preferred_materials) >= 1:
  228. # In some cases we get multiple materials. In that case, prefer materials that are marked as read only.
  229. read_only_preferred_materials = [preferred_material for preferred_material in preferred_materials if preferred_material.isReadOnly()]
  230. if len(read_only_preferred_materials) >= 1:
  231. material = read_only_preferred_materials[0]
  232. else:
  233. material = preferred_materials[0]
  234. else:
  235. Logger.log("w", "The preferred material \"%s\" of machine %s doesn't exist or is not a material profile.", preferred_material_id, machine_id)
  236. # And leave it at the default material.
  237. container_stack.addContainer(material)
  238. # Find a quality to use for this extruder.
  239. quality = container_registry.getEmptyInstanceContainer()
  240. search_criteria = { "type": "quality" }
  241. if machine_definition.getMetaDataEntry("has_machine_quality"):
  242. search_criteria["definition"] = machine_definition_id
  243. if machine_definition.getMetaDataEntry("has_materials") and material:
  244. search_criteria["material"] = material.id
  245. else:
  246. search_criteria["definition"] = "fdmprinter"
  247. preferred_quality = machine_definition.getMetaDataEntry("preferred_quality")
  248. if preferred_quality:
  249. search_criteria["id"] = preferred_quality
  250. containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
  251. if not containers and preferred_quality:
  252. Logger.log("w", "The preferred quality \"%s\" of machine %s doesn't exist or is not a quality profile.", preferred_quality, machine_id)
  253. search_criteria.pop("id", None)
  254. containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
  255. if containers:
  256. quality = containers[0]
  257. container_stack.addContainer(quality)
  258. empty_quality_changes = container_registry.findInstanceContainers(id = "empty_quality_changes")[0]
  259. container_stack.addContainer(empty_quality_changes)
  260. user_profile = container_registry.findInstanceContainers(type = "user", extruder = extruder_stack_id)
  261. if user_profile: # There was already a user profile, loaded from settings.
  262. user_profile = user_profile[0]
  263. else:
  264. user_profile = InstanceContainer(extruder_stack_id + "_current_settings") # Add an empty user profile.
  265. user_profile.addMetaDataEntry("type", "user")
  266. user_profile.addMetaDataEntry("extruder", extruder_stack_id)
  267. user_profile.setDefinition(machine_definition)
  268. container_registry.addContainer(user_profile)
  269. container_stack.addContainer(user_profile)
  270. # regardless of what the next stack is, we have to set it again, because of signal routing.
  271. container_stack.setNextStack(Application.getInstance().getGlobalContainerStack())
  272. container_registry.addContainer(container_stack)
  273. def getAllExtruderValues(self, setting_key):
  274. return self.getAllExtruderSettings(setting_key, "value")
  275. ## Gets a property of a setting for all extruders.
  276. #
  277. # \param setting_key \type{str} The setting to get the property of.
  278. # \param property \type{str} The property to get.
  279. # \return \type{List} the list of results
  280. def getAllExtruderSettings(self, setting_key, property):
  281. global_container_stack = Application.getInstance().getGlobalContainerStack()
  282. if global_container_stack.getProperty("machine_extruder_count", "value") <= 1:
  283. return [global_container_stack.getProperty(setting_key, property)]
  284. result = []
  285. for index in self.extruderIds:
  286. extruder_stack_id = self.extruderIds[str(index)]
  287. stack = ContainerRegistry.getInstance().findContainerStacks(id = extruder_stack_id)[0]
  288. result.append(stack.getProperty(setting_key, property))
  289. return result
  290. ## Gets the extruder stacks that are actually being used at the moment.
  291. #
  292. # An extruder stack is being used if it is the extruder to print any mesh
  293. # with, or if it is the support infill extruder, the support interface
  294. # extruder, or the bed adhesion extruder.
  295. #
  296. # If there are no extruders, this returns the global stack as a singleton
  297. # list.
  298. #
  299. # \return A list of extruder stacks.
  300. def getUsedExtruderStacks(self):
  301. global_stack = Application.getInstance().getGlobalContainerStack()
  302. container_registry = ContainerRegistry.getInstance()
  303. if global_stack.getProperty("machine_extruder_count", "value") <= 1: #For single extrusion.
  304. return [global_stack]
  305. used_extruder_stack_ids = set()
  306. #Get the extruders of all meshes in the scene.
  307. support_enabled = False
  308. support_interface_enabled = False
  309. scene_root = Application.getInstance().getController().getScene().getRoot()
  310. meshes = [node for node in DepthFirstIterator(scene_root) if type(node) is SceneNode and node.isSelectable()] #Only use the nodes that will be printed.
  311. for mesh in meshes:
  312. extruder_stack_id = mesh.callDecoration("getActiveExtruder")
  313. if not extruder_stack_id: #No per-object settings for this node.
  314. extruder_stack_id = self.extruderIds["0"]
  315. used_extruder_stack_ids.add(extruder_stack_id)
  316. #Get whether any of them use support.
  317. per_mesh_stack = mesh.callDecoration("getStack")
  318. if per_mesh_stack:
  319. support_enabled |= per_mesh_stack.getProperty("support_enable", "value")
  320. support_interface_enabled |= per_mesh_stack.getProperty("support_interface_enable", "value")
  321. else: #Take the setting from the build extruder stack.
  322. extruder_stack = container_registry.findContainerStacks(id = extruder_stack_id)[0]
  323. support_enabled |= extruder_stack.getProperty("support_enable", "value")
  324. support_interface_enabled |= extruder_stack.getProperty("support_enable", "value")
  325. #The support extruders.
  326. if support_enabled:
  327. used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_infill_extruder_nr", "value"))])
  328. used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_extruder_nr_layer_0", "value"))])
  329. if support_interface_enabled:
  330. used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("support_interface_extruder_nr", "value"))])
  331. #The platform adhesion extruder. Not used if using none.
  332. if global_stack.getProperty("adhesion_type", "value") != "none":
  333. used_extruder_stack_ids.add(self.extruderIds[str(global_stack.getProperty("adhesion_extruder_nr", "value"))])
  334. try:
  335. return [container_registry.findContainerStacks(id = stack_id)[0] for stack_id in used_extruder_stack_ids]
  336. except IndexError: # One or more of the extruders was not found.
  337. Logger.log("e", "Unable to find one or more of the extruders in %s", used_extruder_stack_ids)
  338. return []
  339. ## Removes the container stack and user profile for the extruders for a specific machine.
  340. #
  341. # \param machine_id The machine to remove the extruders for.
  342. def removeMachineExtruders(self, machine_id):
  343. for extruder in self.getMachineExtruders(machine_id):
  344. containers = ContainerRegistry.getInstance().findInstanceContainers(type = "user", extruder = extruder.getId())
  345. for container in containers:
  346. ContainerRegistry.getInstance().removeContainer(container.getId())
  347. ContainerRegistry.getInstance().removeContainer(extruder.getId())
  348. ## Returns extruders for a specific machine.
  349. #
  350. # \param machine_id The machine to get the extruders of.
  351. def getMachineExtruders(self, machine_id):
  352. if machine_id not in self._extruder_trains:
  353. Logger.log("w", "Tried to get the extruder trains for machine %s, which doesn't exist.", machine_id)
  354. return []
  355. return [self._extruder_trains[machine_id][name] for name in self._extruder_trains[machine_id]]
  356. ## Returns a list containing the global stack and active extruder stacks.
  357. #
  358. # The first element is the global container stack, followed by any extruder stacks.
  359. # \return \type{List[ContainerStack]}
  360. def getActiveGlobalAndExtruderStacks(self):
  361. global_stack = Application.getInstance().getGlobalContainerStack()
  362. if not global_stack:
  363. return None
  364. result = [global_stack]
  365. result.extend(self.getActiveExtruderStacks())
  366. return result
  367. ## Returns the list of active extruder stacks.
  368. #
  369. # \return \type{List[ContainerStack]} a list of
  370. def getActiveExtruderStacks(self):
  371. global_stack = Application.getInstance().getGlobalContainerStack()
  372. result = []
  373. if global_stack:
  374. for extruder in sorted(self._extruder_trains[global_stack.getId()]):
  375. result.append(self._extruder_trains[global_stack.getId()][extruder])
  376. return result
  377. def __globalContainerStackChanged(self) -> None:
  378. self._addCurrentMachineExtruders()
  379. global_container_stack = Application.getInstance().getGlobalContainerStack()
  380. if global_container_stack and global_container_stack.getBottom() and global_container_stack.getBottom().getId() != self._global_container_stack_definition_id:
  381. self._global_container_stack_definition_id = global_container_stack.getBottom().getId()
  382. self.globalContainerStackDefinitionChanged.emit()
  383. self.activeExtruderChanged.emit()
  384. ## Adds the extruders of the currently active machine.
  385. def _addCurrentMachineExtruders(self) -> None:
  386. global_stack = Application.getInstance().getGlobalContainerStack()
  387. if global_stack and global_stack.getBottom():
  388. self.addMachineExtruders(global_stack.getBottom(), global_stack.getId())
  389. ## Get all extruder values for a certain setting.
  390. #
  391. # This is exposed to SettingFunction so it can be used in value functions.
  392. #
  393. # \param key The key of the setting to retieve values for.
  394. #
  395. # \return A list of values for all extruders. If an extruder does not have a value, it will not be in the list.
  396. # If no extruder has the value, the list will contain the global value.
  397. @staticmethod
  398. def getExtruderValues(key):
  399. global_stack = Application.getInstance().getGlobalContainerStack()
  400. result = []
  401. for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
  402. value = extruder.getRawProperty(key, "value")
  403. if value is None:
  404. continue
  405. if isinstance(value, SettingFunction):
  406. value = value(extruder)
  407. result.append(value)
  408. if not result:
  409. result.append(global_stack.getProperty(key, "value"))
  410. return result
  411. ## Get all extruder values for a certain setting.
  412. #
  413. # This is exposed to qml for display purposes
  414. #
  415. # \param key The key of the setting to retieve values for.
  416. #
  417. # \return String representing the extruder values
  418. @pyqtSlot(str, result="QVariant")
  419. def getInstanceExtruderValues(self, key):
  420. return ExtruderManager.getExtruderValues(key)
  421. ## Get the value for a setting from a specific extruder.
  422. #
  423. # This is exposed to SettingFunction to use in value functions.
  424. #
  425. # \param extruder_index The index of the extruder to get the value from.
  426. # \param key The key of the setting to get the value of.
  427. #
  428. # \return The value of the setting for the specified extruder or for the
  429. # global stack if not found.
  430. @staticmethod
  431. def getExtruderValue(extruder_index, key):
  432. extruder = ExtruderManager.getInstance().getExtruderStack(extruder_index)
  433. if extruder:
  434. value = extruder.getRawProperty(key, "value")
  435. if isinstance(value, SettingFunction):
  436. value = value(extruder)
  437. else: #Just a value from global.
  438. value = Application.getInstance().getGlobalContainerStack().getProperty(key, "value")
  439. return value
  440. ## Get the resolve value or value for a given key
  441. #
  442. # This is the effective value for a given key, it is used for values in the global stack.
  443. # This is exposed to SettingFunction to use in value functions.
  444. # \param key The key of the setting to get the value of.
  445. #
  446. # \return The effective value
  447. @staticmethod
  448. def getResolveOrValue(key):
  449. global_stack = Application.getInstance().getGlobalContainerStack()
  450. resolved_value = global_stack.getProperty(key, "resolve")
  451. if resolved_value is not None:
  452. user_container = global_stack.findContainer({"type": "user"})
  453. quality_changes_container = global_stack.findContainer({"type": "quality_changes"})
  454. if user_container.hasProperty(key, "value") or quality_changes_container.hasProperty(key, "value"):
  455. # Normal case
  456. value = global_stack.getProperty(key, "value")
  457. else:
  458. # We have a resolved value and we're using it because of no user and quality_changes value
  459. value = resolved_value
  460. else:
  461. value = global_stack.getRawProperty(key, "value")
  462. return value