mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-12 09:17:50 -06:00
Updated comments in Models
Converted doxygen style comments to reStructuredText style in the files found in Cura/cura/Model directory recursively using the script dox_2_rst.py (provided in the Uranium repo). Comments were manually checked and changed if needed. Note: dox_2rst.py struggles with decorated functions.
This commit is contained in:
parent
d69bf84424
commit
120541a8db
26 changed files with 577 additions and 441 deletions
|
@ -9,47 +9,59 @@ from UM.Logger import Logger
|
|||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
|
||||
|
||||
## A node in the container tree. It represents one container.
|
||||
#
|
||||
# The container it represents is referenced by its container_id. During normal
|
||||
# use of the tree, this container is not constructed. Only when parts of the
|
||||
# tree need to get loaded in the container stack should it get constructed.
|
||||
class ContainerNode:
|
||||
## Creates a new node for the container tree.
|
||||
# \param container_id The ID of the container that this node should
|
||||
# represent.
|
||||
"""A node in the container tree. It represents one container.
|
||||
|
||||
The container it represents is referenced by its container_id. During normal use of the tree, this container is
|
||||
not constructed. Only when parts of the tree need to get loaded in the container stack should it get constructed.
|
||||
"""
|
||||
|
||||
def __init__(self, container_id: str) -> None:
|
||||
"""Creates a new node for the container tree.
|
||||
|
||||
:param container_id: The ID of the container that this node should represent.
|
||||
"""
|
||||
|
||||
self.container_id = container_id
|
||||
self._container = None # type: Optional[InstanceContainer]
|
||||
self.children_map = {} # type: Dict[str, ContainerNode] # Mapping from container ID to container node.
|
||||
|
||||
## Gets the metadata of the container that this node represents.
|
||||
# Getting the metadata from the container directly is about 10x as fast.
|
||||
# \return The metadata of the container in this node.
|
||||
def getMetadata(self) -> Dict[str, Any]:
|
||||
"""Gets the metadata of the container that this node represents.
|
||||
|
||||
Getting the metadata from the container directly is about 10x as fast.
|
||||
|
||||
:return: The metadata of the container in this node.
|
||||
"""
|
||||
|
||||
return ContainerRegistry.getInstance().findContainersMetadata(id = self.container_id)[0]
|
||||
|
||||
## Get an entry from the metadata of the container that this node contains.
|
||||
#
|
||||
# This is just a convenience function.
|
||||
# \param entry The metadata entry key to return.
|
||||
# \param default If the metadata is not present or the container is not
|
||||
# found, the value of this default is returned.
|
||||
# \return The value of the metadata entry, or the default if it was not
|
||||
# present.
|
||||
def getMetaDataEntry(self, entry: str, default: Any = None) -> Any:
|
||||
"""Get an entry from the metadata of the container that this node contains.
|
||||
|
||||
This is just a convenience function.
|
||||
|
||||
:param entry: The metadata entry key to return.
|
||||
:param default: If the metadata is not present or the container is not found, the value of this default is
|
||||
returned.
|
||||
|
||||
:return: The value of the metadata entry, or the default if it was not present.
|
||||
"""
|
||||
|
||||
container_metadata = ContainerRegistry.getInstance().findContainersMetadata(id = self.container_id)
|
||||
if len(container_metadata) == 0:
|
||||
return default
|
||||
return container_metadata[0].get(entry, default)
|
||||
|
||||
## The container that this node's container ID refers to.
|
||||
#
|
||||
# This can be used to finally instantiate the container in order to put it
|
||||
# in the container stack.
|
||||
# \return A container.
|
||||
@property
|
||||
def container(self) -> Optional[InstanceContainer]:
|
||||
"""The container that this node's container ID refers to.
|
||||
|
||||
This can be used to finally instantiate the container in order to put it in the container stack.
|
||||
|
||||
:return: A container.
|
||||
"""
|
||||
|
||||
if not self._container:
|
||||
container_list = ContainerRegistry.getInstance().findInstanceContainers(id = self.container_id)
|
||||
if len(container_list) == 0:
|
||||
|
|
|
@ -19,17 +19,16 @@ if TYPE_CHECKING:
|
|||
from UM.Settings.ContainerStack import ContainerStack
|
||||
|
||||
|
||||
## This class contains a look-up tree for which containers are available at
|
||||
# which stages of configuration.
|
||||
#
|
||||
# The tree starts at the machine definitions. For every distinct definition
|
||||
# there will be one machine node here.
|
||||
#
|
||||
# All of the fallbacks for material choices, quality choices, etc. should be
|
||||
# encoded in this tree. There must always be at least one child node (for
|
||||
# nodes that have children) but that child node may be a node representing the
|
||||
# empty instance container.
|
||||
class ContainerTree:
|
||||
"""This class contains a look-up tree for which containers are available at which stages of configuration.
|
||||
|
||||
The tree starts at the machine definitions. For every distinct definition there will be one machine node here.
|
||||
|
||||
All of the fallbacks for material choices, quality choices, etc. should be encoded in this tree. There must
|
||||
always be at least one child node (for nodes that have children) but that child node may be a node representing
|
||||
the empty instance container.
|
||||
"""
|
||||
|
||||
__instance = None # type: Optional["ContainerTree"]
|
||||
|
||||
@classmethod
|
||||
|
@ -43,13 +42,15 @@ class ContainerTree:
|
|||
self.materialsChanged = Signal() # Emitted when any of the material nodes in the tree got changed.
|
||||
cura.CuraApplication.CuraApplication.getInstance().initializationFinished.connect(self._onStartupFinished) # Start the background task to load more machine nodes after start-up is completed.
|
||||
|
||||
## Get the quality groups available for the currently activated printer.
|
||||
#
|
||||
# This contains all quality groups, enabled or disabled. To check whether
|
||||
# the quality group can be activated, test for the
|
||||
# ``QualityGroup.is_available`` property.
|
||||
# \return For every quality type, one quality group.
|
||||
def getCurrentQualityGroups(self) -> Dict[str, "QualityGroup"]:
|
||||
"""Get the quality groups available for the currently activated printer.
|
||||
|
||||
This contains all quality groups, enabled or disabled. To check whether the quality group can be activated,
|
||||
test for the ``QualityGroup.is_available`` property.
|
||||
|
||||
:return: For every quality type, one quality group.
|
||||
"""
|
||||
|
||||
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
|
||||
if global_stack is None:
|
||||
return {}
|
||||
|
@ -58,14 +59,15 @@ class ContainerTree:
|
|||
extruder_enabled = [extruder.isEnabled for extruder in global_stack.extruderList]
|
||||
return self.machines[global_stack.definition.getId()].getQualityGroups(variant_names, material_bases, extruder_enabled)
|
||||
|
||||
## Get the quality changes groups available for the currently activated
|
||||
# printer.
|
||||
#
|
||||
# This contains all quality changes groups, enabled or disabled. To check
|
||||
# whether the quality changes group can be activated, test for the
|
||||
# ``QualityChangesGroup.is_available`` property.
|
||||
# \return A list of all quality changes groups.
|
||||
def getCurrentQualityChangesGroups(self) -> List["QualityChangesGroup"]:
|
||||
"""Get the quality changes groups available for the currently activated printer.
|
||||
|
||||
This contains all quality changes groups, enabled or disabled. To check whether the quality changes group can
|
||||
be activated, test for the ``QualityChangesGroup.is_available`` property.
|
||||
|
||||
:return: A list of all quality changes groups.
|
||||
"""
|
||||
|
||||
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
|
||||
if global_stack is None:
|
||||
return []
|
||||
|
@ -74,31 +76,43 @@ class ContainerTree:
|
|||
extruder_enabled = [extruder.isEnabled for extruder in global_stack.extruderList]
|
||||
return self.machines[global_stack.definition.getId()].getQualityChangesGroups(variant_names, material_bases, extruder_enabled)
|
||||
|
||||
## Ran after completely starting up the application.
|
||||
def _onStartupFinished(self) -> None:
|
||||
"""Ran after completely starting up the application."""
|
||||
|
||||
currently_added = ContainerRegistry.getInstance().findContainerStacks() # Find all currently added global stacks.
|
||||
JobQueue.getInstance().add(self._MachineNodeLoadJob(self, currently_added))
|
||||
|
||||
## Dictionary-like object that contains the machines.
|
||||
#
|
||||
# This handles the lazy loading of MachineNodes.
|
||||
class _MachineNodeMap:
|
||||
"""Dictionary-like object that contains the machines.
|
||||
|
||||
This handles the lazy loading of MachineNodes.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._machines = {} # type: Dict[str, MachineNode]
|
||||
|
||||
## Returns whether a printer with a certain definition ID exists. This
|
||||
# is regardless of whether or not the printer is loaded yet.
|
||||
# \param definition_id The definition to look for.
|
||||
# \return Whether or not a printer definition exists with that name.
|
||||
def __contains__(self, definition_id: str) -> bool:
|
||||
"""Returns whether a printer with a certain definition ID exists.
|
||||
|
||||
This is regardless of whether or not the printer is loaded yet.
|
||||
|
||||
:param definition_id: The definition to look for.
|
||||
|
||||
:return: Whether or not a printer definition exists with that name.
|
||||
"""
|
||||
|
||||
return len(ContainerRegistry.getInstance().findContainersMetadata(id = definition_id)) > 0
|
||||
|
||||
## Returns a machine node for the specified definition ID.
|
||||
#
|
||||
# If the machine node wasn't loaded yet, this will load it lazily.
|
||||
# \param definition_id The definition to look for.
|
||||
# \return A machine node for that definition.
|
||||
def __getitem__(self, definition_id: str) -> MachineNode:
|
||||
"""Returns a machine node for the specified definition ID.
|
||||
|
||||
If the machine node wasn't loaded yet, this will load it lazily.
|
||||
|
||||
:param definition_id: The definition to look for.
|
||||
|
||||
:return: A machine node for that definition.
|
||||
"""
|
||||
|
||||
if definition_id not in self._machines:
|
||||
start_time = time.time()
|
||||
self._machines[definition_id] = MachineNode(definition_id)
|
||||
|
@ -106,46 +120,58 @@ class ContainerTree:
|
|||
Logger.log("d", "Adding container tree for {definition_id} took {duration} seconds.".format(definition_id = definition_id, duration = time.time() - start_time))
|
||||
return self._machines[definition_id]
|
||||
|
||||
## Gets a machine node for the specified definition ID, with default.
|
||||
#
|
||||
# The default is returned if there is no definition with the specified
|
||||
# ID. If the machine node wasn't loaded yet, this will load it lazily.
|
||||
# \param definition_id The definition to look for.
|
||||
# \param default The machine node to return if there is no machine
|
||||
# with that definition (can be ``None`` optionally or if not
|
||||
# provided).
|
||||
# \return A machine node for that definition, or the default if there
|
||||
# is no definition with the provided definition_id.
|
||||
def get(self, definition_id: str, default: Optional[MachineNode] = None) -> Optional[MachineNode]:
|
||||
"""Gets a machine node for the specified definition ID, with default.
|
||||
|
||||
The default is returned if there is no definition with the specified ID. If the machine node wasn't
|
||||
loaded yet, this will load it lazily.
|
||||
|
||||
:param definition_id: The definition to look for.
|
||||
:param default: The machine node to return if there is no machine with that definition (can be ``None``
|
||||
optionally or if not provided).
|
||||
|
||||
:return: A machine node for that definition, or the default if there is no definition with the provided
|
||||
definition_id.
|
||||
"""
|
||||
|
||||
if definition_id not in self:
|
||||
return default
|
||||
return self[definition_id]
|
||||
|
||||
## Returns whether we've already cached this definition's node.
|
||||
# \param definition_id The definition that we may have cached.
|
||||
# \return ``True`` if it's cached.
|
||||
def is_loaded(self, definition_id: str) -> bool:
|
||||
"""Returns whether we've already cached this definition's node.
|
||||
|
||||
:param definition_id: The definition that we may have cached.
|
||||
|
||||
:return: ``True`` if it's cached.
|
||||
"""
|
||||
|
||||
return definition_id in self._machines
|
||||
|
||||
## Pre-loads all currently added printers as a background task so that
|
||||
# switching printers in the interface is faster.
|
||||
class _MachineNodeLoadJob(Job):
|
||||
## Creates a new background task.
|
||||
# \param tree_root The container tree instance. This cannot be
|
||||
# obtained through the singleton static function since the instance
|
||||
# may not yet be constructed completely.
|
||||
# \param container_stacks All of the stacks to pre-load the container
|
||||
# trees for. This needs to be provided from here because the stacks
|
||||
# need to be constructed on the main thread because they are QObject.
|
||||
"""Pre-loads all currently added printers as a background task so that switching printers in the interface is
|
||||
faster.
|
||||
"""
|
||||
|
||||
def __init__(self, tree_root: "ContainerTree", container_stacks: List["ContainerStack"]) -> None:
|
||||
"""Creates a new background task.
|
||||
|
||||
:param tree_root: The container tree instance. This cannot be obtained through the singleton static
|
||||
function since the instance may not yet be constructed completely.
|
||||
:param container_stacks: All of the stacks to pre-load the container trees for. This needs to be provided
|
||||
from here because the stacks need to be constructed on the main thread because they are QObject.
|
||||
"""
|
||||
|
||||
self.tree_root = tree_root
|
||||
self.container_stacks = container_stacks
|
||||
super().__init__()
|
||||
|
||||
## Starts the background task.
|
||||
#
|
||||
# The ``JobQueue`` will schedule this on a different thread.
|
||||
def run(self) -> None:
|
||||
"""Starts the background task.
|
||||
|
||||
The ``JobQueue`` will schedule this on a different thread.
|
||||
"""
|
||||
|
||||
for stack in self.container_stacks: # Load all currently-added containers.
|
||||
if not isinstance(stack, GlobalStack):
|
||||
continue
|
||||
|
|
|
@ -11,10 +11,12 @@ if TYPE_CHECKING:
|
|||
from cura.Machines.QualityNode import QualityNode
|
||||
|
||||
|
||||
## This class represents an intent profile in the container tree.
|
||||
#
|
||||
# This class has no more subnodes.
|
||||
class IntentNode(ContainerNode):
|
||||
"""This class represents an intent profile in the container tree.
|
||||
|
||||
This class has no more subnodes.
|
||||
"""
|
||||
|
||||
def __init__(self, container_id: str, quality: "QualityNode") -> None:
|
||||
super().__init__(container_id)
|
||||
self.quality = quality
|
||||
|
|
|
@ -13,16 +13,16 @@ from UM.Settings.SettingDefinition import SettingDefinition
|
|||
from UM.Settings.Validator import ValidatorState
|
||||
|
||||
import cura.CuraApplication
|
||||
#
|
||||
# This class performs setting error checks for the currently active machine.
|
||||
#
|
||||
# The whole error checking process is pretty heavy which can take ~0.5 secs, so it can cause GUI to lag.
|
||||
# The idea here is to split the whole error check into small tasks, each of which only checks a single setting key
|
||||
# in a stack. According to my profiling results, the maximal runtime for such a sub-task is <0.03 secs, which should
|
||||
# be good enough. Moreover, if any changes happened to the machine, we can cancel the check in progress without wait
|
||||
# for it to finish the complete work.
|
||||
#
|
||||
|
||||
class MachineErrorChecker(QObject):
|
||||
"""This class performs setting error checks for the currently active machine.
|
||||
|
||||
The whole error checking process is pretty heavy which can take ~0.5 secs, so it can cause GUI to lag. The idea
|
||||
here is to split the whole error check into small tasks, each of which only checks a single setting key in a
|
||||
stack. According to my profiling results, the maximal runtime for such a sub-task is <0.03 secs, which should be
|
||||
good enough. Moreover, if any changes happened to the machine, we can cancel the check in progress without wait
|
||||
for it to finish the complete work.
|
||||
"""
|
||||
|
||||
def __init__(self, parent: Optional[QObject] = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
@ -92,24 +92,37 @@ class MachineErrorChecker(QObject):
|
|||
def needToWaitForResult(self) -> bool:
|
||||
return self._need_to_check or self._check_in_progress
|
||||
|
||||
# Start the error check for property changed
|
||||
# this is seperate from the startErrorCheck because it ignores a number property types
|
||||
def startErrorCheckPropertyChanged(self, key: str, property_name: str) -> None:
|
||||
"""Start the error check for property changed
|
||||
|
||||
this is seperate from the startErrorCheck because it ignores a number property types
|
||||
|
||||
:param key:
|
||||
:param property_name:
|
||||
"""
|
||||
|
||||
if property_name != "value":
|
||||
return
|
||||
self.startErrorCheck()
|
||||
|
||||
# Starts the error check timer to schedule a new error check.
|
||||
def startErrorCheck(self, *args: Any) -> None:
|
||||
"""Starts the error check timer to schedule a new error check.
|
||||
|
||||
:param args:
|
||||
"""
|
||||
|
||||
if not self._check_in_progress:
|
||||
self._need_to_check = True
|
||||
self.needToWaitForResultChanged.emit()
|
||||
self._error_check_timer.start()
|
||||
|
||||
# This function is called by the timer to reschedule a new error check.
|
||||
# If there is no check in progress, it will start a new one. If there is any, it sets the "_need_to_check" flag
|
||||
# to notify the current check to stop and start a new one.
|
||||
def _rescheduleCheck(self) -> None:
|
||||
"""This function is called by the timer to reschedule a new error check.
|
||||
|
||||
If there is no check in progress, it will start a new one. If there is any, it sets the "_need_to_check" flag
|
||||
to notify the current check to stop and start a new one.
|
||||
"""
|
||||
|
||||
if self._check_in_progress and not self._need_to_check:
|
||||
self._need_to_check = True
|
||||
self.needToWaitForResultChanged.emit()
|
||||
|
|
|
@ -17,10 +17,12 @@ from cura.Machines.VariantNode import VariantNode
|
|||
import UM.FlameProfiler
|
||||
|
||||
|
||||
## This class represents a machine in the container tree.
|
||||
#
|
||||
# The subnodes of these nodes are variants.
|
||||
class MachineNode(ContainerNode):
|
||||
"""This class represents a machine in the container tree.
|
||||
|
||||
The subnodes of these nodes are variants.
|
||||
"""
|
||||
|
||||
def __init__(self, container_id: str) -> None:
|
||||
super().__init__(container_id)
|
||||
self.variants = {} # type: Dict[str, VariantNode] # Mapping variant names to their nodes.
|
||||
|
@ -47,20 +49,21 @@ class MachineNode(ContainerNode):
|
|||
|
||||
self._loadAll()
|
||||
|
||||
## Get the available quality groups for this machine.
|
||||
#
|
||||
# This returns all quality groups, regardless of whether they are
|
||||
# available to the combination of extruders or not. On the resulting
|
||||
# quality groups, the is_available property is set to indicate whether the
|
||||
# quality group can be selected according to the combination of extruders
|
||||
# in the parameters.
|
||||
# \param variant_names The names of the variants loaded in each extruder.
|
||||
# \param material_bases The base file names of the materials loaded in
|
||||
# each extruder.
|
||||
# \param extruder_enabled Whether or not the extruders are enabled. This
|
||||
# allows the function to set the is_available properly.
|
||||
# \return For each available quality type, a QualityGroup instance.
|
||||
def getQualityGroups(self, variant_names: List[str], material_bases: List[str], extruder_enabled: List[bool]) -> Dict[str, QualityGroup]:
|
||||
"""Get the available quality groups for this machine.
|
||||
|
||||
This returns all quality groups, regardless of whether they are available to the combination of extruders or
|
||||
not. On the resulting quality groups, the is_available property is set to indicate whether the quality group
|
||||
can be selected according to the combination of extruders in the parameters.
|
||||
|
||||
:param variant_names: The names of the variants loaded in each extruder.
|
||||
:param material_bases: The base file names of the materials loaded in each extruder.
|
||||
:param extruder_enabled: Whether or not the extruders are enabled. This allows the function to set the
|
||||
is_available properly.
|
||||
|
||||
:return: For each available quality type, a QualityGroup instance.
|
||||
"""
|
||||
|
||||
if len(variant_names) != len(material_bases) or len(variant_names) != len(extruder_enabled):
|
||||
Logger.log("e", "The number of extruders in the list of variants (" + str(len(variant_names)) + ") is not equal to the number of extruders in the list of materials (" + str(len(material_bases)) + ") or the list of enabled extruders (" + str(len(extruder_enabled)) + ").")
|
||||
return {}
|
||||
|
@ -98,28 +101,26 @@ class MachineNode(ContainerNode):
|
|||
quality_groups[quality_type].is_available = True
|
||||
return quality_groups
|
||||
|
||||
## Returns all of the quality changes groups available to this printer.
|
||||
#
|
||||
# The quality changes groups store which quality type and intent category
|
||||
# they were made for, but not which material and nozzle. Instead for the
|
||||
# quality type and intent category, the quality changes will always be
|
||||
# available but change the quality type and intent category when
|
||||
# activated.
|
||||
#
|
||||
# The quality changes group does depend on the printer: Which quality
|
||||
# definition is used.
|
||||
#
|
||||
# The quality changes groups that are available do depend on the quality
|
||||
# types that are available, so it must still be known which extruders are
|
||||
# enabled and which materials and variants are loaded in them. This allows
|
||||
# setting the correct is_available flag.
|
||||
# \param variant_names The names of the variants loaded in each extruder.
|
||||
# \param material_bases The base file names of the materials loaded in
|
||||
# each extruder.
|
||||
# \param extruder_enabled For each extruder whether or not they are
|
||||
# enabled.
|
||||
# \return List of all quality changes groups for the printer.
|
||||
def getQualityChangesGroups(self, variant_names: List[str], material_bases: List[str], extruder_enabled: List[bool]) -> List[QualityChangesGroup]:
|
||||
"""Returns all of the quality changes groups available to this printer.
|
||||
|
||||
The quality changes groups store which quality type and intent category they were made for, but not which
|
||||
material and nozzle. Instead for the quality type and intent category, the quality changes will always be
|
||||
available but change the quality type and intent category when activated.
|
||||
|
||||
The quality changes group does depend on the printer: Which quality definition is used.
|
||||
|
||||
The quality changes groups that are available do depend on the quality types that are available, so it must
|
||||
still be known which extruders are enabled and which materials and variants are loaded in them. This allows
|
||||
setting the correct is_available flag.
|
||||
|
||||
:param variant_names: The names of the variants loaded in each extruder.
|
||||
:param material_bases: The base file names of the materials loaded in each extruder.
|
||||
:param extruder_enabled: For each extruder whether or not they are enabled.
|
||||
|
||||
:return: List of all quality changes groups for the printer.
|
||||
"""
|
||||
|
||||
machine_quality_changes = ContainerRegistry.getInstance().findContainersMetadata(type = "quality_changes", definition = self.quality_definition) # All quality changes for each extruder.
|
||||
|
||||
groups_by_name = {} #type: Dict[str, QualityChangesGroup] # Group quality changes profiles by their display name. The display name must be unique for quality changes. This finds profiles that belong together in a group.
|
||||
|
@ -147,18 +148,19 @@ class MachineNode(ContainerNode):
|
|||
|
||||
return list(groups_by_name.values())
|
||||
|
||||
## Gets the preferred global quality node, going by the preferred quality
|
||||
# type.
|
||||
#
|
||||
# If the preferred global quality is not in there, an arbitrary global
|
||||
# quality is taken.
|
||||
# If there are no global qualities, an empty quality is returned.
|
||||
def preferredGlobalQuality(self) -> "QualityNode":
|
||||
"""Gets the preferred global quality node, going by the preferred quality type.
|
||||
|
||||
If the preferred global quality is not in there, an arbitrary global quality is taken. If there are no global
|
||||
qualities, an empty quality is returned.
|
||||
"""
|
||||
|
||||
return self.global_qualities.get(self.preferred_quality_type, next(iter(self.global_qualities.values())))
|
||||
|
||||
## (Re)loads all variants under this printer.
|
||||
@UM.FlameProfiler.profile
|
||||
def _loadAll(self) -> None:
|
||||
"""(Re)loads all variants under this printer."""
|
||||
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
if not self.has_variants:
|
||||
self.variants["empty"] = VariantNode("empty_variant", machine = self)
|
||||
|
|
|
@ -7,18 +7,21 @@ if TYPE_CHECKING:
|
|||
from cura.Machines.MaterialNode import MaterialNode
|
||||
|
||||
|
||||
## A MaterialGroup represents a group of material InstanceContainers that are derived from a single material profile.
|
||||
# The main InstanceContainer which has the ID of the material profile file name is called the "root_material". For
|
||||
# example: "generic_abs" is the root material (ID) of "generic_abs_ultimaker3" and "generic_abs_ultimaker3_AA_0.4",
|
||||
# and "generic_abs_ultimaker3" and "generic_abs_ultimaker3_AA_0.4" are derived materials of "generic_abs".
|
||||
#
|
||||
# Using "generic_abs" as an example, the MaterialGroup for "generic_abs" will contain the following information:
|
||||
# - name: "generic_abs", root_material_id
|
||||
# - root_material_node: MaterialNode of "generic_abs"
|
||||
# - derived_material_node_list: A list of MaterialNodes that are derived from "generic_abs",
|
||||
# so "generic_abs_ultimaker3", "generic_abs_ultimaker3_AA_0.4", etc.
|
||||
#
|
||||
class MaterialGroup:
|
||||
"""A MaterialGroup represents a group of material InstanceContainers that are derived from a single material profile.
|
||||
|
||||
The main InstanceContainer which has the ID of the material profile file name is called the "root_material". For
|
||||
example: "generic_abs" is the root material (ID) of "generic_abs_ultimaker3" and "generic_abs_ultimaker3_AA_0.4",
|
||||
and "generic_abs_ultimaker3" and "generic_abs_ultimaker3_AA_0.4" are derived materials of "generic_abs".
|
||||
|
||||
Using "generic_abs" as an example, the MaterialGroup for "generic_abs" will contain the following information:
|
||||
- name: "generic_abs", root_material_id
|
||||
- root_material_node: MaterialNode of "generic_abs"
|
||||
- derived_material_node_list: A list of MaterialNodes that are derived from "generic_abs", so
|
||||
"generic_abs_ultimaker3", "generic_abs_ultimaker3_AA_0.4", etc.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("name", "is_read_only", "root_material_node", "derived_material_node_list")
|
||||
|
||||
def __init__(self, name: str, root_material_node: "MaterialNode") -> None:
|
||||
|
|
|
@ -15,10 +15,12 @@ if TYPE_CHECKING:
|
|||
from cura.Machines.VariantNode import VariantNode
|
||||
|
||||
|
||||
## Represents a material in the container tree.
|
||||
#
|
||||
# Its subcontainers are quality profiles.
|
||||
class MaterialNode(ContainerNode):
|
||||
"""Represents a material in the container tree.
|
||||
|
||||
Its subcontainers are quality profiles.
|
||||
"""
|
||||
|
||||
def __init__(self, container_id: str, variant: "VariantNode") -> None:
|
||||
super().__init__(container_id)
|
||||
self.variant = variant
|
||||
|
@ -34,16 +36,16 @@ class MaterialNode(ContainerNode):
|
|||
container_registry.containerRemoved.connect(self._onRemoved)
|
||||
container_registry.containerMetaDataChanged.connect(self._onMetadataChanged)
|
||||
|
||||
## Finds the preferred quality for this printer with this material and this
|
||||
# variant loaded.
|
||||
#
|
||||
# If the preferred quality is not available, an arbitrary quality is
|
||||
# returned. If there is a configuration mistake (like a typo in the
|
||||
# preferred quality) this returns a random available quality. If there are
|
||||
# no available qualities, this will return the empty quality node.
|
||||
# \return The node for the preferred quality, or any arbitrary quality if
|
||||
# there is no match.
|
||||
def preferredQuality(self) -> QualityNode:
|
||||
"""Finds the preferred quality for this printer with this material and this variant loaded.
|
||||
|
||||
If the preferred quality is not available, an arbitrary quality is returned. If there is a configuration
|
||||
mistake (like a typo in the preferred quality) this returns a random available quality. If there are no
|
||||
available qualities, this will return the empty quality node.
|
||||
|
||||
:return: The node for the preferred quality, or any arbitrary quality if there is no match.
|
||||
"""
|
||||
|
||||
for quality_id, quality_node in self.qualities.items():
|
||||
if self.variant.machine.preferred_quality_type == quality_node.quality_type:
|
||||
return quality_node
|
||||
|
@ -107,10 +109,13 @@ class MaterialNode(ContainerNode):
|
|||
if not self.qualities:
|
||||
self.qualities["empty_quality"] = QualityNode("empty_quality", parent = self)
|
||||
|
||||
## Triggered when any container is removed, but only handles it when the
|
||||
# container is removed that this node represents.
|
||||
# \param container The container that was allegedly removed.
|
||||
def _onRemoved(self, container: ContainerInterface) -> None:
|
||||
"""Triggered when any container is removed, but only handles it when the container is removed that this node
|
||||
represents.
|
||||
|
||||
:param container: The container that was allegedly removed.
|
||||
"""
|
||||
|
||||
if container.getId() == self.container_id:
|
||||
# Remove myself from my parent.
|
||||
if self.base_file in self.variant.materials:
|
||||
|
@ -119,13 +124,15 @@ class MaterialNode(ContainerNode):
|
|||
self.variant.materials["empty_material"] = MaterialNode("empty_material", variant = self.variant)
|
||||
self.materialChanged.emit(self)
|
||||
|
||||
## Triggered when any metadata changed in any container, but only handles
|
||||
# it when the metadata of this node is changed.
|
||||
# \param container The container whose metadata changed.
|
||||
# \param kwargs Key-word arguments provided when changing the metadata.
|
||||
# These are ignored. As far as I know they are never provided to this
|
||||
# call.
|
||||
def _onMetadataChanged(self, container: ContainerInterface, **kwargs: Any) -> None:
|
||||
"""Triggered when any metadata changed in any container, but only handles it when the metadata of this node is
|
||||
changed.
|
||||
|
||||
:param container: The container whose metadata changed.
|
||||
:param kwargs: Key-word arguments provided when changing the metadata. These are ignored. As far as I know they
|
||||
are never provided to this call.
|
||||
"""
|
||||
|
||||
if container.getId() != self.container_id:
|
||||
return
|
||||
|
||||
|
|
|
@ -13,11 +13,13 @@ from cura.Machines.ContainerTree import ContainerTree
|
|||
from cura.Machines.MaterialNode import MaterialNode
|
||||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
|
||||
|
||||
## This is the base model class for GenericMaterialsModel and MaterialBrandsModel.
|
||||
# Those 2 models are used by the material drop down menu to show generic materials and branded materials separately.
|
||||
# The extruder position defined here is being used to bound a menu to the correct extruder. This is used in the top
|
||||
# bar menu "Settings" -> "Extruder nr" -> "Material" -> this menu
|
||||
class BaseMaterialsModel(ListModel):
|
||||
"""This is the base model class for GenericMaterialsModel and MaterialBrandsModel.
|
||||
|
||||
Those 2 models are used by the material drop down menu to show generic materials and branded materials
|
||||
separately. The extruder position defined here is being used to bound a menu to the correct extruder. This is
|
||||
used in the top bar menu "Settings" -> "Extruder nr" -> "Material" -> this menu
|
||||
"""
|
||||
|
||||
extruderPositionChanged = pyqtSignal()
|
||||
enabledChanged = pyqtSignal()
|
||||
|
@ -121,10 +123,13 @@ class BaseMaterialsModel(ListModel):
|
|||
def enabled(self):
|
||||
return self._enabled
|
||||
|
||||
## Triggered when a list of materials changed somewhere in the container
|
||||
# tree. This change may trigger an _update() call when the materials
|
||||
# changed for the configuration that this model is looking for.
|
||||
def _materialsListChanged(self, material: MaterialNode) -> None:
|
||||
"""Triggered when a list of materials changed somewhere in the container
|
||||
|
||||
tree. This change may trigger an _update() call when the materials changed for the configuration that this
|
||||
model is looking for.
|
||||
"""
|
||||
|
||||
if self._extruder_stack is None:
|
||||
return
|
||||
if material.variant.container_id != self._extruder_stack.variant.getId():
|
||||
|
@ -136,14 +141,15 @@ class BaseMaterialsModel(ListModel):
|
|||
return
|
||||
self._onChanged()
|
||||
|
||||
## Triggered when the list of favorite materials is changed.
|
||||
def _favoritesChanged(self, material_base_file: str) -> None:
|
||||
"""Triggered when the list of favorite materials is changed."""
|
||||
|
||||
if material_base_file in self._available_materials:
|
||||
self._onChanged()
|
||||
|
||||
## This is an abstract method that needs to be implemented by the specific
|
||||
# models themselves.
|
||||
def _update(self):
|
||||
"""This is an abstract method that needs to be implemented by the specific models themselves. """
|
||||
|
||||
self._favorite_ids = set(cura.CuraApplication.CuraApplication.getInstance().getPreferences().getValue("cura/favorite_materials").split(";"))
|
||||
|
||||
# Update the available materials (ContainerNode) for the current active machine and extruder setup.
|
||||
|
@ -163,10 +169,10 @@ class BaseMaterialsModel(ListModel):
|
|||
approximate_material_diameter = extruder_stack.getApproximateMaterialDiameter()
|
||||
self._available_materials = {key: material for key, material in materials.items() if float(material.getMetaDataEntry("approximate_diameter", -1)) == approximate_material_diameter}
|
||||
|
||||
## This method is used by all material models in the beginning of the
|
||||
# _update() method in order to prevent errors. It's the same in all models
|
||||
# so it's placed here for easy access.
|
||||
def _canUpdate(self):
|
||||
"""This method is used by all material models in the beginning of the _update() method in order to prevent
|
||||
errors. It's the same in all models so it's placed here for easy access. """
|
||||
|
||||
global_stack = self._machine_manager.activeMachine
|
||||
if global_stack is None or not self._enabled:
|
||||
return False
|
||||
|
@ -177,9 +183,10 @@ class BaseMaterialsModel(ListModel):
|
|||
|
||||
return True
|
||||
|
||||
## 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):
|
||||
"""This is another convenience function which is shared by all material models so it's put here to avoid having
|
||||
so much duplicated code. """
|
||||
|
||||
metadata_list = CuraContainerRegistry.getInstance().findContainersMetadata(id = container_node.container_id)
|
||||
if not metadata_list:
|
||||
return None
|
||||
|
|
|
@ -14,9 +14,8 @@ if TYPE_CHECKING:
|
|||
from UM.Settings.Interfaces import ContainerInterface
|
||||
|
||||
|
||||
## This model is used for the custom profile items in the profile drop down
|
||||
# menu.
|
||||
class CustomQualityProfilesDropDownMenuModel(QualityProfilesDropDownMenuModel):
|
||||
"""This model is used for the custom profile items in the profile drop down menu."""
|
||||
|
||||
def __init__(self, parent: Optional["QObject"] = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
|
|
@ -9,9 +9,9 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class DiscoveredCloudPrintersModel(ListModel):
|
||||
"""
|
||||
Model used to inform the application about newly added cloud printers, which are discovered from the user's account
|
||||
"""
|
||||
"""Model used to inform the application about newly added cloud printers, which are discovered from the user's
|
||||
account """
|
||||
|
||||
DeviceKeyRole = Qt.UserRole + 1
|
||||
DeviceNameRole = Qt.UserRole + 2
|
||||
DeviceTypeRole = Qt.UserRole + 3
|
||||
|
@ -31,18 +31,24 @@ class DiscoveredCloudPrintersModel(ListModel):
|
|||
self._application = application # type: CuraApplication
|
||||
|
||||
def addDiscoveredCloudPrinters(self, new_devices: List[Dict[str, str]]) -> None:
|
||||
"""
|
||||
Adds all the newly discovered cloud printers into the DiscoveredCloudPrintersModel.
|
||||
"""Adds all the newly discovered cloud printers into the DiscoveredCloudPrintersModel.
|
||||
|
||||
Example new_devices entry:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
:param new_devices: List of dictionaries which contain information about added cloud printers. Example:
|
||||
{
|
||||
"key": "YjW8pwGYcaUvaa0YgVyWeFkX3z",
|
||||
"name": "NG 001",
|
||||
"machine_type": "Ultimaker S5",
|
||||
"firmware_version": "5.5.12.202001"
|
||||
}
|
||||
|
||||
:param new_devices: List of dictionaries which contain information about added cloud printers.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self._discovered_cloud_printers_list.extend(new_devices)
|
||||
self._update()
|
||||
|
||||
|
@ -51,21 +57,21 @@ class DiscoveredCloudPrintersModel(ListModel):
|
|||
|
||||
@pyqtSlot()
|
||||
def clear(self) -> None:
|
||||
"""
|
||||
Clears the contents of the DiscoveredCloudPrintersModel.
|
||||
"""Clears the contents of the DiscoveredCloudPrintersModel.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self._discovered_cloud_printers_list = []
|
||||
self._update()
|
||||
self.cloudPrintersDetectedChanged.emit(False)
|
||||
|
||||
def _update(self) -> None:
|
||||
"""
|
||||
Sorts the newly discovered cloud printers by name and then updates the ListModel.
|
||||
"""Sorts the newly discovered cloud printers by name and then updates the ListModel.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
items = self._discovered_cloud_printers_list[:]
|
||||
items.sort(key = lambda k: k["name"])
|
||||
self.setItems(items)
|
||||
|
|
|
@ -115,12 +115,11 @@ class DiscoveredPrinter(QObject):
|
|||
return catalog.i18nc("@label", "Available networked printers")
|
||||
|
||||
|
||||
#
|
||||
# Discovered printers are all the printers that were found on the network, which provide a more convenient way
|
||||
# to add networked printers (Plugin finds a bunch of printers, user can select one from the list, plugin can then
|
||||
# add that printer to Cura as the active one).
|
||||
#
|
||||
class DiscoveredPrintersModel(QObject):
|
||||
"""Discovered printers are all the printers that were found on the network, which provide a more convenient way to
|
||||
add networked printers (Plugin finds a bunch of printers, user can select one from the list, plugin can then add
|
||||
that printer to Cura as the active one).
|
||||
"""
|
||||
|
||||
def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
@ -254,8 +253,14 @@ class DiscoveredPrintersModel(QObject):
|
|||
del self._discovered_printer_by_ip_dict[ip_address]
|
||||
self.discoveredPrintersChanged.emit()
|
||||
|
||||
# A convenience function for QML to create a machine (GlobalStack) out of the given discovered printer.
|
||||
# This function invokes the given discovered printer's "create_callback" to do this.
|
||||
|
||||
@pyqtSlot("QVariant")
|
||||
def createMachineFromDiscoveredPrinter(self, discovered_printer: "DiscoveredPrinter") -> None:
|
||||
"""A convenience function for QML to create a machine (GlobalStack) out of the given discovered printer.
|
||||
|
||||
This function invokes the given discovered printer's "create_callback" to do this
|
||||
|
||||
:param discovered_printer:
|
||||
"""
|
||||
|
||||
discovered_printer.create_callback(discovered_printer.getKey())
|
||||
|
|
|
@ -15,27 +15,27 @@ if TYPE_CHECKING:
|
|||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
## Model that holds extruders.
|
||||
#
|
||||
# This model is designed for use by any list of extruders, but specifically
|
||||
# intended for drop-down lists of the current machine's extruders in place of
|
||||
# settings.
|
||||
class ExtrudersModel(ListModel):
|
||||
"""Model that holds extruders.
|
||||
|
||||
This model is designed for use by any list of extruders, but specifically intended for drop-down lists of the
|
||||
current machine's extruders in place of settings.
|
||||
"""
|
||||
|
||||
# The ID of the container stack for the extruder.
|
||||
IdRole = Qt.UserRole + 1
|
||||
|
||||
## Human-readable name of the extruder.
|
||||
NameRole = Qt.UserRole + 2
|
||||
"""Human-readable name of the extruder."""
|
||||
|
||||
## Colour of the material loaded in the extruder.
|
||||
ColorRole = Qt.UserRole + 3
|
||||
"""Colour of the material loaded in the extruder."""
|
||||
|
||||
## Index of the extruder, which is also the value of the setting itself.
|
||||
#
|
||||
# An index of 0 indicates the first extruder, an index of 1 the second
|
||||
# one, and so on. This is the value that will be saved in instance
|
||||
# containers.
|
||||
IndexRole = Qt.UserRole + 4
|
||||
"""Index of the extruder, which is also the value of the setting itself.
|
||||
|
||||
An index of 0 indicates the first extruder, an index of 1 the second one, and so on. This is the value that will
|
||||
be saved in instance containers. """
|
||||
|
||||
# The ID of the definition of the extruder.
|
||||
DefinitionRole = Qt.UserRole + 5
|
||||
|
@ -50,18 +50,18 @@ class ExtrudersModel(ListModel):
|
|||
MaterialBrandRole = Qt.UserRole + 9
|
||||
ColorNameRole = Qt.UserRole + 10
|
||||
|
||||
## Is the extruder enabled?
|
||||
EnabledRole = Qt.UserRole + 11
|
||||
"""Is the extruder enabled?"""
|
||||
|
||||
## List of colours to display if there is no material or the material has no known
|
||||
# colour.
|
||||
defaultColors = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"]
|
||||
"""List of colours to display if there is no material or the material has no known colour. """
|
||||
|
||||
## Initialises the extruders model, defining the roles and listening for
|
||||
# changes in the data.
|
||||
#
|
||||
# \param parent Parent QtObject of this list.
|
||||
def __init__(self, parent = None):
|
||||
"""Initialises the extruders model, defining the roles and listening for changes in the data.
|
||||
|
||||
:param parent: Parent QtObject of this list.
|
||||
"""
|
||||
|
||||
super().__init__(parent)
|
||||
|
||||
self.addRoleName(self.IdRole, "id")
|
||||
|
@ -101,14 +101,15 @@ class ExtrudersModel(ListModel):
|
|||
def addOptionalExtruder(self):
|
||||
return self._add_optional_extruder
|
||||
|
||||
## Links to the stack-changed signal of the new extruders when an extruder
|
||||
# is swapped out or added in the current machine.
|
||||
#
|
||||
# \param machine_id The machine for which the extruders changed. This is
|
||||
# filled by the ExtruderManager.extrudersChanged signal when coming from
|
||||
# that signal. Application.globalContainerStackChanged doesn't fill this
|
||||
# signal; it's assumed to be the current printer in that case.
|
||||
def _extrudersChanged(self, machine_id = None):
|
||||
"""Links to the stack-changed signal of the new extruders when an extruder is swapped out or added in the
|
||||
current machine.
|
||||
|
||||
:param machine_id: The machine for which the extruders changed. This is filled by the
|
||||
ExtruderManager.extrudersChanged signal when coming from that signal. Application.globalContainerStackChanged
|
||||
doesn't fill this signal; it's assumed to be the current printer in that case.
|
||||
"""
|
||||
|
||||
machine_manager = Application.getInstance().getMachineManager()
|
||||
if machine_id is not None:
|
||||
if machine_manager.activeMachine is None:
|
||||
|
@ -146,11 +147,13 @@ class ExtrudersModel(ListModel):
|
|||
def _updateExtruders(self):
|
||||
self._update_extruder_timer.start()
|
||||
|
||||
## Update the list of extruders.
|
||||
#
|
||||
# This should be called whenever the list of extruders changes.
|
||||
@UM.FlameProfiler.profile
|
||||
def __updateExtruders(self):
|
||||
"""Update the list of extruders.
|
||||
|
||||
This should be called whenever the list of extruders changes.
|
||||
"""
|
||||
|
||||
extruders_changed = False
|
||||
|
||||
if self.count != 0:
|
||||
|
|
|
@ -4,16 +4,17 @@
|
|||
from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel
|
||||
import cura.CuraApplication # To listen to changes to the preferences.
|
||||
|
||||
## Model that shows the list of favorite materials.
|
||||
class FavoriteMaterialsModel(BaseMaterialsModel):
|
||||
"""Model that shows the list of favorite materials."""
|
||||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
cura.CuraApplication.CuraApplication.getInstance().getPreferences().preferenceChanged.connect(self._onFavoritesChanged)
|
||||
self._onChanged()
|
||||
|
||||
## Triggered when any preference changes, but only handles it when the list
|
||||
# of favourites is changed.
|
||||
def _onFavoritesChanged(self, preference_key: str) -> None:
|
||||
"""Triggered when any preference changes, but only handles it when the list of favourites is changed. """
|
||||
|
||||
if preference_key != "cura/favorite_materials":
|
||||
return
|
||||
self._onChanged()
|
||||
|
|
|
@ -11,13 +11,13 @@ if TYPE_CHECKING:
|
|||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
|
||||
#
|
||||
# This model holds all first-start machine actions for the currently active machine. It has 2 roles:
|
||||
# - title : the title/name of the action
|
||||
# - content : the QObject of the QML content of the action
|
||||
# - action : the MachineAction object itself
|
||||
#
|
||||
class FirstStartMachineActionsModel(ListModel):
|
||||
"""This model holds all first-start machine actions for the currently active machine. It has 2 roles:
|
||||
|
||||
- title : the title/name of the action
|
||||
- content : the QObject of the QML content of the action
|
||||
- action : the MachineAction object itself
|
||||
"""
|
||||
|
||||
TitleRole = Qt.UserRole + 1
|
||||
ContentRole = Qt.UserRole + 2
|
||||
|
@ -73,9 +73,10 @@ class FirstStartMachineActionsModel(ListModel):
|
|||
self._current_action_index += 1
|
||||
self.currentActionIndexChanged.emit()
|
||||
|
||||
# Resets the current action index to 0 so the wizard panel can show actions from the beginning.
|
||||
@pyqtSlot()
|
||||
def reset(self) -> None:
|
||||
"""Resets the current action index to 0 so the wizard panel can show actions from the beginning."""
|
||||
|
||||
self._current_action_index = 0
|
||||
self.currentActionIndexChanged.emit()
|
||||
|
||||
|
|
|
@ -42,8 +42,9 @@ class GlobalStacksModel(ListModel):
|
|||
CuraContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged)
|
||||
self._updateDelayed()
|
||||
|
||||
## Handler for container added/removed events from registry
|
||||
def _onContainerChanged(self, container) -> None:
|
||||
"""Handler for container added/removed events from registry"""
|
||||
|
||||
# We only need to update when the added / removed container GlobalStack
|
||||
if isinstance(container, GlobalStack):
|
||||
self._updateDelayed()
|
||||
|
|
|
@ -18,9 +18,9 @@ from UM.i18n import i18nCatalog
|
|||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
## Lists the intent categories that are available for the current printer
|
||||
# configuration.
|
||||
class IntentCategoryModel(ListModel):
|
||||
"""Lists the intent categories that are available for the current printer configuration. """
|
||||
|
||||
NameRole = Qt.UserRole + 1
|
||||
IntentCategoryRole = Qt.UserRole + 2
|
||||
WeightRole = Qt.UserRole + 3
|
||||
|
@ -31,10 +31,12 @@ class IntentCategoryModel(ListModel):
|
|||
|
||||
_translations = collections.OrderedDict() # type: "collections.OrderedDict[str,Dict[str,Optional[str]]]"
|
||||
|
||||
# Translations to user-visible string. Ordered by weight.
|
||||
# TODO: Create a solution for this name and weight to be used dynamically.
|
||||
@classmethod
|
||||
def _get_translations(cls):
|
||||
"""Translations to user-visible string. Ordered by weight.
|
||||
|
||||
TODO: Create a solution for this name and weight to be used dynamically.
|
||||
"""
|
||||
if len(cls._translations) == 0:
|
||||
cls._translations["default"] = {
|
||||
"name": catalog.i18nc("@label", "Default")
|
||||
|
@ -53,9 +55,12 @@ class IntentCategoryModel(ListModel):
|
|||
}
|
||||
return cls._translations
|
||||
|
||||
## Creates a new model for a certain intent category.
|
||||
# \param The category to list the intent profiles for.
|
||||
def __init__(self, intent_category: str) -> None:
|
||||
"""Creates a new model for a certain intent category.
|
||||
|
||||
:param intent_category: category to list the intent profiles for.
|
||||
"""
|
||||
|
||||
super().__init__()
|
||||
self._intent_category = intent_category
|
||||
|
||||
|
@ -84,16 +89,18 @@ class IntentCategoryModel(ListModel):
|
|||
|
||||
self.update()
|
||||
|
||||
## Updates the list of intents if an intent profile was added or removed.
|
||||
def _onContainerChange(self, container: "ContainerInterface") -> None:
|
||||
"""Updates the list of intents if an intent profile was added or removed."""
|
||||
|
||||
if container.getMetaDataEntry("type") == "intent":
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
self._update_timer.start()
|
||||
|
||||
## Updates the list of intents.
|
||||
def _update(self) -> None:
|
||||
"""Updates the list of intents."""
|
||||
|
||||
available_categories = IntentManager.getInstance().currentAvailableIntentCategories()
|
||||
result = []
|
||||
for category in available_categories:
|
||||
|
@ -109,9 +116,9 @@ class IntentCategoryModel(ListModel):
|
|||
result.sort(key = lambda k: k["weight"])
|
||||
self.setItems(result)
|
||||
|
||||
## Get a display value for a category.
|
||||
## for categories and keys
|
||||
@staticmethod
|
||||
def translation(category: str, key: str, default: Optional[str] = None):
|
||||
"""Get a display value for a category.for categories and keys"""
|
||||
|
||||
display_strings = IntentCategoryModel._get_translations().get(category, {})
|
||||
return display_strings.get(key, default)
|
||||
|
|
|
@ -98,8 +98,9 @@ class IntentModel(ListModel):
|
|||
new_items = sorted(new_items, key = lambda x: x["layer_height"])
|
||||
self.setItems(new_items)
|
||||
|
||||
## Get the active materials for all extruders. No duplicates will be returned
|
||||
def _getActiveMaterials(self) -> Set["MaterialNode"]:
|
||||
"""Get the active materials for all extruders. No duplicates will be returned"""
|
||||
|
||||
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
|
||||
if global_stack is None:
|
||||
return set()
|
||||
|
|
|
@ -19,28 +19,31 @@ if TYPE_CHECKING:
|
|||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
## Proxy class to the materials page in the preferences.
|
||||
#
|
||||
# This class handles the actions in that page, such as creating new materials,
|
||||
# renaming them, etc.
|
||||
class MaterialManagementModel(QObject):
|
||||
## Triggered when a favorite is added or removed.
|
||||
# \param The base file of the material is provided as parameter when this
|
||||
# emits.
|
||||
favoritesChanged = pyqtSignal(str)
|
||||
"""Proxy class to the materials page in the preferences.
|
||||
|
||||
This class handles the actions in that page, such as creating new materials, renaming them, etc.
|
||||
"""
|
||||
|
||||
favoritesChanged = pyqtSignal(str)
|
||||
"""Triggered when a favorite is added or removed.
|
||||
|
||||
:param The base file of the material is provided as parameter when this emits
|
||||
"""
|
||||
|
||||
## Can a certain material be deleted, or is it still in use in one of the
|
||||
# container stacks anywhere?
|
||||
#
|
||||
# We forbid the user from deleting a material if it's in use in any stack.
|
||||
# Deleting it while it's in use can lead to corrupted stacks. In the
|
||||
# future we might enable this functionality again (deleting the material
|
||||
# from those stacks) but for now it is easier to prevent the user from
|
||||
# doing this.
|
||||
# \param material_node The ContainerTree node of the material to check.
|
||||
# \return Whether or not the material can be removed.
|
||||
@pyqtSlot("QVariant", result = bool)
|
||||
def canMaterialBeRemoved(self, material_node: "MaterialNode") -> bool:
|
||||
"""Can a certain material be deleted, or is it still in use in one of the container stacks anywhere?
|
||||
|
||||
We forbid the user from deleting a material if it's in use in any stack. Deleting it while it's in use can
|
||||
lead to corrupted stacks. In the future we might enable this functionality again (deleting the material from
|
||||
those stacks) but for now it is easier to prevent the user from doing this.
|
||||
|
||||
:param material_node: The ContainerTree node of the material to check.
|
||||
|
||||
:return: Whether or not the material can be removed.
|
||||
"""
|
||||
|
||||
container_registry = CuraContainerRegistry.getInstance()
|
||||
ids_to_remove = {metadata.get("id", "") for metadata in container_registry.findInstanceContainersMetadata(base_file = material_node.base_file)}
|
||||
for extruder_stack in container_registry.findContainerStacks(type = "extruder_train"):
|
||||
|
@ -48,11 +51,14 @@ class MaterialManagementModel(QObject):
|
|||
return False
|
||||
return True
|
||||
|
||||
## Change the user-visible name of a material.
|
||||
# \param material_node The ContainerTree node of the material to rename.
|
||||
# \param name The new name for the material.
|
||||
@pyqtSlot("QVariant", str)
|
||||
def setMaterialName(self, material_node: "MaterialNode", name: str) -> None:
|
||||
"""Change the user-visible name of a material.
|
||||
|
||||
:param material_node: The ContainerTree node of the material to rename.
|
||||
:param name: The new name for the material.
|
||||
"""
|
||||
|
||||
container_registry = CuraContainerRegistry.getInstance()
|
||||
root_material_id = material_node.base_file
|
||||
if container_registry.isReadOnly(root_material_id):
|
||||
|
@ -60,18 +66,20 @@ class MaterialManagementModel(QObject):
|
|||
return
|
||||
return container_registry.findContainers(id = root_material_id)[0].setName(name)
|
||||
|
||||
## Deletes a material from Cura.
|
||||
#
|
||||
# This function does not do any safety checking any more. Please call this
|
||||
# function only if:
|
||||
# - The material is not read-only.
|
||||
# - The material is not used in any stacks.
|
||||
# If the material was not lazy-loaded yet, this will fully load the
|
||||
# container. When removing this material node, all other materials with
|
||||
# the same base fill will also be removed.
|
||||
# \param material_node The material to remove.
|
||||
@pyqtSlot("QVariant")
|
||||
def removeMaterial(self, material_node: "MaterialNode") -> None:
|
||||
"""Deletes a material from Cura.
|
||||
|
||||
This function does not do any safety checking any more. Please call this function only if:
|
||||
- The material is not read-only.
|
||||
- The material is not used in any stacks.
|
||||
|
||||
If the material was not lazy-loaded yet, this will fully load the container. When removing this material
|
||||
node, all other materials with the same base fill will also be removed.
|
||||
|
||||
:param material_node: The material to remove.
|
||||
"""
|
||||
|
||||
container_registry = CuraContainerRegistry.getInstance()
|
||||
materials_this_base_file = container_registry.findContainersMetadata(base_file = material_node.base_file)
|
||||
|
||||
|
@ -89,17 +97,19 @@ class MaterialManagementModel(QObject):
|
|||
for material_metadata in materials_this_base_file:
|
||||
container_registry.removeContainer(material_metadata["id"])
|
||||
|
||||
## Creates a duplicate of a material with the same GUID and base_file
|
||||
# metadata.
|
||||
# \param base_file: The base file of the material to duplicate.
|
||||
# \param new_base_id A new material ID for the base material. The IDs of
|
||||
# the submaterials will be based off this one. If not provided, a material
|
||||
# ID will be generated automatically.
|
||||
# \param new_metadata Metadata for the new material. If not provided, this
|
||||
# will be duplicated from the original material.
|
||||
# \return The root material ID of the duplicate material.
|
||||
def duplicateMaterialByBaseFile(self, base_file: str, new_base_id: Optional[str] = None,
|
||||
new_metadata: Optional[Dict[str, Any]] = None) -> Optional[str]:
|
||||
"""Creates a duplicate of a material with the same GUID and base_file metadata
|
||||
|
||||
:param base_file: The base file of the material to duplicate.
|
||||
:param new_base_id: A new material ID for the base material. The IDs of the submaterials will be based off this
|
||||
one. If not provided, a material ID will be generated automatically.
|
||||
:param new_metadata: Metadata for the new material. If not provided, this will be duplicated from the original
|
||||
material.
|
||||
|
||||
:return: The root material ID of the duplicate material.
|
||||
"""
|
||||
|
||||
container_registry = CuraContainerRegistry.getInstance()
|
||||
|
||||
root_materials = container_registry.findContainers(id = base_file)
|
||||
|
@ -171,29 +181,32 @@ class MaterialManagementModel(QObject):
|
|||
|
||||
return new_base_id
|
||||
|
||||
## Creates a duplicate of a material with the same GUID and base_file
|
||||
# metadata.
|
||||
# \param material_node The node representing the material to duplicate.
|
||||
# \param new_base_id A new material ID for the base material. The IDs of
|
||||
# the submaterials will be based off this one. If not provided, a material
|
||||
# ID will be generated automatically.
|
||||
# \param new_metadata Metadata for the new material. If not provided, this
|
||||
# will be duplicated from the original material.
|
||||
# \return The root material ID of the duplicate material.
|
||||
@pyqtSlot("QVariant", result = str)
|
||||
def duplicateMaterial(self, material_node: "MaterialNode", new_base_id: Optional[str] = None,
|
||||
new_metadata: Optional[Dict[str, Any]] = None) -> Optional[str]:
|
||||
"""Creates a duplicate of a material with the same GUID and base_file metadata
|
||||
|
||||
:param material_node: The node representing the material to duplicate.
|
||||
:param new_base_id: A new material ID for the base material. The IDs of the submaterials will be based off this
|
||||
one. If not provided, a material ID will be generated automatically.
|
||||
:param new_metadata: Metadata for the new material. If not provided, this will be duplicated from the original
|
||||
material.
|
||||
|
||||
:return: The root material ID of the duplicate material.
|
||||
"""
|
||||
return self.duplicateMaterialByBaseFile(material_node.base_file, new_base_id, new_metadata)
|
||||
|
||||
## Create a new material by cloning the preferred material for the current
|
||||
# material diameter and generate a new GUID.
|
||||
#
|
||||
# The material type is explicitly left to be the one from the preferred
|
||||
# material, since this allows the user to still have SOME profiles to work
|
||||
# with.
|
||||
# \return The ID of the newly created material.
|
||||
@pyqtSlot(result = str)
|
||||
def createMaterial(self) -> str:
|
||||
"""Create a new material by cloning the preferred material for the current material diameter and generate a new
|
||||
GUID.
|
||||
|
||||
The material type is explicitly left to be the one from the preferred material, since this allows the user to
|
||||
still have SOME profiles to work with.
|
||||
|
||||
:return: The ID of the newly created material.
|
||||
"""
|
||||
|
||||
# Ensure all settings are saved.
|
||||
application = cura.CuraApplication.CuraApplication.getInstance()
|
||||
application.saveSettings()
|
||||
|
@ -218,10 +231,13 @@ class MaterialManagementModel(QObject):
|
|||
self.duplicateMaterial(preferred_material_node, new_base_id = new_id, new_metadata = new_metadata)
|
||||
return new_id
|
||||
|
||||
## Adds a certain material to the favorite materials.
|
||||
# \param material_base_file The base file of the material to add.
|
||||
@pyqtSlot(str)
|
||||
def addFavorite(self, material_base_file: str) -> None:
|
||||
"""Adds a certain material to the favorite materials.
|
||||
|
||||
:param material_base_file: The base file of the material to add.
|
||||
"""
|
||||
|
||||
application = cura.CuraApplication.CuraApplication.getInstance()
|
||||
favorites = application.getPreferences().getValue("cura/favorite_materials").split(";")
|
||||
if material_base_file not in favorites:
|
||||
|
@ -230,11 +246,13 @@ class MaterialManagementModel(QObject):
|
|||
application.saveSettings()
|
||||
self.favoritesChanged.emit(material_base_file)
|
||||
|
||||
## Removes a certain material from the favorite materials.
|
||||
#
|
||||
# If the material was not in the favorite materials, nothing happens.
|
||||
@pyqtSlot(str)
|
||||
def removeFavorite(self, material_base_file: str) -> None:
|
||||
"""Removes a certain material from the favorite materials.
|
||||
|
||||
If the material was not in the favorite materials, nothing happens.
|
||||
"""
|
||||
|
||||
application = cura.CuraApplication.CuraApplication.getInstance()
|
||||
favorites = application.getPreferences().getValue("cura/favorite_materials").split(";")
|
||||
try:
|
||||
|
|
|
@ -9,11 +9,11 @@ from UM.Scene.Selection import Selection
|
|||
from UM.Qt.ListModel import ListModel
|
||||
|
||||
|
||||
#
|
||||
# This is the model for multi build plate feature.
|
||||
# This has nothing to do with the build plate types you can choose on the sidebar for a machine.
|
||||
#
|
||||
class MultiBuildPlateModel(ListModel):
|
||||
"""This is the model for multi build plate feature.
|
||||
|
||||
This has nothing to do with the build plate types you can choose on the sidebar for a machine.
|
||||
"""
|
||||
|
||||
maxBuildPlateChanged = pyqtSignal()
|
||||
activeBuildPlateChanged = pyqtSignal()
|
||||
|
@ -39,9 +39,10 @@ class MultiBuildPlateModel(ListModel):
|
|||
self._max_build_plate = max_build_plate
|
||||
self.maxBuildPlateChanged.emit()
|
||||
|
||||
## Return the highest build plate number
|
||||
@pyqtProperty(int, notify = maxBuildPlateChanged)
|
||||
def maxBuildPlate(self):
|
||||
"""Return the highest build plate number"""
|
||||
|
||||
return self._max_build_plate
|
||||
|
||||
def setActiveBuildPlate(self, nr):
|
||||
|
|
|
@ -26,10 +26,9 @@ if TYPE_CHECKING:
|
|||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
|
||||
#
|
||||
# This the QML model for the quality management page.
|
||||
#
|
||||
class QualityManagementModel(ListModel):
|
||||
"""This the QML model for the quality management page."""
|
||||
|
||||
NameRole = Qt.UserRole + 1
|
||||
IsReadOnlyRole = Qt.UserRole + 2
|
||||
QualityGroupRole = Qt.UserRole + 3
|
||||
|
@ -74,11 +73,13 @@ class QualityManagementModel(ListModel):
|
|||
def _onChange(self) -> None:
|
||||
self._update_timer.start()
|
||||
|
||||
## Deletes a custom profile. It will be gone forever.
|
||||
# \param quality_changes_group The quality changes group representing the
|
||||
# profile to delete.
|
||||
@pyqtSlot(QObject)
|
||||
def removeQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup") -> None:
|
||||
"""Deletes a custom profile. It will be gone forever.
|
||||
|
||||
:param quality_changes_group: The quality changes group representing the profile to delete.
|
||||
"""
|
||||
|
||||
Logger.log("i", "Removing quality changes group {group_name}".format(group_name = quality_changes_group.name))
|
||||
removed_quality_changes_ids = set()
|
||||
container_registry = cura.CuraApplication.CuraApplication.getInstance().getContainerRegistry()
|
||||
|
@ -95,16 +96,19 @@ class QualityManagementModel(ListModel):
|
|||
if extruder_stack.qualityChanges.getId() in removed_quality_changes_ids:
|
||||
extruder_stack.qualityChanges = empty_quality_changes_container
|
||||
|
||||
## Rename a custom profile.
|
||||
#
|
||||
# Because the names must be unique, the new name may not actually become
|
||||
# the name that was given. The actual name is returned by this function.
|
||||
# \param quality_changes_group The custom profile that must be renamed.
|
||||
# \param new_name The desired name for the profile.
|
||||
# \return The actual new name of the profile, after making the name
|
||||
# unique.
|
||||
@pyqtSlot(QObject, str, result = str)
|
||||
def renameQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup", new_name: str) -> str:
|
||||
"""Rename a custom profile.
|
||||
|
||||
Because the names must be unique, the new name may not actually become the name that was given. The actual
|
||||
name is returned by this function.
|
||||
|
||||
:param quality_changes_group: The custom profile that must be renamed.
|
||||
:param new_name: The desired name for the profile.
|
||||
|
||||
:return: The actual new name of the profile, after making the name unique.
|
||||
"""
|
||||
|
||||
Logger.log("i", "Renaming QualityChangesGroup {old_name} to {new_name}.".format(old_name = quality_changes_group.name, new_name = new_name))
|
||||
if new_name == quality_changes_group.name:
|
||||
Logger.log("i", "QualityChangesGroup name {name} unchanged.".format(name = quality_changes_group.name))
|
||||
|
@ -138,13 +142,16 @@ class QualityManagementModel(ListModel):
|
|||
|
||||
return new_name
|
||||
|
||||
## Duplicates a given quality profile OR quality changes profile.
|
||||
# \param new_name The desired name of the new profile. This will be made
|
||||
# unique, so it might end up with a different name.
|
||||
# \param quality_model_item The item of this model to duplicate, as
|
||||
# dictionary. See the descriptions of the roles of this list model.
|
||||
@pyqtSlot(str, "QVariantMap")
|
||||
def duplicateQualityChanges(self, new_name: str, quality_model_item: Dict[str, Any]) -> None:
|
||||
"""Duplicates a given quality profile OR quality changes profile.
|
||||
|
||||
:param new_name: The desired name of the new profile. This will be made unique, so it might end up with a
|
||||
different name.
|
||||
:param quality_model_item: The item of this model to duplicate, as dictionary. See the descriptions of the
|
||||
roles of this list model.
|
||||
"""
|
||||
|
||||
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
|
||||
if not global_stack:
|
||||
Logger.log("i", "No active global stack, cannot duplicate quality (changes) profile.")
|
||||
|
@ -170,18 +177,18 @@ class QualityManagementModel(ListModel):
|
|||
new_id = container_registry.uniqueName(container.getId())
|
||||
container_registry.addContainer(container.duplicate(new_id, new_name))
|
||||
|
||||
## Create quality changes containers from the user containers in the active
|
||||
# stacks.
|
||||
#
|
||||
# This will go through the global and extruder stacks and create
|
||||
# quality_changes containers from the user containers in each stack. These
|
||||
# then replace the quality_changes containers in the stack and clear the
|
||||
# user settings.
|
||||
# \param base_name The new name for the quality changes profile. The final
|
||||
# name of the profile might be different from this, because it needs to be
|
||||
# made unique.
|
||||
@pyqtSlot(str)
|
||||
def createQualityChanges(self, base_name: str) -> None:
|
||||
"""Create quality changes containers from the user containers in the active stacks.
|
||||
|
||||
This will go through the global and extruder stacks and create quality_changes containers from the user
|
||||
containers in each stack. These then replace the quality_changes containers in the stack and clear the user
|
||||
settings.
|
||||
|
||||
:param base_name: The new name for the quality changes profile. The final name of the profile might be
|
||||
different from this, because it needs to be made unique.
|
||||
"""
|
||||
|
||||
machine_manager = cura.CuraApplication.CuraApplication.getInstance().getMachineManager()
|
||||
|
||||
global_stack = machine_manager.activeMachine
|
||||
|
@ -220,14 +227,16 @@ class QualityManagementModel(ListModel):
|
|||
|
||||
container_registry.addContainer(new_changes)
|
||||
|
||||
## Create a quality changes container with the given set-up.
|
||||
# \param quality_type The quality type of the new container.
|
||||
# \param intent_category The intent category of the new container.
|
||||
# \param new_name The name of the container. This name must be unique.
|
||||
# \param machine The global stack to create the profile for.
|
||||
# \param extruder_stack The extruder stack to create the profile for. If
|
||||
# not provided, only a global container will be created.
|
||||
def _createQualityChanges(self, quality_type: str, intent_category: Optional[str], new_name: str, machine: "GlobalStack", extruder_stack: Optional["ExtruderStack"]) -> "InstanceContainer":
|
||||
"""Create a quality changes container with the given set-up.
|
||||
|
||||
:param quality_type: The quality type of the new container.
|
||||
:param intent_category: The intent category of the new container.
|
||||
:param new_name: The name of the container. This name must be unique.
|
||||
:param machine: The global stack to create the profile for.
|
||||
:param extruder_stack: The extruder stack to create the profile for. If not provided, only a global container will be created.
|
||||
"""
|
||||
|
||||
container_registry = cura.CuraApplication.CuraApplication.getInstance().getContainerRegistry()
|
||||
base_id = machine.definition.getId() if extruder_stack is None else extruder_stack.getId()
|
||||
new_id = base_id + "_" + new_name
|
||||
|
@ -253,11 +262,13 @@ class QualityManagementModel(ListModel):
|
|||
quality_changes.setMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.getInstance().SettingVersion)
|
||||
return quality_changes
|
||||
|
||||
## Triggered when any container changed.
|
||||
#
|
||||
# This filters the updates to the container manager: When it applies to
|
||||
# the list of quality changes, we need to update our list.
|
||||
def _qualityChangesListChanged(self, container: "ContainerInterface") -> None:
|
||||
"""Triggered when any container changed.
|
||||
|
||||
This filters the updates to the container manager: When it applies to the list of quality changes, we need to
|
||||
update our list.
|
||||
"""
|
||||
|
||||
if container.getMetaDataEntry("type") == "quality_changes":
|
||||
self._update()
|
||||
|
||||
|
@ -366,18 +377,19 @@ class QualityManagementModel(ListModel):
|
|||
|
||||
self.setItems(item_list)
|
||||
|
||||
# TODO: Duplicated code here from InstanceContainersModel. Refactor and remove this later.
|
||||
#
|
||||
## Gets a list of the possible file filters that the plugins have
|
||||
# registered they can read or write. The convenience meta-filters
|
||||
# "All Supported Types" and "All Files" are added when listing
|
||||
# readers, but not when listing writers.
|
||||
#
|
||||
# \param io_type \type{str} name of the needed IO type
|
||||
# \return A list of strings indicating file name filters for a file
|
||||
# dialog.
|
||||
@pyqtSlot(str, result = "QVariantList")
|
||||
def getFileNameFilters(self, io_type):
|
||||
"""Gets a list of the possible file filters that the plugins have registered they can read or write.
|
||||
|
||||
The convenience meta-filters "All Supported Types" and "All Files" are added when listing readers,
|
||||
but not when listing writers.
|
||||
|
||||
:param io_type: name of the needed IO type
|
||||
:return: A list of strings indicating file name filters for a file dialog.
|
||||
|
||||
TODO: Duplicated code here from InstanceContainersModel. Refactor and remove this later.
|
||||
"""
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("uranium")
|
||||
#TODO: This function should be in UM.Resources!
|
||||
|
@ -394,9 +406,11 @@ class QualityManagementModel(ListModel):
|
|||
filters.append(catalog.i18nc("@item:inlistbox", "All Files (*)")) # Also allow arbitrary files, if the user so prefers.
|
||||
return filters
|
||||
|
||||
## Gets a list of profile reader or writer plugins
|
||||
# \return List of tuples of (plugin_id, meta_data).
|
||||
def _getIOPlugins(self, io_type):
|
||||
"""Gets a list of profile reader or writer plugins
|
||||
|
||||
:return: List of tuples of (plugin_id, meta_data).
|
||||
"""
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
pr = PluginRegistry.getInstance()
|
||||
active_plugin_ids = pr.getActivePlugins()
|
||||
|
|
|
@ -10,10 +10,9 @@ from cura.Machines.ContainerTree import ContainerTree
|
|||
from cura.Machines.Models.MachineModelUtils import fetchLayerHeight
|
||||
|
||||
|
||||
#
|
||||
# QML Model for all built-in quality profiles. This model is used for the drop-down quality menu.
|
||||
#
|
||||
class QualityProfilesDropDownMenuModel(ListModel):
|
||||
"""QML Model for all built-in quality profiles. This model is used for the drop-down quality menu."""
|
||||
|
||||
NameRole = Qt.UserRole + 1
|
||||
QualityTypeRole = Qt.UserRole + 2
|
||||
LayerHeightRole = Qt.UserRole + 3
|
||||
|
|
|
@ -10,10 +10,9 @@ from UM.Qt.ListModel import ListModel
|
|||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
|
||||
#
|
||||
# This model is used to show details settings of the selected quality in the quality management page.
|
||||
#
|
||||
class QualitySettingsModel(ListModel):
|
||||
"""This model is used to show details settings of the selected quality in the quality management page."""
|
||||
|
||||
KeyRole = Qt.UserRole + 1
|
||||
LabelRole = Qt.UserRole + 2
|
||||
UnitRole = Qt.UserRole + 3
|
||||
|
|
|
@ -6,12 +6,12 @@ from typing import Any, Dict, Optional
|
|||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
|
||||
|
||||
|
||||
## Data struct to group several quality changes instance containers together.
|
||||
#
|
||||
# Each group represents one "custom profile" as the user sees it, which
|
||||
# contains an instance container for the global stack and one instance
|
||||
# container per extruder.
|
||||
class QualityChangesGroup(QObject):
|
||||
"""Data struct to group several quality changes instance containers together.
|
||||
|
||||
Each group represents one "custom profile" as the user sees it, which contains an instance container for the
|
||||
global stack and one instance container per extruder.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, quality_type: str, intent_category: str, parent: Optional["QObject"] = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
|
|
@ -9,28 +9,34 @@ from UM.Util import parseBool
|
|||
from cura.Machines.ContainerNode import ContainerNode
|
||||
|
||||
|
||||
## A QualityGroup represents a group of quality containers that must be applied
|
||||
# to each ContainerStack when it's used.
|
||||
#
|
||||
# A concrete example: When there are two extruders and the user selects the
|
||||
# quality type "normal", this quality type must be applied to all stacks in a
|
||||
# machine, although each stack can have different containers. So one global
|
||||
# profile gets put on the global stack and one extruder profile gets put on
|
||||
# each extruder stack. This quality group then contains the following
|
||||
# profiles (for instance):
|
||||
# GlobalStack ExtruderStack 1 ExtruderStack 2
|
||||
# quality container: um3_global_normal um3_aa04_pla_normal um3_aa04_abs_normal
|
||||
#
|
||||
# The purpose of these quality groups is to group the containers that can be
|
||||
# applied to a configuration, so that when a quality level is selected, the
|
||||
# container can directly be applied to each stack instead of looking them up
|
||||
# again.
|
||||
class QualityGroup:
|
||||
## Constructs a new group.
|
||||
# \param name The user-visible name for the group.
|
||||
# \param quality_type The quality level that each profile in this group
|
||||
# has.
|
||||
"""A QualityGroup represents a group of quality containers that must be applied to each ContainerStack when it's
|
||||
used.
|
||||
|
||||
A concrete example: When there are two extruders and the user selects the quality type "normal", this quality
|
||||
type must be applied to all stacks in a machine, although each stack can have different containers. So one global
|
||||
profile gets put on the global stack and one extruder profile gets put on each extruder stack. This quality group
|
||||
then contains the following profiles (for instance):
|
||||
- GlobalStack
|
||||
- ExtruderStack 1
|
||||
- ExtruderStack 2
|
||||
quality container:
|
||||
- um3_global_normal
|
||||
- um3_aa04_pla_normal
|
||||
- um3_aa04_abs_normal
|
||||
|
||||
The purpose of these quality groups is to group the containers that can be applied to a configuration,
|
||||
so that when a quality level is selected, the container can directly be applied to each stack instead of looking
|
||||
them up again.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, quality_type: str) -> None:
|
||||
"""Constructs a new group.
|
||||
|
||||
:param name: The user-visible name for the group.
|
||||
:param quality_type: The quality level that each profile in this group has.
|
||||
"""
|
||||
|
||||
self.name = name
|
||||
self.node_for_global = None # type: Optional[ContainerNode]
|
||||
self.nodes_for_extruders = {} # type: Dict[int, ContainerNode]
|
||||
|
|
|
@ -13,12 +13,14 @@ if TYPE_CHECKING:
|
|||
from cura.Machines.MachineNode import MachineNode
|
||||
|
||||
|
||||
## Represents a quality profile in the container tree.
|
||||
#
|
||||
# This may either be a normal quality profile or a global quality profile.
|
||||
#
|
||||
# Its subcontainers are intent profiles.
|
||||
class QualityNode(ContainerNode):
|
||||
"""Represents a quality profile in the container tree.
|
||||
|
||||
This may either be a normal quality profile or a global quality profile.
|
||||
|
||||
Its subcontainers are intent profiles.
|
||||
"""
|
||||
|
||||
def __init__(self, container_id: str, parent: Union["MaterialNode", "MachineNode"]) -> None:
|
||||
super().__init__(container_id)
|
||||
self.parent = parent
|
||||
|
|
|
@ -17,16 +17,16 @@ if TYPE_CHECKING:
|
|||
from cura.Machines.MachineNode import MachineNode
|
||||
|
||||
|
||||
## This class represents an extruder variant in the container tree.
|
||||
#
|
||||
# The subnodes of these nodes are materials.
|
||||
#
|
||||
# This node contains materials with ALL filament diameters underneath it. The
|
||||
# tree of this variant is not specific to one global stack, so because the
|
||||
# list of materials can be different per stack depending on the compatible
|
||||
# material diameter setting, we cannot filter them here. Filtering must be
|
||||
# done in the model.
|
||||
class VariantNode(ContainerNode):
|
||||
"""This class represents an extruder variant in the container tree.
|
||||
|
||||
The subnodes of these nodes are materials.
|
||||
|
||||
This node contains materials with ALL filament diameters underneath it. The tree of this variant is not specific
|
||||
to one global stack, so because the list of materials can be different per stack depending on the compatible
|
||||
material diameter setting, we cannot filter them here. Filtering must be done in the model.
|
||||
"""
|
||||
|
||||
def __init__(self, container_id: str, machine: "MachineNode") -> None:
|
||||
super().__init__(container_id)
|
||||
self.machine = machine
|
||||
|
@ -39,9 +39,10 @@ class VariantNode(ContainerNode):
|
|||
container_registry.containerRemoved.connect(self._materialRemoved)
|
||||
self._loadAll()
|
||||
|
||||
## (Re)loads all materials under this variant.
|
||||
@UM.FlameProfiler.profile
|
||||
def _loadAll(self) -> None:
|
||||
"""(Re)loads all materials under this variant."""
|
||||
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
|
||||
if not self.machine.has_materials:
|
||||
|
@ -69,18 +70,18 @@ class VariantNode(ContainerNode):
|
|||
if not self.materials:
|
||||
self.materials["empty_material"] = MaterialNode("empty_material", variant = self)
|
||||
|
||||
## Finds the preferred material for this printer with this nozzle in one of
|
||||
# the extruders.
|
||||
#
|
||||
# If the preferred material is not available, an arbitrary material is
|
||||
# returned. If there is a configuration mistake (like a typo in the
|
||||
# preferred material) this returns a random available material. If there
|
||||
# are no available materials, this will return the empty material node.
|
||||
# \param approximate_diameter The desired approximate diameter of the
|
||||
# material.
|
||||
# \return The node for the preferred material, or any arbitrary material
|
||||
# if there is no match.
|
||||
def preferredMaterial(self, approximate_diameter: int) -> MaterialNode:
|
||||
"""Finds the preferred material for this printer with this nozzle in one of the extruders.
|
||||
|
||||
If the preferred material is not available, an arbitrary material is returned. If there is a configuration
|
||||
mistake (like a typo in the preferred material) this returns a random available material. If there are no
|
||||
available materials, this will return the empty material node.
|
||||
|
||||
:param approximate_diameter: The desired approximate diameter of the material.
|
||||
|
||||
:return: The node for the preferred material, or any arbitrary material if there is no match.
|
||||
"""
|
||||
|
||||
for base_material, material_node in self.materials.items():
|
||||
if self.machine.preferred_material == base_material and approximate_diameter == int(material_node.getMetaDataEntry("approximate_diameter")):
|
||||
return material_node
|
||||
|
@ -107,10 +108,10 @@ class VariantNode(ContainerNode):
|
|||
))
|
||||
return fallback
|
||||
|
||||
## When a material gets added to the set of profiles, we need to update our
|
||||
# tree here.
|
||||
@UM.FlameProfiler.profile
|
||||
def _materialAdded(self, container: ContainerInterface) -> None:
|
||||
"""When a material gets added to the set of profiles, we need to update our tree here."""
|
||||
|
||||
if container.getMetaDataEntry("type") != "material":
|
||||
return # Not interested.
|
||||
if not ContainerRegistry.getInstance().findContainersMetadata(id = container.getId()):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue