Let Quality/Material/Variant Managers be a proper singleton

Rather than a singleton contained within the CuraApplication class.

Contributes to issue CURA-6600.
This commit is contained in:
Ghostkeeper 2019-08-08 17:04:53 +02:00
parent f31d7798ce
commit 65360c31ef
No known key found for this signature in database
GPG key ID: 86BEF881AE2CF276
4 changed files with 88 additions and 76 deletions

View file

@ -15,7 +15,7 @@ from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qm
from UM.i18n import i18nCatalog
from UM.Application import Application
from UM.Decorators import override
from UM.Decorators import override, deprecated
from UM.FlameProfiler import pyqtSlot
from UM.Logger import Logger
from UM.Message import Message
@ -23,7 +23,6 @@ from UM.Platform import Platform
from UM.PluginError import PluginNotFoundError
from UM.Resources import Resources
from UM.Preferences import Preferences
from UM.Qt.Bindings import MainWindow
from UM.Qt.QtApplication import QtApplication # The class we're inheriting from.
import UM.Util
from UM.View.SelectionPass import SelectionPass # For typing.
@ -47,7 +46,6 @@ from UM.Scene.Selection import Selection
from UM.Scene.ToolHandle import ToolHandle
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
from UM.Settings.SettingFunction import SettingFunction
@ -73,6 +71,8 @@ from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
from cura.Scene import ZOffsetDecorator
from cura.Machines.MachineErrorChecker import MachineErrorChecker
import cura.Machines.MaterialManager #Imported like this to prevent circular imports.
import cura.Machines.QualityManager #Imported like this to prevent circular imports.
from cura.Machines.VariantManager import VariantManager
from cura.Machines.Models.BuildPlateModel import BuildPlateModel
@ -136,8 +136,6 @@ from cura import ApplicationMetadata, UltimakerCloudAuthentication
from cura.Settings.GlobalStack import GlobalStack
if TYPE_CHECKING:
from cura.Machines.MaterialManager import MaterialManager
from cura.Machines.QualityManager import QualityManager
from UM.Settings.EmptyInstanceContainer import EmptyInstanceContainer
numpy.seterr(all = "ignore")
@ -205,9 +203,7 @@ class CuraApplication(QtApplication):
self.empty_quality_container = None # type: EmptyInstanceContainer
self.empty_quality_changes_container = None # type: EmptyInstanceContainer
self._variant_manager = None
self._material_manager = None
self._quality_manager = None
self._machine_manager = None
self._extruder_manager = None
self._container_manager = None
@ -734,21 +730,6 @@ class CuraApplication(QtApplication):
def run(self):
super().run()
container_registry = self._container_registry
Logger.log("i", "Initializing variant manager")
self._variant_manager = VariantManager(container_registry)
self._variant_manager.initialize()
Logger.log("i", "Initializing material manager")
from cura.Machines.MaterialManager import MaterialManager
self._material_manager = MaterialManager(container_registry, parent = self)
self._material_manager.initialize()
Logger.log("i", "Initializing quality manager")
from cura.Machines.QualityManager import QualityManager
self._quality_manager = QualityManager(self, parent = self)
self._quality_manager.initialize()
Logger.log("i", "Initializing machine manager")
self._machine_manager = MachineManager(self, parent = self)
@ -928,16 +909,19 @@ class CuraApplication(QtApplication):
self._extruder_manager = ExtruderManager()
return self._extruder_manager
@deprecated("Use the ContainerTree structure instead.", since = "4.3")
def getVariantManager(self, *args) -> VariantManager:
return self._variant_manager
return VariantManager.getInstance()
# Can't deprecate this function since the deprecation marker collides with pyqtSlot!
@pyqtSlot(result = QObject)
def getMaterialManager(self, *args) -> "MaterialManager":
return self._material_manager
return cura.Machines.MaterialManager.MaterialManager.getInstance()
# Can't deprecate this function since the deprecation marker collides with pyqtSlot!
@pyqtSlot(result = QObject)
def getQualityManager(self, *args) -> "QualityManager":
return self._quality_manager
return cura.Machines.QualityManager.QualityManager.getInstance()
def getIntentManager(self, *args) -> IntentManager:
return IntentManager.getInstance()

View file

@ -1,4 +1,4 @@
# Copyright (c) 2018 Ultimaker B.V.
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from collections import defaultdict, OrderedDict
@ -8,12 +8,13 @@ from typing import Dict, Optional, TYPE_CHECKING, Any, Set, List, cast, Tuple
from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot
from UM.Application import Application
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
from UM.Decorators import deprecated
from UM.Logger import Logger
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.SettingFunction import SettingFunction
from UM.Util import parseBool
import cura.CuraApplication #Imported like this to prevent circular imports.
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
from .MaterialNode import MaterialNode
from .MaterialGroup import MaterialGroup
@ -37,15 +38,20 @@ if TYPE_CHECKING:
# because it's simple.
#
class MaterialManager(QObject):
__instance = None
@classmethod
@deprecated("Use the ContainerTree structure instead.", since = "4.3")
def getInstance(cls) -> "MaterialManager":
if cls.__instance is None:
cls.__instance = MaterialManager()
return cls.__instance
materialsUpdated = pyqtSignal() # Emitted whenever the material lookup tables are updated.
favoritesUpdated = pyqtSignal() # Emitted whenever the favorites are changed
def __init__(self, container_registry, parent = None):
def __init__(self, parent = None):
super().__init__(parent)
self._application = Application.getInstance()
self._container_registry = container_registry # type: ContainerRegistry
# Material_type -> generic material metadata
self._fallback_materials_map = dict() # type: Dict[str, Dict[str, Any]]
@ -81,16 +87,18 @@ class MaterialManager(QObject):
self._update_timer.setSingleShot(True)
self._update_timer.timeout.connect(self._updateMaps)
self._container_registry.containerMetaDataChanged.connect(self._onContainerMetadataChanged)
self._container_registry.containerAdded.connect(self._onContainerMetadataChanged)
self._container_registry.containerRemoved.connect(self._onContainerMetadataChanged)
container_registry = CuraContainerRegistry.getInstance()
container_registry.containerMetaDataChanged.connect(self._onContainerMetadataChanged)
container_registry.containerAdded.connect(self._onContainerMetadataChanged)
container_registry.containerRemoved.connect(self._onContainerMetadataChanged)
self._favorites = set() # type: Set[str]
def initialize(self) -> None:
# Find all materials and put them in a matrix for quick search.
container_registry = CuraContainerRegistry.getInstance()
material_metadatas = {metadata["id"]: metadata for metadata in
self._container_registry.findContainersMetadata(type = "material") if
container_registry.findContainersMetadata(type = "material") if
metadata.get("GUID")} # type: Dict[str, Dict[str, Any]]
self._material_group_map = dict() # type: Dict[str, MaterialGroup]
@ -107,7 +115,7 @@ class MaterialManager(QObject):
continue
if root_material_id not in self._material_group_map:
self._material_group_map[root_material_id] = MaterialGroup(root_material_id, MaterialNode(material_metadatas[root_material_id]))
self._material_group_map[root_material_id].is_read_only = self._container_registry.isReadOnly(root_material_id)
self._material_group_map[root_material_id].is_read_only = container_registry.isReadOnly(root_material_id)
group = self._material_group_map[root_material_id]
# Store this material in the group of the appropriate root material.
@ -206,7 +214,7 @@ class MaterialManager(QObject):
for material_metadata in material_metadatas.values():
self.__addMaterialMetadataIntoLookupTree(material_metadata)
favorites = self._application.getPreferences().getValue("cura/favorite_materials")
favorites = cura.CuraApplication.CuraApplication.getInstance().getPreferences().getValue("cura/favorite_materials")
for item in favorites.split(";"):
self._favorites.add(item)
@ -239,7 +247,7 @@ class MaterialManager(QObject):
(buildplate_name, VariantType.BUILD_PLATE),
]
variant_manager = self._application.getVariantManager()
variant_manager = cura.CuraApplication.CuraApplication.getInstance().getVariantManager()
machine_node = machine_nozzle_buildplate_material_map[definition]
current_node = machine_node
@ -264,7 +272,7 @@ class MaterialManager(QObject):
if error_message is not None:
Logger.log("e", "%s It will not be added into the material lookup tree.", error_message)
self._container_registry.addWrongContainerId(material_metadata["id"])
CuraContainerRegistry.getInstance().addWrongContainerId(material_metadata["id"])
return
# Add the material to the current tree node, which is the deepest (the most specific) branch we can find.
@ -537,6 +545,7 @@ class MaterialManager(QObject):
Logger.log("i", "Unable to remove the material with id %s, because it doesn't exist.", root_material_id)
return
container_registry = CuraContainerRegistry.getInstance()
nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list
# Sort all nodes with respect to the container ID lengths in the ascending order so the base material container
# will be the first one to be removed. We need to do this to ensure that all containers get loaded & deleted.
@ -545,11 +554,11 @@ class MaterialManager(QObject):
# list, so removeContainer() can ignore those ones.
for node in nodes_to_remove:
container_id = node.getMetaDataEntry("id", "")
results = self._container_registry.findContainers(id = container_id)
results = container_registry.findContainers(id = container_id)
if not results:
self._container_registry.addWrongContainerId(container_id)
container_registry.addWrongContainerId(container_id)
for node in nodes_to_remove:
self._container_registry.removeContainer(node.getMetaDataEntry("id", ""))
container_registry.removeContainer(node.getMetaDataEntry("id", ""))
#
# Methods for GUI
@ -567,7 +576,7 @@ class MaterialManager(QObject):
nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list
ids_to_remove = [node.getMetaDataEntry("id", "") for node in nodes_to_remove]
for extruder_stack in self._container_registry.findContainerStacks(type="extruder_train"):
for extruder_stack in CuraContainerRegistry.getInstance().findContainerStacks(type = "extruder_train"):
if extruder_stack.material.getId() in ids_to_remove:
return False
return True
@ -577,7 +586,7 @@ class MaterialManager(QObject):
root_material_id = material_node.getMetaDataEntry("base_file")
if root_material_id is None:
return
if self._container_registry.isReadOnly(root_material_id):
if CuraContainerRegistry.getInstance().isReadOnly(root_material_id):
Logger.log("w", "Cannot set name of read-only container %s.", root_material_id)
return
@ -614,12 +623,13 @@ class MaterialManager(QObject):
return None
# Ensure all settings are saved.
self._application.saveSettings()
cura.CuraApplication.CuraApplication.getInstance().saveSettings()
# Create a new ID & container to hold the data.
new_containers = []
container_registry = CuraContainerRegistry.getInstance()
if new_base_id is None:
new_base_id = self._container_registry.uniqueName(base_container.getId())
new_base_id = container_registry.uniqueName(base_container.getId())
new_base_container = copy.deepcopy(base_container)
new_base_container.getMetaData()["id"] = new_base_id
new_base_container.getMetaData()["base_file"] = new_base_id
@ -652,7 +662,7 @@ class MaterialManager(QObject):
for container_to_add in new_containers:
container_to_add.setDirty(True)
self._container_registry.addContainer(container_to_add)
container_registry.addContainer(container_to_add)
# if the duplicated material was favorite then the new material should also be added to favorite.
if root_material_id in self.getFavorites():
@ -668,12 +678,13 @@ class MaterialManager(QObject):
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
# Ensure all settings are saved.
self._application.saveSettings()
application = cura.CuraApplication.CuraApplication.getInstance()
application.saveSettings()
machine_manager = self._application.getMachineManager()
machine_manager = application.getMachineManager()
extruder_stack = machine_manager.activeStack
machine_definition = self._application.getGlobalContainerStack().definition
machine_definition = application.getGlobalContainerStack().definition
root_material_id = machine_definition.getMetaDataEntry("preferred_material", default = "generic_pla")
approximate_diameter = str(extruder_stack.approximateMaterialDiameter)
@ -685,7 +696,7 @@ class MaterialManager(QObject):
return ""
# Create a new ID & container to hold the data.
new_id = self._container_registry.uniqueName("custom_material")
new_id = CuraContainerRegistry.getInstance().uniqueName("custom_material")
new_metadata = {"name": catalog.i18nc("@label", "Custom Material"),
"brand": catalog.i18nc("@label", "Custom"),
"GUID": str(uuid.uuid4()),
@ -702,8 +713,8 @@ class MaterialManager(QObject):
self.materialsUpdated.emit()
# Ensure all settings are saved.
self._application.getPreferences().setValue("cura/favorite_materials", ";".join(list(self._favorites)))
self._application.saveSettings()
cura.CuraApplication.CuraApplication.getInstance().getPreferences().setValue("cura/favorite_materials", ";".join(list(self._favorites)))
cura.CuraApplication.CuraApplication.getInstance().saveSettings()
@pyqtSlot(str)
def removeFavorite(self, root_material_id: str) -> None:
@ -715,8 +726,8 @@ class MaterialManager(QObject):
self.materialsUpdated.emit()
# Ensure all settings are saved.
self._application.getPreferences().setValue("cura/favorite_materials", ";".join(list(self._favorites)))
self._application.saveSettings()
cura.CuraApplication.CuraApplication.getInstance().getPreferences().setValue("cura/favorite_materials", ";".join(list(self._favorites)))
cura.CuraApplication.CuraApplication.getInstance().saveSettings()
@pyqtSlot()
def getFavorites(self):

View file

@ -1,4 +1,4 @@
# Copyright (c) 2018 Ultimaker B.V.
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import TYPE_CHECKING, Optional, cast, Dict, List, Set
@ -9,10 +9,11 @@ from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
from UM.Logger import Logger
from UM.Util import parseBool
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Decorators import deprecated
import cura.CuraApplication
from cura.Settings.ExtruderStack import ExtruderStack
from cura.Machines.ContainerTree import ContainerTree
from .QualityGroup import QualityGroup
from .QualityNode import QualityNode
@ -20,7 +21,6 @@ if TYPE_CHECKING:
from UM.Settings.Interfaces import DefinitionContainerInterface
from cura.Settings.GlobalStack import GlobalStack
from .QualityChangesGroup import QualityChangesGroup
from cura.CuraApplication import CuraApplication
#
@ -34,17 +34,25 @@ if TYPE_CHECKING:
# because it's simple.
#
class QualityManager(QObject):
__instance = None
@classmethod
@deprecated("Use the ContainerTree structure instead.", since = "4.3")
def getInstance(cls) -> "QualityManager":
if cls.__instance is None:
cls.__instance = QualityManager()
return cls.__instance
qualitiesUpdated = pyqtSignal()
def __init__(self, application: "CuraApplication", parent = None) -> None:
def __init__(self, parent = None) -> None:
super().__init__(parent)
self._application = application
self._material_manager = self._application.getMaterialManager()
self._container_registry = self._application.getContainerRegistry()
application = cura.CuraApplication.CuraApplication.getInstance()
self._material_manager = application.getMaterialManager()
self._container_registry = application.getContainerRegistry()
self._empty_quality_container = self._application.empty_quality_container
self._empty_quality_changes_container = self._application.empty_quality_changes_container
self._empty_quality_container = application.empty_quality_container
self._empty_quality_changes_container = application.empty_quality_changes_container
# For quality lookup
self._machine_nozzle_buildplate_material_quality_type_to_quality_dict = {} # type: Dict[str, QualityNode]
@ -422,8 +430,9 @@ class QualityManager(QObject):
quality_changes_group.name = new_name
self._application.getMachineManager().activeQualityChanged.emit()
self._application.getMachineManager().activeQualityGroupChanged.emit()
application = cura.CuraApplication.CuraApplication.getInstance()
application.getMachineManager().activeQualityChanged.emit()
application.getMachineManager().activeQualityGroupChanged.emit()
return new_name
@ -432,7 +441,7 @@ class QualityManager(QObject):
#
@pyqtSlot(str, "QVariantMap")
def duplicateQualityChanges(self, quality_changes_name: str, quality_model_item) -> None:
global_stack = self._application.getGlobalContainerStack()
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if not global_stack:
Logger.log("i", "No active global stack, cannot duplicate quality changes.")
return
@ -461,7 +470,7 @@ class QualityManager(QObject):
# stack and clear the user settings.
@pyqtSlot(str)
def createQualityChanges(self, base_name: str) -> None:
machine_manager = self._application.getMachineManager()
machine_manager = cura.CuraApplication.CuraApplication.getInstance().getMachineManager()
global_stack = machine_manager.activeMachine
if not global_stack:
@ -522,7 +531,7 @@ class QualityManager(QObject):
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
quality_changes.setDefinition(machine_definition_id)
quality_changes.setMetaDataEntry("setting_version", self._application.SettingVersion)
quality_changes.setMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.getInstance().SettingVersion)
return quality_changes

View file

@ -1,16 +1,17 @@
# Copyright (c) 2018 Ultimaker B.V.
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from collections import OrderedDict
from typing import Optional, TYPE_CHECKING, Dict
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
from UM.Decorators import deprecated
from UM.Logger import Logger
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Util import parseBool
from cura.Machines.ContainerNode import ContainerNode
from cura.Machines.VariantType import VariantType, ALL_VARIANT_TYPES
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
from cura.Settings.GlobalStack import GlobalStack
if TYPE_CHECKING:
@ -35,10 +36,16 @@ if TYPE_CHECKING:
# A container is loaded when getVariant() is called to load a variant InstanceContainer.
#
class VariantManager:
__instance = None
def __init__(self, container_registry: ContainerRegistry) -> None:
self._container_registry = container_registry
@classmethod
@deprecated("Use the ContainerTree structure instead.", since = "4.3")
def getInstance(cls) -> "VariantManager":
if cls.__instance is None:
cls.__instance = VariantManager()
return cls.__instance
def __init__(self) -> None:
self._machine_to_variant_dict_map = dict() # type: Dict[str, Dict["VariantType", Dict[str, ContainerNode]]]
self._machine_to_buildplate_dict_map = dict() # type: Dict[str, Dict[str, ContainerNode]]
@ -53,7 +60,8 @@ class VariantManager:
self._machine_to_buildplate_dict_map = OrderedDict()
# Cache all variants from the container registry to a variant map for better searching and organization.
variant_metadata_list = self._container_registry.findContainersMetadata(type = "variant")
container_registry = CuraContainerRegistry.getInstance
variant_metadata_list = container_registry.findContainersMetadata(type = "variant")
for variant_metadata in variant_metadata_list:
if variant_metadata["id"] in self._exclude_variant_id_list:
Logger.log("d", "Exclude variant [%s]", variant_metadata["id"])
@ -85,7 +93,7 @@ class VariantManager:
if variant_definition not in self._machine_to_buildplate_dict_map:
self._machine_to_buildplate_dict_map[variant_definition] = OrderedDict()
variant_container = self._container_registry.findContainers(type = "variant", id = variant_metadata["id"])[0]
variant_container = container_registry.findContainers(type = "variant", id = variant_metadata["id"])[0]
buildplate_type = variant_container.getProperty("machine_buildplate_type", "value")
if buildplate_type not in self._machine_to_buildplate_dict_map[variant_definition]:
self._machine_to_variant_dict_map[variant_definition][buildplate_type] = dict()