mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Added typing for various setting classes
This commit is contained in:
parent
d01ec7872d
commit
e7d9f0ce45
16 changed files with 162 additions and 116 deletions
|
@ -114,6 +114,8 @@ from UM.FlameProfiler import pyqtSlot
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from plugins.SliceInfoPlugin.SliceInfo import SliceInfo
|
||||
from cura.Machines.MaterialManager import MaterialManager
|
||||
from cura.Machines.QualityManager import QualityManager
|
||||
|
||||
|
||||
numpy.seterr(all = "ignore")
|
||||
|
@ -807,20 +809,20 @@ class CuraApplication(QtApplication):
|
|||
self._machine_manager = MachineManager(self)
|
||||
return self._machine_manager
|
||||
|
||||
def getExtruderManager(self, *args):
|
||||
def getExtruderManager(self, *args) -> ExtruderManager:
|
||||
if self._extruder_manager is None:
|
||||
self._extruder_manager = ExtruderManager()
|
||||
return self._extruder_manager
|
||||
|
||||
def getVariantManager(self, *args):
|
||||
def getVariantManager(self, *args) -> VariantManager:
|
||||
return self._variant_manager
|
||||
|
||||
@pyqtSlot(result = QObject)
|
||||
def getMaterialManager(self, *args):
|
||||
def getMaterialManager(self, *args) -> "MaterialManager":
|
||||
return self._material_manager
|
||||
|
||||
@pyqtSlot(result = QObject)
|
||||
def getQualityManager(self, *args):
|
||||
def getQualityManager(self, *args) -> "QualityManager":
|
||||
return self._quality_manager
|
||||
|
||||
def getObjectsModel(self, *args):
|
||||
|
@ -829,23 +831,23 @@ class CuraApplication(QtApplication):
|
|||
return self._object_manager
|
||||
|
||||
@pyqtSlot(result = QObject)
|
||||
def getMultiBuildPlateModel(self, *args):
|
||||
def getMultiBuildPlateModel(self, *args) -> MultiBuildPlateModel:
|
||||
if self._multi_build_plate_model is None:
|
||||
self._multi_build_plate_model = MultiBuildPlateModel(self)
|
||||
return self._multi_build_plate_model
|
||||
|
||||
@pyqtSlot(result = QObject)
|
||||
def getBuildPlateModel(self, *args):
|
||||
def getBuildPlateModel(self, *args) -> BuildPlateModel:
|
||||
if self._build_plate_model is None:
|
||||
self._build_plate_model = BuildPlateModel(self)
|
||||
return self._build_plate_model
|
||||
|
||||
def getCuraSceneController(self, *args):
|
||||
def getCuraSceneController(self, *args) -> CuraSceneController:
|
||||
if self._cura_scene_controller is None:
|
||||
self._cura_scene_controller = CuraSceneController.createCuraSceneController()
|
||||
return self._cura_scene_controller
|
||||
|
||||
def getSettingInheritanceManager(self, *args):
|
||||
def getSettingInheritanceManager(self, *args) -> SettingInheritanceManager:
|
||||
if self._setting_inheritance_manager is None:
|
||||
self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager()
|
||||
return self._setting_inheritance_manager
|
||||
|
|
|
@ -24,29 +24,34 @@ if TYPE_CHECKING:
|
|||
# This is used in Variant, Material, and Quality Managers.
|
||||
#
|
||||
class ContainerNode:
|
||||
__slots__ = ("metadata", "container", "children_map")
|
||||
__slots__ = ("_metadata", "container", "children_map")
|
||||
|
||||
def __init__(self, metadata: Optional[Dict[str, Any]] = None) -> None:
|
||||
self.metadata = metadata
|
||||
self._metadata = metadata
|
||||
self.container = None
|
||||
self.children_map = OrderedDict() #type: OrderedDict[str, Union[QualityGroup, ContainerNode]]
|
||||
self.children_map = OrderedDict() # type: ignore # This is because it's children are supposed to override it.
|
||||
|
||||
## Get an entry value from the metadata
|
||||
def getMetaDataEntry(self, entry: str, default: Any = None) -> Any:
|
||||
if self.metadata is None:
|
||||
if self._metadata is None:
|
||||
return default
|
||||
return self.metadata.get(entry, default)
|
||||
return self._metadata.get(entry, default)
|
||||
|
||||
def getMetadata(self) -> Dict[str, Any]:
|
||||
if self._metadata is None:
|
||||
return {}
|
||||
return self._metadata
|
||||
|
||||
def getChildNode(self, child_key: str) -> Optional["ContainerNode"]:
|
||||
return self.children_map.get(child_key)
|
||||
|
||||
def getContainer(self) -> Optional["InstanceContainer"]:
|
||||
if self.metadata is None:
|
||||
if self._metadata is None:
|
||||
Logger.log("e", "Cannot get container for a ContainerNode without metadata.")
|
||||
return None
|
||||
|
||||
if self.container is None:
|
||||
container_id = self.metadata["id"]
|
||||
container_id = self._metadata["id"]
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
container_list = ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
|
||||
if not container_list:
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
from collections import defaultdict, OrderedDict
|
||||
import copy
|
||||
import uuid
|
||||
import json
|
||||
from typing import Dict, Optional, TYPE_CHECKING
|
||||
from typing import Dict, Optional, TYPE_CHECKING, Any, Set, List, cast, Tuple
|
||||
|
||||
from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot
|
||||
|
||||
|
@ -47,18 +46,27 @@ class MaterialManager(QObject):
|
|||
self._application = Application.getInstance()
|
||||
self._container_registry = container_registry # type: ContainerRegistry
|
||||
|
||||
self._fallback_materials_map = dict() # material_type -> generic material metadata
|
||||
self._material_group_map = dict() # root_material_id -> MaterialGroup
|
||||
self._diameter_machine_nozzle_buildplate_material_map = dict() # approximate diameter str -> dict(machine_definition_id -> MaterialNode)
|
||||
# Material_type -> generic material metadata
|
||||
self._fallback_materials_map = dict() # type: Dict[str, Dict[str, Any]]
|
||||
|
||||
# Root_material_id -> MaterialGroup
|
||||
self._material_group_map = dict() # type: Dict[str, MaterialGroup]
|
||||
|
||||
# Approximate diameter str
|
||||
self._diameter_machine_nozzle_buildplate_material_map = dict() # type: Dict[str, Dict[str, MaterialNode]]
|
||||
|
||||
# We're using these two maps to convert between the specific diameter material id and the generic material id
|
||||
# because the generic material ids are used in qualities and definitions, while the specific diameter material is meant
|
||||
# i.e. generic_pla -> generic_pla_175
|
||||
self._material_diameter_map = defaultdict(dict) # root_material_id -> approximate diameter str -> root_material_id for that diameter
|
||||
self._diameter_material_map = dict() # material id including diameter (generic_pla_175) -> material root id (generic_pla)
|
||||
# root_material_id -> approximate diameter str -> root_material_id for that diameter
|
||||
self._material_diameter_map = defaultdict(dict) # type: Dict[str, Dict[str, str]]
|
||||
|
||||
# Material id including diameter (generic_pla_175) -> material root id (generic_pla)
|
||||
self._diameter_material_map = dict() # type: Dict[str, str]
|
||||
|
||||
# This is used in Legacy UM3 send material function and the material management page.
|
||||
self._guid_material_groups_map = defaultdict(list) # GUID -> a list of material_groups
|
||||
# GUID -> a list of material_groups
|
||||
self._guid_material_groups_map = defaultdict(list) # type: Dict[str, List[MaterialGroup]]
|
||||
|
||||
# The machine definition ID for the non-machine-specific materials.
|
||||
# This is used as the last fallback option if the given machine-specific material(s) cannot be found.
|
||||
|
@ -77,15 +85,15 @@ class MaterialManager(QObject):
|
|||
self._container_registry.containerAdded.connect(self._onContainerMetadataChanged)
|
||||
self._container_registry.containerRemoved.connect(self._onContainerMetadataChanged)
|
||||
|
||||
self._favorites = set()
|
||||
self._favorites = set() # type: Set[str]
|
||||
|
||||
def initialize(self):
|
||||
def initialize(self) -> None:
|
||||
# Find all materials and put them in a matrix for quick search.
|
||||
material_metadatas = {metadata["id"]: metadata for metadata in
|
||||
self._container_registry.findContainersMetadata(type = "material") if
|
||||
metadata.get("GUID")}
|
||||
metadata.get("GUID")} # type: Dict[str, Dict[str, Any]]
|
||||
|
||||
self._material_group_map = dict()
|
||||
self._material_group_map = dict() # type: Dict[str, MaterialGroup]
|
||||
|
||||
# Map #1
|
||||
# root_material_id -> MaterialGroup
|
||||
|
@ -94,7 +102,7 @@ class MaterialManager(QObject):
|
|||
if material_id == "empty_material":
|
||||
continue
|
||||
|
||||
root_material_id = material_metadata.get("base_file")
|
||||
root_material_id = material_metadata.get("base_file", "")
|
||||
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)
|
||||
|
@ -110,26 +118,26 @@ class MaterialManager(QObject):
|
|||
|
||||
# Map #1.5
|
||||
# GUID -> material group list
|
||||
self._guid_material_groups_map = defaultdict(list)
|
||||
self._guid_material_groups_map = defaultdict(list) # type: Dict[str, List[MaterialGroup]]
|
||||
for root_material_id, material_group in self._material_group_map.items():
|
||||
guid = material_group.root_material_node.metadata["GUID"]
|
||||
guid = material_group.root_material_node.getMetaDataEntry("GUID", "")
|
||||
self._guid_material_groups_map[guid].append(material_group)
|
||||
|
||||
# Map #2
|
||||
# Lookup table for material type -> fallback material metadata, only for read-only materials
|
||||
grouped_by_type_dict = dict()
|
||||
grouped_by_type_dict = dict() # type: Dict[str, Any]
|
||||
material_types_without_fallback = set()
|
||||
for root_material_id, material_node in self._material_group_map.items():
|
||||
material_type = material_node.root_material_node.metadata["material"]
|
||||
material_type = material_node.root_material_node.getMetaDataEntry("material", "")
|
||||
if material_type not in grouped_by_type_dict:
|
||||
grouped_by_type_dict[material_type] = {"generic": None,
|
||||
"others": []}
|
||||
material_types_without_fallback.add(material_type)
|
||||
brand = material_node.root_material_node.metadata["brand"]
|
||||
brand = material_node.root_material_node.getMetaDataEntry("brand", "")
|
||||
if brand.lower() == "generic":
|
||||
to_add = True
|
||||
if material_type in grouped_by_type_dict:
|
||||
diameter = material_node.root_material_node.metadata.get("approximate_diameter")
|
||||
diameter = material_node.root_material_node.getMetaDataEntry("approximate_diameter", "")
|
||||
if diameter != self._default_approximate_diameter_for_quality_search:
|
||||
to_add = False # don't add if it's not the default diameter
|
||||
|
||||
|
@ -138,7 +146,7 @@ class MaterialManager(QObject):
|
|||
# - if it's in the list, it means that is a new material without fallback
|
||||
# - if it is not, then it is a custom material with a fallback material (parent)
|
||||
if material_type in material_types_without_fallback:
|
||||
grouped_by_type_dict[material_type] = material_node.root_material_node.metadata
|
||||
grouped_by_type_dict[material_type] = material_node.root_material_node._metadata
|
||||
material_types_without_fallback.remove(material_type)
|
||||
|
||||
# Remove the materials that have no fallback materials
|
||||
|
@ -155,15 +163,15 @@ class MaterialManager(QObject):
|
|||
self._diameter_material_map = dict()
|
||||
|
||||
# Group the material IDs by the same name, material, brand, and color but with different diameters.
|
||||
material_group_dict = dict()
|
||||
material_group_dict = dict() # type: Dict[Tuple[Any], Dict[str, str]]
|
||||
keys_to_fetch = ("name", "material", "brand", "color")
|
||||
for root_material_id, machine_node in self._material_group_map.items():
|
||||
root_material_metadata = machine_node.root_material_node.metadata
|
||||
root_material_metadata = machine_node.root_material_node._metadata
|
||||
|
||||
key_data = []
|
||||
key_data_list = [] # type: List[Any]
|
||||
for key in keys_to_fetch:
|
||||
key_data.append(root_material_metadata.get(key))
|
||||
key_data = tuple(key_data)
|
||||
key_data_list.append(machine_node.root_material_node.getMetaDataEntry(key))
|
||||
key_data = cast(Tuple[Any], tuple(key_data_list)) # type: Tuple[Any]
|
||||
|
||||
# If the key_data doesn't exist, it doesn't matter if the material is read only...
|
||||
if key_data not in material_group_dict:
|
||||
|
@ -172,8 +180,8 @@ class MaterialManager(QObject):
|
|||
# ...but if key_data exists, we just overwrite it if the material is read only, otherwise we skip it
|
||||
if not machine_node.is_read_only:
|
||||
continue
|
||||
approximate_diameter = root_material_metadata.get("approximate_diameter")
|
||||
material_group_dict[key_data][approximate_diameter] = root_material_metadata["id"]
|
||||
approximate_diameter = machine_node.root_material_node.getMetaDataEntry("approximate_diameter", "")
|
||||
material_group_dict[key_data][approximate_diameter] = machine_node.root_material_node.getMetaDataEntry("id", "")
|
||||
|
||||
# Map [root_material_id][diameter] -> root_material_id for this diameter
|
||||
for data_dict in material_group_dict.values():
|
||||
|
@ -192,7 +200,7 @@ class MaterialManager(QObject):
|
|||
|
||||
# Map #4
|
||||
# "machine" -> "nozzle name" -> "buildplate name" -> "root material ID" -> specific material InstanceContainer
|
||||
self._diameter_machine_nozzle_buildplate_material_map = dict()
|
||||
self._diameter_machine_nozzle_buildplate_material_map = dict() # type: Dict[str, Dict[str, MaterialNode]]
|
||||
for material_metadata in material_metadatas.values():
|
||||
self.__addMaterialMetadataIntoLookupTree(material_metadata)
|
||||
|
||||
|
@ -203,7 +211,7 @@ class MaterialManager(QObject):
|
|||
self._favorites.add(item)
|
||||
self.favoritesUpdated.emit()
|
||||
|
||||
def __addMaterialMetadataIntoLookupTree(self, material_metadata: dict) -> None:
|
||||
def __addMaterialMetadataIntoLookupTree(self, material_metadata: Dict[str, Any]) -> None:
|
||||
material_id = material_metadata["id"]
|
||||
|
||||
# We don't store empty material in the lookup tables
|
||||
|
@ -290,7 +298,7 @@ class MaterialManager(QObject):
|
|||
return self._material_diameter_map.get(root_material_id, {}).get(approximate_diameter, root_material_id)
|
||||
|
||||
def getRootMaterialIDWithoutDiameter(self, root_material_id: str) -> str:
|
||||
return self._diameter_material_map.get(root_material_id)
|
||||
return self._diameter_material_map.get(root_material_id, "")
|
||||
|
||||
def getMaterialGroupListByGUID(self, guid: str) -> Optional[list]:
|
||||
return self._guid_material_groups_map.get(guid)
|
||||
|
@ -351,7 +359,7 @@ class MaterialManager(QObject):
|
|||
# A convenience function to get available materials for the given machine with the extruder position.
|
||||
#
|
||||
def getAvailableMaterialsForMachineExtruder(self, machine: "GlobalStack",
|
||||
extruder_stack: "ExtruderStack") -> Optional[dict]:
|
||||
extruder_stack: "ExtruderStack") -> Optional[Dict[str, MaterialNode]]:
|
||||
buildplate_name = machine.getBuildplateName()
|
||||
nozzle_name = None
|
||||
if extruder_stack.variant.getId() != "empty_variant":
|
||||
|
@ -368,7 +376,7 @@ class MaterialManager(QObject):
|
|||
# 2. cannot find any material InstanceContainers with the given settings.
|
||||
#
|
||||
def getMaterialNode(self, machine_definition_id: str, nozzle_name: Optional[str],
|
||||
buildplate_name: Optional[str], diameter: float, root_material_id: str) -> Optional["InstanceContainer"]:
|
||||
buildplate_name: Optional[str], diameter: float, root_material_id: str) -> Optional["MaterialNode"]:
|
||||
# round the diameter to get the approximate diameter
|
||||
rounded_diameter = str(round(diameter))
|
||||
if rounded_diameter not in self._diameter_machine_nozzle_buildplate_material_map:
|
||||
|
@ -377,7 +385,7 @@ class MaterialManager(QObject):
|
|||
return None
|
||||
|
||||
# If there are nozzle materials, get the nozzle-specific material
|
||||
machine_nozzle_buildplate_material_map = self._diameter_machine_nozzle_buildplate_material_map[rounded_diameter]
|
||||
machine_nozzle_buildplate_material_map = self._diameter_machine_nozzle_buildplate_material_map[rounded_diameter] # type: Dict[str, MaterialNode]
|
||||
machine_node = machine_nozzle_buildplate_material_map.get(machine_definition_id)
|
||||
nozzle_node = None
|
||||
buildplate_node = None
|
||||
|
@ -426,7 +434,7 @@ class MaterialManager(QObject):
|
|||
# Look at the guid to material dictionary
|
||||
root_material_id = None
|
||||
for material_group in self._guid_material_groups_map[material_guid]:
|
||||
root_material_id = material_group.root_material_node.metadata["id"]
|
||||
root_material_id = cast(str, material_group.root_material_node.getMetaDataEntry("id", ""))
|
||||
break
|
||||
|
||||
if not root_material_id:
|
||||
|
@ -502,7 +510,7 @@ class MaterialManager(QObject):
|
|||
# Sets the new name for the given material.
|
||||
#
|
||||
@pyqtSlot("QVariant", str)
|
||||
def setMaterialName(self, material_node: "MaterialNode", name: str):
|
||||
def setMaterialName(self, material_node: "MaterialNode", name: str) -> None:
|
||||
root_material_id = material_node.getMetaDataEntry("base_file")
|
||||
if root_material_id is None:
|
||||
return
|
||||
|
@ -520,7 +528,7 @@ class MaterialManager(QObject):
|
|||
# Removes the given material.
|
||||
#
|
||||
@pyqtSlot("QVariant")
|
||||
def removeMaterial(self, material_node: "MaterialNode"):
|
||||
def removeMaterial(self, material_node: "MaterialNode") -> None:
|
||||
root_material_id = material_node.getMetaDataEntry("base_file")
|
||||
if root_material_id is not None:
|
||||
self.removeMaterialByRootId(root_material_id)
|
||||
|
@ -530,8 +538,8 @@ class MaterialManager(QObject):
|
|||
# Returns the root material ID of the duplicated material if successful.
|
||||
#
|
||||
@pyqtSlot("QVariant", result = str)
|
||||
def duplicateMaterial(self, material_node, new_base_id = None, new_metadata = None) -> Optional[str]:
|
||||
root_material_id = material_node.metadata["base_file"]
|
||||
def duplicateMaterial(self, material_node: MaterialNode, new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
|
||||
root_material_id = cast(str, material_node.getMetaDataEntry("base_file", ""))
|
||||
|
||||
material_group = self.getMaterialGroup(root_material_id)
|
||||
if not material_group:
|
||||
|
@ -586,7 +594,7 @@ class MaterialManager(QObject):
|
|||
|
||||
#
|
||||
# Create a new material by cloning Generic PLA for the current material diameter and generate a new GUID.
|
||||
#
|
||||
# Returns the ID of the newly created material.
|
||||
@pyqtSlot(result = str)
|
||||
def createMaterial(self) -> str:
|
||||
from UM.i18n import i18nCatalog
|
||||
|
@ -619,7 +627,7 @@ class MaterialManager(QObject):
|
|||
return new_id
|
||||
|
||||
@pyqtSlot(str)
|
||||
def addFavorite(self, root_material_id: str):
|
||||
def addFavorite(self, root_material_id: str) -> None:
|
||||
self._favorites.add(root_material_id)
|
||||
self.favoritesUpdated.emit()
|
||||
|
||||
|
@ -628,7 +636,7 @@ class MaterialManager(QObject):
|
|||
self._application.saveSettings()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def removeFavorite(self, root_material_id: str):
|
||||
def removeFavorite(self, root_material_id: str) -> None:
|
||||
self._favorites.remove(root_material_id)
|
||||
self.favoritesUpdated.emit()
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from typing import Optional, Dict
|
||||
|
||||
from typing import Optional, Dict, Any
|
||||
from collections import OrderedDict
|
||||
from .ContainerNode import ContainerNode
|
||||
|
||||
|
||||
|
@ -14,6 +14,12 @@ from .ContainerNode import ContainerNode
|
|||
class MaterialNode(ContainerNode):
|
||||
__slots__ = ("material_map", "children_map")
|
||||
|
||||
def __init__(self, metadata: Optional[dict] = None) -> None:
|
||||
def __init__(self, metadata: Optional[Dict[str, Any]] = None) -> None:
|
||||
super().__init__(metadata = metadata)
|
||||
self.material_map = {} # type: Dict[str, MaterialNode] # material_root_id -> material_node
|
||||
|
||||
# We overide this as we want to indicate that MaterialNodes can only contain other material nodes.
|
||||
self.children_map = OrderedDict() # type: OrderedDict[str, "MaterialNode"]
|
||||
|
||||
def getChildNode(self, child_key: str) -> Optional["MaterialNode"]:
|
||||
return self.children_map.get(child_key)
|
|
@ -113,7 +113,7 @@ class BaseMaterialsModel(ListModel):
|
|||
## This is another convenience function which is shared by all material
|
||||
# models so it's put here to avoid having so much duplicated code.
|
||||
def _createMaterialItem(self, root_material_id, container_node):
|
||||
metadata = container_node.metadata
|
||||
metadata = container_node.getMetadata()
|
||||
item = {
|
||||
"root_material_id": root_material_id,
|
||||
"id": metadata["id"],
|
||||
|
|
|
@ -23,7 +23,7 @@ class FavoriteMaterialsModel(BaseMaterialsModel):
|
|||
item_list = []
|
||||
|
||||
for root_material_id, container_node in self._available_materials.items():
|
||||
metadata = container_node.metadata
|
||||
metadata = container_node.getMetadata()
|
||||
|
||||
# Do not include the materials from a to-be-removed package
|
||||
if bool(metadata.get("removed", False)):
|
||||
|
|
|
@ -23,7 +23,7 @@ class GenericMaterialsModel(BaseMaterialsModel):
|
|||
item_list = []
|
||||
|
||||
for root_material_id, container_node in self._available_materials.items():
|
||||
metadata = container_node.metadata
|
||||
metadata = container_node.getMetadata()
|
||||
|
||||
# Do not include the materials from a to-be-removed package
|
||||
if bool(metadata.get("removed", False)):
|
||||
|
|
|
@ -41,21 +41,19 @@ class MaterialBrandsModel(BaseMaterialsModel):
|
|||
|
||||
# Part 1: Generate the entire tree of brands -> material types -> spcific materials
|
||||
for root_material_id, container_node in self._available_materials.items():
|
||||
metadata = container_node.metadata
|
||||
|
||||
# Do not include the materials from a to-be-removed package
|
||||
if bool(metadata.get("removed", False)):
|
||||
if bool(container_node.getMetaDataEntry("removed", False)):
|
||||
continue
|
||||
|
||||
# Add brands we haven't seen yet to the dict, skipping generics
|
||||
brand = metadata["brand"]
|
||||
brand = container_node.getMetaDataEntry("brand", "")
|
||||
if brand.lower() == "generic":
|
||||
continue
|
||||
if brand not in brand_group_dict:
|
||||
brand_group_dict[brand] = {}
|
||||
|
||||
# Add material types we haven't seen yet to the dict
|
||||
material_type = metadata["material"]
|
||||
material_type = container_node.getMetaDataEntry("material", "")
|
||||
if material_type not in brand_group_dict[brand]:
|
||||
brand_group_dict[brand][material_type] = []
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class QualityChangesGroup(QualityGroup):
|
|||
super().__init__(name, quality_type, parent)
|
||||
self._container_registry = Application.getInstance().getContainerRegistry()
|
||||
|
||||
def addNode(self, node: "QualityNode"):
|
||||
def addNode(self, node: "QualityNode") -> None:
|
||||
extruder_position = node.getMetaDataEntry("position")
|
||||
|
||||
if extruder_position is None and self.node_for_global is not None or extruder_position in self.nodes_for_extruders: #We would be overwriting another node.
|
||||
|
|
|
@ -6,6 +6,7 @@ from typing import Dict, Optional, List, Set
|
|||
from PyQt5.QtCore import QObject, pyqtSlot
|
||||
from cura.Machines.ContainerNode import ContainerNode
|
||||
|
||||
|
||||
#
|
||||
# A QualityGroup represents a group of containers that must be applied to each ContainerStack when it's used.
|
||||
# Some concrete examples are Quality and QualityChanges: when we select quality type "normal", this quality type
|
||||
|
|
|
@ -221,12 +221,12 @@ class QualityManager(QObject):
|
|||
for node in nodes_to_check:
|
||||
if node and node.quality_type_map:
|
||||
quality_node = list(node.quality_type_map.values())[0]
|
||||
is_global_quality = parseBool(quality_node.metadata.get("global_quality", False))
|
||||
is_global_quality = parseBool(quality_node.getMetaDataEntry("global_quality", False))
|
||||
if not is_global_quality:
|
||||
continue
|
||||
|
||||
for quality_type, quality_node in node.quality_type_map.items():
|
||||
quality_group = QualityGroup(quality_node.metadata["name"], quality_type)
|
||||
quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
|
||||
quality_group.node_for_global = quality_node
|
||||
quality_group_dict[quality_type] = quality_group
|
||||
break
|
||||
|
@ -310,13 +310,13 @@ class QualityManager(QObject):
|
|||
if has_extruder_specific_qualities:
|
||||
# Only include variant qualities; skip non global qualities
|
||||
quality_node = list(node.quality_type_map.values())[0]
|
||||
is_global_quality = parseBool(quality_node.metadata.get("global_quality", False))
|
||||
is_global_quality = parseBool(quality_node.getMetaDataEntry("global_quality", False))
|
||||
if is_global_quality:
|
||||
continue
|
||||
|
||||
for quality_type, quality_node in node.quality_type_map.items():
|
||||
if quality_type not in quality_group_dict:
|
||||
quality_group = QualityGroup(quality_node.metadata["name"], quality_type)
|
||||
quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
|
||||
quality_group_dict[quality_type] = quality_group
|
||||
|
||||
quality_group = quality_group_dict[quality_type]
|
||||
|
@ -350,7 +350,7 @@ class QualityManager(QObject):
|
|||
for node in nodes_to_check:
|
||||
if node and node.quality_type_map:
|
||||
for quality_type, quality_node in node.quality_type_map.items():
|
||||
quality_group = QualityGroup(quality_node.metadata["name"], quality_type)
|
||||
quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
|
||||
quality_group.node_for_global = quality_node
|
||||
quality_group_dict[quality_type] = quality_group
|
||||
break
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
import os
|
||||
import urllib.parse
|
||||
import uuid
|
||||
from typing import Any
|
||||
from typing import Dict, Union, Optional
|
||||
from typing import Dict, Union, Any, TYPE_CHECKING, List
|
||||
|
||||
from PyQt5.QtCore import QObject, QUrl, QVariant
|
||||
from PyQt5.QtCore import QObject, QUrl
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
from UM.Logger import Logger
|
||||
|
@ -21,6 +21,18 @@ from UM.Settings.ContainerStack import ContainerStack
|
|||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Machines.ContainerNode import ContainerNode
|
||||
from cura.Machines.MaterialNode import MaterialNode
|
||||
from cura.Machines.QualityChangesGroup import QualityChangesGroup
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from cura.Settings.MachineManager import MachineManager
|
||||
from cura.Machines.MaterialManager import MaterialManager
|
||||
from cura.Machines.QualityManager import QualityManager
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
|
@ -31,19 +43,19 @@ catalog = i18nCatalog("cura")
|
|||
# when a certain action happens. This can be done through this class.
|
||||
class ContainerManager(QObject):
|
||||
|
||||
def __init__(self, application):
|
||||
def __init__(self, application: "CuraApplication") -> None:
|
||||
if ContainerManager.__instance is not None:
|
||||
raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__)
|
||||
ContainerManager.__instance = self
|
||||
|
||||
super().__init__(parent = application)
|
||||
|
||||
self._application = application
|
||||
self._plugin_registry = self._application.getPluginRegistry()
|
||||
self._container_registry = self._application.getContainerRegistry()
|
||||
self._machine_manager = self._application.getMachineManager()
|
||||
self._material_manager = self._application.getMaterialManager()
|
||||
self._quality_manager = self._application.getQualityManager()
|
||||
self._application = application # type: CuraApplication
|
||||
self._plugin_registry = self._application.getPluginRegistry() # type: PluginRegistry
|
||||
self._container_registry = self._application.getContainerRegistry() # type: ContainerRegistry
|
||||
self._machine_manager = self._application.getMachineManager() # type: MachineManager
|
||||
self._material_manager = self._application.getMaterialManager() # type: MaterialManager
|
||||
self._quality_manager = self._application.getQualityManager() # type: QualityManager
|
||||
self._container_name_filters = {} # type: Dict[str, Dict[str, Any]]
|
||||
|
||||
@pyqtSlot(str, str, result=str)
|
||||
|
@ -69,21 +81,23 @@ class ContainerManager(QObject):
|
|||
# by using "/" as a separator. For example, to change an entry "foo" in a
|
||||
# dictionary entry "bar", you can specify "bar/foo" as entry name.
|
||||
#
|
||||
# \param container_id \type{str} The ID of the container to change.
|
||||
# \param container_node \type{ContainerNode}
|
||||
# \param entry_name \type{str} The name of the metadata entry to change.
|
||||
# \param entry_value The new value of the entry.
|
||||
#
|
||||
# \return True if successful, False if not.
|
||||
# TODO: This is ONLY used by MaterialView for material containers. Maybe refactor this.
|
||||
# Update: In order for QML to use objects and sub objects, those (sub) objects must all be QObject. Is that what we want?
|
||||
@pyqtSlot("QVariant", str, str)
|
||||
def setContainerMetaDataEntry(self, container_node, entry_name, entry_value):
|
||||
root_material_id = container_node.metadata["base_file"]
|
||||
def setContainerMetaDataEntry(self, container_node: "ContainerNode", entry_name: str, entry_value: str) -> bool:
|
||||
root_material_id = container_node.getMetaDataEntry("base_file", "")
|
||||
if self._container_registry.isReadOnly(root_material_id):
|
||||
Logger.log("w", "Cannot set metadata of read-only container %s.", root_material_id)
|
||||
return False
|
||||
|
||||
material_group = self._material_manager.getMaterialGroup(root_material_id)
|
||||
if material_group is None:
|
||||
Logger.log("w", "Unable to find material group for: %s.", root_material_id)
|
||||
return False
|
||||
|
||||
entries = entry_name.split("/")
|
||||
entry_name = entries.pop()
|
||||
|
@ -91,7 +105,7 @@ class ContainerManager(QObject):
|
|||
sub_item_changed = False
|
||||
if entries:
|
||||
root_name = entries.pop(0)
|
||||
root = material_group.root_material_node.metadata.get(root_name)
|
||||
root = material_group.root_material_node.getMetaDataEntry(root_name)
|
||||
|
||||
item = root
|
||||
for _ in range(len(entries)):
|
||||
|
@ -109,9 +123,10 @@ class ContainerManager(QObject):
|
|||
container.setMetaDataEntry(entry_name, entry_value)
|
||||
if sub_item_changed: #If it was only a sub-item that has changed then the setMetaDataEntry won't correctly notice that something changed, and we must manually signal that the metadata changed.
|
||||
container.metaDataChanged.emit(container)
|
||||
return True
|
||||
|
||||
@pyqtSlot(str, result = str)
|
||||
def makeUniqueName(self, original_name):
|
||||
def makeUniqueName(self, original_name: str) -> str:
|
||||
return self._container_registry.uniqueName(original_name)
|
||||
|
||||
## Get a list of string that can be used as name filters for a Qt File Dialog
|
||||
|
@ -125,7 +140,7 @@ class ContainerManager(QObject):
|
|||
#
|
||||
# \return A string list with name filters.
|
||||
@pyqtSlot(str, result = "QStringList")
|
||||
def getContainerNameFilters(self, type_name):
|
||||
def getContainerNameFilters(self, type_name: str) -> List[str]:
|
||||
if not self._container_name_filters:
|
||||
self._updateContainerNameFilters()
|
||||
|
||||
|
@ -257,7 +272,7 @@ class ContainerManager(QObject):
|
|||
#
|
||||
# \return \type{bool} True if successful, False if not.
|
||||
@pyqtSlot(result = bool)
|
||||
def updateQualityChanges(self):
|
||||
def updateQualityChanges(self) -> bool:
|
||||
global_stack = self._machine_manager.activeMachine
|
||||
if not global_stack:
|
||||
return False
|
||||
|
@ -313,10 +328,10 @@ class ContainerManager(QObject):
|
|||
# \param material_id \type{str} the id of the material for which to get the linked materials.
|
||||
# \return \type{list} a list of names of materials with the same GUID
|
||||
@pyqtSlot("QVariant", bool, result = "QStringList")
|
||||
def getLinkedMaterials(self, material_node, exclude_self = False):
|
||||
guid = material_node.metadata["GUID"]
|
||||
def getLinkedMaterials(self, material_node: "MaterialNode", exclude_self: bool = False):
|
||||
guid = material_node.getMetaDataEntry("GUID", "")
|
||||
|
||||
self_root_material_id = material_node.metadata["base_file"]
|
||||
self_root_material_id = material_node.getMetaDataEntry("base_file")
|
||||
material_group_list = self._material_manager.getMaterialGroupListByGUID(guid)
|
||||
|
||||
linked_material_names = []
|
||||
|
@ -324,15 +339,19 @@ class ContainerManager(QObject):
|
|||
for material_group in material_group_list:
|
||||
if exclude_self and material_group.name == self_root_material_id:
|
||||
continue
|
||||
linked_material_names.append(material_group.root_material_node.metadata["name"])
|
||||
linked_material_names.append(material_group.root_material_node.getMetaDataEntry("name", ""))
|
||||
return linked_material_names
|
||||
|
||||
## Unlink a material from all other materials by creating a new GUID
|
||||
# \param material_id \type{str} the id of the material to create a new GUID for.
|
||||
@pyqtSlot("QVariant")
|
||||
def unlinkMaterial(self, material_node):
|
||||
def unlinkMaterial(self, material_node: "MaterialNode") -> None:
|
||||
# Get the material group
|
||||
material_group = self._material_manager.getMaterialGroup(material_node.metadata["base_file"])
|
||||
material_group = self._material_manager.getMaterialGroup(material_node.getMetaDataEntry("base_file", ""))
|
||||
|
||||
if material_group is None:
|
||||
Logger.log("w", "Unable to find material group for %s", material_node)
|
||||
return
|
||||
|
||||
# Generate a new GUID
|
||||
new_guid = str(uuid.uuid4())
|
||||
|
@ -344,7 +363,7 @@ class ContainerManager(QObject):
|
|||
if container is not None:
|
||||
container.setMetaDataEntry("GUID", new_guid)
|
||||
|
||||
def _performMerge(self, merge_into, merge, clear_settings = True):
|
||||
def _performMerge(self, merge_into: InstanceContainer, merge: InstanceContainer, clear_settings: bool = True) -> None:
|
||||
if merge == merge_into:
|
||||
return
|
||||
|
||||
|
@ -400,7 +419,7 @@ class ContainerManager(QObject):
|
|||
|
||||
## Import single profile, file_url does not have to end with curaprofile
|
||||
@pyqtSlot(QUrl, result="QVariantMap")
|
||||
def importProfile(self, file_url):
|
||||
def importProfile(self, file_url: QUrl):
|
||||
if not file_url.isValid():
|
||||
return
|
||||
path = file_url.toLocalFile()
|
||||
|
@ -409,7 +428,7 @@ class ContainerManager(QObject):
|
|||
return self._container_registry.importProfile(path)
|
||||
|
||||
@pyqtSlot(QObject, QUrl, str)
|
||||
def exportQualityChangesGroup(self, quality_changes_group, file_url: QUrl, file_type: str):
|
||||
def exportQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup", file_url: QUrl, file_type: str) -> None:
|
||||
if not file_url.isValid():
|
||||
return
|
||||
path = file_url.toLocalFile()
|
||||
|
|
|
@ -15,7 +15,7 @@ from UM.Settings.SettingFunction import SettingFunction
|
|||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext
|
||||
|
||||
from typing import Optional, TYPE_CHECKING, Dict, List, Any
|
||||
from typing import Optional, TYPE_CHECKING, Dict, List, Any, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cura.Settings.ExtruderStack import ExtruderStack
|
||||
|
@ -39,9 +39,12 @@ class ExtruderManager(QObject):
|
|||
self._application = cura.CuraApplication.CuraApplication.getInstance()
|
||||
|
||||
# Per machine, a dictionary of extruder container stack IDs. Only for separately defined extruders.
|
||||
self._extruder_trains = {} # type: Dict[str, Dict[str, ExtruderStack]]
|
||||
self._extruder_trains = {} # type: Dict[str, Dict[str, "ExtruderStack"]]
|
||||
self._active_extruder_index = -1 # Indicates the index of the active extruder stack. -1 means no active extruder stack
|
||||
self._selected_object_extruders = [] # type: List[ExtruderStack]
|
||||
|
||||
# TODO; I have no idea why this is a union of ID's and extruder stacks. This needs to be fixed at some point.
|
||||
self._selected_object_extruders = [] # type: List[Union[str, "ExtruderStack"]]
|
||||
|
||||
self._addCurrentMachineExtruders()
|
||||
|
||||
Selection.selectionChanged.connect(self.resetSelectedObjectExtruders)
|
||||
|
@ -80,7 +83,7 @@ class ExtruderManager(QObject):
|
|||
## Gets a dict with the extruder stack ids with the extruder number as the key.
|
||||
@pyqtProperty("QVariantMap", notify = extrudersChanged)
|
||||
def extruderIds(self) -> Dict[str, str]:
|
||||
extruder_stack_ids = {}
|
||||
extruder_stack_ids = {} # type: Dict[str, str]
|
||||
|
||||
global_container_stack = self._application.getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
|
@ -115,7 +118,7 @@ class ExtruderManager(QObject):
|
|||
|
||||
## Provides a list of extruder IDs used by the current selected objects.
|
||||
@pyqtProperty("QVariantList", notify = selectedObjectExtrudersChanged)
|
||||
def selectedObjectExtruders(self) -> List[str]:
|
||||
def selectedObjectExtruders(self) -> List[Union[str, "ExtruderStack"]]:
|
||||
if not self._selected_object_extruders:
|
||||
object_extruders = set()
|
||||
|
||||
|
@ -140,7 +143,7 @@ class ExtruderManager(QObject):
|
|||
elif current_extruder_trains:
|
||||
object_extruders.add(current_extruder_trains[0].getId())
|
||||
|
||||
self._selected_object_extruders = list(object_extruders)
|
||||
self._selected_object_extruders = list(object_extruders) # type: List[Union[str, "ExtruderStack"]]
|
||||
|
||||
return self._selected_object_extruders
|
||||
|
||||
|
@ -149,7 +152,7 @@ class ExtruderManager(QObject):
|
|||
# This will trigger a recalculation of the extruders used for the
|
||||
# selection.
|
||||
def resetSelectedObjectExtruders(self) -> None:
|
||||
self._selected_object_extruders = []
|
||||
self._selected_object_extruders = [] # type: List[Union[str, "ExtruderStack"]]
|
||||
self.selectedObjectExtrudersChanged.emit()
|
||||
|
||||
@pyqtSlot(result = QObject)
|
||||
|
|
|
@ -892,7 +892,11 @@ class MachineManager(QObject):
|
|||
extruder_nr = node.callDecoration("getActiveExtruderPosition")
|
||||
|
||||
if extruder_nr is not None and int(extruder_nr) > extruder_count - 1:
|
||||
node.callDecoration("setActiveExtruder", extruder_manager.getExtruderStack(extruder_count - 1).getId())
|
||||
extruder = extruder_manager.getExtruderStack(extruder_count - 1)
|
||||
if extruder is not None:
|
||||
node.callDecoration("setActiveExtruder", extruder.getId())
|
||||
else:
|
||||
Logger.log("w", "Could not find extruder to set active.")
|
||||
|
||||
# Make sure one of the extruder stacks is active
|
||||
extruder_manager.setActiveExtruderIndex(0)
|
||||
|
|
|
@ -934,7 +934,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
root_material_id)
|
||||
|
||||
if material_node is not None and material_node.getContainer() is not None:
|
||||
extruder_stack.material = material_node.getContainer()
|
||||
extruder_stack.material = material_node.getContainer() # type: InstanceContainer
|
||||
|
||||
def _applyChangesToMachine(self, global_stack, extruder_stack_dict):
|
||||
# Clear all first
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue