mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-09-27 04:23:14 +08:00
Merge pull request #6542 from Ultimaker/CURA-6793_performance
Performance improvements
This commit is contained in:
commit
b9b086a8c0
@ -126,10 +126,6 @@ class BuildVolume(SceneNode):
|
||||
# Therefore this works.
|
||||
self._machine_manager.activeQualityChanged.connect(self._onStackChanged)
|
||||
|
||||
# This should also ways work, and it is semantically more correct,
|
||||
# but it does not update the disallowed areas after material change
|
||||
self._machine_manager.activeStackChanged.connect(self._onStackChanged)
|
||||
|
||||
# Enable and disable extruder
|
||||
self._machine_manager.extruderChanged.connect(self.updateNodeBoundaryCheck)
|
||||
|
||||
|
@ -1,21 +1,22 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Job import Job # For our background task of loading MachineNodes lazily.
|
||||
from UM.JobQueue import JobQueue # For our background task of loading MachineNodes lazily.
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry # To listen to containers being added.
|
||||
from UM.Settings.Interfaces import ContainerInterface
|
||||
from UM.Signal import Signal
|
||||
import cura.CuraApplication # Imported like this to prevent circular dependencies.
|
||||
from cura.Machines.MachineNode import MachineNode
|
||||
from cura.Settings.GlobalStack import GlobalStack # To listen only to global stacks being added.
|
||||
|
||||
from typing import Dict, List, TYPE_CHECKING
|
||||
from typing import Dict, List, Optional, TYPE_CHECKING
|
||||
import time
|
||||
import UM.FlameProfiler
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cura.Machines.QualityGroup import QualityGroup
|
||||
from cura.Machines.QualityChangesGroup import QualityChangesGroup
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
|
||||
|
||||
## This class contains a look-up tree for which containers are available at
|
||||
@ -38,12 +39,9 @@ class ContainerTree:
|
||||
return cls.__instance
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.machines = {} # type: Dict[str, MachineNode] # Mapping from definition ID to machine nodes.
|
||||
self.machines = self._MachineNodeMap() # Mapping from definition ID to machine nodes with lazy loading.
|
||||
self.materialsChanged = Signal() # Emitted when any of the material nodes in the tree got changed.
|
||||
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
container_registry.containerAdded.connect(self._machineAdded)
|
||||
self._loadAll()
|
||||
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.
|
||||
#
|
||||
@ -76,54 +74,85 @@ 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)
|
||||
|
||||
# Add a machine node by the id of it's definition.
|
||||
# This is automatically called by the _machineAdded function, but it's sometimes needed to add a machine node
|
||||
# faster than would have been done when waiting on any signals (for instance; when creating an entirely new machine)
|
||||
@UM.FlameProfiler.profile
|
||||
def addMachineNodeByDefinitionId(self, definition_id: str) -> None:
|
||||
if definition_id in self.machines:
|
||||
return # Already have this definition ID.
|
||||
## Ran after completely starting up the application.
|
||||
def _onStartupFinished(self):
|
||||
currently_added = ContainerRegistry.getInstance().findContainerStacks() # Find all currently added global stacks.
|
||||
JobQueue.getInstance().add(self._MachineNodeLoadJob(self, currently_added))
|
||||
|
||||
start_time = time.time()
|
||||
self.machines[definition_id] = MachineNode(definition_id)
|
||||
self.machines[definition_id].materialsChanged.connect(self.materialsChanged)
|
||||
Logger.log("d", "Adding container tree for {definition_id} took {duration} seconds.".format(definition_id = definition_id, duration = time.time() - start_time))
|
||||
## Dictionary-like object that contains the machines.
|
||||
#
|
||||
# This handles the lazy loading of MachineNodes.
|
||||
class _MachineNodeMap:
|
||||
def __init__(self) -> None:
|
||||
self._machines = {} # type: Dict[str, MachineNode]
|
||||
|
||||
## Builds the initial container tree.
|
||||
def _loadAll(self):
|
||||
Logger.log("i", "Building container tree.")
|
||||
start_time = time.time()
|
||||
all_stacks = ContainerRegistry.getInstance().findContainerStacks()
|
||||
for stack in all_stacks:
|
||||
if not isinstance(stack, GlobalStack):
|
||||
continue # Only want to load global stacks. We don't need to create a tree for extruder definitions.
|
||||
definition_id = stack.definition.getId()
|
||||
if definition_id not in self.machines:
|
||||
definition_start_time = time.time()
|
||||
self.machines[definition_id] = MachineNode(definition_id)
|
||||
self.machines[definition_id].materialsChanged.connect(self.materialsChanged)
|
||||
Logger.log("d", "Adding container tree for {definition_id} took {duration} seconds.".format(definition_id = definition_id, duration = time.time() - definition_start_time))
|
||||
## 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:
|
||||
return len(ContainerRegistry.getInstance().findContainersMetadata(id = definition_id)) > 0
|
||||
|
||||
Logger.log("d", "Building the container tree took %s seconds", time.time() - start_time)
|
||||
## 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:
|
||||
if definition_id not in self._machines:
|
||||
start_time = time.time()
|
||||
self._machines[definition_id] = MachineNode(definition_id)
|
||||
self._machines[definition_id].materialsChanged.connect(ContainerTree.getInstance().materialsChanged)
|
||||
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]
|
||||
|
||||
## When a printer gets added, we need to build up the tree for that container.
|
||||
def _machineAdded(self, container_stack: ContainerInterface) -> None:
|
||||
if not isinstance(container_stack, GlobalStack):
|
||||
return # Not our concern.
|
||||
self.addMachineNodeByDefinitionId(container_stack.definition.getId())
|
||||
## 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]:
|
||||
if definition_id not in self:
|
||||
return default
|
||||
return self[definition_id]
|
||||
|
||||
## For debugging purposes, visualise the entire container tree as it stands
|
||||
# now.
|
||||
def _visualise_tree(self) -> str:
|
||||
lines = ["% CONTAINER TREE"] # Start with array and then combine into string, for performance.
|
||||
for machine in self.machines.values():
|
||||
lines.append(" # " + machine.container_id)
|
||||
for variant in machine.variants.values():
|
||||
lines.append(" * " + variant.container_id)
|
||||
for material in variant.materials.values():
|
||||
lines.append(" + " + material.container_id)
|
||||
for quality in material.qualities.values():
|
||||
lines.append(" - " + quality.container_id)
|
||||
for intent in quality.intents.values():
|
||||
lines.append(" . " + intent.container_id)
|
||||
return "\n".join(lines)
|
||||
## 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:
|
||||
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.
|
||||
def __init__(self, tree_root: "ContainerTree", container_stacks: List["ContainerStack"]):
|
||||
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:
|
||||
for stack in self.container_stacks: # Load all currently-added containers.
|
||||
if not isinstance(stack, GlobalStack):
|
||||
continue
|
||||
# Allow a thread switch after every container.
|
||||
# Experimentally, sleep(0) didn't allow switching. sleep(0.1) or sleep(0.2) neither.
|
||||
# We're in no hurry though. Half a second is fine.
|
||||
time.sleep(0.5)
|
||||
definition_id = stack.definition.getId()
|
||||
if not self.tree_root.machines.is_loaded(definition_id):
|
||||
_ = self.tree_root.machines[definition_id]
|
||||
|
@ -177,5 +177,7 @@ class MachineNode(ContainerNode):
|
||||
global_qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.quality_definition, global_quality = "True") # First try specific to this printer.
|
||||
if len(global_qualities) == 0: # This printer doesn't override the global qualities.
|
||||
global_qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = "fdmprinter", global_quality = "True") # Otherwise pick the global global qualities.
|
||||
if len(global_qualities) == 0: # There are no global qualities either?! Something went very wrong, but we'll not crash and properly fill the tree.
|
||||
global_qualities = [cura.CuraApplication.CuraApplication.getInstance().empty_quality_container.getMetaData()]
|
||||
for global_quality in global_qualities:
|
||||
self.global_qualities[global_quality["quality_type"]] = QualityNode(global_quality["id"], parent = self)
|
||||
|
@ -53,7 +53,7 @@ class BaseMaterialsModel(ListModel):
|
||||
self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack)
|
||||
self._updateExtruderStack()
|
||||
|
||||
# Update this model when switching machines, when adding materials or changing their metadata.
|
||||
# Update this model when switching machines or tabs, when adding materials or changing their metadata.
|
||||
self._machine_manager.activeStackChanged.connect(self._onChanged)
|
||||
ContainerTree.getInstance().materialsChanged.connect(self._materialsListChanged)
|
||||
self._application.getMaterialManagementModel().favoritesChanged.connect(self._onChanged)
|
||||
@ -140,8 +140,8 @@ class BaseMaterialsModel(ListModel):
|
||||
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.
|
||||
## This is an abstract method that needs to be implemented by the specific
|
||||
# models themselves.
|
||||
def _update(self):
|
||||
self._favorite_ids = set(cura.CuraApplication.CuraApplication.getInstance().getPreferences().getValue("cura/favorite_materials").split(";"))
|
||||
|
||||
@ -155,7 +155,7 @@ class BaseMaterialsModel(ListModel):
|
||||
nozzle_name = extruder_stack.variant.getName()
|
||||
materials = ContainerTree.getInstance().machines[global_stack.definition.getId()].variants[nozzle_name].materials
|
||||
approximate_material_diameter = extruder_stack.getApproximateMaterialDiameter()
|
||||
self._available_materials = {key: material for key, material in materials.items() if float(material.container.getMetaDataEntry("approximate_diameter")) == approximate_material_diameter}
|
||||
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
|
||||
|
@ -27,7 +27,7 @@ class FavoriteMaterialsModel(BaseMaterialsModel):
|
||||
|
||||
for root_material_id, container_node in self._available_materials.items():
|
||||
# Do not include the materials from a to-be-removed package
|
||||
if bool(container_node.container.getMetaDataEntry("removed", False)):
|
||||
if bool(container_node.getMetaDataEntry("removed", False)):
|
||||
continue
|
||||
|
||||
# Only add results for favorite materials
|
||||
|
@ -18,11 +18,11 @@ class GenericMaterialsModel(BaseMaterialsModel):
|
||||
|
||||
for root_material_id, container_node in self._available_materials.items():
|
||||
# Do not include the materials from a to-be-removed package
|
||||
if bool(container_node.container.getMetaDataEntry("removed", False)):
|
||||
if bool(container_node.getMetaDataEntry("removed", False)):
|
||||
continue
|
||||
|
||||
# Only add results for generic materials
|
||||
if container_node.container.getMetaDataEntry("brand", "unknown").lower() != "generic":
|
||||
if container_node.getMetaDataEntry("brand", "unknown").lower() != "generic":
|
||||
continue
|
||||
|
||||
item = self._createMaterialItem(root_material_id, container_node)
|
||||
|
@ -61,11 +61,9 @@ class IntentCategoryModel(ListModel):
|
||||
|
||||
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChange)
|
||||
ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChange)
|
||||
|
||||
machine_manager = application.getMachineManager()
|
||||
machine_manager.globalContainerChanged.connect(self.update)
|
||||
machine_manager.activeQualityGroupChanged.connect(self.update)
|
||||
machine_manager.activeStackChanged.connect(self.update)
|
||||
machine_manager = cura.CuraApplication.CuraApplication.getInstance().getMachineManager()
|
||||
machine_manager.activeMaterialChanged.connect(self.update)
|
||||
machine_manager.activeVariantChanged.connect(self.update)
|
||||
machine_manager.extruderChanged.connect(self.update)
|
||||
|
||||
extruder_manager = application.getExtruderManager()
|
||||
|
@ -37,18 +37,18 @@ 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():
|
||||
# Do not include the materials from a to-be-removed package
|
||||
if bool(container_node.container.getMetaDataEntry("removed", False)):
|
||||
if bool(container_node.getMetaDataEntry("removed", False)):
|
||||
continue
|
||||
|
||||
# Add brands we haven't seen yet to the dict, skipping generics
|
||||
brand = container_node.container.getMetaDataEntry("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 = container_node.container.getMetaDataEntry("material", "")
|
||||
material_type = container_node.getMetaDataEntry("material", "")
|
||||
if material_type not in brand_group_dict[brand]:
|
||||
brand_group_dict[brand][material_type] = []
|
||||
|
||||
|
@ -3,9 +3,9 @@
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.Qt.ListModel import ListModel
|
||||
import cura.CuraApplication # Imported like this to prevent circular dependencies.
|
||||
from cura.Machines.ContainerTree import ContainerTree
|
||||
|
||||
|
||||
@ -21,16 +21,13 @@ class NozzleModel(ListModel):
|
||||
self.addRoleName(self.HotendNameRole, "hotend_name")
|
||||
self.addRoleName(self.ContainerNodeRole, "container_node")
|
||||
|
||||
self._application = Application.getInstance()
|
||||
self._machine_manager = self._application.getMachineManager()
|
||||
|
||||
self._machine_manager.globalContainerChanged.connect(self._update)
|
||||
cura.CuraApplication.CuraApplication.getInstance().getMachineManager().globalContainerChanged.connect(self._update)
|
||||
self._update()
|
||||
|
||||
def _update(self):
|
||||
Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
|
||||
|
||||
global_stack = self._machine_manager.activeMachine
|
||||
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
|
||||
if global_stack is None:
|
||||
self.setItems([])
|
||||
return
|
||||
|
@ -40,7 +40,8 @@ class QualityProfilesDropDownMenuModel(ListModel):
|
||||
|
||||
application.globalContainerStackChanged.connect(self._onChange)
|
||||
machine_manager.activeQualityGroupChanged.connect(self._onChange)
|
||||
machine_manager.activeStackChanged.connect(self._onChange)
|
||||
machine_manager.activeMaterialChanged.connect(self._onChange)
|
||||
machine_manager.activeVariantChanged.connect(self._onChange)
|
||||
machine_manager.extruderChanged.connect(self._onChange)
|
||||
|
||||
extruder_manager = application.getExtruderManager()
|
||||
|
@ -68,7 +68,7 @@ class QualityGroup:
|
||||
if not node.container:
|
||||
Logger.log("w", "Node {0} doesn't have a container.".format(node.container_id))
|
||||
return
|
||||
is_experimental = parseBool(node.container.getMetaDataEntry("is_experimental", False))
|
||||
is_experimental = parseBool(node.getMetaDataEntry("is_experimental", False))
|
||||
self.is_experimental |= is_experimental
|
||||
|
||||
def setExtruderNode(self, position: int, node: "ContainerNode") -> None:
|
||||
@ -78,5 +78,5 @@ class QualityGroup:
|
||||
if not node.container:
|
||||
Logger.log("w", "Node {0} doesn't have a container.".format(node.container_id))
|
||||
return
|
||||
is_experimental = parseBool(node.container.getMetaDataEntry("is_experimental", False))
|
||||
is_experimental = parseBool(node.getMetaDataEntry("is_experimental", False))
|
||||
self.is_experimental |= is_experimental
|
||||
|
@ -85,7 +85,7 @@ class ContainerManager(QObject):
|
||||
if container_node.container is None:
|
||||
Logger.log("w", "Container node {0} doesn't have a container.".format(container_node.container_id))
|
||||
return False
|
||||
root_material_id = container_node.container.getMetaDataEntry("base_file", "")
|
||||
root_material_id = container_node.getMetaDataEntry("base_file", "")
|
||||
container_registry = cura.CuraApplication.CuraApplication.getInstance().getContainerRegistry()
|
||||
if container_registry.isReadOnly(root_material_id):
|
||||
Logger.log("w", "Cannot set metadata of read-only container %s.", root_material_id)
|
||||
@ -350,8 +350,7 @@ class ContainerManager(QObject):
|
||||
@pyqtSlot("QVariant")
|
||||
def unlinkMaterial(self, material_node: "MaterialNode") -> None:
|
||||
# Get the material group
|
||||
if material_node.container is None:
|
||||
Logger.log("w", "Material node {0} doesn't have a container.".format(material_node.container_id))
|
||||
if material_node.container is None: # Failed to lazy-load this container.
|
||||
return
|
||||
root_material_query = cura.CuraApplication.CuraApplication.getInstance().getContainerRegistry().findInstanceContainers(id = material_node.getMetaDataEntry("base_file", ""))
|
||||
if not root_material_query:
|
||||
|
@ -37,10 +37,6 @@ class CuraStackBuilder:
|
||||
return None
|
||||
|
||||
machine_definition = definitions[0]
|
||||
# The container tree listens to the containerAdded signal to add the definition and build the tree,
|
||||
# but that signal is emitted with a delay which might not have passed yet.
|
||||
# Therefore we must make sure that it's manually added here.
|
||||
container_tree.addMachineNodeByDefinitionId(machine_definition.getId())
|
||||
machine_node = container_tree.machines[machine_definition.getId()]
|
||||
|
||||
generated_name = registry.createUniqueName("machine", "", name, machine_definition.getName())
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant # For communicating data and events to Qt.
|
||||
@ -42,8 +42,6 @@ class ExtruderManager(QObject):
|
||||
# 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)
|
||||
|
||||
## Signal to notify other components when the list of extruders for a machine definition changes.
|
||||
@ -311,38 +309,33 @@ class ExtruderManager(QObject):
|
||||
|
||||
self.resetSelectedObjectExtruders()
|
||||
|
||||
## Adds the extruders of the currently active machine.
|
||||
def _addCurrentMachineExtruders(self) -> None:
|
||||
global_stack = self._application.getGlobalContainerStack()
|
||||
## Adds the extruders to the selected machine.
|
||||
def addMachineExtruders(self, global_stack: GlobalStack) -> None:
|
||||
extruders_changed = False
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
global_stack_id = global_stack.getId()
|
||||
|
||||
if global_stack:
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
global_stack_id = global_stack.getId()
|
||||
# Gets the extruder trains that we just created as well as any that still existed.
|
||||
extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = global_stack_id)
|
||||
|
||||
# Gets the extruder trains that we just created as well as any that still existed.
|
||||
extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = global_stack_id)
|
||||
# Make sure the extruder trains for the new machine can be placed in the set of sets
|
||||
if global_stack_id not in self._extruder_trains:
|
||||
self._extruder_trains[global_stack_id] = {}
|
||||
extruders_changed = True
|
||||
|
||||
# Make sure the extruder trains for the new machine can be placed in the set of sets
|
||||
if global_stack_id not in self._extruder_trains:
|
||||
self._extruder_trains[global_stack_id] = {}
|
||||
extruders_changed = True
|
||||
# Register the extruder trains by position
|
||||
for extruder_train in extruder_trains:
|
||||
extruder_position = extruder_train.getMetaDataEntry("position")
|
||||
self._extruder_trains[global_stack_id][extruder_position] = extruder_train
|
||||
|
||||
# Register the extruder trains by position
|
||||
for extruder_train in extruder_trains:
|
||||
extruder_position = extruder_train.getMetaDataEntry("position")
|
||||
self._extruder_trains[global_stack_id][extruder_position] = extruder_train
|
||||
# regardless of what the next stack is, we have to set it again, because of signal routing. ???
|
||||
extruder_train.setParent(global_stack)
|
||||
extruder_train.setNextStack(global_stack)
|
||||
extruders_changed = True
|
||||
|
||||
# regardless of what the next stack is, we have to set it again, because of signal routing. ???
|
||||
extruder_train.setParent(global_stack)
|
||||
extruder_train.setNextStack(global_stack)
|
||||
extruders_changed = True
|
||||
|
||||
self.fixSingleExtrusionMachineExtruderDefinition(global_stack)
|
||||
if extruders_changed:
|
||||
self.extrudersChanged.emit(global_stack_id)
|
||||
self.setActiveExtruderIndex(0)
|
||||
self.activeExtruderChanged.emit()
|
||||
self.fixSingleExtrusionMachineExtruderDefinition(global_stack)
|
||||
if extruders_changed:
|
||||
self.extrudersChanged.emit(global_stack_id)
|
||||
|
||||
# After 3.4, all single-extrusion machines have their own extruder definition files instead of reusing
|
||||
# "fdmextruder". We need to check a machine here so its extruder definition is correct according to this.
|
||||
|
@ -139,8 +139,8 @@ class MachineManager(QObject):
|
||||
activeVariantChanged = pyqtSignal()
|
||||
activeQualityChanged = pyqtSignal()
|
||||
activeIntentChanged = pyqtSignal()
|
||||
activeStackChanged = pyqtSignal() # Emitted whenever the active stack is changed (ie: when changing between extruders, changing a profile, but not when changing a value)
|
||||
extruderChanged = pyqtSignal()
|
||||
activeStackChanged = pyqtSignal() # Emitted whenever the active extruder stack is changed (ie: when switching the active extruder tab or changing between printers)
|
||||
extruderChanged = pyqtSignal() # Emitted whenever an extruder is activated or deactivated or the default extruder changes.
|
||||
|
||||
activeStackValueChanged = pyqtSignal() # Emitted whenever a value inside the active stack is changed.
|
||||
activeStackValidationChanged = pyqtSignal() # Emitted whenever a validation inside active container is changed
|
||||
@ -217,6 +217,7 @@ class MachineManager(QObject):
|
||||
return 0
|
||||
return len(general_definition_containers[0].getAllKeys())
|
||||
|
||||
## Triggered when the global container stack is changed in CuraApplication.
|
||||
def _onGlobalContainerChanged(self) -> None:
|
||||
if self._global_container_stack:
|
||||
try:
|
||||
@ -268,11 +269,7 @@ class MachineManager(QObject):
|
||||
|
||||
def _onActiveExtruderStackChanged(self) -> None:
|
||||
self.blurSettings.emit() # Ensure no-one has focus.
|
||||
if self._active_container_stack is not None:
|
||||
self._active_container_stack.pyqtContainersChanged.disconnect(self.activeStackChanged) # Unplug from the old one.
|
||||
self._active_container_stack = ExtruderManager.getInstance().getActiveExtruderStack()
|
||||
if self._active_container_stack is not None:
|
||||
self._active_container_stack.pyqtContainersChanged.connect(self.activeStackChanged) # Plug into the new one.
|
||||
|
||||
def __emitChangedSignals(self) -> None:
|
||||
self.activeQualityChanged.emit()
|
||||
@ -296,7 +293,6 @@ class MachineManager(QObject):
|
||||
self.blurSettings.emit() # Ensure no-one has focus.
|
||||
|
||||
container_registry = CuraContainerRegistry.getInstance()
|
||||
|
||||
containers = container_registry.findContainerStacks(id = stack_id)
|
||||
if not containers:
|
||||
return
|
||||
@ -306,21 +302,25 @@ class MachineManager(QObject):
|
||||
# Make sure that the default machine actions for this machine have been added
|
||||
self._application.getMachineActionManager().addDefaultMachineActions(global_stack)
|
||||
|
||||
ExtruderManager.getInstance().fixSingleExtrusionMachineExtruderDefinition(global_stack)
|
||||
extruder_manager = ExtruderManager.getInstance()
|
||||
extruder_manager.fixSingleExtrusionMachineExtruderDefinition(global_stack)
|
||||
if not global_stack.isValid():
|
||||
# Mark global stack as invalid
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(global_stack.getId())
|
||||
return # We're done here
|
||||
|
||||
self._global_container_stack = global_stack
|
||||
extruder_manager.addMachineExtruders(global_stack)
|
||||
self._application.setGlobalContainerStack(global_stack)
|
||||
ExtruderManager.getInstance()._globalContainerStackChanged()
|
||||
self._onGlobalContainerChanged()
|
||||
|
||||
# Switch to the first enabled extruder
|
||||
self.updateDefaultExtruder()
|
||||
default_extruder_position = int(self.defaultExtruderPosition)
|
||||
ExtruderManager.getInstance().setActiveExtruderIndex(default_extruder_position)
|
||||
old_active_extruder_index = extruder_manager.activeExtruderIndex
|
||||
extruder_manager.setActiveExtruderIndex(default_extruder_position)
|
||||
if old_active_extruder_index == default_extruder_position:
|
||||
# This signal might not have been emitted yet (if it didn't change) but we still want the models to update that depend on it because we changed the contents of the containers too.
|
||||
extruder_manager.activeExtruderChanged.emit()
|
||||
|
||||
self.__emitChangedSignals()
|
||||
|
||||
|
@ -29,6 +29,7 @@ def createMockedStack(definition_id: str):
|
||||
def container_registry():
|
||||
result = MagicMock()
|
||||
result.findContainerStacks = MagicMock(return_value = [createMockedStack("machine_1"), createMockedStack("machine_2")])
|
||||
result.findContainersMetadata = lambda id: [{"id": id}] if id in {"machine_1", "machine_2"} else []
|
||||
return result
|
||||
|
||||
@pytest.fixture
|
||||
@ -37,41 +38,11 @@ def application():
|
||||
|
||||
|
||||
def test_containerTreeInit(container_registry):
|
||||
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)):
|
||||
container_tree = ContainerTree()
|
||||
|
||||
assert "machine_1" in container_tree.machines
|
||||
assert "machine_2" in container_tree.machines
|
||||
|
||||
|
||||
def test_newMachineAdded(container_registry):
|
||||
mocked_definition_container = MagicMock(spec=DefinitionContainer)
|
||||
mocked_definition_container.getId = MagicMock(return_value = "machine_3")
|
||||
|
||||
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value = container_registry)):
|
||||
container_tree = ContainerTree()
|
||||
# machine_3 shouldn't be there, as on init it wasn't in the registry
|
||||
assert "machine_3" not in container_tree.machines
|
||||
|
||||
# It should only react when a globalStack is added.
|
||||
container_tree._machineAdded(mocked_definition_container)
|
||||
assert "machine_3" not in container_tree.machines
|
||||
|
||||
container_tree._machineAdded(createMockedStack("machine_3"))
|
||||
assert "machine_3" in container_tree.machines
|
||||
|
||||
|
||||
def test_alreadyKnownMachineAdded(container_registry):
|
||||
mocked_definition_container = MagicMock(spec = DefinitionContainer)
|
||||
mocked_definition_container.getId = MagicMock(return_value = "machine_2")
|
||||
|
||||
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value = container_registry)):
|
||||
container_tree = ContainerTree()
|
||||
assert len(container_tree.machines) == 2
|
||||
|
||||
# The ID is already there, so no machine should be added.
|
||||
container_tree._machineAdded(mocked_definition_container)
|
||||
assert len(container_tree.machines) == 2
|
||||
assert "machine_1" in container_tree.machines
|
||||
assert "machine_2" in container_tree.machines
|
||||
|
||||
def test_getCurrentQualityGroupsNoGlobalStack(container_registry):
|
||||
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value = container_registry)):
|
||||
@ -84,7 +55,7 @@ def test_getCurrentQualityGroupsNoGlobalStack(container_registry):
|
||||
def test_getCurrentQualityGroups(container_registry, application):
|
||||
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value = container_registry)):
|
||||
container_tree = ContainerTree()
|
||||
container_tree.machines["current_global_stack"] = MagicMock() # Mock so that we can track whether the getQualityGroups function gets called with correct parameters.
|
||||
container_tree.machines._machines["current_global_stack"] = MagicMock() # Mock so that we can track whether the getQualityGroups function gets called with correct parameters.
|
||||
|
||||
with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value = application)):
|
||||
result = container_tree.getCurrentQualityGroups()
|
||||
@ -108,7 +79,7 @@ def test_getCurrentQualityChangesGroupsNoGlobalStack(container_registry):
|
||||
def test_getCurrentQualityChangesGroups(container_registry, application):
|
||||
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value = container_registry)):
|
||||
container_tree = ContainerTree()
|
||||
container_tree.machines["current_global_stack"] = MagicMock() # Mock so that we can track whether the getQualityGroups function gets called with correct parameters.
|
||||
container_tree.machines._machines["current_global_stack"] = MagicMock() # Mock so that we can track whether the getQualityGroups function gets called with correct parameters.
|
||||
|
||||
with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value = application)):
|
||||
result = container_tree.getCurrentQualityChangesGroups()
|
||||
|
Loading…
x
Reference in New Issue
Block a user