Merge remote-tracking branch 'origin/feature_intent_container_tree' into feature_intent_upgrade

This commit is contained in:
Lipu Fei 2019-09-19 14:14:29 +02:00
commit 823f7e5921
14 changed files with 159 additions and 206 deletions

View File

@ -131,8 +131,8 @@ class MaterialManager(QObject):
# Fetch the available materials (ContainerNode) for the current active machine and extruder setup.
materials = self.getAvailableMaterials(machine.definition.getId(), nozzle_name)
compatible_material_diameter = str(round(extruder_stack.getCompatibleMaterialDiameter()))
result = {key: material for key, material in materials.items() if material.container and material.container.getMetaDataEntry("approximate_diameter") == compatible_material_diameter}
compatible_material_diameter = extruder_stack.getApproximateMaterialDiameter()
result = {key: material for key, material in materials.items() if material.container and float(material.container.getMetaDataEntry("approximate_diameter")) == compatible_material_diameter}
return result
#
@ -171,9 +171,11 @@ class MaterialManager(QObject):
def getMaterialNodeByType(self, global_stack: "GlobalStack", position: str, nozzle_name: str,
buildplate_name: Optional[str], material_guid: str) -> Optional["MaterialNode"]:
machine_definition = global_stack.definition
variant_name = global_stack.extruders[position].variant.getName()
extruder = global_stack.extruderList[int(position)]
variant_name = extruder.variant.getName()
approximate_diameter = extruder.getApproximateMaterialDiameter()
return self.getMaterialNode(machine_definition.getId(), variant_name, buildplate_name, 3, material_guid)
return self.getMaterialNode(machine_definition.getId(), variant_name, buildplate_name, approximate_diameter, material_guid)
# There are 2 ways to get fallback materials;
# - A fallback by type (@sa getFallbackMaterialIdByMaterialType), which adds the generic version of this material
@ -266,128 +268,59 @@ class MaterialManager(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:
if material_node.container is None:
return
root_material_id = material_node.container.getMetaDataEntry("base_file")
if root_material_id is None:
return
if CuraContainerRegistry.getInstance().isReadOnly(root_material_id):
Logger.log("w", "Cannot set name of read-only container %s.", root_material_id)
return
containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = root_material_id)
containers[0].setName(name)
return cura.CuraApplication.CuraApplication.getMaterialManagementModel().setMaterialName(material_node, 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:
if material_node.container is None:
return
root_material_id = material_node.container.getMetaDataEntry("base_file")
if root_material_id is not None:
self.removeMaterialByRootId(root_material_id)
return cura.CuraApplication.CuraApplication.getMaterialManagementModel().setMaterialName(material_node)
def duplicateMaterialByRootId(self, root_material_id: str, new_base_id: Optional[str] = None,
new_metadata: Optional[Dict[str, Any]] = None) -> Optional[str]:
container_registry = CuraContainerRegistry.getInstance()
results = container_registry.findContainers(id=root_material_id)
if not results:
Logger.log("i", "Unable to duplicate the material with id %s, because it doesn't exist.", root_material_id)
return None
base_container = results[0]
# Ensure all settings are saved.
cura.CuraApplication.CuraApplication.getInstance().saveSettings()
# Create a new ID & container to hold the data.
new_containers = []
if new_base_id is None:
new_base_id = container_registry.uniqueName(base_container.getId())
new_base_container = copy.deepcopy(base_container)
new_base_container.getMetaData()["id"] = new_base_id
new_base_container.getMetaData()["base_file"] = new_base_id
if new_metadata is not None:
for key, value in new_metadata.items():
new_base_container.getMetaData()[key] = value
new_containers.append(new_base_container)
# Clone all of them.
for container_to_copy in container_registry.findContainers(base_file=root_material_id):
if container_to_copy.getId() == root_material_id:
continue # We already have that one, skip it
new_id = new_base_id
if container_to_copy.getMetaDataEntry("definition") != "fdmprinter":
new_id += "_" + container_to_copy.getMetaDataEntry("definition")
if container_to_copy.getMetaDataEntry("variant_name"):
nozzle_name = container_to_copy.getMetaDataEntry("variant_name")
new_id += "_" + nozzle_name.replace(" ", "_")
new_container = copy.deepcopy(container_to_copy)
new_container.getMetaData()["id"] = new_id
new_container.getMetaData()["base_file"] = new_base_id
if new_metadata is not None:
for key, value in new_metadata.items():
new_container.getMetaData()[key] = value
new_containers.append(new_container)
for container_to_add in new_containers:
container_to_add.setDirty(True)
container_registry.addContainer(container_to_add)
# if the duplicated material was favorite then the new material should also be added to favorite.
if root_material_id in self.getFavorites():
cura.CuraApplication.CuraApplication.getInstance().getMaterialManagementModel().addFavorite(new_base_id)
return new_base_id
#
# Creates a duplicate of a material, which has the same GUID and base_file metadata.
# Returns the root material ID of the duplicated material if successful.
#
@pyqtSlot("QVariant", result = str)
def duplicateMaterial(self, material_node: MaterialNode, new_base_id: Optional[str] = None,
new_metadata: Optional[Dict[str, Any]] = None) -> str:
if material_node.container is None:
Logger.log("e", "Material node {0} doesn't have container.".format(material_node.container_id))
def duplicateMaterialByRootId(self, root_material_id: str, new_base_id: Optional[str] = None, new_metadata: Optional[Dict[str, Any]] = None) -> Optional[str]:
result = cura.CuraApplication.CuraApplication.getInstance().getMaterialManagementModel().duplicateMaterialByBaseFile(root_material_id, new_base_id, new_metadata)
if result is None:
return "ERROR"
root_material_id = cast(str, material_node.container.getMetaDataEntry("base_file", ""))
new_material_id = self.duplicateMaterialByRootId(root_material_id, new_base_id, new_metadata)
return new_material_id if new_material_id is not None else "ERROR"
return result
# Create a new material by cloning Generic PLA for the current material diameter and generate a new GUID.
# Returns the ID of the newly created material.
## 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) -> str:
result = cura.CuraApplication.CuraApplication.getInstance().getMaterialManagementModel().duplicateMaterial(material_node, new_base_id, new_metadata)
if result is None:
return "ERROR"
return result
## 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:
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
# Ensure all settings are saved.
application = cura.CuraApplication.CuraApplication.getInstance()
application.saveSettings()
machine_manager = application.getMachineManager()
extruder_stack = machine_manager.activeStack
global_stack = application.getGlobalContainerStack()
if global_stack is None:
Logger.log("e", "Global stack not present!")
return "ERROR"
machine_definition = global_stack.definition
root_material_id = machine_definition.getMetaDataEntry("preferred_material", default = "generic_pla")
approximate_diameter = str(extruder_stack.approximateMaterialDiameter)
root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_diameter)
# Create a new ID & container to hold the data.
new_id = CuraContainerRegistry.getInstance().uniqueName("custom_material")
new_metadata = {"name": catalog.i18nc("@label", "Custom Material"),
"brand": catalog.i18nc("@label", "Custom"),
"GUID": str(uuid.uuid4()),
}
self.duplicateMaterialByRootId(root_material_id, new_base_id = new_id, new_metadata = new_metadata)
return new_id
return cura.CuraApplication.CuraApplication.getMaterialManagementModel().createMaterial()
@pyqtSlot(str)
def addFavorite(self, root_material_id: str) -> None:

View File

@ -127,8 +127,8 @@ class BaseMaterialsModel(ListModel):
return
nozzle_name = extruder_stack.variant.getName()
materials = ContainerTree.getInstance().machines[global_stack.definition.getId()].variants[nozzle_name].materials
compatible_material_diameter = str(round(extruder_stack.getCompatibleMaterialDiameter()))
self._available_materials = {key: material for key, material in materials.items() if material.container.getMetaDataEntry("approximate_diameter") == compatible_material_diameter}
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}
## 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

View File

@ -10,7 +10,7 @@ from cura.Settings.IntentManager import IntentManager
from UM.Qt.ListModel import ListModel
from UM.Settings.ContainerRegistry import ContainerRegistry #To update the list if anything changes.
from PyQt5.QtCore import pyqtProperty, pyqtSignal
import cura.CuraApplication
if TYPE_CHECKING:
from UM.Settings.ContainerRegistry import ContainerInterface
@ -48,7 +48,7 @@ class IntentCategoryModel(ListModel):
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChange)
ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChange)
IntentManager.getInstance().configurationChanged.connect(self.update)
cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeStackChanged.connect(self.update)
self.update()

View File

@ -66,12 +66,26 @@ class IntentModel(ListModel):
container_tree = ContainerTree.getInstance()
machine_node = container_tree.machines[global_stack.definition.getId()]
active_extruder = ExtruderManager.getInstance().getActiveExtruderStack()
# We can't just look at the active extruder, since it is possible for only one extruder to have an intent
# and the other extruders have no intent (eg, default). It is not possible for one extruder to have intent A and
# the other to have B.
# So we will use the first extruder that we find that has an intent that is not default as the "active" extruder
active_extruder = None
for extruder in global_stack.extruderList:
if extruder.intent.getMetaDataEntry("intent_category", "default") == "default":
if active_extruder is None:
active_extruder = extruder # If there is no extruder found and the intent is default, use that.
else: # We found an intent, use that extruder as "active"
active_extruder = extruder
if not active_extruder:
return
active_variant_name = active_extruder.variant.getMetaDataEntry("name")
active_variant_node = machine_node.variants[active_variant_name]
active_material_node = active_variant_node.materials[active_extruder.material.getMetaDataEntry("base_file")]
layer_heights_added = []
for quality_id, quality_node in active_material_node.qualities.items():
if quality_node.quality_type not in quality_groups: # Don't add the empty quality type (or anything else that would crash, defensively).

View File

@ -73,20 +73,19 @@ class MaterialManagementModel(QObject):
## 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 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.
@pyqtSlot("QVariant", result = str)
def duplicateMaterial(self, material_node: "MaterialNode", new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
def duplicateMaterialByBaseFile(self, base_file: str, new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
container_registry = CuraContainerRegistry.getInstance()
root_materials = container_registry.findContainers(id = material_node.base_file)
root_materials = container_registry.findContainers(id = base_file)
if not root_materials:
Logger.log("i", "Unable to duplicate the root material with ID {root_id}, because it doesn't exist.".format(root_id = material_node.base_file))
Logger.log("i", "Unable to duplicate the root material with ID {root_id}, because it doesn't exist.".format(root_id = base_file))
return None
root_material = root_materials[0]
@ -105,8 +104,8 @@ class MaterialManagementModel(QObject):
new_containers = [new_root_material]
# Clone all submaterials.
for container_to_copy in container_registry.findInstanceContainers(base_file = material_node.base_file):
if container_to_copy.getId() == material_node.base_file:
for container_to_copy in container_registry.findInstanceContainers(base_file = base_file):
if container_to_copy.getId() == base_file:
continue # We already have that one. Skip it.
new_id = new_base_id
definition = container_to_copy.getMetaDataEntry("definition")
@ -129,12 +128,25 @@ class MaterialManagementModel(QObject):
# If the duplicated material was favorite then the new material should also be added to the favorites.
favorites_set = set(application.getPreferences().getValue("cura/favorite_materials").split(";"))
if material_node.base_file in favorites_set:
if base_file in favorites_set:
favorites_set.add(new_base_id)
application.getPreferences().setValue("cura/favorite_materials", ";".join(favorites_set))
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: Dict[str, Any] = None) -> Optional[str]:
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.
#

View File

@ -78,9 +78,7 @@ class QualityProfilesDropDownMenuModel(ListModel):
quality_group_dict = ContainerTree.getInstance().getCurrentQualityGroups()
item_list = []
for key in quality_group_dict:
quality_group = quality_group_dict[key]
for quality_group in quality_group_dict.values():
layer_height = self._fetchLayerHeight(quality_group)
item = {"name": quality_group.name,

View File

@ -5,7 +5,6 @@ from typing import Any, Dict, List, Optional, TYPE_CHECKING
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
from UM.Logger import Logger
from UM.Util import parseBool
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Decorators import deprecated

View File

@ -4,6 +4,7 @@
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
from typing import Any, Dict, List, Optional, Set, Tuple, TYPE_CHECKING
import cura.CuraApplication
from UM.Logger import Logger
from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.cura_empty_instance_containers import empty_intent_container
from UM.Settings.InstanceContainer import InstanceContainer
@ -11,20 +12,12 @@ from UM.Settings.InstanceContainer import InstanceContainer
if TYPE_CHECKING:
from UM.Settings.InstanceContainer import InstanceContainer
## Front-end for querying which intents are available for a certain
# configuration.
#
# CURRENTLY THIS CLASS CONTAINS ONLY SOME PSEUDOCODE OF WHAT WE ARE SUPPOSED
# TO IMPLEMENT.
class IntentManager(QObject):
__instance = None
def __init__(self) -> None:
super().__init__()
cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeStackChanged.connect(self.configurationChanged)
self.configurationChanged.connect(self.selectDefaultIntent)
pass
## This class is a singleton.
@classmethod
def getInstance(cls):
@ -32,7 +25,6 @@ class IntentManager(QObject):
cls.__instance = IntentManager()
return cls.__instance
configurationChanged = pyqtSignal() #Triggered when something changed in the rest of the stack.
intentCategoryChanged = pyqtSignal() #Triggered when we switch categories.
## Gets the metadata dictionaries of all intent profiles for a given
@ -40,12 +32,16 @@ class IntentManager(QObject):
#
# \param definition_id ID of the printer.
# \param nozzle_name Name of the nozzle.
# \param material_id ID of the material.
# \param material_base_file The base_file of the material.
# \return A list of metadata dictionaries matching the search criteria, or
# an empty list if nothing was found.
def intentMetadatas(self, definition_id: str, nozzle_name: str, material_id: str) -> List[Dict[str, Any]]:
registry = cura.CuraApplication.CuraApplication.getInstance().getContainerRegistry()
return registry.findContainersMetadata(type = "intent", definition = definition_id, variant = nozzle_name, material = material_id)
def intentMetadatas(self, definition_id: str, nozzle_name: str, material_base_file: str) -> List[Dict[str, Any]]:
material_node = ContainerTree.getInstance().machines[definition_id].variants[nozzle_name].materials[material_base_file]
intent_metadatas = []
for quality_node in material_node.qualities.values():
for intent_node in quality_node.intents.values():
intent_metadatas.append(intent_node.getMetadata())
return intent_metadatas
## Collects and returns all intent categories available for the given
# parameters. Note that the 'default' category is always available.
@ -77,7 +73,6 @@ class IntentManager(QObject):
# even though there should always be defaults. The problem then is what to do with the quality_types.
# Currently _also_ inconsistent with 'currentAvailableIntentCategories', which _does_ return default.
quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
# TODO: These quality nodes in that tree already contain the intent nodes. We can optimise this.
available_quality_types = {quality_group.quality_type for quality_group in quality_groups.values() if quality_group.node_for_global is not None}
final_intent_ids = set() # type: Set[str]
@ -131,6 +126,7 @@ class IntentManager(QObject):
## Apply intent on the stacks.
@pyqtSlot(str, str)
def selectIntent(self, intent_category: str, quality_type: str) -> None:
Logger.log("i", "Attempting to set intent_category to [%s] and quality type to [%s]", intent_category, quality_type)
old_intent_category = self.currentIntentCategory
application = cura.CuraApplication.CuraApplication.getInstance()
global_stack = application.getGlobalContainerStack()
@ -147,13 +143,4 @@ class IntentManager(QObject):
extruder_stack.intent = self.getDefaultIntent()
application.getMachineManager().setQualityGroupByQualityType(quality_type)
if old_intent_category != intent_category:
self.intentCategoryChanged.emit()
## Selects the default intents on every extruder.
def selectDefaultIntent(self) -> None:
application = cura.CuraApplication.CuraApplication.getInstance()
global_stack = application.getGlobalContainerStack()
if global_stack is None:
return
for extruder_stack in global_stack.extruderList:
extruder_stack.intent = self.getDefaultIntent()
self.intentCategoryChanged.emit()

View File

@ -26,7 +26,6 @@ import cura.CuraApplication # Imported like this to prevent circular references
from cura.Machines.ContainerNode import ContainerNode
from cura.Machines.ContainerTree import ContainerTree
from cura.Machines.MaterialManager import MaterialManager
from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice, ConnectionType
from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel
@ -616,10 +615,15 @@ class MachineManager(QObject):
@pyqtProperty(str, notify=activeIntentChanged)
def activeIntentCategory(self):
global_container_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if not self._active_container_stack:
if not global_container_stack:
return ""
intent_category = self._active_container_stack.intent.getMetaDataEntry("intent_category")
intent_category = "default"
for extruder in global_container_stack.extruderList:
category = extruder.intent.getMetaDataEntry("intent_category", "default")
if category != "default" and category != intent_category:
intent_category = category
return intent_category
## Returns whether there is anything unsupported in the current set-up.
@ -1301,8 +1305,7 @@ class MachineManager(QObject):
self._setMaterial(position_item, new_material)
else:
# The current material is not available, find the preferred one.
material_diameter = self._global_container_stack.extruders[position].getCompatibleMaterialDiameter()
approximate_material_diameter = round(material_diameter)
approximate_material_diameter = int(self._global_container_stack.extruderList[int(position)].getApproximateMaterialDiameter())
material_node = nozzle_node.preferredMaterial(approximate_material_diameter)
self._setMaterial(position_item, material_node)
@ -1343,6 +1346,7 @@ class MachineManager(QObject):
if self._global_container_stack is None:
return
self.blurSettings.emit()
container_registry = CuraContainerRegistry.getInstance()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self.switchPrinterType(configuration.printerType)
@ -1379,20 +1383,18 @@ class MachineManager(QObject):
else:
machine_node = ContainerTree.getInstance().machines.get(self._global_container_stack.definition.getId())
variant_node = machine_node.variants.get(extruder_configuration.hotendID)
if variant_node:
self._setVariantNode(position, variant_node)
else:
self._global_container_stack.extruders[position].variant = empty_variant_container
self._setVariantNode(position, variant_node)
material_container_node = MaterialManager.getInstance().getMaterialNodeByType(self._global_container_stack,
position,
extruder_configuration.hotendID,
configuration.buildplateConfiguration,
extruder_configuration.material.guid)
if material_container_node:
self._setMaterial(position, material_container_node)
else:
self._global_container_stack.extruders[position].material = empty_material_container
# Find the material profile that the printer has stored.
# This might find one of the duplicates if the user duplicated the material to sync with. But that's okay; both have this GUID so both are correct.
approximate_diameter = int(self._global_container_stack.extruderList[int(position)].getApproximateMaterialDiameter())
materials_with_guid = container_registry.findInstanceContainersMetadata(guid = extruder_configuration.material.guid, approximate_diameter = approximate_diameter)
material_container_node = variant_node.preferredMaterial(approximate_diameter)
if materials_with_guid: # We also have the material profile that the printer wants to share.
base_file = materials_with_guid[0]["base_file"]
material_container_node = variant_node.materials.get(base_file, default = material_container_node) # If Cura thinks that the selected material is not available for this printer, revert to the preferred material.
self._setMaterial(position, material_container_node)
self._global_container_stack.extruders[position].setEnabled(True)
self.updateMaterialWithVariant(position)
@ -1431,17 +1433,12 @@ class MachineManager(QObject):
def setMaterialById(self, position: str, root_material_id: str) -> None:
if self._global_container_stack is None:
return
buildplate_name = None
if self._global_container_stack.variant.getId() != "empty_variant":
buildplate_name = self._global_container_stack.variant.getName()
machine_definition_id = self._global_container_stack.definition.id
position = str(position)
extruder_stack = self._global_container_stack.extruders[position]
nozzle_name = extruder_stack.variant.getName()
material_diameter = extruder_stack.getApproximateMaterialDiameter()
material_node = MaterialManager.getInstance().getMaterialNode(machine_definition_id, nozzle_name, buildplate_name,
material_diameter, root_material_id)
material_node = ContainerTree.getInstance().machines[machine_definition_id].variants[nozzle_name].materials[root_material_id]
self.setMaterial(position, material_node)
## Global_stack: if you want to provide your own global_stack instead of the current active one
@ -1624,7 +1621,3 @@ class MachineManager(QObject):
abbr_machine += stripped_word
return abbr_machine
# Gets all machines that belong to the given group_id.
def getMachinesInGroup(self, group_id: str) -> List["GlobalStack"]:
return self._container_registry.findContainerStacks(type = "machine", group_id = group_id)

View File

@ -11,7 +11,9 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Util import parseBool
import cura.CuraApplication # Imported like this to prevent circular dependencies.
from cura.MachineAction import MachineAction
from cura.Machines.ContainerTree import ContainerTree # To re-build the machine node when hasMaterials changes.
from cura.Settings.CuraStackBuilder import CuraStackBuilder
from cura.Settings.cura_empty_instance_containers import isEmptyContainer
@ -41,6 +43,9 @@ class MachineSettingsAction(MachineAction):
self._backend = self._application.getBackend()
self.onFinished.connect(self._onFinished)
# If the g-code flavour changes between UltiGCode and another flavour, we need to update the container tree.
self._application.globalContainerStackChanged.connect(self._updateHasMaterialsInContainerTree)
# Which container index in a stack to store machine setting changes.
@pyqtProperty(int, constant = True)
def storeContainerIndex(self) -> int:
@ -51,6 +56,16 @@ class MachineSettingsAction(MachineAction):
if isinstance(container, DefinitionContainer) and container.getMetaDataEntry("type") == "machine":
self._application.getMachineActionManager().addSupportedAction(container.getId(), self.getKey())
## Triggered when the global container stack changes or when the g-code
# flavour setting is changed.
def _updateHasMaterialsInContainerTree(self) -> None:
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()]
if machine_node.has_materials != parseBool(global_stack.getMetaDataEntry("has_materials")): # May have changed due to the g-code flavour.
machine_node.has_materials = parseBool(global_stack.getMetaDataEntry("has_materials"))
machine_node._loadAll()
def _reset(self):
global_stack = self._application.getMachineManager().activeMachine
if not global_stack:
@ -98,11 +113,8 @@ class MachineSettingsAction(MachineAction):
return
machine_manager = self._application.getMachineManager()
material_manager = self._application.getMaterialManager()
extruder_positions = list(global_stack.extruders.keys())
has_materials = global_stack.getProperty("machine_gcode_flavor", "value") != "UltiGCode"
material_node = None
if has_materials:
global_stack.setMetaDataEntry("has_materials", True)
else:
@ -111,11 +123,15 @@ class MachineSettingsAction(MachineAction):
if "has_materials" in global_stack.getMetaData():
global_stack.removeMetaDataEntry("has_materials")
self._updateHasMaterialsInContainerTree()
# set materials
for position in extruder_positions:
if has_materials:
material_node = material_manager.getDefaultMaterial(global_stack, position, None)
machine_manager.setMaterial(position, material_node)
machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()]
for position, extruder in enumerate(global_stack.extruderList):
#Find out what material we need to default to.
approximate_diameter = round(extruder.getProperty("material_diameter", "value"))
material_node = machine_node.variants[extruder.variant.getName()].preferredMaterial(approximate_diameter)
machine_manager.setMaterial(str(position), material_node)
self._application.globalContainerStackChanged.emit()

View File

@ -69,9 +69,9 @@ class SendMaterialJob(Job):
def _sendMaterials(self, materials_to_send: Set[str]) -> None:
container_registry = CuraApplication.getInstance().getContainerRegistry()
all_materials = container_registry.findInstanceContainersMetadata(type = "material")
all_root_materials = {material["base_file"] for material in all_materials if "base_file" in material} # Filters out uniques by making it a set. Don't include files without base file (i.e. empty material).
all_base_files = {material["base_file"] for material in all_materials if "base_file" in material} # Filters out uniques by making it a set. Don't include files without base file (i.e. empty material).
for root_material_id in all_root_materials:
for root_material_id in all_base_files:
if root_material_id not in materials_to_send:
# If the material does not have to be sent we skip it.
continue
@ -129,10 +129,10 @@ class SendMaterialJob(Job):
def _getLocalMaterials() -> Dict[str, LocalMaterial]:
result = {} # type: Dict[str, LocalMaterial]
all_materials = CuraApplication.getInstance().getContainerRegistry().findInstanceContainersMetadata(type = "material")
all_root_materials = [material for material in all_materials if material["id"] == material.get("base_file")] # Don't send materials without base_file: The empty material doesn't need to be sent.
all_base_files = [material for material in all_materials if material["id"] == material.get("base_file")] # Don't send materials without base_file: The empty material doesn't need to be sent.
# Find the latest version of all material containers in the registry.
for material_metadata in all_root_materials:
for material_metadata in all_base_files:
try:
# material version must be an int
material_metadata["version"] = int(material_metadata["version"])

View File

@ -22,7 +22,7 @@ Button
background: Rectangle
{
id: backgroundRectangle
border.width: 1
border.width: UM.Theme.getSize("default_lining").width
border.color: button.checked ? UM.Theme.getColor("setting_control_border_highlight") : "transparent"
color: button.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent"
radius: UM.Theme.getSize("action_button_radius").width

View File

@ -182,11 +182,12 @@ Popup
Rectangle
{
height: 1
height: UM.Theme.getSize("default_lining").height
anchors.left: parent.left
anchors.right: parent.right
color: borderColor
}
MenuButton
{
labelText: Cura.Actions.addProfile.text

View File

@ -14,7 +14,7 @@ Item
property color activeColor: UM.Theme.getColor("primary")
property color inactiveColor: UM.Theme.getColor("slider_groove")
property color defaultItemColor: UM.Theme.getColor("small_button_active")
property int checkboxSize: UM.Theme.getSize("radio_button").height * 0.75
property int checkboxSize: Math.round(UM.Theme.getSize("radio_button").height * 0.75)
property int inactiveMarkerSize: 2 * barSize
property int barSize: UM.Theme.getSize("slider_groove_radius").height
property var isCheckedFunction // Function that accepts the modelItem and returns if the item should be active.
@ -36,8 +36,8 @@ Item
{
left: buttonBar.left
right: buttonBar.right
leftMargin: (checkboxSize - inactiveMarkerSize) / 2
rightMargin: (checkboxSize - inactiveMarkerSize) / 2
leftMargin: Math.round((checkboxSize - inactiveMarkerSize) / 2)
rightMargin: Math.round((checkboxSize - inactiveMarkerSize) / 2)
verticalCenter: parent.verticalCenter
}
}
@ -72,7 +72,7 @@ Item
property Item previousItem: repeater.itemAt(index - 1)
height: barSize
width: buttonBar.width / (repeater.count - 1) - activeComponent.width - 2
width: Math.round(buttonBar.width / (repeater.count - 1) - activeComponent.width - 2)
color: defaultItemColor
anchors
@ -110,7 +110,7 @@ Item
anchors.horizontalCenter: parent.horizontalCenter
height: inactiveMarkerSize
width: inactiveMarkerSize
radius: width / 2
radius: Math.round(width / 2)
color: inactiveColor
}
}
@ -132,7 +132,7 @@ Item
{
height: checkboxSize
width: checkboxSize
radius: width / 2
radius: Math.round(width / 2)
border.color: defaultItemColor
@ -143,7 +143,7 @@ Item
margins: 3
fill: parent
}
radius: width / 2
radius: Math.round(width / 2)
color: activeColor
visible: checkbox.checked
}