mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-25 07:33:57 -06:00
Merge branch 'master' of github.com:Ultimaker/Cura into network_rewrite
This commit is contained in:
commit
24bd32477a
127 changed files with 1078 additions and 824 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -46,6 +46,7 @@ plugins/cura-siemensnx-plugin
|
|||
plugins/CuraVariSlicePlugin
|
||||
plugins/CuraLiveScriptingPlugin
|
||||
plugins/CuraPrintProfileCreator
|
||||
plugins/OctoPrintPlugin
|
||||
|
||||
#Build stuff
|
||||
CMakeCache.txt
|
||||
|
|
|
@ -3,7 +3,7 @@ Cura
|
|||
|
||||
This is the new, shiny frontend for Cura. [daid/Cura](https://github.com/daid/Cura.git) is the old legacy Cura that everyone knows and loves/hates.
|
||||
|
||||
We re-worked the whole GUI code at Ultimaker, because the old code started to become a unmaintainable.
|
||||
We re-worked the whole GUI code at Ultimaker, because the old code started to become unmaintainable.
|
||||
|
||||
|
||||
Logging Issues
|
||||
|
|
|
@ -56,11 +56,6 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
if self._node is None:
|
||||
return None
|
||||
|
||||
if getattr(self._node, "_non_printing_mesh", False):
|
||||
# infill_mesh, cutting_mesh and anti_overhang_mesh do not need a convex hull
|
||||
# node._non_printing_mesh is set in SettingOverrideDecorator
|
||||
return None
|
||||
|
||||
hull = self._compute2DConvexHull()
|
||||
|
||||
if self._global_stack and self._node:
|
||||
|
|
|
@ -59,7 +59,7 @@ class CrashHandler:
|
|||
self.data = dict()
|
||||
self.data["time_stamp"] = time.time()
|
||||
|
||||
Logger.log("c", "An uncaught exception has occurred!")
|
||||
Logger.log("c", "An uncaught error has occurred!")
|
||||
for line in traceback.format_exception(exception_type, value, tb):
|
||||
for part in line.rstrip("\n").split("\n"):
|
||||
Logger.log("c", part)
|
||||
|
@ -90,7 +90,7 @@ class CrashHandler:
|
|||
|
||||
def _messageWidget(self):
|
||||
label = QLabel()
|
||||
label.setText(catalog.i18nc("@label crash message", """<p><b>A fatal exception has occurred. Please send us this Crash Report to fix the problem</p></b>
|
||||
label.setText(catalog.i18nc("@label crash message", """<p><b>A fatal error has occurred. Please send us this Crash Report to fix the problem</p></b>
|
||||
<p>Please use the "Send report" button to post a bug report automatically to our servers</p>
|
||||
"""))
|
||||
|
||||
|
@ -143,7 +143,7 @@ class CrashHandler:
|
|||
|
||||
def _exceptionInfoWidget(self):
|
||||
group = QGroupBox()
|
||||
group.setTitle(catalog.i18nc("@title:groupbox", "Exception traceback"))
|
||||
group.setTitle(catalog.i18nc("@title:groupbox", "Error traceback"))
|
||||
layout = QVBoxLayout()
|
||||
|
||||
text_area = QTextEdit()
|
||||
|
|
|
@ -172,13 +172,14 @@ class CuraApplication(QtApplication):
|
|||
Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances")
|
||||
Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
|
||||
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.DefinitionChangesContainer)
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality_changes")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer, "variant")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer, "material")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer, "user")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack, "extruder_train")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack, "machine")
|
||||
ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
|
||||
|
||||
## Initialise the version upgrade manager with Cura's storage paths.
|
||||
# Needs to be here to prevent circular dependencies.
|
||||
|
@ -266,17 +267,17 @@ class CuraApplication(QtApplication):
|
|||
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
|
||||
empty_variant_container = copy.deepcopy(empty_container)
|
||||
empty_variant_container._id = "empty_variant"
|
||||
empty_variant_container.setMetaDataEntry("id", "empty_variant")
|
||||
empty_variant_container.addMetaDataEntry("type", "variant")
|
||||
ContainerRegistry.getInstance().addContainer(empty_variant_container)
|
||||
|
||||
empty_material_container = copy.deepcopy(empty_container)
|
||||
empty_material_container._id = "empty_material"
|
||||
empty_material_container.setMetaDataEntry("id", "empty_material")
|
||||
empty_material_container.addMetaDataEntry("type", "material")
|
||||
ContainerRegistry.getInstance().addContainer(empty_material_container)
|
||||
|
||||
empty_quality_container = copy.deepcopy(empty_container)
|
||||
empty_quality_container._id = "empty_quality"
|
||||
empty_quality_container.setMetaDataEntry("id", "empty_quality")
|
||||
empty_quality_container.setName("Not Supported")
|
||||
empty_quality_container.addMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_container.addMetaDataEntry("type", "quality")
|
||||
|
@ -284,12 +285,12 @@ class CuraApplication(QtApplication):
|
|||
ContainerRegistry.getInstance().addContainer(empty_quality_container)
|
||||
|
||||
empty_quality_changes_container = copy.deepcopy(empty_container)
|
||||
empty_quality_changes_container._id = "empty_quality_changes"
|
||||
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
|
||||
empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
|
||||
|
||||
with ContainerRegistry.getInstance().lockFile():
|
||||
ContainerRegistry.getInstance().load()
|
||||
ContainerRegistry.getInstance().loadAllMetadata()
|
||||
|
||||
# set the setting version for Preferences
|
||||
preferences = Preferences.getInstance()
|
||||
|
@ -312,6 +313,7 @@ class CuraApplication(QtApplication):
|
|||
preferences.addPreference("cura/material_settings", "{}")
|
||||
|
||||
preferences.addPreference("view/invert_zoom", False)
|
||||
preferences.addPreference("cura/sidebar_collapse", False)
|
||||
|
||||
self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement")
|
||||
|
||||
|
@ -469,69 +471,10 @@ class CuraApplication(QtApplication):
|
|||
if not self._started: # Do not do saving during application start
|
||||
return
|
||||
|
||||
# Lock file for "more" atomically loading and saving to/from config dir.
|
||||
with ContainerRegistry.getInstance().lockFile():
|
||||
for instance in ContainerRegistry.getInstance().findInstanceContainers():
|
||||
if not instance.isDirty():
|
||||
continue
|
||||
|
||||
try:
|
||||
data = instance.serialize()
|
||||
except NotImplementedError:
|
||||
continue
|
||||
except Exception:
|
||||
Logger.logException("e", "An exception occurred when serializing container %s", instance.getId())
|
||||
continue
|
||||
|
||||
mime_type = ContainerRegistry.getMimeTypeForContainer(type(instance))
|
||||
file_name = urllib.parse.quote_plus(instance.getId()) + "." + mime_type.preferredSuffix
|
||||
instance_type = instance.getMetaDataEntry("type")
|
||||
path = None
|
||||
if instance_type == "material":
|
||||
path = Resources.getStoragePath(self.ResourceTypes.MaterialInstanceContainer, file_name)
|
||||
elif instance_type == "quality" or instance_type == "quality_changes":
|
||||
path = Resources.getStoragePath(self.ResourceTypes.QualityInstanceContainer, file_name)
|
||||
elif instance_type == "user":
|
||||
path = Resources.getStoragePath(self.ResourceTypes.UserInstanceContainer, file_name)
|
||||
elif instance_type == "variant":
|
||||
path = Resources.getStoragePath(self.ResourceTypes.VariantInstanceContainer, file_name)
|
||||
elif instance_type == "definition_changes":
|
||||
path = Resources.getStoragePath(self.ResourceTypes.DefinitionChangesContainer, file_name)
|
||||
|
||||
if path:
|
||||
instance.setPath(path)
|
||||
with SaveFile(path, "wt") as f:
|
||||
f.write(data)
|
||||
|
||||
for stack in ContainerRegistry.getInstance().findContainerStacks():
|
||||
self.saveStack(stack)
|
||||
ContainerRegistry.getInstance().saveDirtyContainers()
|
||||
|
||||
def saveStack(self, stack):
|
||||
if not stack.isDirty():
|
||||
return
|
||||
try:
|
||||
data = stack.serialize()
|
||||
except NotImplementedError:
|
||||
return
|
||||
except Exception:
|
||||
Logger.logException("e", "An exception occurred when serializing container %s", stack.getId())
|
||||
return
|
||||
|
||||
mime_type = ContainerRegistry.getMimeTypeForContainer(type(stack))
|
||||
file_name = urllib.parse.quote_plus(stack.getId()) + "." + mime_type.preferredSuffix
|
||||
|
||||
path = None
|
||||
if isinstance(stack, GlobalStack):
|
||||
path = Resources.getStoragePath(self.ResourceTypes.MachineStack, file_name)
|
||||
elif isinstance(stack, ExtruderStack):
|
||||
path = Resources.getStoragePath(self.ResourceTypes.ExtruderStack, file_name)
|
||||
else:
|
||||
path = Resources.getStoragePath(Resources.ContainerStacks, file_name)
|
||||
|
||||
stack.setPath(path)
|
||||
with SaveFile(path, "wt") as f:
|
||||
f.write(data)
|
||||
|
||||
ContainerRegistry.getInstance().saveContainer(stack)
|
||||
|
||||
@pyqtSlot(str, result = QUrl)
|
||||
def getDefaultPath(self, key):
|
||||
|
@ -733,7 +676,7 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self.exec_()
|
||||
|
||||
def getMachineManager(self, *args):
|
||||
def getMachineManager(self, *args) -> MachineManager:
|
||||
if self._machine_manager is None:
|
||||
self._machine_manager = MachineManager.createMachineManager()
|
||||
return self._machine_manager
|
||||
|
|
|
@ -238,7 +238,7 @@ class PrintInformation(QObject):
|
|||
pass
|
||||
|
||||
active_material_id = Application.getInstance().getMachineManager().activeMaterialId
|
||||
active_material_containers = ContainerRegistry.getInstance().findInstanceContainers(id=active_material_id)
|
||||
active_material_containers = ContainerRegistry.getInstance().findInstanceContainers(id = active_material_id)
|
||||
|
||||
if active_material_containers:
|
||||
self._active_material_container = active_material_containers[0]
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
# This collects a lot of quality and quality changes related code which was split between ContainerManager
|
||||
# and the MachineManager and really needs to usable from both.
|
||||
from typing import List, Optional, Dict, TYPE_CHECKING
|
||||
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
||||
|
@ -33,16 +32,16 @@ class QualityManager:
|
|||
# \param quality_name
|
||||
# \param machine_definition (Optional) \type{DefinitionContainerInterface} If nothing is
|
||||
# specified then the currently selected machine definition is used.
|
||||
# \param material_containers (Optional) \type{List[InstanceContainer]} If nothing is specified then
|
||||
# the current set of selected materials is used.
|
||||
# \param material_containers_metadata If nothing is specified then the
|
||||
# current set of selected materials is used.
|
||||
# \return the matching quality container \type{InstanceContainer}
|
||||
def findQualityByName(self, quality_name: str, machine_definition: Optional["DefinitionContainerInterface"] = None, material_containers: List[InstanceContainer] = None) -> Optional[InstanceContainer]:
|
||||
def findQualityByName(self, quality_name: str, machine_definition: Optional["DefinitionContainerInterface"] = None, material_containers_metadata: Optional[List[Dict[str, Any]]] = None) -> Optional[InstanceContainer]:
|
||||
criteria = {"type": "quality", "name": quality_name}
|
||||
result = self._getFilteredContainersForStack(machine_definition, material_containers, **criteria)
|
||||
result = self._getFilteredContainersForStack(machine_definition, material_containers_metadata, **criteria)
|
||||
|
||||
# Fall back to using generic materials and qualities if nothing could be found.
|
||||
if not result and material_containers and len(material_containers) == 1:
|
||||
basic_materials = self._getBasicMaterials(material_containers[0])
|
||||
if not result and material_containers_metadata and len(material_containers_metadata) == 1:
|
||||
basic_materials = self._getBasicMaterialMetadatas(material_containers_metadata[0])
|
||||
result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria)
|
||||
|
||||
return result[0] if result else None
|
||||
|
@ -108,18 +107,18 @@ class QualityManager:
|
|||
# \param quality_type \type{str} the name of the quality type to search for.
|
||||
# \param machine_definition (Optional) \type{InstanceContainer} If nothing is
|
||||
# specified then the currently selected machine definition is used.
|
||||
# \param material_containers (Optional) \type{List[InstanceContainer]} If nothing is specified then
|
||||
# the current set of selected materials is used.
|
||||
# \param material_containers_metadata If nothing is specified then the
|
||||
# current set of selected materials is used.
|
||||
# \return the matching quality container \type{InstanceContainer}
|
||||
def findQualityByQualityType(self, quality_type: str, machine_definition: Optional["DefinitionContainerInterface"] = None, material_containers: List[InstanceContainer] = None, **kwargs) -> InstanceContainer:
|
||||
def findQualityByQualityType(self, quality_type: str, machine_definition: Optional["DefinitionContainerInterface"] = None, material_containers_metadata: Optional[List[Dict[str, Any]]] = None, **kwargs) -> InstanceContainer:
|
||||
criteria = kwargs
|
||||
criteria["type"] = "quality"
|
||||
if quality_type:
|
||||
criteria["quality_type"] = quality_type
|
||||
result = self._getFilteredContainersForStack(machine_definition, material_containers, **criteria)
|
||||
result = self._getFilteredContainersForStack(machine_definition, material_containers_metadata, **criteria)
|
||||
# Fall back to using generic materials and qualities if nothing could be found.
|
||||
if not result and material_containers and len(material_containers) == 1:
|
||||
basic_materials = self._getBasicMaterials(material_containers[0])
|
||||
if not result and material_containers_metadata and len(material_containers_metadata) == 1:
|
||||
basic_materials = self._getBasicMaterialMetadatas(material_containers_metadata[0])
|
||||
if basic_materials:
|
||||
result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria)
|
||||
return result[0] if result else None
|
||||
|
@ -131,9 +130,9 @@ class QualityManager:
|
|||
# \return \type{List[InstanceContainer]} the list of suitable qualities.
|
||||
def findAllQualitiesForMachineMaterial(self, machine_definition: "DefinitionContainerInterface", material_container: InstanceContainer) -> List[InstanceContainer]:
|
||||
criteria = {"type": "quality"}
|
||||
result = self._getFilteredContainersForStack(machine_definition, [material_container], **criteria)
|
||||
result = self._getFilteredContainersForStack(machine_definition, [material_container.getMetaData()], **criteria)
|
||||
if not result:
|
||||
basic_materials = self._getBasicMaterials(material_container)
|
||||
basic_materials = self._getBasicMaterialMetadatas(material_container.getMetaData())
|
||||
if basic_materials:
|
||||
result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria)
|
||||
|
||||
|
@ -202,22 +201,34 @@ class QualityManager:
|
|||
## Fetch more basic versions of a material.
|
||||
#
|
||||
# This tries to find a generic or basic version of the given material.
|
||||
# \param material_container \type{InstanceContainer} the material
|
||||
# \return \type{List[InstanceContainer]} a list of the basic materials or an empty list if one could not be found.
|
||||
def _getBasicMaterials(self, material_container: InstanceContainer):
|
||||
base_material = material_container.getMetaDataEntry("material")
|
||||
material_container_definition = material_container.getDefinition()
|
||||
if material_container_definition and material_container_definition.getMetaDataEntry("has_machine_quality"):
|
||||
definition_id = material_container.getDefinition().getMetaDataEntry("quality_definition", material_container.getDefinition().getId())
|
||||
else:
|
||||
# \param material_container \type{Dict[str, Any]} The metadata of a
|
||||
# material to find the basic versions of.
|
||||
# \return \type{List[Dict[str, Any]]} A list of the metadata of basic
|
||||
# materials, or an empty list if none could be found.
|
||||
def _getBasicMaterialMetadatas(self, material_container: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
if "definition" not in material_container:
|
||||
definition_id = "fdmprinter"
|
||||
else:
|
||||
material_container_definition = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = material_container["definition"])
|
||||
if not material_container_definition:
|
||||
definition_id = "fdmprinter"
|
||||
else:
|
||||
material_container_definition = material_container_definition[0]
|
||||
if "has_machine_quality" not in material_container_definition:
|
||||
definition_id = "fdmprinter"
|
||||
else:
|
||||
definition_id = material_container_definition.get("quality_definition", material_container_definition["id"])
|
||||
|
||||
base_material = material_container.get("material")
|
||||
if base_material:
|
||||
# There is a basic material specified
|
||||
criteria = { "type": "material", "name": base_material, "definition": definition_id }
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(**criteria)
|
||||
containers = [basic_material for basic_material in containers if
|
||||
basic_material.getMetaDataEntry("variant") == material_container.getMetaDataEntry(
|
||||
"variant")]
|
||||
criteria = {
|
||||
"type": "material",
|
||||
"name": base_material,
|
||||
"definition": definition_id,
|
||||
"variant": material_container.get("variant")
|
||||
}
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(**criteria)
|
||||
return containers
|
||||
|
||||
return []
|
||||
|
@ -225,29 +236,25 @@ class QualityManager:
|
|||
def _getFilteredContainers(self, **kwargs):
|
||||
return self._getFilteredContainersForStack(None, None, **kwargs)
|
||||
|
||||
def _getFilteredContainersForStack(self, machine_definition: "DefinitionContainerInterface" = None, material_containers: List[InstanceContainer] = None, **kwargs):
|
||||
def _getFilteredContainersForStack(self, machine_definition: "DefinitionContainerInterface" = None, material_metadata: Optional[List[Dict[str, Any]]] = None, **kwargs):
|
||||
# Fill in any default values.
|
||||
if machine_definition is None:
|
||||
machine_definition = Application.getInstance().getGlobalContainerStack().getBottom()
|
||||
quality_definition_id = machine_definition.getMetaDataEntry("quality_definition")
|
||||
if quality_definition_id is not None:
|
||||
machine_definition = ContainerRegistry.getInstance().findDefinitionContainers(id=quality_definition_id)[0]
|
||||
machine_definition = ContainerRegistry.getInstance().findDefinitionContainers(id = quality_definition_id)[0]
|
||||
|
||||
# for convenience
|
||||
if material_containers is None:
|
||||
material_containers = []
|
||||
|
||||
if not material_containers:
|
||||
if not material_metadata:
|
||||
active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()
|
||||
if active_stacks:
|
||||
material_containers = [stack.material for stack in active_stacks]
|
||||
material_metadata = [stack.material.getMetaData() for stack in active_stacks]
|
||||
|
||||
criteria = kwargs
|
||||
filter_by_material = False
|
||||
|
||||
machine_definition = self.getParentMachineDefinition(machine_definition)
|
||||
criteria["definition"] = machine_definition.getId()
|
||||
found_containers_with_machine_definition = ContainerRegistry.getInstance().findInstanceContainers(**criteria)
|
||||
found_containers_with_machine_definition = ContainerRegistry.getInstance().findInstanceContainersMetadata(**criteria)
|
||||
whole_machine_definition = self.getWholeMachineDefinition(machine_definition)
|
||||
if whole_machine_definition.getMetaDataEntry("has_machine_quality"):
|
||||
definition_id = machine_definition.getMetaDataEntry("quality_definition", whole_machine_definition.getId())
|
||||
|
@ -261,12 +268,12 @@ class QualityManager:
|
|||
# Stick the material IDs in a set
|
||||
material_ids = set()
|
||||
|
||||
for material_instance in material_containers:
|
||||
for material_instance in material_metadata:
|
||||
if material_instance is not None:
|
||||
# Add the parent material too.
|
||||
for basic_material in self._getBasicMaterials(material_instance):
|
||||
material_ids.add(basic_material.getId())
|
||||
material_ids.add(material_instance.getId())
|
||||
for basic_material in self._getBasicMaterialMetadatas(material_instance):
|
||||
material_ids.add(basic_material["id"])
|
||||
material_ids.add(material_instance["id"])
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(**criteria)
|
||||
|
||||
result = []
|
||||
|
@ -292,13 +299,13 @@ class QualityManager:
|
|||
# We have a normal (whole) machine defintion
|
||||
quality_definition = machine_definition.getMetaDataEntry("quality_definition")
|
||||
if quality_definition is not None:
|
||||
parent_machine_definition = container_registry.findDefinitionContainers(id=quality_definition)[0]
|
||||
parent_machine_definition = container_registry.findDefinitionContainers(id = quality_definition)[0]
|
||||
return self.getParentMachineDefinition(parent_machine_definition)
|
||||
else:
|
||||
return machine_definition
|
||||
else:
|
||||
# This looks like an extruder. Find the rest of the machine.
|
||||
whole_machine = container_registry.findDefinitionContainers(id=machine_entry)[0]
|
||||
whole_machine = container_registry.findDefinitionContainers(id = machine_entry)[0]
|
||||
parent_machine = self.getParentMachineDefinition(whole_machine)
|
||||
if whole_machine is parent_machine:
|
||||
# This extruder already belongs to a 'parent' machine def.
|
||||
|
@ -307,7 +314,7 @@ class QualityManager:
|
|||
# Look up the corresponding extruder definition in the parent machine definition.
|
||||
extruder_position = machine_definition.getMetaDataEntry("position")
|
||||
parent_extruder_id = parent_machine.getMetaDataEntry("machine_extruder_trains")[extruder_position]
|
||||
return container_registry.findDefinitionContainers(id=parent_extruder_id)[0]
|
||||
return container_registry.findDefinitionContainers(id = parent_extruder_id)[0]
|
||||
|
||||
## Get the whole/global machine definition from an extruder definition.
|
||||
#
|
||||
|
@ -321,5 +328,5 @@ class QualityManager:
|
|||
return machine_definition
|
||||
else:
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
whole_machine = container_registry.findDefinitionContainers(id=machine_entry)[0]
|
||||
whole_machine = container_registry.findDefinitionContainers(id = machine_entry)[0]
|
||||
return whole_machine
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import copy
|
||||
import os.path
|
||||
import urllib
|
||||
import uuid
|
||||
from typing import Dict, Union
|
||||
from typing import Any, Dict, List, Union
|
||||
|
||||
from PyQt5.QtCore import QObject, QUrl, QVariant
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
|
@ -55,7 +56,8 @@ class ContainerManager(QObject):
|
|||
# \return The ID of the new container, or an empty string if duplication failed.
|
||||
@pyqtSlot(str, result = str)
|
||||
def duplicateContainer(self, container_id):
|
||||
containers = self._container_registry.findContainers(None, id = container_id)
|
||||
#TODO: It should be able to duplicate a container of which only the metadata is known.
|
||||
containers = self._container_registry.findContainers(id = container_id)
|
||||
if not containers:
|
||||
Logger.log("w", "Could duplicate container %s because it was not found.", container_id)
|
||||
return ""
|
||||
|
@ -98,14 +100,14 @@ class ContainerManager(QObject):
|
|||
# \return True if successful, False if not.
|
||||
@pyqtSlot(str, str, str, result = bool)
|
||||
def renameContainer(self, container_id, new_id, new_name):
|
||||
containers = self._container_registry.findContainers(None, id = container_id)
|
||||
containers = self._container_registry.findContainers(id = container_id)
|
||||
if not containers:
|
||||
Logger.log("w", "Could rename container %s because it was not found.", container_id)
|
||||
return False
|
||||
|
||||
container = containers[0]
|
||||
# First, remove the container from the registry. This will clean up any files related to the container.
|
||||
self._container_registry.removeContainer(container)
|
||||
self._container_registry.removeContainer(container_id)
|
||||
|
||||
# Ensure we have a unique name for the container
|
||||
new_name = self._container_registry.uniqueName(new_name)
|
||||
|
@ -126,9 +128,9 @@ class ContainerManager(QObject):
|
|||
# \return True if the container was successfully removed, False if not.
|
||||
@pyqtSlot(str, result = bool)
|
||||
def removeContainer(self, container_id):
|
||||
containers = self._container_registry.findContainers(None, id = container_id)
|
||||
containers = self._container_registry.findContainers(id = container_id)
|
||||
if not containers:
|
||||
Logger.log("w", "Could remove container %s because it was not found.", container_id)
|
||||
Logger.log("w", "Could not remove container %s because it was not found.", container_id)
|
||||
return False
|
||||
|
||||
self._container_registry.removeContainer(containers[0].getId())
|
||||
|
@ -146,14 +148,14 @@ class ContainerManager(QObject):
|
|||
# \return True if successfully merged, False if not.
|
||||
@pyqtSlot(str, result = bool)
|
||||
def mergeContainers(self, merge_into_id, merge_id):
|
||||
containers = self._container_registry.findContainers(None, id = merge_into_id)
|
||||
containers = self._container_registry.findContainers(id = merge_into_id)
|
||||
if not containers:
|
||||
Logger.log("w", "Could merge into container %s because it was not found.", merge_into_id)
|
||||
return False
|
||||
|
||||
merge_into = containers[0]
|
||||
|
||||
containers = self._container_registry.findContainers(None, id = merge_id)
|
||||
containers = self._container_registry.findContainers(id = merge_id)
|
||||
if not containers:
|
||||
Logger.log("w", "Could not merge container %s because it was not found", merge_id)
|
||||
return False
|
||||
|
@ -175,13 +177,13 @@ class ContainerManager(QObject):
|
|||
# \return True if successful, False if not.
|
||||
@pyqtSlot(str, result = bool)
|
||||
def clearContainer(self, container_id):
|
||||
containers = self._container_registry.findContainers(None, id = container_id)
|
||||
if not containers:
|
||||
Logger.log("w", "Could clear container %s because it was not found.", container_id)
|
||||
if self._container_registry.isReadOnly(container_id):
|
||||
Logger.log("w", "Cannot clear read-only container %s", container_id)
|
||||
return False
|
||||
|
||||
if containers[0].isReadOnly():
|
||||
Logger.log("w", "Cannot clear read-only container %s", container_id)
|
||||
containers = self._container_registry.findContainers(id = container_id)
|
||||
if not containers:
|
||||
Logger.log("w", "Could clear container %s because it was not found.", container_id)
|
||||
return False
|
||||
|
||||
containers[0].clear()
|
||||
|
@ -190,16 +192,12 @@ class ContainerManager(QObject):
|
|||
|
||||
@pyqtSlot(str, str, result=str)
|
||||
def getContainerMetaDataEntry(self, container_id, entry_name):
|
||||
containers = self._container_registry.findContainers(None, id=container_id)
|
||||
if not containers:
|
||||
metadatas = self._container_registry.findContainersMetadata(id = container_id)
|
||||
if not metadatas:
|
||||
Logger.log("w", "Could not get metadata of container %s because it was not found.", container_id)
|
||||
return ""
|
||||
|
||||
result = containers[0].getMetaDataEntry(entry_name)
|
||||
if result is not None:
|
||||
return str(result)
|
||||
else:
|
||||
return ""
|
||||
return str(metadatas[0].get(entry_name, ""))
|
||||
|
||||
## Set a metadata entry of the specified container.
|
||||
#
|
||||
|
@ -215,17 +213,17 @@ class ContainerManager(QObject):
|
|||
# \return True if successful, False if not.
|
||||
@pyqtSlot(str, str, str, result = bool)
|
||||
def setContainerMetaDataEntry(self, container_id, entry_name, entry_value):
|
||||
containers = self._container_registry.findContainers(None, id = container_id)
|
||||
if self._container_registry.isReadOnly(container_id):
|
||||
Logger.log("w", "Cannot set metadata of read-only container %s.", container_id)
|
||||
return False
|
||||
|
||||
containers = self._container_registry.findContainers(id = container_id) #We need the complete container, since we need to know whether the container is read-only or not.
|
||||
if not containers:
|
||||
Logger.log("w", "Could not set metadata of container %s because it was not found.", container_id)
|
||||
return False
|
||||
|
||||
container = containers[0]
|
||||
|
||||
if container.isReadOnly():
|
||||
Logger.log("w", "Cannot set metadata of read-only container %s.", container_id)
|
||||
return False
|
||||
|
||||
entries = entry_name.split("/")
|
||||
entry_name = entries.pop()
|
||||
|
||||
|
@ -265,17 +263,17 @@ class ContainerManager(QObject):
|
|||
# \return True if successful, False if not.
|
||||
@pyqtSlot(str, str, str, str, result = bool)
|
||||
def setContainerProperty(self, container_id, setting_key, property_name, property_value):
|
||||
containers = self._container_registry.findContainers(None, id = container_id)
|
||||
if self._container_registry.isReadOnly(container_id):
|
||||
Logger.log("w", "Cannot set properties of read-only container %s.", container_id)
|
||||
return False
|
||||
|
||||
containers = self._container_registry.findContainers(id = container_id)
|
||||
if not containers:
|
||||
Logger.log("w", "Could not set properties of container %s because it was not found.", container_id)
|
||||
return False
|
||||
|
||||
container = containers[0]
|
||||
|
||||
if container.isReadOnly():
|
||||
Logger.log("w", "Cannot set properties of read-only container %s.", container_id)
|
||||
return False
|
||||
|
||||
container.setProperty(setting_key, property_name, property_value)
|
||||
|
||||
basefile = container.getMetaDataEntry("base_file", container_id)
|
||||
|
@ -311,35 +309,30 @@ class ContainerManager(QObject):
|
|||
## Set the name of the specified container.
|
||||
@pyqtSlot(str, str, result = bool)
|
||||
def setContainerName(self, container_id, new_name):
|
||||
containers = self._container_registry.findContainers(None, id = container_id)
|
||||
if self._container_registry.isReadOnly(container_id):
|
||||
Logger.log("w", "Cannot set name of read-only container %s.", container_id)
|
||||
return False
|
||||
|
||||
containers = self._container_registry.findContainers(id = container_id) #We need to get the full container, not just metadata, since we need to know whether it's read-only.
|
||||
if not containers:
|
||||
Logger.log("w", "Could not set name of container %s because it was not found.", container_id)
|
||||
return False
|
||||
|
||||
container = containers[0]
|
||||
|
||||
if container.isReadOnly():
|
||||
Logger.log("w", "Cannot set name of read-only container %s.", container_id)
|
||||
return False
|
||||
|
||||
container.setName(new_name)
|
||||
containers[0].setName(new_name)
|
||||
|
||||
return True
|
||||
|
||||
## Find instance containers matching certain criteria.
|
||||
#
|
||||
# This effectively forwards to ContainerRegistry::findInstanceContainers.
|
||||
# This effectively forwards to
|
||||
# ContainerRegistry::findInstanceContainersMetadata.
|
||||
#
|
||||
# \param criteria A dict of key - value pairs to search for.
|
||||
#
|
||||
# \return A list of container IDs that match the given criteria.
|
||||
@pyqtSlot("QVariantMap", result = "QVariantList")
|
||||
def findInstanceContainers(self, criteria):
|
||||
result = []
|
||||
for entry in self._container_registry.findInstanceContainers(**criteria):
|
||||
result.append(entry.getId())
|
||||
|
||||
return result
|
||||
return [entry["id"] for entry in self._container_registry.findInstanceContainersMetadata(**criteria)]
|
||||
|
||||
@pyqtSlot(str, result = bool)
|
||||
def isContainerUsed(self, container_id):
|
||||
|
@ -347,15 +340,17 @@ class ContainerManager(QObject):
|
|||
# check if this is a material container. If so, check if any material with the same base is being used by any
|
||||
# stacks.
|
||||
container_ids_to_check = [container_id]
|
||||
container_results = self._container_registry.findInstanceContainers(id = container_id, type = "material")
|
||||
container_results = self._container_registry.findInstanceContainersMetadata(id = container_id, type = "material")
|
||||
if container_results:
|
||||
this_container = container_results[0]
|
||||
material_base_file = this_container.getMetaDataEntry("base_file", this_container.getId())
|
||||
material_base_file = this_container["id"]
|
||||
if "base_file" in this_container:
|
||||
material_base_file = this_container["base_file"]
|
||||
# check all material container IDs with the same base
|
||||
material_containers = self._container_registry.findInstanceContainers(base_file = material_base_file,
|
||||
material_containers = self._container_registry.findInstanceContainersMetadata(base_file = material_base_file,
|
||||
type = "material")
|
||||
if material_containers:
|
||||
container_ids_to_check = [container.getId() for container in material_containers]
|
||||
container_ids_to_check = [container["id"] for container in material_containers]
|
||||
|
||||
all_stacks = self._container_registry.findContainerStacks()
|
||||
for stack in all_stacks:
|
||||
|
@ -423,7 +418,7 @@ class ContainerManager(QObject):
|
|||
else:
|
||||
mime_type = self._container_name_filters[file_type]["mime"]
|
||||
|
||||
containers = self._container_registry.findContainers(None, id = container_id)
|
||||
containers = self._container_registry.findContainers(id = container_id)
|
||||
if not containers:
|
||||
return { "status": "error", "message": "Container not found"}
|
||||
container = containers[0]
|
||||
|
@ -627,9 +622,9 @@ class ContainerManager(QObject):
|
|||
|
||||
elif activate_quality:
|
||||
definition_id = "fdmprinter" if not self._machine_manager.filterQualityByMachine else self._machine_manager.activeDefinitionId
|
||||
containers = self._container_registry.findInstanceContainers(type = "quality", definition = definition_id, quality_type = activate_quality_type)
|
||||
containers = self._container_registry.findInstanceContainersMetadata(type = "quality", definition = definition_id, quality_type = activate_quality_type)
|
||||
if containers:
|
||||
self._machine_manager.setActiveQuality(containers[0].getId())
|
||||
self._machine_manager.setActiveQuality(containers[0]["id"])
|
||||
self._machine_manager.activeQualityChanged.emit()
|
||||
|
||||
return containers_found
|
||||
|
@ -664,11 +659,13 @@ class ContainerManager(QObject):
|
|||
|
||||
container_registry = self._container_registry
|
||||
|
||||
containers_to_rename = self._container_registry.findInstanceContainers(type = "quality_changes", name = quality_name)
|
||||
containers_to_rename = self._container_registry.findInstanceContainersMetadata(type = "quality_changes", name = quality_name)
|
||||
|
||||
for container in containers_to_rename:
|
||||
stack_id = container.getMetaDataEntry("extruder", global_stack.getId())
|
||||
container_registry.renameContainer(container.getId(), new_name, self._createUniqueId(stack_id, new_name))
|
||||
stack_id = global_stack.getId()
|
||||
if "extruder" in container:
|
||||
stack_id = container["extruder"]
|
||||
container_registry.renameContainer(container["id"], new_name, self._createUniqueId(stack_id, new_name))
|
||||
|
||||
if not containers_to_rename:
|
||||
Logger.log("e", "Unable to rename %s, because we could not find the profile", quality_name)
|
||||
|
@ -693,27 +690,29 @@ class ContainerManager(QObject):
|
|||
machine_definition = global_stack.getBottom()
|
||||
|
||||
active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()
|
||||
material_containers = [stack.material for stack in active_stacks]
|
||||
if active_stacks is None:
|
||||
return ""
|
||||
material_metadatas = [stack.material.getMetaData() for stack in active_stacks]
|
||||
|
||||
result = self._duplicateQualityOrQualityChangesForMachineType(quality_name, base_name,
|
||||
QualityManager.getInstance().getParentMachineDefinition(machine_definition),
|
||||
material_containers)
|
||||
material_metadatas)
|
||||
return result[0].getName() if result else ""
|
||||
|
||||
## Duplicate a quality or quality changes profile specific to a machine type
|
||||
#
|
||||
# \param quality_name \type{str} the name of the quality or quality changes container to duplicate.
|
||||
# \param base_name \type{str} the desired name for the new container.
|
||||
# \param machine_definition \type{DefinitionContainer}
|
||||
# \param material_instances \type{List[InstanceContainer]}
|
||||
# \return \type{str} the name of the newly created container.
|
||||
def _duplicateQualityOrQualityChangesForMachineType(self, quality_name, base_name, machine_definition, material_instances):
|
||||
# \param quality_name The name of the quality or quality changes container to duplicate.
|
||||
# \param base_name The desired name for the new container.
|
||||
# \param machine_definition The machine with the specific machine type.
|
||||
# \param material_metadatas Metadata of materials
|
||||
# \return List of duplicated quality profiles.
|
||||
def _duplicateQualityOrQualityChangesForMachineType(self, quality_name: str, base_name: str, machine_definition: DefinitionContainer, material_metadatas: List[Dict[str, Any]]) -> List[InstanceContainer]:
|
||||
Logger.log("d", "Attempting to duplicate the quality %s", quality_name)
|
||||
|
||||
if base_name is None:
|
||||
base_name = quality_name
|
||||
# Try to find a Quality with the name.
|
||||
container = QualityManager.getInstance().findQualityByName(quality_name, machine_definition, material_instances)
|
||||
container = QualityManager.getInstance().findQualityByName(quality_name, machine_definition, material_metadatas)
|
||||
if container:
|
||||
Logger.log("d", "We found a quality to duplicate.")
|
||||
return self._duplicateQualityForMachineType(container, base_name, machine_definition)
|
||||
|
@ -722,7 +721,7 @@ class ContainerManager(QObject):
|
|||
return self._duplicateQualityChangesForMachineType(quality_name, base_name, machine_definition)
|
||||
|
||||
# Duplicate a quality profile
|
||||
def _duplicateQualityForMachineType(self, quality_container, base_name, machine_definition):
|
||||
def _duplicateQualityForMachineType(self, quality_container, base_name, machine_definition) -> List[InstanceContainer]:
|
||||
if base_name is None:
|
||||
base_name = quality_container.getName()
|
||||
new_name = self._container_registry.uniqueName(base_name)
|
||||
|
@ -746,7 +745,7 @@ class ContainerManager(QObject):
|
|||
return new_change_instances
|
||||
|
||||
# Duplicate a quality changes container
|
||||
def _duplicateQualityChangesForMachineType(self, quality_changes_name, base_name, machine_definition):
|
||||
def _duplicateQualityChangesForMachineType(self, quality_changes_name, base_name, machine_definition) -> List[InstanceContainer]:
|
||||
new_change_instances = []
|
||||
for container in QualityManager.getInstance().findQualityChangesByName(quality_changes_name,
|
||||
machine_definition):
|
||||
|
@ -765,27 +764,57 @@ class ContainerManager(QObject):
|
|||
# \return \type{str} the id of the newly created container.
|
||||
@pyqtSlot(str, result = str)
|
||||
def duplicateMaterial(self, material_id: str) -> str:
|
||||
containers = self._container_registry.findInstanceContainers(id=material_id)
|
||||
if not containers:
|
||||
original = self._container_registry.findContainersMetadata(id = material_id)
|
||||
if not original:
|
||||
Logger.log("d", "Unable to duplicate the material with id %s, because it doesn't exist.", material_id)
|
||||
return ""
|
||||
original = original[0]
|
||||
|
||||
base_container_id = original.get("base_file")
|
||||
base_container = self._container_registry.findContainers(id = base_container_id)
|
||||
if not base_container:
|
||||
Logger.log("d", "Unable to duplicate the material with id {material_id}, because base_file {base_container_id} doesn't exist.".format(material_id = material_id, base_container_id = base_container_id))
|
||||
return ""
|
||||
base_container = base_container[0]
|
||||
|
||||
#We'll copy all containers with the same base.
|
||||
#This way the correct variant and machine still gets assigned when loading the copy of the material.
|
||||
containers_to_copy = self._container_registry.findInstanceContainers(base_file = base_container_id)
|
||||
|
||||
# Ensure all settings are saved.
|
||||
Application.getInstance().saveSettings()
|
||||
|
||||
# Create a new ID & container to hold the data.
|
||||
new_id = self._container_registry.uniqueName(material_id)
|
||||
container_type = type(containers[0]) # Could be either a XMLMaterialProfile or a InstanceContainer
|
||||
duplicated_container = container_type(new_id)
|
||||
new_containers = []
|
||||
new_base_id = self._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
|
||||
new_containers.append(new_base_container)
|
||||
|
||||
# Instead of duplicating we load the data from the basefile again.
|
||||
# This ensures that the inheritance goes well and all "cut up" subclasses of the xmlMaterial profile
|
||||
# are also correctly created.
|
||||
with open(containers[0].getPath(), encoding="utf-8") as f:
|
||||
duplicated_container.deserialize(f.read())
|
||||
duplicated_container.setDirty(True)
|
||||
self._container_registry.addContainer(duplicated_container)
|
||||
return self._getMaterialContainerIdForActiveMachine(new_id)
|
||||
#Clone all of them.
|
||||
clone_of_original = None #Keeping track of which one is the clone of the original material, since we need to return that.
|
||||
for container_to_copy in containers_to_copy:
|
||||
#Create unique IDs for every clone.
|
||||
current_id = container_to_copy.getId()
|
||||
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"):
|
||||
variant = self._container_registry.findContainers(id = container_to_copy.getMetaDataEntry("variant"))[0]
|
||||
new_id += "_" + variant.getName().replace(" ", "_")
|
||||
if current_id == material_id:
|
||||
clone_of_original = new_id
|
||||
|
||||
new_container = copy.deepcopy(container_to_copy)
|
||||
new_container.getMetaData()["id"] = new_id
|
||||
new_container.getMetaData()["base_file"] = new_base_id
|
||||
new_containers.append(new_container)
|
||||
|
||||
for container_to_add in new_containers:
|
||||
container_to_add.setDirty(True)
|
||||
ContainerRegistry.getInstance().addContainer(container_to_add)
|
||||
return self._getMaterialContainerIdForActiveMachine(clone_of_original)
|
||||
|
||||
## Create a new material by cloning Generic PLA for the current material diameter and setting the GUID to something unqiue
|
||||
#
|
||||
|
@ -800,12 +829,12 @@ class ContainerManager(QObject):
|
|||
return ""
|
||||
|
||||
approximate_diameter = str(round(global_stack.getProperty("material_diameter", "value")))
|
||||
containers = self._container_registry.findInstanceContainers(id = "generic_pla*", approximate_diameter = approximate_diameter)
|
||||
containers = self._container_registry.findInstanceContainersMetadata(id = "generic_pla*", approximate_diameter = approximate_diameter)
|
||||
if not containers:
|
||||
Logger.log("d", "Unable to create a new material by cloning Generic PLA, because it cannot be found for the material diameter for this machine.")
|
||||
return ""
|
||||
|
||||
base_file = containers[0].getMetaDataEntry("base_file")
|
||||
base_file = containers[0].get("base_file")
|
||||
containers = self._container_registry.findInstanceContainers(id = base_file)
|
||||
if not containers:
|
||||
Logger.log("d", "Unable to create a new material by cloning Generic PLA, because the base file for Generic PLA for this machine can not be found.")
|
||||
|
@ -846,14 +875,14 @@ class ContainerManager(QObject):
|
|||
has_variants = parseBool(global_stack.getMetaDataEntry("has_variants", default = False))
|
||||
if has_machine_materials or has_variant_materials:
|
||||
if has_variants:
|
||||
materials = self._container_registry.findInstanceContainers(type = "material", base_file = base_file, definition = global_stack.getBottom().getId(), variant = self._machine_manager.activeVariantId)
|
||||
materials = self._container_registry.findInstanceContainersMetadata(type = "material", base_file = base_file, definition = global_stack.getBottom().getId(), variant = self._machine_manager.activeVariantId)
|
||||
else:
|
||||
materials = self._container_registry.findInstanceContainers(type = "material", base_file = base_file, definition = global_stack.getBottom().getId())
|
||||
materials = self._container_registry.findInstanceContainersMetadata(type = "material", base_file = base_file, definition = global_stack.getBottom().getId())
|
||||
|
||||
if materials:
|
||||
return materials[0].getId()
|
||||
return materials[0]["id"]
|
||||
|
||||
Logger.log("w", "Unable to find a suitable container based on %s for the current machine .", base_file)
|
||||
Logger.log("w", "Unable to find a suitable container based on %s for the current machine.", base_file)
|
||||
return "" # do not activate a new material if a container can not be found
|
||||
|
||||
return base_file
|
||||
|
@ -864,25 +893,25 @@ class ContainerManager(QObject):
|
|||
# \return \type{list} a list of names of materials with the same GUID
|
||||
@pyqtSlot(str, result = "QStringList")
|
||||
def getLinkedMaterials(self, material_id: str):
|
||||
containers = self._container_registry.findInstanceContainers(id=material_id)
|
||||
containers = self._container_registry.findInstanceContainersMetadata(id = material_id)
|
||||
if not containers:
|
||||
Logger.log("d", "Unable to find materials linked to material with id %s, because it doesn't exist.", material_id)
|
||||
return []
|
||||
|
||||
material_container = containers[0]
|
||||
material_base_file = material_container.getMetaDataEntry("base_file", "")
|
||||
material_guid = material_container.getMetaDataEntry("GUID", "")
|
||||
material_base_file = material_container.get("base_file", "")
|
||||
material_guid = material_container.get("GUID", "")
|
||||
if not material_guid:
|
||||
Logger.log("d", "Unable to find materials linked to material with id %s, because it doesn't have a GUID.", material_id)
|
||||
return []
|
||||
|
||||
containers = self._container_registry.findInstanceContainers(type = "material", GUID = material_guid)
|
||||
containers = self._container_registry.findInstanceContainersMetadata(type = "material", GUID = material_guid)
|
||||
linked_material_names = []
|
||||
for container in containers:
|
||||
if container.getId() in [material_id, material_base_file] or container.getMetaDataEntry("base_file") != container.getId():
|
||||
if container["id"] in [material_id, material_base_file] or container.get("base_file") != container["id"]:
|
||||
continue
|
||||
|
||||
linked_material_names.append(container.getName())
|
||||
linked_material_names.append(container["name"])
|
||||
return linked_material_names
|
||||
|
||||
## Unlink a material from all other materials by creating a new GUID
|
||||
|
@ -968,14 +997,6 @@ class ContainerManager(QObject):
|
|||
name_filter = "{0} ({1})".format(mime_type.comment, suffix_list)
|
||||
self._container_name_filters[name_filter] = entry
|
||||
|
||||
## Get containers filtered by machine type and material if required.
|
||||
#
|
||||
# \param kwargs Initial search criteria that the containers need to match.
|
||||
#
|
||||
# \return A list of containers matching the search criteria.
|
||||
def _getFilteredContainers(self, **kwargs):
|
||||
return QualityManager.getInstance()._getFilteredContainers(**kwargs)
|
||||
|
||||
## Creates a unique ID for a container by prefixing the name with the stack ID.
|
||||
#
|
||||
# This method creates a unique ID for a container by prefixing it with a specified stack ID.
|
||||
|
@ -1015,9 +1036,9 @@ class ContainerManager(QObject):
|
|||
|
||||
# If the machine specifies qualities should be filtered, ensure we match the current criteria.
|
||||
if not machine_definition.getMetaDataEntry("has_machine_quality"):
|
||||
quality_changes.setDefinition(self._container_registry.findContainers(id = "fdmprinter")[0])
|
||||
quality_changes.setDefinition("fdmprinter")
|
||||
else:
|
||||
quality_changes.setDefinition(QualityManager.getInstance().getParentMachineDefinition(machine_definition))
|
||||
quality_changes.setDefinition(QualityManager.getInstance().getParentMachineDefinition(machine_definition).getId())
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
quality_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||
|
|
|
@ -44,7 +44,6 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
# Global stack based on metadata information.
|
||||
@override(ContainerRegistry)
|
||||
def addContainer(self, container):
|
||||
|
||||
# Note: Intentional check with type() because we want to ignore subclasses
|
||||
if type(container) == ContainerStack:
|
||||
container = self._convertContainerStack(container)
|
||||
|
@ -89,8 +88,8 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
def _containerExists(self, container_type, container_name):
|
||||
container_class = ContainerStack if container_type == "machine" else InstanceContainer
|
||||
|
||||
return self.findContainers(container_class, id = container_name, type = container_type, ignore_case = True) or \
|
||||
self.findContainers(container_class, name = container_name, type = container_type)
|
||||
return self.findContainersMetadata(id = container_name, type = container_type, ignore_case = True) or \
|
||||
self.findContainersMetadata(container_type = container_class, name = container_name, type = container_type)
|
||||
|
||||
## Exports an profile to a file
|
||||
#
|
||||
|
@ -119,7 +118,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
found_containers = []
|
||||
extruder_positions = []
|
||||
for instance_id in instance_ids:
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(id=instance_id)
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(id = instance_id)
|
||||
if containers:
|
||||
found_containers.append(containers[0])
|
||||
|
||||
|
@ -129,9 +128,9 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
# Global stack
|
||||
extruder_positions.append(-1)
|
||||
else:
|
||||
extruder_containers = ContainerRegistry.getInstance().findDefinitionContainers(id=extruder_id)
|
||||
extruder_containers = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = extruder_id)
|
||||
if extruder_containers:
|
||||
extruder_positions.append(int(extruder_containers[0].getMetaDataEntry("position", 0)))
|
||||
extruder_positions.append(int(extruder_containers[0].get("position", 0)))
|
||||
else:
|
||||
extruder_positions.append(0)
|
||||
# Ensure the profiles are always exported in order (global, extruder 0, extruder 1, ...)
|
||||
|
@ -274,7 +273,6 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
#
|
||||
# \return None if configuring was successful or an error message if an error occurred.
|
||||
def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str) -> Optional[str]:
|
||||
profile.setReadOnly(False)
|
||||
profile.setDirty(True) # Ensure the profiles are correctly saved
|
||||
|
||||
new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile"))
|
||||
|
@ -292,7 +290,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
|
||||
quality_type_criteria = {"quality_type": quality_type}
|
||||
if self._machineHasOwnQualities():
|
||||
profile.setDefinition(self._activeQualityDefinition())
|
||||
profile.setDefinition(self._activeQualityDefinition().getId())
|
||||
if self._machineHasOwnMaterials():
|
||||
active_material_id = self._activeMaterialId()
|
||||
if active_material_id and active_material_id != "empty": # only update if there is an active material
|
||||
|
@ -302,7 +300,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
quality_type_criteria["definition"] = profile.getDefinition().getId()
|
||||
|
||||
else:
|
||||
profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0])
|
||||
profile.setDefinition(fdmprinter)
|
||||
quality_type_criteria["definition"] = "fdmprinter"
|
||||
|
||||
machine_definition = Application.getInstance().getGlobalContainerStack().getBottom()
|
||||
|
@ -349,7 +347,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(global_container_stack.getBottom())
|
||||
definition = self.findDefinitionContainers(id=definition_id)[0]
|
||||
definition = self.findDefinitionContainers(id = definition_id)[0]
|
||||
|
||||
if definition:
|
||||
return definition
|
||||
|
@ -534,13 +532,13 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
# set after upgrading, because the proper global stack was not yet loaded. This method
|
||||
# makes sure those extruders also get the right stack set.
|
||||
def _connectUpgradedExtruderStacksToMachines(self):
|
||||
extruder_stacks = self.findContainers(ExtruderStack.ExtruderStack)
|
||||
extruder_stacks = self.findContainers(container_type = ExtruderStack.ExtruderStack)
|
||||
for extruder_stack in extruder_stacks:
|
||||
if extruder_stack.getNextStack():
|
||||
# Has the right next stack, so ignore it.
|
||||
continue
|
||||
|
||||
machines = ContainerRegistry.getInstance().findContainerStacks(id=extruder_stack.getMetaDataEntry("machine", ""))
|
||||
machines = ContainerRegistry.getInstance().findContainerStacks(id = extruder_stack.getMetaDataEntry("machine", ""))
|
||||
if machines:
|
||||
extruder_stack.setNextStack(machines[0])
|
||||
else:
|
||||
|
|
|
@ -14,7 +14,7 @@ from UM.Settings.ContainerStack import ContainerStack, InvalidContainerStackErro
|
|||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.Interfaces import ContainerInterface
|
||||
from UM.Settings.Interfaces import ContainerInterface, DefinitionContainerInterface
|
||||
|
||||
from . import Exceptions
|
||||
|
||||
|
@ -246,7 +246,7 @@ class CuraContainerStack(ContainerStack):
|
|||
## Set the definition container.
|
||||
#
|
||||
# \param new_quality_changes The new definition container. It is expected to have a "type" metadata entry with the value "quality_changes".
|
||||
def setDefinition(self, new_definition: DefinitionContainer) -> None:
|
||||
def setDefinition(self, new_definition: DefinitionContainerInterface) -> None:
|
||||
self.replaceContainer(_ContainerIndexes.Definition, new_definition)
|
||||
|
||||
## Set the definition container by an ID.
|
||||
|
@ -487,12 +487,18 @@ class CuraContainerStack(ContainerStack):
|
|||
search_criteria.pop("name", None)
|
||||
materials = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
|
||||
|
||||
if materials:
|
||||
return materials[0]
|
||||
|
||||
if not materials:
|
||||
Logger.log("w", "Could not find a valid material for stack {stack}", stack = self.id)
|
||||
return None
|
||||
|
||||
for material in materials:
|
||||
# Prefer a read-only material
|
||||
if ContainerRegistry.getInstance().isReadOnly(material.getId()):
|
||||
return material
|
||||
|
||||
return materials[0]
|
||||
|
||||
|
||||
## Find the quality that should be used as "default" quality.
|
||||
#
|
||||
# This will search for qualities that match the current definition and pick the preferred one,
|
||||
|
@ -502,7 +508,7 @@ class CuraContainerStack(ContainerStack):
|
|||
def findDefaultQuality(self) -> Optional[ContainerInterface]:
|
||||
definition = self._getMachineDefinition()
|
||||
registry = ContainerRegistry.getInstance()
|
||||
material_container = self.material if self.material != self._empty_instance_container else None
|
||||
material_container = self.material if self.material.getId() not in (self._empty_material.getId(), self._empty_instance_container.getId()) else None
|
||||
|
||||
search_criteria = {"type": "quality"}
|
||||
|
||||
|
@ -546,7 +552,7 @@ class CuraContainerStack(ContainerStack):
|
|||
material_search_criteria = {"type": "material", "material": material_container.getMetaDataEntry("material"), "color_name": "Generic"}
|
||||
if definition.getMetaDataEntry("has_machine_quality"):
|
||||
if self.material != self._empty_instance_container:
|
||||
material_search_criteria["definition"] = material_container.getDefinition().id
|
||||
material_search_criteria["definition"] = material_container.getMetaDataEntry("definition")
|
||||
|
||||
if definition.getMetaDataEntry("has_variants"):
|
||||
material_search_criteria["variant"] = material_container.getMetaDataEntry("variant")
|
||||
|
@ -557,10 +563,10 @@ class CuraContainerStack(ContainerStack):
|
|||
material_search_criteria["variant"] = self.variant.id
|
||||
else:
|
||||
material_search_criteria["definition"] = "fdmprinter"
|
||||
material_containers = registry.findInstanceContainers(**material_search_criteria)
|
||||
material_containers = registry.findInstanceContainersMetadata(**material_search_criteria)
|
||||
# Try all materials to see if there is a quality profile available.
|
||||
for material_container in material_containers:
|
||||
search_criteria["material"] = material_container.getId()
|
||||
search_criteria["material"] = material_container["id"]
|
||||
|
||||
containers = registry.findInstanceContainers(**search_criteria)
|
||||
if containers:
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
from UM.Logger import Logger
|
||||
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.Interfaces import DefinitionContainerInterface
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
|
@ -34,7 +34,7 @@ class CuraStackBuilder:
|
|||
# Make sure the new name does not collide with any definition or (quality) profile
|
||||
# createUniqueName() only looks at other stacks, but not at definitions or quality profiles
|
||||
# Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true
|
||||
if registry.findContainers(id = generated_name):
|
||||
if registry.findContainersMetadata(id = generated_name):
|
||||
generated_name = registry.uniqueName(generated_name)
|
||||
|
||||
new_global_stack = cls.createGlobalStack(
|
||||
|
@ -55,12 +55,12 @@ class CuraStackBuilder:
|
|||
new_extruder_id = registry.uniqueName(machine_definition.getName() + " " + extruder_definition.id)
|
||||
new_extruder = cls.createExtruderStack(
|
||||
new_extruder_id,
|
||||
definition=extruder_definition,
|
||||
machine_definition=machine_definition,
|
||||
quality="default",
|
||||
material="default",
|
||||
variant="default",
|
||||
next_stack=new_global_stack
|
||||
definition = extruder_definition,
|
||||
machine_definition_id = machine_definition.getId(),
|
||||
quality = "default",
|
||||
material = "default",
|
||||
variant = "default",
|
||||
next_stack = new_global_stack
|
||||
)
|
||||
new_global_stack.addExtruder(new_extruder)
|
||||
else:
|
||||
|
@ -74,7 +74,7 @@ class CuraStackBuilder:
|
|||
new_extruder = cls.createExtruderStack(
|
||||
new_extruder_id,
|
||||
definition = extruder_definition,
|
||||
machine_definition = machine_definition,
|
||||
machine_definition_id = machine_definition.getId(),
|
||||
quality = "default",
|
||||
material = "default",
|
||||
variant = "default",
|
||||
|
@ -88,12 +88,13 @@ class CuraStackBuilder:
|
|||
#
|
||||
# \param new_stack_id The ID of the new stack.
|
||||
# \param definition The definition to base the new stack on.
|
||||
# \param machine_definition The machine definition to use for the user container.
|
||||
# \param machine_definition_id The ID of the machine definition to use for
|
||||
# the user container.
|
||||
# \param kwargs You can add keyword arguments to specify IDs of containers to use for a specific type, for example "variant": "0.4mm"
|
||||
#
|
||||
# \return A new Global stack instance with the specified parameters.
|
||||
@classmethod
|
||||
def createExtruderStack(cls, new_stack_id: str, definition: DefinitionContainer, machine_definition: DefinitionContainer, **kwargs) -> ExtruderStack:
|
||||
def createExtruderStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, machine_definition_id: str, **kwargs) -> ExtruderStack:
|
||||
stack = ExtruderStack(new_stack_id)
|
||||
stack.setName(definition.getName())
|
||||
stack.setDefinition(definition)
|
||||
|
@ -108,7 +109,7 @@ class CuraStackBuilder:
|
|||
user_container.addMetaDataEntry("extruder", new_stack_id)
|
||||
from cura.CuraApplication import CuraApplication
|
||||
user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||
user_container.setDefinition(machine_definition)
|
||||
user_container.setDefinition(machine_definition_id)
|
||||
|
||||
stack.setUserChanges(user_container)
|
||||
|
||||
|
@ -148,7 +149,7 @@ class CuraStackBuilder:
|
|||
#
|
||||
# \return A new Global stack instance with the specified parameters.
|
||||
@classmethod
|
||||
def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainer, **kwargs) -> GlobalStack:
|
||||
def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, **kwargs) -> GlobalStack:
|
||||
stack = GlobalStack(new_stack_id)
|
||||
stack.setDefinition(definition)
|
||||
|
||||
|
@ -157,7 +158,7 @@ class CuraStackBuilder:
|
|||
user_container.addMetaDataEntry("machine", new_stack_id)
|
||||
from cura.CuraApplication import CuraApplication
|
||||
user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||
user_container.setDefinition(definition)
|
||||
user_container.setDefinition(definition.getId())
|
||||
|
||||
stack.setUserChanges(user_container)
|
||||
|
||||
|
@ -193,8 +194,7 @@ class CuraStackBuilder:
|
|||
unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name)
|
||||
|
||||
definition_changes_container = InstanceContainer(unique_container_name)
|
||||
definition = container_stack.getBottom()
|
||||
definition_changes_container.setDefinition(definition)
|
||||
definition_changes_container.setDefinition(container_stack.getBottom().getId())
|
||||
definition_changes_container.addMetaDataEntry("type", "definition_changes")
|
||||
definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||
|
||||
|
|
|
@ -130,6 +130,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
self._active_machine_extruders = []
|
||||
extruder_manager = Application.getInstance().getExtruderManager()
|
||||
for extruder in extruder_manager.getExtruderStacks():
|
||||
if extruder is None: #This extruder wasn't loaded yet. This happens asynchronously while this model is constructed from QML.
|
||||
continue
|
||||
extruder.containersChanged.connect(self._onExtruderStackContainersChanged)
|
||||
self._active_machine_extruders.append(extruder)
|
||||
|
||||
|
|
|
@ -43,15 +43,11 @@ class GlobalStack(CuraContainerStack):
|
|||
def getLoadingPriority(cls) -> int:
|
||||
return 2
|
||||
|
||||
def getConfigurationTypeFromSerialized(self, serialized: str) -> Optional[str]:
|
||||
configuration_type = None
|
||||
try:
|
||||
parser = self._readAndValidateSerialized(serialized)
|
||||
configuration_type = parser["metadata"].get("type")
|
||||
@classmethod
|
||||
def getConfigurationTypeFromSerialized(cls, serialized: str) -> Optional[str]:
|
||||
configuration_type = super().getConfigurationTypeFromSerialized(serialized)
|
||||
if configuration_type == "machine":
|
||||
configuration_type = "machine_stack"
|
||||
except Exception as e:
|
||||
Logger.log("e", "Could not get configuration type: %s", e)
|
||||
return "machine_stack"
|
||||
return configuration_type
|
||||
|
||||
## Add an extruder to the list of extruders of this stack.
|
||||
|
@ -67,7 +63,7 @@ class GlobalStack(CuraContainerStack):
|
|||
return
|
||||
|
||||
if any(item.getId() == extruder.id for item in self._extruders.values()):
|
||||
Logger.log("w", "Extruder [%s] has already been added to this stack [%s]", extruder.id, self._id)
|
||||
Logger.log("w", "Extruder [%s] has already been added to this stack [%s]", extruder.id, self.getId())
|
||||
return
|
||||
|
||||
self._extruders[position] = extruder
|
||||
|
|
|
@ -32,6 +32,7 @@ from .CuraStackBuilder import CuraStackBuilder
|
|||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
from cura.Settings.ProfilesModel import ProfilesModel
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -60,9 +61,11 @@ class MachineManager(QObject):
|
|||
self._instance_container_timer = QTimer()
|
||||
self._instance_container_timer.setInterval(250)
|
||||
self._instance_container_timer.setSingleShot(True)
|
||||
self._instance_container_timer.timeout.connect(self.__onInstanceContainersChanged)
|
||||
self._instance_container_timer.timeout.connect(self.__emitChangedSignals)
|
||||
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||
Application.getInstance().getContainerRegistry().containerLoadComplete.connect(self._onInstanceContainersChanged)
|
||||
self._connected_to_profiles_model = False
|
||||
|
||||
## When the global container is changed, active material probably needs to be updated.
|
||||
self.globalContainerChanged.connect(self.activeMaterialChanged)
|
||||
|
@ -104,7 +107,7 @@ class MachineManager(QObject):
|
|||
# There might already be some output devices by the time the signal is connected
|
||||
self._onOutputDevicesChanged()
|
||||
|
||||
if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacks(id = active_machine_id):
|
||||
if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacksMetadata(id = active_machine_id):
|
||||
# An active machine was saved, so restore it.
|
||||
self.setActiveMachine(active_machine_id)
|
||||
# Make sure _active_container_stack is properly initiated
|
||||
|
@ -117,6 +120,10 @@ class MachineManager(QObject):
|
|||
"The selected material is incompatible with the selected machine or configuration."),
|
||||
title = catalog.i18nc("@info:title", "Incompatible Material"))
|
||||
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId)
|
||||
if containers:
|
||||
containers[0].nameChanged.connect(self._onMaterialNameChanged)
|
||||
|
||||
globalContainerChanged = pyqtSignal() # Emitted whenever the global stack is changed (ie: when changing between printers, changing a global profile, but not when changing a value)
|
||||
activeMaterialChanged = pyqtSignal()
|
||||
activeVariantChanged = pyqtSignal()
|
||||
|
@ -166,7 +173,7 @@ class MachineManager(QObject):
|
|||
if not self._global_container_stack:
|
||||
return
|
||||
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(type="variant", definition=self._global_container_stack.getBottom().getId(), name=hotend_id)
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "variant", definition = self._global_container_stack.definition.getId(), name = hotend_id)
|
||||
if containers: # New material ID is known
|
||||
extruder_manager = ExtruderManager.getInstance()
|
||||
machine_id = self.activeMachineId
|
||||
|
@ -178,10 +185,10 @@ class MachineManager(QObject):
|
|||
break
|
||||
if matching_extruder and matching_extruder.variant.getName() != hotend_id:
|
||||
# Save the material that needs to be changed. Multiple changes will be handled by the callback.
|
||||
self._auto_hotends_changed[str(index)] = containers[0].getId()
|
||||
self._auto_hotends_changed[str(index)] = containers[0]["id"]
|
||||
self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback)
|
||||
else:
|
||||
Logger.log("w", "No variant found for printer definition %s with id %s" % (self._global_container_stack.getBottom().getId(), hotend_id))
|
||||
Logger.log("w", "No variant found for printer definition %s with id %s" % (self._global_container_stack.definition.getId(), hotend_id))
|
||||
|
||||
def _onMaterialIdChanged(self, index: Union[str, int], material_id: str):
|
||||
if not self._global_container_stack:
|
||||
|
@ -191,7 +198,7 @@ class MachineManager(QObject):
|
|||
if self._global_container_stack.getMetaDataEntry("has_machine_materials", False):
|
||||
definition_id = self.activeQualityDefinitionId
|
||||
extruder_manager = ExtruderManager.getInstance()
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(type = "material", definition = definition_id, GUID = material_id)
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "material", definition = definition_id, GUID = material_id)
|
||||
if containers: # New material ID is known
|
||||
extruders = list(extruder_manager.getMachineExtruders(self.activeMachineId))
|
||||
matching_extruder = None
|
||||
|
@ -202,15 +209,15 @@ class MachineManager(QObject):
|
|||
|
||||
if matching_extruder and matching_extruder.material.getMetaDataEntry("GUID") != material_id:
|
||||
# Save the material that needs to be changed. Multiple changes will be handled by the callback.
|
||||
if self._global_container_stack.getBottom().getMetaDataEntry("has_variants") and matching_extruder.variant:
|
||||
variant_id = self.getQualityVariantId(self._global_container_stack.getBottom(), matching_extruder.variant)
|
||||
if self._global_container_stack.definition.getMetaDataEntry("has_variants") and matching_extruder.variant:
|
||||
variant_id = self.getQualityVariantId(self._global_container_stack.definition, matching_extruder.variant)
|
||||
for container in containers:
|
||||
if container.getMetaDataEntry("variant") == variant_id:
|
||||
self._auto_materials_changed[str(index)] = container.getId()
|
||||
if container.get("variant") == variant_id:
|
||||
self._auto_materials_changed[str(index)] = container["id"]
|
||||
break
|
||||
else:
|
||||
# Just use the first result we found.
|
||||
self._auto_materials_changed[str(index)] = containers[0].getId()
|
||||
self._auto_materials_changed[str(index)] = containers[0]["id"]
|
||||
self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback)
|
||||
else:
|
||||
Logger.log("w", "No material definition found for printer definition %s and GUID %s" % (definition_id, material_id))
|
||||
|
@ -328,14 +335,24 @@ class MachineManager(QObject):
|
|||
# on _active_container_stack. If it changes, then the properties change.
|
||||
self.activeQualityChanged.emit()
|
||||
|
||||
def __onInstanceContainersChanged(self):
|
||||
def __emitChangedSignals(self):
|
||||
self.activeQualityChanged.emit()
|
||||
self.activeVariantChanged.emit()
|
||||
self.activeMaterialChanged.emit()
|
||||
self._updateStacksHaveErrors() # Prevents unwanted re-slices after changing machine
|
||||
self._error_check_timer.start()
|
||||
|
||||
def _onProfilesModelChanged(self, *args):
|
||||
self.__emitChangedSignals()
|
||||
|
||||
def _onInstanceContainersChanged(self, container):
|
||||
# This should not trigger the ProfilesModel to be created, or there will be an infinite recursion
|
||||
if not self._connected_to_profiles_model and ProfilesModel.hasInstance():
|
||||
# This triggers updating the qualityModel in SidebarSimple whenever ProfilesModel is updated
|
||||
Logger.log("d", "Connecting profiles model...")
|
||||
ProfilesModel.getInstance().itemsChanged.connect(self._onProfilesModelChanged)
|
||||
self._connected_to_profiles_model = True
|
||||
|
||||
self._instance_container_timer.start()
|
||||
|
||||
def _onPropertyChanged(self, key: str, property_name: str):
|
||||
|
@ -355,7 +372,7 @@ class MachineManager(QObject):
|
|||
if containers:
|
||||
Application.getInstance().setGlobalContainerStack(containers[0])
|
||||
|
||||
self.__onInstanceContainersChanged()
|
||||
self.__emitChangedSignals()
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
def addMachine(self, name: str, definition_id: str) -> None:
|
||||
|
@ -703,10 +720,7 @@ class MachineManager(QObject):
|
|||
## Check if a container is read_only
|
||||
@pyqtSlot(str, result = bool)
|
||||
def isReadOnly(self, container_id: str) -> bool:
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
|
||||
if not containers or not self._active_container_stack:
|
||||
return True
|
||||
return containers[0].isReadOnly()
|
||||
return ContainerRegistry.getInstance().isReadOnly(container_id)
|
||||
|
||||
## Copy the value of the setting of the current extruder to all other extruders as well as the global container.
|
||||
@pyqtSlot(str)
|
||||
|
@ -774,7 +788,7 @@ class MachineManager(QObject):
|
|||
if quality_type:
|
||||
candidate_quality = quality_manager.findQualityByQualityType(quality_type,
|
||||
quality_manager.getWholeMachineDefinition(global_stack.definition),
|
||||
[material_container])
|
||||
[material_container.getMetaData()])
|
||||
|
||||
if not candidate_quality or candidate_quality.getId() == self._empty_quality_changes_container:
|
||||
Logger.log("d", "Attempting to find fallback quality")
|
||||
|
@ -822,7 +836,7 @@ class MachineManager(QObject):
|
|||
preferred_material_name = None
|
||||
if old_material:
|
||||
preferred_material_name = old_material.getName()
|
||||
preferred_material_id = self._updateMaterialContainer(self._global_container_stack.getBottom(), self._global_container_stack, containers[0], preferred_material_name).id
|
||||
preferred_material_id = self._updateMaterialContainer(self._global_container_stack.definition, self._global_container_stack, containers[0], preferred_material_name).id
|
||||
self.setActiveMaterial(preferred_material_id)
|
||||
else:
|
||||
Logger.log("w", "While trying to set the active variant, no variant was found to replace.")
|
||||
|
@ -834,15 +848,15 @@ class MachineManager(QObject):
|
|||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self.blurSettings.emit()
|
||||
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(id = quality_id)
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = quality_id)
|
||||
if not containers or not self._global_container_stack:
|
||||
return
|
||||
|
||||
# Quality profile come in two flavours: type=quality and type=quality_changes
|
||||
# If we found a quality_changes profile then look up its parent quality profile.
|
||||
container_type = containers[0].getMetaDataEntry("type")
|
||||
quality_name = containers[0].getName()
|
||||
quality_type = containers[0].getMetaDataEntry("quality_type")
|
||||
container_type = containers[0].get("type")
|
||||
quality_name = containers[0]["name"]
|
||||
quality_type = containers[0].get("quality_type")
|
||||
|
||||
# Get quality container and optionally the quality_changes container.
|
||||
if container_type == "quality":
|
||||
|
@ -850,7 +864,7 @@ class MachineManager(QObject):
|
|||
elif container_type == "quality_changes":
|
||||
new_quality_settings_list = self._determineQualityAndQualityChangesForQualityChanges(quality_name)
|
||||
else:
|
||||
Logger.log("e", "Tried to set quality to a container that is not of the right type")
|
||||
Logger.log("e", "Tried to set quality to a container that is not of the right type: {container_id}".format(container_id = containers[0]["id"]))
|
||||
return
|
||||
|
||||
# Check if it was at all possible to find new settings
|
||||
|
@ -939,18 +953,18 @@ class MachineManager(QObject):
|
|||
if not global_container_stack:
|
||||
return []
|
||||
|
||||
global_machine_definition = quality_manager.getParentMachineDefinition(global_container_stack.getBottom())
|
||||
global_machine_definition = quality_manager.getParentMachineDefinition(global_container_stack.definition)
|
||||
extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
|
||||
|
||||
# find qualities for extruders
|
||||
for extruder_stack in extruder_stacks:
|
||||
material = extruder_stack.material
|
||||
material_metadata = extruder_stack.material.getMetaData()
|
||||
|
||||
# TODO: fix this
|
||||
if self._new_material_container and extruder_stack.getId() == self._active_container_stack.getId():
|
||||
material = self._new_material_container
|
||||
material_metadata = self._new_material_container.getMetaData()
|
||||
|
||||
quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material])
|
||||
quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material_metadata])
|
||||
|
||||
if not quality:
|
||||
# No quality profile is found for this quality type.
|
||||
|
@ -1000,12 +1014,6 @@ class MachineManager(QObject):
|
|||
Logger.log("e", "Could not find the global quality changes container with name %s", quality_changes_name)
|
||||
return None
|
||||
|
||||
material = global_container_stack.material
|
||||
|
||||
# find a quality type that matches both machine and materials
|
||||
if self._new_material_container and self._active_container_stack.getId() == global_container_stack.getId():
|
||||
material = self._new_material_container
|
||||
|
||||
# For the global stack, find a quality which matches the quality_type in
|
||||
# the quality changes profile and also satisfies any material constraints.
|
||||
quality_type = global_quality_changes.getMetaDataEntry("quality_type")
|
||||
|
@ -1025,12 +1033,12 @@ class MachineManager(QObject):
|
|||
if not quality_changes:
|
||||
quality_changes = self._empty_quality_changes_container
|
||||
|
||||
material = extruder_stack.material
|
||||
material_metadata = extruder_stack.material.getMetaData()
|
||||
|
||||
if self._new_material_container and self._active_container_stack.getId() == extruder_stack.getId():
|
||||
material = self._new_material_container
|
||||
material_metadata = self._new_material_container.getMetaData()
|
||||
|
||||
quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material])
|
||||
quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material_metadata])
|
||||
|
||||
if not quality:
|
||||
# No quality profile found for this quality type.
|
||||
|
@ -1043,7 +1051,7 @@ class MachineManager(QObject):
|
|||
})
|
||||
|
||||
# append the global quality changes
|
||||
global_quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material], global_quality = "True")
|
||||
global_quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, global_quality = "True")
|
||||
|
||||
# if there is not global quality but we're using a single extrusion machine, copy the quality of the first extruder - CURA-4482
|
||||
if not global_quality and len(extruder_stacks) == 1:
|
||||
|
@ -1097,18 +1105,14 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify = globalContainerChanged)
|
||||
def activeDefinitionId(self) -> str:
|
||||
if self._global_container_stack:
|
||||
definition = self._global_container_stack.getBottom()
|
||||
if definition:
|
||||
return definition.id
|
||||
return self._global_container_stack.definition.id
|
||||
|
||||
return ""
|
||||
|
||||
@pyqtProperty(str, notify=globalContainerChanged)
|
||||
def activeDefinitionName(self) -> str:
|
||||
if self._global_container_stack:
|
||||
definition = self._global_container_stack.getBottom()
|
||||
if definition:
|
||||
return definition.getName()
|
||||
return self._global_container_stack.definition.getName()
|
||||
|
||||
return ""
|
||||
|
||||
|
@ -1118,7 +1122,7 @@ class MachineManager(QObject):
|
|||
@pyqtProperty(str, notify = globalContainerChanged)
|
||||
def activeQualityDefinitionId(self) -> str:
|
||||
if self._global_container_stack:
|
||||
return self.getQualityDefinitionId(self._global_container_stack.getBottom())
|
||||
return self.getQualityDefinitionId(self._global_container_stack.definition)
|
||||
return ""
|
||||
|
||||
## Get the Definition ID to use to select quality profiles for machines of the specified definition
|
||||
|
@ -1136,7 +1140,7 @@ class MachineManager(QObject):
|
|||
if self._active_container_stack:
|
||||
variant = self._active_container_stack.variant
|
||||
if variant:
|
||||
return self.getQualityVariantId(self._global_container_stack.getBottom(), variant)
|
||||
return self.getQualityVariantId(self._global_container_stack.definition, variant)
|
||||
return ""
|
||||
|
||||
## Get the Variant ID to use to select quality profiles for variants of the specified definitions
|
||||
|
@ -1160,7 +1164,7 @@ class MachineManager(QObject):
|
|||
def activeDefinitionVariantsName(self) -> str:
|
||||
fallback_title = catalog.i18nc("@label", "Nozzle")
|
||||
if self._global_container_stack:
|
||||
return self._global_container_stack.getBottom().getMetaDataEntry("variants_name", fallback_title)
|
||||
return self._global_container_stack.definition.getMetaDataEntry("variants_name", fallback_title)
|
||||
|
||||
return fallback_title
|
||||
|
||||
|
@ -1169,7 +1173,7 @@ class MachineManager(QObject):
|
|||
container_registry = ContainerRegistry.getInstance()
|
||||
machine_stack = container_registry.findContainerStacks(id = machine_id)
|
||||
if machine_stack:
|
||||
new_name = container_registry.createUniqueName("machine", machine_stack[0].getName(), new_name, machine_stack[0].getBottom().getName())
|
||||
new_name = container_registry.createUniqueName("machine", machine_stack[0].getName(), new_name, machine_stack[0].definition.getName())
|
||||
machine_stack[0].setName(new_name)
|
||||
self.globalContainerChanged.emit()
|
||||
|
||||
|
@ -1180,15 +1184,15 @@ class MachineManager(QObject):
|
|||
|
||||
# activate a new machine before removing a machine because this is safer
|
||||
if activate_new_machine:
|
||||
machine_stacks = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
|
||||
other_machine_stacks = [s for s in machine_stacks if s.getId() != machine_id]
|
||||
machine_stacks = ContainerRegistry.getInstance().findContainerStacksMetadata(type = "machine")
|
||||
other_machine_stacks = [s for s in machine_stacks if s["id"] != machine_id]
|
||||
if other_machine_stacks:
|
||||
self.setActiveMachine(other_machine_stacks[0].getId())
|
||||
self.setActiveMachine(other_machine_stacks[0]["id"])
|
||||
|
||||
ExtruderManager.getInstance().removeMachineExtruders(machine_id)
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(type = "user", machine = machine_id)
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id)
|
||||
for container in containers:
|
||||
ContainerRegistry.getInstance().removeContainer(container.getId())
|
||||
ContainerRegistry.getInstance().removeContainer(container["id"])
|
||||
ContainerRegistry.getInstance().removeContainer(machine_id)
|
||||
|
||||
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||
|
@ -1225,9 +1229,9 @@ class MachineManager(QObject):
|
|||
# \returns DefinitionID (string) if found, None otherwise
|
||||
@pyqtSlot(str, result = str)
|
||||
def getDefinitionByMachineId(self, machine_id: str) -> str:
|
||||
containers = ContainerRegistry.getInstance().findContainerStacks(id=machine_id)
|
||||
containers = ContainerRegistry.getInstance().findContainerStacks(id = machine_id)
|
||||
if containers:
|
||||
return containers[0].getBottom().getId()
|
||||
return containers[0].definition.getId()
|
||||
|
||||
@staticmethod
|
||||
def createMachineManager():
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Any, List
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry #To listen for changes to the materials.
|
||||
from UM.Settings.Models.InstanceContainersModel import InstanceContainersModel #We're extending this class.
|
||||
|
||||
|
@ -18,8 +19,19 @@ class MaterialsModel(InstanceContainersModel):
|
|||
# \param container The container whose metadata was changed.
|
||||
def _onContainerMetaDataChanged(self, container):
|
||||
if container.getMetaDataEntry("type") == "material": #Only need to update if a material was changed.
|
||||
self._update()
|
||||
self._container_change_timer.start()
|
||||
|
||||
def _onContainerChanged(self, container):
|
||||
if container.getMetaDataEntry("type", "") == "material":
|
||||
super()._onContainerChanged(container)
|
||||
|
||||
## Group brand together
|
||||
def _sortKey(self, item) -> List[Any]:
|
||||
result = []
|
||||
result.append(item["metadata"]["brand"])
|
||||
result.append(item["metadata"]["material"])
|
||||
result.append(item["metadata"]["name"])
|
||||
result.append(item["metadata"]["color_name"])
|
||||
result.append(item["metadata"]["id"])
|
||||
result.extend(super()._sortKey(item))
|
||||
return result
|
||||
|
|
|
@ -49,6 +49,10 @@ class ProfilesModel(InstanceContainersModel):
|
|||
ProfilesModel.__instance = cls()
|
||||
return ProfilesModel.__instance
|
||||
|
||||
@classmethod
|
||||
def hasInstance(cls) -> bool:
|
||||
return ProfilesModel.__instance is not None
|
||||
|
||||
__instance = None # type: "ProfilesModel"
|
||||
|
||||
## Fetch the list of containers to display.
|
||||
|
@ -57,8 +61,7 @@ class ProfilesModel(InstanceContainersModel):
|
|||
def _fetchInstanceContainers(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack is None:
|
||||
return []
|
||||
|
||||
return {}, {}
|
||||
global_stack_definition = global_container_stack.definition
|
||||
|
||||
# Get the list of extruders and place the selected extruder at the front of the list.
|
||||
|
@ -88,11 +91,10 @@ class ProfilesModel(InstanceContainersModel):
|
|||
not_supported_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0]
|
||||
result.append(not_supported_container)
|
||||
|
||||
return result
|
||||
return {item.getId():item for item in result}, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet.
|
||||
|
||||
## Re-computes the items in this model, and adds the layer height role.
|
||||
def _recomputeItems(self):
|
||||
|
||||
# Some globals that we can re-use.
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack is None:
|
||||
|
@ -112,8 +114,12 @@ class ProfilesModel(InstanceContainersModel):
|
|||
# active machine and material, and later yield the right ones.
|
||||
tmp_all_quality_items = OrderedDict()
|
||||
for item in super()._recomputeItems():
|
||||
profile = container_registry.findContainers(id=item["id"])
|
||||
quality_type = profile[0].getMetaDataEntry("quality_type") if profile else ""
|
||||
|
||||
profiles = container_registry.findContainersMetadata(id = item["id"])
|
||||
if not profiles or "quality_type" not in profiles[0]:
|
||||
quality_type = ""
|
||||
else:
|
||||
quality_type = profiles[0]["quality_type"]
|
||||
|
||||
if quality_type not in tmp_all_quality_items:
|
||||
tmp_all_quality_items[quality_type] = {"suitable_container": None, "all_containers": []}
|
||||
|
|
|
@ -18,7 +18,7 @@ class QualityAndUserProfilesModel(ProfilesModel):
|
|||
def _fetchInstanceContainers(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if not global_container_stack:
|
||||
return []
|
||||
return {}, {}
|
||||
|
||||
# Fetch the list of quality changes.
|
||||
quality_manager = QualityManager.getInstance()
|
||||
|
@ -35,10 +35,12 @@ class QualityAndUserProfilesModel(ProfilesModel):
|
|||
|
||||
# Filter the quality_change by the list of available quality_types
|
||||
quality_type_set = set([x.getMetaDataEntry("quality_type") for x in quality_list])
|
||||
filtered_quality_changes = [qc for qc in quality_changes_list if
|
||||
filtered_quality_changes = {qc.getId():qc for qc in quality_changes_list if
|
||||
qc.getMetaDataEntry("quality_type") in quality_type_set and
|
||||
qc.getMetaDataEntry("extruder") is not None and
|
||||
(qc.getMetaDataEntry("extruder") == active_extruder.definition.getMetaDataEntry("quality_definition") or
|
||||
qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())]
|
||||
qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())}
|
||||
|
||||
return quality_list + filtered_quality_changes
|
||||
result = filtered_quality_changes
|
||||
result.update({q.getId():q for q in quality_list})
|
||||
return result, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet.
|
|
@ -92,7 +92,6 @@ class QualitySettingsModel(UM.Qt.ListModel.ListModel):
|
|||
|
||||
items = []
|
||||
|
||||
settings = collections.OrderedDict()
|
||||
definition_container = Application.getInstance().getGlobalContainerStack().getBottom()
|
||||
|
||||
containers = self._container_registry.findInstanceContainers(id = self._quality_id)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from UM.Application import Application
|
||||
|
||||
from UM.Application import Application
|
||||
from cura.QualityManager import QualityManager
|
||||
from cura.Settings.ProfilesModel import ProfilesModel
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
@ -12,13 +12,23 @@ class UserProfilesModel(ProfilesModel):
|
|||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
#Need to connect to the metaDataChanged signal of the active materials.
|
||||
self.__current_extruders = []
|
||||
self.__current_materials = []
|
||||
|
||||
Application.getInstance().getExtruderManager().extrudersChanged.connect(self.__onExtrudersChanged)
|
||||
self.__onExtrudersChanged()
|
||||
self.__current_materials = [extruder.material for extruder in self.__current_extruders]
|
||||
for material in self.__current_materials:
|
||||
material.metaDataChanged.connect(self._onContainerChanged)
|
||||
|
||||
## Fetch the list of containers to display.
|
||||
#
|
||||
# See UM.Settings.Models.InstanceContainersModel._fetchInstanceContainers().
|
||||
def _fetchInstanceContainers(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if not global_container_stack:
|
||||
return []
|
||||
return {}, {}
|
||||
|
||||
# Fetch the list of quality changes.
|
||||
quality_manager = QualityManager.getInstance()
|
||||
|
@ -35,10 +45,36 @@ class UserProfilesModel(ProfilesModel):
|
|||
|
||||
# Filter the quality_change by the list of available quality_types
|
||||
quality_type_set = set([x.getMetaDataEntry("quality_type") for x in quality_list])
|
||||
filtered_quality_changes = [qc for qc in quality_changes_list if
|
||||
|
||||
filtered_quality_changes = {qc.getId():qc for qc in quality_changes_list if
|
||||
qc.getMetaDataEntry("quality_type") in quality_type_set and
|
||||
qc.getMetaDataEntry("extruder") is not None and
|
||||
(qc.getMetaDataEntry("extruder") == active_extruder.definition.getMetaDataEntry("quality_definition") or
|
||||
qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())]
|
||||
qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())}
|
||||
|
||||
return filtered_quality_changes
|
||||
return filtered_quality_changes, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet.
|
||||
|
||||
## Called when a container changed on an extruder stack.
|
||||
#
|
||||
# If it's the material we need to connect to the metaDataChanged signal of
|
||||
# that.
|
||||
def __onContainerChanged(self, new_container):
|
||||
#Careful not to update when a quality or quality changes profile changed!
|
||||
#If you then update you're going to have an infinite recursion because the update may change the container.
|
||||
if new_container.getMetaDataEntry("type") == "material":
|
||||
for material in self.__current_materials:
|
||||
material.metaDataChanged.disconnect(self._onContainerChanged)
|
||||
self.__current_materials = [extruder.material for extruder in self.__current_extruders]
|
||||
for material in self.__current_materials:
|
||||
material.metaDataChanged.connect(self._onContainerChanged)
|
||||
|
||||
## Called when the current set of extruders change.
|
||||
#
|
||||
# This makes sure that we are listening to the signal for when the
|
||||
# materials change.
|
||||
def __onExtrudersChanged(self):
|
||||
for extruder in self.__current_extruders:
|
||||
extruder.containersChanged.disconnect(self.__onContainerChanged)
|
||||
self.__current_extruders = Application.getInstance().getExtruderManager().getExtruderStacks()
|
||||
for extruder in self.__current_extruders:
|
||||
extruder.containersChanged.connect(self.__onContainerChanged)
|
|
@ -12,7 +12,8 @@ from UM.Platform import Platform
|
|||
#WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612
|
||||
if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX
|
||||
# For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
|
||||
if platform.linux_distribution()[0] in ("debian", "Ubuntu", "LinuxMint"): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
|
||||
linux_distro_name = platform.linux_distribution()[0].lower()
|
||||
if linux_distro_name in ("debian", "ubuntu", "linuxmint", "fedora"): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
|
||||
import ctypes
|
||||
from ctypes.util import find_library
|
||||
libGL = find_library("GL")
|
||||
|
|
|
@ -117,7 +117,7 @@ class ThreeMFReader(MeshReader):
|
|||
|
||||
# Get the definition & set it
|
||||
definition = QualityManager.getInstance().getParentMachineDefinition(global_container_stack.getBottom())
|
||||
um_node.callDecoration("getStack").getTop().setDefinition(definition)
|
||||
um_node.callDecoration("getStack").getTop().setDefinition(definition.getId())
|
||||
|
||||
setting_container = um_node.callDecoration("getStack").getTop()
|
||||
|
||||
|
|
|
@ -122,7 +122,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
Logger.log("w", "Could not find reader that was able to read the scene data for 3MF workspace")
|
||||
return WorkspaceReader.PreReadResult.failed
|
||||
|
||||
machine_name = ""
|
||||
machine_type = ""
|
||||
variant_type_name = i18n_catalog.i18nc("@label", "Nozzle")
|
||||
|
||||
|
@ -133,9 +132,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
# A few lists of containers in this project files.
|
||||
# When loading the global stack file, it may be associated with those containers, which may or may not be
|
||||
# in Cura already, so we need to provide them as alternative search lists.
|
||||
definition_container_list = []
|
||||
instance_container_list = []
|
||||
material_container_list = []
|
||||
|
||||
resolve_strategy_keys = ["machine", "material", "quality_changes"]
|
||||
self._resolve_strategies = {k: None for k in resolve_strategy_keys}
|
||||
|
@ -149,21 +146,20 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
|
||||
for each_definition_container_file in definition_container_files:
|
||||
container_id = self._stripFileToId(each_definition_container_file)
|
||||
definitions = self._container_registry.findDefinitionContainers(id=container_id)
|
||||
definitions = self._container_registry.findDefinitionContainersMetadata(id = container_id)
|
||||
|
||||
if not definitions:
|
||||
definition_container = DefinitionContainer(container_id)
|
||||
definition_container.deserialize(archive.open(each_definition_container_file).read().decode("utf-8"),
|
||||
file_name = each_definition_container_file)
|
||||
definition_container.deserialize(archive.open(each_definition_container_file).read().decode("utf-8"), file_name = each_definition_container_file)
|
||||
definition_container = definition_container.getMetaData()
|
||||
|
||||
else:
|
||||
definition_container = definitions[0]
|
||||
definition_container_list.append(definition_container)
|
||||
|
||||
definition_container_type = definition_container.getMetaDataEntry("type")
|
||||
definition_container_type = definition_container.get("type")
|
||||
if definition_container_type == "machine":
|
||||
machine_type = definition_container.getName()
|
||||
variant_type_name = definition_container.getMetaDataEntry("variants_name", variant_type_name)
|
||||
machine_type = definition_container["name"]
|
||||
variant_type_name = definition_container.get("variants_name", variant_type_name)
|
||||
|
||||
machine_definition_container_count += 1
|
||||
elif definition_container_type == "extruder":
|
||||
|
@ -187,11 +183,10 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
material_container_files = [name for name in cura_file_names if name.endswith(self._material_container_suffix)]
|
||||
for material_container_file in material_container_files:
|
||||
container_id = self._stripFileToId(material_container_file)
|
||||
materials = self._container_registry.findInstanceContainers(id=container_id)
|
||||
material_labels.append(self._getMaterialLabelFromSerialized(archive.open(material_container_file).read().decode("utf-8")))
|
||||
if materials:
|
||||
if self._container_registry.findContainersMetadata(id = container_id): #This material already exists.
|
||||
containers_found_dict["material"] = True
|
||||
if not materials[0].isReadOnly(): # Only non readonly materials can be in conflict
|
||||
if not self._container_registry.isReadOnly(container_id): # Only non readonly materials can be in conflict
|
||||
material_conflict = True
|
||||
Job.yieldThread()
|
||||
|
||||
|
@ -462,7 +457,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
extruder_stack_id_map = {} # new and old ExtruderStack IDs map
|
||||
if self._resolve_strategies["machine"] == "new":
|
||||
# We need a new id if the id already exists
|
||||
if self._container_registry.findContainerStacks(id = global_stack_id_original):
|
||||
if self._container_registry.findContainerStacksMetadata(id = global_stack_id_original):
|
||||
global_stack_id_new = self.getNewId(global_stack_id_original)
|
||||
global_stack_need_rename = True
|
||||
|
||||
|
@ -471,7 +466,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
for each_extruder_stack_file in extruder_stack_files:
|
||||
old_container_id = self._stripFileToId(each_extruder_stack_file)
|
||||
new_container_id = old_container_id
|
||||
if self._container_registry.findContainerStacks(id = old_container_id):
|
||||
if self._container_registry.findContainerStacksMetadata(id = old_container_id):
|
||||
# get a new name for this extruder
|
||||
new_container_id = self.getNewId(old_container_id)
|
||||
|
||||
|
@ -485,7 +480,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
|
||||
for definition_container_file in definition_container_files:
|
||||
container_id = self._stripFileToId(definition_container_file)
|
||||
definitions = self._container_registry.findDefinitionContainers(id = container_id)
|
||||
definitions = self._container_registry.findDefinitionContainersMetadata(id = container_id)
|
||||
if not definitions:
|
||||
definition_container = DefinitionContainer(container_id)
|
||||
definition_container.deserialize(archive.open(definition_container_file).read().decode("utf-8"),
|
||||
|
@ -512,7 +507,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
containers_to_add.append(material_container)
|
||||
else:
|
||||
material_container = materials[0]
|
||||
if not material_container.isReadOnly(): # Only create new materials if they are not read only.
|
||||
if not self._container_registry.isReadOnly(container_id): # Only create new materials if they are not read only.
|
||||
if self._resolve_strategies["material"] == "override":
|
||||
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
|
||||
file_name = material_container_file)
|
||||
|
@ -579,7 +574,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
if old_extruder_id:
|
||||
new_extruder_id = extruder_stack_id_map[old_extruder_id]
|
||||
new_id = new_extruder_id + "_current_settings"
|
||||
instance_container._id = new_id
|
||||
instance_container.setMetaDataEntry("id", new_id)
|
||||
instance_container.setName(new_id)
|
||||
instance_container.setMetaDataEntry("extruder", new_extruder_id)
|
||||
containers_to_add.append(instance_container)
|
||||
|
@ -588,7 +583,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
if machine_id:
|
||||
new_machine_id = self.getNewId(machine_id)
|
||||
new_id = new_machine_id + "_current_settings"
|
||||
instance_container._id = new_id
|
||||
instance_container.setMetadataEntry("id", new_id)
|
||||
instance_container.setName(new_id)
|
||||
instance_container.setMetaDataEntry("machine", new_machine_id)
|
||||
containers_to_add.append(instance_container)
|
||||
|
@ -644,7 +639,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
machine_extruder_count = definition_changes_extruder_count
|
||||
|
||||
else:
|
||||
existing_container = self._container_registry.findInstanceContainers(id = container_id)
|
||||
existing_container = self._container_registry.findInstanceContainersMetadata(id = container_id)
|
||||
if not existing_container:
|
||||
containers_to_add.append(instance_container)
|
||||
if global_stack_need_rename:
|
||||
|
@ -670,14 +665,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
|
||||
# HACK
|
||||
# There is a machine, check if it has authentication data. If so, keep that data.
|
||||
network_authentication_id = container_stacks[0].getMetaDataEntry("network_authentication_id")
|
||||
network_authentication_key = container_stacks[0].getMetaDataEntry("network_authentication_key")
|
||||
container_stacks[0].deserialize(archive.open(global_stack_file).read().decode("utf-8"),
|
||||
file_name = global_stack_file)
|
||||
network_authentication_id = stack.getMetaDataEntry("network_authentication_id")
|
||||
network_authentication_key = stack.getMetaDataEntry("network_authentication_key")
|
||||
stack.deserialize(archive.open(global_stack_file).read().decode("utf-8"), file_name = global_stack_file)
|
||||
if network_authentication_id:
|
||||
container_stacks[0].addMetaDataEntry("network_authentication_id", network_authentication_id)
|
||||
stack.addMetaDataEntry("network_authentication_id", network_authentication_id)
|
||||
if network_authentication_key:
|
||||
container_stacks[0].addMetaDataEntry("network_authentication_key", network_authentication_key)
|
||||
stack.addMetaDataEntry("network_authentication_key", network_authentication_key)
|
||||
|
||||
elif self._resolve_strategies["machine"] == "new":
|
||||
# create a new global stack
|
||||
|
|
|
@ -97,7 +97,7 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
|||
file_in_archive.compress_type = zipfile.ZIP_DEFLATED
|
||||
|
||||
# Do not include the network authentication keys
|
||||
ignore_keys = ["network_authentication_id", "network_authentication_key"]
|
||||
ignore_keys = {"network_authentication_id", "network_authentication_key"}
|
||||
serialized_data = container.serialize(ignored_metadata_keys = ignore_keys)
|
||||
|
||||
archive.writestr(file_in_archive, serialized_data)
|
||||
|
|
|
@ -86,6 +86,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
#
|
||||
self._global_container_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
Application.getInstance().getExtruderManager().activeExtruderChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
Application.getInstance().stacksValidationFinished.connect(self._onStackErrorCheckFinished)
|
||||
|
|
|
@ -131,12 +131,21 @@ class StartSliceJob(Job):
|
|||
Logger.log("w", "No objects suitable for one at a time found, or no correct order found")
|
||||
else:
|
||||
temp_list = []
|
||||
has_printing_mesh = False
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
|
||||
if not getattr(node, "_outside_buildarea", False) or getattr(node, "_non_printing_mesh", False):
|
||||
_non_printing_mesh = getattr(node, "_non_printing_mesh", False)
|
||||
if not getattr(node, "_outside_buildarea", False) or _non_printing_mesh:
|
||||
temp_list.append(node)
|
||||
if not _non_printing_mesh:
|
||||
has_printing_mesh = True
|
||||
Job.yieldThread()
|
||||
|
||||
#If the list doesn't have any model with suitable settings then clean the list
|
||||
# otherwise CuraEngine will crash
|
||||
if not has_printing_mesh:
|
||||
temp_list.clear()
|
||||
|
||||
if temp_list:
|
||||
object_groups.append(temp_list)
|
||||
|
||||
|
|
|
@ -74,12 +74,13 @@ class GCodeWriter(MeshWriter):
|
|||
## Create a new container with container 2 as base and container 1 written over it.
|
||||
def _createFlattenedContainerInstance(self, instance_container1, instance_container2):
|
||||
flat_container = InstanceContainer(instance_container2.getName())
|
||||
if instance_container1.getDefinition():
|
||||
flat_container.setDefinition(instance_container1.getDefinition())
|
||||
else:
|
||||
flat_container.setDefinition(instance_container2.getDefinition())
|
||||
|
||||
# The metadata includes id, name and definition
|
||||
flat_container.setMetaData(copy.deepcopy(instance_container2.getMetaData()))
|
||||
|
||||
if instance_container1.getDefinition():
|
||||
flat_container.setDefinition(instance_container1.getDefinition().getId())
|
||||
|
||||
for key in instance_container2.getAllKeys():
|
||||
flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value"))
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ class LegacyProfileReader(ProfileReader):
|
|||
Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?")
|
||||
return None
|
||||
current_printer_definition = global_container_stack.getBottom()
|
||||
profile.setDefinition(current_printer_definition)
|
||||
profile.setDefinition(current_printer_definition.getId())
|
||||
for new_setting in dict_of_doom["translation"]: # Evaluate all new settings that would get a value from the translations.
|
||||
old_setting_expression = dict_of_doom["translation"][new_setting]
|
||||
compiled = compile(old_setting_expression, new_setting, "eval")
|
||||
|
|
|
@ -22,7 +22,7 @@ Cura.MachineAction
|
|||
onModelChanged:
|
||||
{
|
||||
var extruderCount = base.extrudersModel.rowCount();
|
||||
base.extruderTabsCount = extruderCount > 1 ? extruderCount : 0;
|
||||
base.extruderTabsCount = extruderCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,6 @@ Cura.MachineAction
|
|||
|
||||
UM.TooltipArea
|
||||
{
|
||||
visible: manager.definedExtruderCount > 1
|
||||
height: childrenRect.height
|
||||
width: childrenRect.width
|
||||
text: machineExtruderCountProvider.properties.description
|
||||
|
@ -291,15 +290,6 @@ Cura.MachineAction
|
|||
property var afterOnEditingFinished: manager.updateMaterialForDiameter
|
||||
property string label: catalog.i18nc("@label", "Material diameter")
|
||||
}
|
||||
Loader
|
||||
{
|
||||
id: nozzleSizeField
|
||||
visible: !Cura.MachineManager.hasVariants && machineExtruderCountProvider.properties.value == 1
|
||||
sourceComponent: numericTextFieldWithUnit
|
||||
property string settingKey: "machine_nozzle_size"
|
||||
property string label: catalog.i18nc("@label", "Nozzle size")
|
||||
property string unit: catalog.i18nc("@label", "mm")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ import Cura 1.0 as Cura
|
|||
|
||||
Item
|
||||
{
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
// We show a nice overlay on the 3D viewer when the current output device has no monitor view
|
||||
Rectangle
|
||||
{
|
||||
|
@ -29,6 +32,9 @@ Item
|
|||
{
|
||||
id: monitorViewComponent
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
property real maximumWidth: parent.width
|
||||
property real maximumHeight: parent.height
|
||||
|
||||
|
|
|
@ -179,7 +179,7 @@ class VersionUpgrade30to31(VersionUpgrade):
|
|||
if not os.path.isfile(file_path):
|
||||
continue
|
||||
|
||||
parser = configparser.ConfigParser()
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
try:
|
||||
parser.read([file_path])
|
||||
except:
|
||||
|
@ -213,7 +213,7 @@ class VersionUpgrade30to31(VersionUpgrade):
|
|||
|
||||
new_filename = machine_name + "_" + "fdmextruder" + suffix
|
||||
|
||||
extruder_quality_changes_parser = configparser.ConfigParser()
|
||||
extruder_quality_changes_parser = configparser.ConfigParser(interpolation = None)
|
||||
extruder_quality_changes_parser.add_section("general")
|
||||
extruder_quality_changes_parser["general"]["version"] = str(2)
|
||||
extruder_quality_changes_parser["general"]["name"] = global_quality_changes["general"]["name"]
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
|
||||
import copy
|
||||
import io
|
||||
from typing import List, Optional
|
||||
import json #To parse the product-to-id mapping file.
|
||||
import os.path #To find the product-to-id mapping.
|
||||
import sys
|
||||
from typing import Any, Dict, List, Optional
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from UM.Resources import Resources
|
||||
|
@ -14,7 +17,6 @@ import UM.Dictionary
|
|||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
|
||||
## Handles serializing and deserializing material containers from an XML file
|
||||
class XmlMaterialProfile(InstanceContainer):
|
||||
CurrentFdmMaterialVersion = "1.3"
|
||||
|
@ -42,25 +44,18 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
def getInheritedFiles(self):
|
||||
return self._inherited_files
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
def setReadOnly(self, read_only):
|
||||
super().setReadOnly(read_only)
|
||||
|
||||
basefile = self.getMetaDataEntry("base_file", self._id) # if basefile is self.id, this is a basefile.
|
||||
for container in ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile):
|
||||
container._read_only = read_only # prevent loop instead of calling setReadOnly
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
# set the meta data for all machine / variant combinations
|
||||
def setMetaDataEntry(self, key, value):
|
||||
if self.isReadOnly():
|
||||
registry = ContainerRegistry.getInstance()
|
||||
if registry.isReadOnly(self.getId()):
|
||||
return
|
||||
|
||||
super().setMetaDataEntry(key, value)
|
||||
|
||||
basefile = self.getMetaDataEntry("base_file", self._id) #if basefile is self.id, this is a basefile.
|
||||
basefile = self.getMetaDataEntry("base_file", self.getId()) #if basefile is self.getId, this is a basefile.
|
||||
# Update all containers that share basefile
|
||||
for container in ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile):
|
||||
for container in registry.findInstanceContainers(base_file = basefile):
|
||||
if container.getMetaDataEntry(key, None) != value: # Prevent recursion
|
||||
container.setMetaDataEntry(key, value)
|
||||
|
||||
|
@ -68,7 +63,8 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
# without this function the setName would only set the name of the specific nozzle / material / machine combination container
|
||||
# The function is a bit tricky. It will not set the name of all containers if it has the correct name itself.
|
||||
def setName(self, new_name):
|
||||
if self.isReadOnly():
|
||||
registry = ContainerRegistry.getInstance()
|
||||
if registry.isReadOnly(self.getId()):
|
||||
return
|
||||
|
||||
# Not only is this faster, it also prevents a major loop that causes a stack overflow.
|
||||
|
@ -77,10 +73,10 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
|
||||
super().setName(new_name)
|
||||
|
||||
basefile = self.getMetaDataEntry("base_file", self._id) # if basefile is self.id, this is a basefile.
|
||||
basefile = self.getMetaDataEntry("base_file", self.getId()) # if basefile is self.getId, this is a basefile.
|
||||
# Update the basefile as well, this is actually what we're trying to do
|
||||
# Update all containers that share GUID and basefile
|
||||
containers = ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile)
|
||||
containers = registry.findInstanceContainers(base_file = basefile)
|
||||
for container in containers:
|
||||
container.setName(new_name)
|
||||
|
||||
|
@ -88,33 +84,20 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
def setDirty(self, dirty):
|
||||
super().setDirty(dirty)
|
||||
base_file = self.getMetaDataEntry("base_file", None)
|
||||
if base_file is not None and base_file != self._id:
|
||||
containers = ContainerRegistry.getInstance().findContainers(id=base_file)
|
||||
registry = ContainerRegistry.getInstance()
|
||||
if base_file is not None and base_file != self.getId() and not registry.isReadOnly(base_file):
|
||||
containers = registry.findContainers(id = base_file)
|
||||
if containers:
|
||||
base_container = containers[0]
|
||||
if not base_container.isReadOnly():
|
||||
base_container.setDirty(dirty)
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
# def setProperty(self, key, property_name, property_value, container = None):
|
||||
# if self.isReadOnly():
|
||||
# return
|
||||
#
|
||||
# super().setProperty(key, property_name, property_value)
|
||||
#
|
||||
# basefile = self.getMetaDataEntry("base_file", self._id) #if basefile is self.id, this is a basefile.
|
||||
# for container in UM.Settings.ContainerRegistry.ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile):
|
||||
# if not container.isReadOnly():
|
||||
# container.setDirty(True)
|
||||
containers[0].setDirty(dirty)
|
||||
|
||||
## Overridden from InstanceContainer
|
||||
# base file: common settings + supported machines
|
||||
# machine / variant combination: only changes for itself.
|
||||
def serialize(self, ignored_metadata_keys: Optional[List] = None):
|
||||
def serialize(self, ignored_metadata_keys: Optional[set] = None):
|
||||
registry = ContainerRegistry.getInstance()
|
||||
|
||||
base_file = self.getMetaDataEntry("base_file", "")
|
||||
if base_file and self.id != base_file:
|
||||
if base_file and self.getId() != base_file:
|
||||
# Since we create an instance of XmlMaterialProfile for each machine and nozzle in the profile,
|
||||
# we should only serialize the "base" material definition, since that can then take care of
|
||||
# serializing the machine/nozzle specific profiles.
|
||||
|
@ -132,8 +115,8 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
metadata = copy.deepcopy(self.getMetaData())
|
||||
# setting_version is derived from the "version" tag in the schema, so don't serialize it into a file
|
||||
if ignored_metadata_keys is None:
|
||||
ignored_metadata_keys = []
|
||||
ignored_metadata_keys = ignored_metadata_keys + ["setting_version"]
|
||||
ignored_metadata_keys = set()
|
||||
ignored_metadata_keys |= {"setting_version"}
|
||||
# remove the keys that we want to ignore in the metadata
|
||||
for key in ignored_metadata_keys:
|
||||
if key in metadata:
|
||||
|
@ -146,6 +129,9 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
metadata.pop("type", "")
|
||||
metadata.pop("base_file", "")
|
||||
metadata.pop("approximate_diameter", "")
|
||||
metadata.pop("id", "")
|
||||
metadata.pop("container_type", "")
|
||||
metadata.pop("name", "")
|
||||
|
||||
## Begin Name Block
|
||||
builder.start("name")
|
||||
|
@ -163,7 +149,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
builder.end("color")
|
||||
|
||||
builder.start("label")
|
||||
builder.data(self._name)
|
||||
builder.data(self.getName())
|
||||
builder.end("label")
|
||||
|
||||
builder.end("name")
|
||||
|
@ -195,16 +181,16 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
## Begin Settings Block
|
||||
builder.start("settings")
|
||||
|
||||
if self.getDefinition().id == "fdmprinter":
|
||||
if self.getDefinition().getId() == "fdmprinter":
|
||||
for instance in self.findInstances():
|
||||
self._addSettingElement(builder, instance)
|
||||
|
||||
machine_container_map = {}
|
||||
machine_nozzle_map = {}
|
||||
|
||||
all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID"), base_file = self._id)
|
||||
all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID"), base_file = self.getId())
|
||||
for container in all_containers:
|
||||
definition_id = container.getDefinition().id
|
||||
definition_id = container.getDefinition().getId()
|
||||
if definition_id == "fdmprinter":
|
||||
continue
|
||||
|
||||
|
@ -222,17 +208,16 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
machine_container_map[definition_id] = container
|
||||
|
||||
# Map machine human-readable names to IDs
|
||||
product_id_map = {}
|
||||
for container in registry.findDefinitionContainers(type = "machine"):
|
||||
product_id_map[container.getName()] = container.getId()
|
||||
product_id_map = self.getProductIdMap()
|
||||
|
||||
for definition_id, container in machine_container_map.items():
|
||||
definition = container.getDefinition()
|
||||
try:
|
||||
product = UM.Dictionary.findKey(product_id_map, definition_id)
|
||||
except ValueError:
|
||||
# An unknown product id; export it anyway
|
||||
|
||||
product = definition_id
|
||||
for product_name, product_id_list in product_id_map.items():
|
||||
if definition_id in product_id_list:
|
||||
product = product_name
|
||||
break
|
||||
|
||||
builder.start("machine")
|
||||
builder.start("machine_identifier", {
|
||||
|
@ -242,7 +227,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
builder.end("machine_identifier")
|
||||
|
||||
for instance in container.findInstances():
|
||||
if self.getDefinition().id == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value:
|
||||
if self.getDefinition().getId() == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value:
|
||||
# If the settings match that of the base profile, just skip since we inherit the base profile.
|
||||
continue
|
||||
|
||||
|
@ -250,11 +235,12 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
|
||||
# Find all hotend sub-profiles corresponding to this material and machine and add them to this profile.
|
||||
for hotend_id, hotend in machine_nozzle_map[definition_id].items():
|
||||
variant_containers = registry.findInstanceContainers(id = hotend.getMetaDataEntry("variant"))
|
||||
variant_containers = registry.findInstanceContainersMetadata(id = hotend.getMetaDataEntry("variant"))
|
||||
if not variant_containers:
|
||||
continue
|
||||
|
||||
builder.start("hotend", {"id": variant_containers[0].getName()})
|
||||
# The hotend identifier is not the containers name, but its "name".
|
||||
builder.start("hotend", {"id": variant_containers[0]["name"]})
|
||||
|
||||
# Compatible is a special case, as it's added as a meta data entry (instead of an instance).
|
||||
compatible = hotend.getMetaDataEntry("compatible")
|
||||
|
@ -397,15 +383,18 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
first.append(element)
|
||||
|
||||
def clearData(self):
|
||||
self._metadata = {}
|
||||
self._name = ""
|
||||
self._metadata = {
|
||||
"id": self.getId(),
|
||||
"name": ""
|
||||
}
|
||||
self._definition = None
|
||||
self._instances = {}
|
||||
self._read_only = False
|
||||
self._dirty = False
|
||||
self._path = ""
|
||||
|
||||
def getConfigurationTypeFromSerialized(self, serialized: str) -> Optional[str]:
|
||||
@classmethod
|
||||
def getConfigurationTypeFromSerialized(cls, serialized: str) -> Optional[str]:
|
||||
return "materials"
|
||||
|
||||
@classmethod
|
||||
|
@ -415,9 +404,9 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
version = XmlMaterialProfile.Version
|
||||
# get setting version
|
||||
if "version" in data.attrib:
|
||||
setting_version = XmlMaterialProfile.xmlVersionToSettingVersion(data.attrib["version"])
|
||||
setting_version = cls.xmlVersionToSettingVersion(data.attrib["version"])
|
||||
else:
|
||||
setting_version = XmlMaterialProfile.xmlVersionToSettingVersion("1.2")
|
||||
setting_version = cls.xmlVersionToSettingVersion("1.2")
|
||||
|
||||
return version * 1000000 + setting_version
|
||||
|
||||
|
@ -431,15 +420,18 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
try:
|
||||
data = ET.fromstring(serialized)
|
||||
except:
|
||||
Logger.logException("e", "An exception occured while parsing the material profile")
|
||||
Logger.logException("e", "An exception occurred while parsing the material profile")
|
||||
return
|
||||
|
||||
# Reset previous metadata
|
||||
old_id = self.getId()
|
||||
self.clearData() # Ensure any previous data is gone.
|
||||
meta_data = {}
|
||||
meta_data["type"] = "material"
|
||||
meta_data["base_file"] = self.id
|
||||
meta_data["base_file"] = self.getId()
|
||||
meta_data["status"] = "unknown" # TODO: Add material verification
|
||||
meta_data["id"] = old_id
|
||||
meta_data["container_type"] = XmlMaterialProfile
|
||||
|
||||
common_setting_values = {}
|
||||
|
||||
|
@ -454,8 +446,8 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
else:
|
||||
meta_data["setting_version"] = self.xmlVersionToSettingVersion("1.2") #1.2 and lower didn't have that version number there yet.
|
||||
|
||||
metadata = data.iterfind("./um:metadata/*", self.__namespaces)
|
||||
for entry in metadata:
|
||||
meta_data["name"] = "Unknown Material" #In case the name tag is missing.
|
||||
for entry in data.iterfind("./um:metadata/*", self.__namespaces):
|
||||
tag_name = _tag_without_namespace(entry)
|
||||
|
||||
if tag_name == "name":
|
||||
|
@ -465,9 +457,9 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
label = entry.find("./um:label", self.__namespaces)
|
||||
|
||||
if label is not None:
|
||||
self._name = label.text
|
||||
meta_data["name"] = label.text
|
||||
else:
|
||||
self._name = self._profile_name(material.text, color.text)
|
||||
meta_data["name"] = self._profile_name(material.text, color.text)
|
||||
meta_data["brand"] = brand.text
|
||||
meta_data["material"] = material.text
|
||||
meta_data["color_name"] = color.text
|
||||
|
@ -499,8 +491,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
|
||||
meta_data["approximate_diameter"] = str(round(float(property_values.get("diameter", 2.85)))) # In mm
|
||||
meta_data["properties"] = property_values
|
||||
|
||||
self.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0])
|
||||
meta_data["definition"] = "fdmprinter"
|
||||
|
||||
common_compatibility = True
|
||||
settings = data.iterfind("./um:settings/um:setting", self.__namespaces)
|
||||
|
@ -518,9 +509,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
self._dirty = False
|
||||
|
||||
# Map machine human-readable names to IDs
|
||||
product_id_map = {}
|
||||
for container in ContainerRegistry.getInstance().findDefinitionContainers(type = "machine"):
|
||||
product_id_map[container.getName()] = container.getId()
|
||||
product_id_map = self.getProductIdMap()
|
||||
|
||||
machines = data.iterfind("./um:settings/um:machine", self.__namespaces)
|
||||
for machine in machines:
|
||||
|
@ -542,42 +531,43 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
|
||||
identifiers = machine.iterfind("./um:machine_identifier", self.__namespaces)
|
||||
for identifier in identifiers:
|
||||
machine_id = product_id_map.get(identifier.get("product"), None)
|
||||
if machine_id is None:
|
||||
# Lets try again with some naive heuristics.
|
||||
machine_id = identifier.get("product").replace(" ", "").lower()
|
||||
machine_id_list = product_id_map.get(identifier.get("product"), [])
|
||||
if not machine_id_list:
|
||||
machine_id_list = self.getPossibleDefinitionIDsFromName(identifier.get("product"))
|
||||
|
||||
definitions = ContainerRegistry.getInstance().findDefinitionContainers(id = machine_id)
|
||||
for machine_id in machine_id_list:
|
||||
definitions = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = machine_id)
|
||||
if not definitions:
|
||||
# Logger.log("w", "No definition found for machine ID %s", machine_id)
|
||||
Logger.log("w", "No definition found for machine ID %s", machine_id)
|
||||
continue
|
||||
|
||||
Logger.log("d", "Found definition for machine ID %s", machine_id)
|
||||
definition = definitions[0]
|
||||
|
||||
machine_manufacturer = identifier.get("manufacturer", definition.getMetaDataEntry("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition.
|
||||
machine_manufacturer = identifier.get("manufacturer", definition.get("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition.
|
||||
|
||||
if machine_compatibility:
|
||||
new_material_id = self.id + "_" + machine_id
|
||||
new_material_id = self.getId() + "_" + machine_id
|
||||
|
||||
# The child or derived material container may already exist. This can happen when a material in a
|
||||
# project file and the a material in Cura have the same ID.
|
||||
# In the case if a derived material already exists, override that material container because if
|
||||
# the data in the parent material has been changed, the derived ones should be updated too.
|
||||
found_materials = ContainerRegistry.getInstance().findInstanceContainers(id = new_material_id)
|
||||
if ContainerRegistry.getInstance().isLoaded(new_material_id):
|
||||
new_material = ContainerRegistry.getInstance().findContainers(id = new_material_id)[0]
|
||||
is_new_material = False
|
||||
if found_materials:
|
||||
new_material = found_materials[0]
|
||||
else:
|
||||
new_material = XmlMaterialProfile(new_material_id)
|
||||
is_new_material = True
|
||||
|
||||
# Update the private directly, as we want to prevent the lookup that is done when using setName
|
||||
new_material._name = self.getName()
|
||||
new_material.setMetaData(copy.deepcopy(self.getMetaData()))
|
||||
new_material.setDefinition(definition)
|
||||
new_material.getMetaData()["id"] = new_material_id
|
||||
new_material.getMetaData()["name"] = self.getName()
|
||||
new_material.setDefinition(machine_id)
|
||||
# Don't use setMetadata, as that overrides it for all materials with same base file
|
||||
new_material.getMetaData()["compatible"] = machine_compatibility
|
||||
new_material.getMetaData()["machine_manufacturer"] = machine_manufacturer
|
||||
new_material.getMetaData()["definition"] = machine_id
|
||||
|
||||
new_material.setCachedValues(cached_machine_setting_properties)
|
||||
|
||||
|
@ -592,13 +582,12 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
if hotend_id is None:
|
||||
continue
|
||||
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainers(id = hotend_id)
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = hotend_id)
|
||||
if not variant_containers:
|
||||
# It is not really properly defined what "ID" is so also search for variants by name.
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainers(definition = definition.id, name = hotend_id)
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = hotend_id)
|
||||
|
||||
if not variant_containers:
|
||||
#Logger.log("d", "No variants found with ID or name %s for machine %s", hotend_id, definition.id)
|
||||
continue
|
||||
|
||||
hotend_compatibility = machine_compatibility
|
||||
|
@ -614,26 +603,26 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
else:
|
||||
Logger.log("d", "Unsupported material setting %s", key)
|
||||
|
||||
new_hotend_id = self.id + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
|
||||
new_hotend_id = self.getId() + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
|
||||
|
||||
# Same as machine compatibility, keep the derived material containers consistent with the parent
|
||||
# material
|
||||
found_materials = ContainerRegistry.getInstance().findInstanceContainers(id = new_hotend_id)
|
||||
if ContainerRegistry.getInstance().isLoaded(new_hotend_id):
|
||||
new_hotend_material = ContainerRegistry.getInstance().findContainers(id = new_hotend_id)[0]
|
||||
is_new_material = False
|
||||
if found_materials:
|
||||
new_hotend_material = found_materials[0]
|
||||
else:
|
||||
new_hotend_material = XmlMaterialProfile(new_hotend_id)
|
||||
is_new_material = True
|
||||
|
||||
# Update the private directly, as we want to prevent the lookup that is done when using setName
|
||||
new_hotend_material._name = self.getName()
|
||||
new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData()))
|
||||
new_hotend_material.setDefinition(definition)
|
||||
new_hotend_material.addMetaDataEntry("variant", variant_containers[0].id)
|
||||
new_hotend_material.getMetaData()["id"] = new_hotend_id
|
||||
new_hotend_material.getMetaData()["name"] = self.getName()
|
||||
new_hotend_material.getMetaData()["variant"] = variant_containers[0]["id"]
|
||||
new_hotend_material.setDefinition(machine_id)
|
||||
# Don't use setMetadata, as that overrides it for all materials with same base file
|
||||
new_hotend_material.getMetaData()["compatible"] = hotend_compatibility
|
||||
new_hotend_material.getMetaData()["machine_manufacturer"] = machine_manufacturer
|
||||
new_hotend_material.getMetaData()["definition"] = machine_id
|
||||
|
||||
cached_hotend_setting_properties = cached_machine_setting_properties.copy()
|
||||
cached_hotend_setting_properties.update(hotend_setting_values)
|
||||
|
@ -645,9 +634,182 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
if is_new_material:
|
||||
containers_to_add.append(new_hotend_material)
|
||||
|
||||
# there is only one ID for a machine. Once we have reached here, it means we have already found
|
||||
# a workable ID for that machine, so there is no need to continue
|
||||
break
|
||||
|
||||
for container_to_add in containers_to_add:
|
||||
ContainerRegistry.getInstance().addContainer(container_to_add)
|
||||
|
||||
@classmethod
|
||||
def deserializeMetadata(cls, serialized: str, container_id: str) -> List[Dict[str, Any]]:
|
||||
result_metadata = [] #All the metadata that we found except the base (because the base is returned).
|
||||
|
||||
#Update the serialized data to the latest version.
|
||||
serialized = cls._updateSerialized(serialized)
|
||||
|
||||
base_metadata = {
|
||||
"type": "material",
|
||||
"status": "unknown", #TODO: Add material verification.
|
||||
"container_type": XmlMaterialProfile,
|
||||
"id": container_id,
|
||||
"base_file": container_id
|
||||
}
|
||||
|
||||
try:
|
||||
data = ET.fromstring(serialized)
|
||||
except:
|
||||
Logger.logException("e", "An exception occurred while parsing the material profile")
|
||||
return []
|
||||
|
||||
#TODO: Implement the <inherits> tag. It's unused at the moment though.
|
||||
|
||||
if "version" in data.attrib:
|
||||
base_metadata["setting_version"] = cls.xmlVersionToSettingVersion(data.attrib["version"])
|
||||
else:
|
||||
base_metadata["setting_version"] = cls.xmlVersionToSettingVersion("1.2") #1.2 and lower didn't have that version number there yet.
|
||||
|
||||
for entry in data.iterfind("./um:metadata/*", cls.__namespaces):
|
||||
tag_name = _tag_without_namespace(entry)
|
||||
|
||||
if tag_name == "name":
|
||||
brand = entry.find("./um:brand", cls.__namespaces)
|
||||
material = entry.find("./um:material", cls.__namespaces)
|
||||
color = entry.find("./um:color", cls.__namespaces)
|
||||
label = entry.find("./um:label", cls.__namespaces)
|
||||
|
||||
if label is not None:
|
||||
base_metadata["name"] = label.text
|
||||
else:
|
||||
base_metadata["name"] = cls._profile_name(material.text, color.text)
|
||||
base_metadata["brand"] = brand.text
|
||||
base_metadata["material"] = material.text
|
||||
base_metadata["color_name"] = color.text
|
||||
continue
|
||||
|
||||
#Setting_version is derived from the "version" tag in the schema earlier, so don't set it here.
|
||||
if tag_name == "setting_version":
|
||||
continue
|
||||
|
||||
base_metadata[tag_name] = entry.text
|
||||
|
||||
if "description" not in base_metadata:
|
||||
base_metadata["description"] = ""
|
||||
if "adhesion_info" not in base_metadata:
|
||||
base_metadata["adhesion_info"] = ""
|
||||
|
||||
property_values = {}
|
||||
properties = data.iterfind("./um:properties/*", cls.__namespaces)
|
||||
for entry in properties:
|
||||
tag_name = _tag_without_namespace(entry)
|
||||
property_values[tag_name] = entry.text
|
||||
|
||||
base_metadata["approximate_diameter"] = str(round(float(property_values.get("diameter", 2.85)))) # In mm
|
||||
base_metadata["properties"] = property_values
|
||||
base_metadata["definition"] = "fdmprinter"
|
||||
|
||||
compatible_entries = data.iterfind("./um:settings/um:setting[@key='hardware compatible']", cls.__namespaces)
|
||||
try:
|
||||
common_compatibility = cls._parseCompatibleValue(next(compatible_entries).text)
|
||||
except StopIteration: #No 'hardware compatible' setting.
|
||||
common_compatibility = True
|
||||
base_metadata["compatible"] = common_compatibility
|
||||
result_metadata.append(base_metadata)
|
||||
|
||||
# Map machine human-readable names to IDs
|
||||
product_id_map = cls.getProductIdMap()
|
||||
|
||||
for machine in data.iterfind("./um:settings/um:machine", cls.__namespaces):
|
||||
machine_compatibility = common_compatibility
|
||||
for entry in machine.iterfind("./um:setting", cls.__namespaces):
|
||||
key = entry.get("key")
|
||||
if key == "hardware compatible":
|
||||
machine_compatibility = cls._parseCompatibleValue(entry.text)
|
||||
|
||||
for identifier in machine.iterfind("./um:machine_identifier", cls.__namespaces):
|
||||
machine_id_list = product_id_map.get(identifier.get("product"), [])
|
||||
if not machine_id_list:
|
||||
machine_id_list = cls.getPossibleDefinitionIDsFromName(identifier.get("product"))
|
||||
|
||||
for machine_id in machine_id_list:
|
||||
definition_metadata = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = machine_id)
|
||||
if not definition_metadata:
|
||||
Logger.log("w", "No definition found for machine ID %s", machine_id)
|
||||
continue
|
||||
|
||||
Logger.log("d", "Found def for machine [%s]", machine_id)
|
||||
definition_metadata = definition_metadata[0]
|
||||
|
||||
machine_manufacturer = identifier.get("manufacturer", definition_metadata.get("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition.
|
||||
|
||||
if machine_compatibility:
|
||||
new_material_id = container_id + "_" + machine_id
|
||||
|
||||
# The child or derived material container may already exist. This can happen when a material in a
|
||||
# project file and the a material in Cura have the same ID.
|
||||
# In the case if a derived material already exists, override that material container because if
|
||||
# the data in the parent material has been changed, the derived ones should be updated too.
|
||||
found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_material_id)
|
||||
if found_materials:
|
||||
new_material_metadata = found_materials[0]
|
||||
else:
|
||||
new_material_metadata = {}
|
||||
|
||||
new_material_metadata.update(base_metadata)
|
||||
new_material_metadata["id"] = new_material_id
|
||||
new_material_metadata["compatible"] = machine_compatibility
|
||||
new_material_metadata["machine_manufacturer"] = machine_manufacturer
|
||||
new_material_metadata["definition"] = machine_id
|
||||
|
||||
if len(found_materials) == 0: #This is a new material.
|
||||
result_metadata.append(new_material_metadata)
|
||||
|
||||
for hotend in machine.iterfind("./um:hotend", cls.__namespaces):
|
||||
hotend_id = hotend.get("id")
|
||||
if hotend_id is None:
|
||||
continue
|
||||
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = hotend_id)
|
||||
if not variant_containers:
|
||||
# It is not really properly defined what "ID" is so also search for variants by name.
|
||||
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = hotend_id)
|
||||
|
||||
hotend_compatibility = machine_compatibility
|
||||
for entry in hotend.iterfind("./um:setting", cls.__namespaces):
|
||||
key = entry.get("key")
|
||||
if key == "hardware compatible":
|
||||
hotend_compatibility = cls._parseCompatibleValue(entry.text)
|
||||
|
||||
new_hotend_id = container_id + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
|
||||
|
||||
# Same as machine compatibility, keep the derived material containers consistent with the parent
|
||||
# material
|
||||
found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_hotend_id)
|
||||
if found_materials:
|
||||
new_hotend_material_metadata = found_materials[0]
|
||||
else:
|
||||
new_hotend_material_metadata = {}
|
||||
|
||||
new_hotend_material_metadata.update(base_metadata)
|
||||
if variant_containers:
|
||||
new_hotend_material_metadata["variant"] = variant_containers[0]["id"]
|
||||
else:
|
||||
new_hotend_material_metadata["variant"] = hotend_id
|
||||
_with_missing_variants.append(new_hotend_material_metadata)
|
||||
new_hotend_material_metadata["compatible"] = hotend_compatibility
|
||||
new_hotend_material_metadata["machine_manufacturer"] = machine_manufacturer
|
||||
new_hotend_material_metadata["id"] = new_hotend_id
|
||||
new_hotend_material_metadata["definition"] = machine_id
|
||||
|
||||
if len(found_materials) == 0:
|
||||
result_metadata.append(new_hotend_material_metadata)
|
||||
|
||||
# there is only one ID for a machine. Once we have reached here, it means we have already found
|
||||
# a workable ID for that machine, so there is no need to continue
|
||||
break
|
||||
|
||||
return result_metadata
|
||||
|
||||
def _addSettingElement(self, builder, instance):
|
||||
try:
|
||||
key = UM.Dictionary.findKey(self.__material_settings_setting_map, instance.definition.key)
|
||||
|
@ -658,16 +820,58 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
builder.data(str(instance.value))
|
||||
builder.end("setting")
|
||||
|
||||
def _profile_name(self, material_name, color_name):
|
||||
@classmethod
|
||||
def _profile_name(cls, material_name, color_name):
|
||||
if color_name != "Generic":
|
||||
return "%s %s" % (color_name, material_name)
|
||||
else:
|
||||
return material_name
|
||||
|
||||
@classmethod
|
||||
def getPossibleDefinitionIDsFromName(cls, name):
|
||||
name_parts = name.lower().split(" ")
|
||||
merged_name_parts = []
|
||||
for part in name_parts:
|
||||
if len(part) == 0:
|
||||
continue
|
||||
if len(merged_name_parts) == 0:
|
||||
merged_name_parts.append(part)
|
||||
continue
|
||||
if part.isdigit():
|
||||
# for names with digit(s) such as Ultimaker 3 Extended, we generate an ID like
|
||||
# "ultimaker3_extended", ignoring the space between "Ultimaker" and "3".
|
||||
merged_name_parts[-1] = merged_name_parts[-1] + part
|
||||
else:
|
||||
merged_name_parts.append(part)
|
||||
|
||||
id_list = [name.lower().replace(" ", ""), # simply removing all spaces
|
||||
name.lower().replace(" ", "_"), # simply replacing all spaces with underscores
|
||||
"_".join(merged_name_parts),
|
||||
]
|
||||
|
||||
return id_list
|
||||
|
||||
## Gets a mapping from product names in the XML files to their definition
|
||||
# IDs.
|
||||
#
|
||||
# This loads the mapping from a file.
|
||||
@classmethod
|
||||
def getProductIdMap(cls) -> Dict[str, List[str]]:
|
||||
product_to_id_file = os.path.join(os.path.dirname(sys.modules[cls.__module__].__file__), "product_to_id.json")
|
||||
with open(product_to_id_file) as f:
|
||||
product_to_id_map = json.load(f)
|
||||
product_to_id_map = {key: [value] for key, value in product_to_id_map.items()}
|
||||
return product_to_id_map
|
||||
|
||||
## Parse the value of the "material compatible" property.
|
||||
def _parseCompatibleValue(self, value: str):
|
||||
@classmethod
|
||||
def _parseCompatibleValue(cls, value: str):
|
||||
return value in {"yes", "unknown"}
|
||||
|
||||
## Small string representation for debugging.
|
||||
def __str__(self):
|
||||
return "<XmlMaterialProfile '{my_id}' ('{name}') from base file '{base_file}'>".format(my_id = self.getId(), name = self.getName(), base_file = self.getMetaDataEntry("base_file"))
|
||||
|
||||
# Map XML file setting names to internal names
|
||||
__material_settings_setting_map = {
|
||||
"print temperature": "default_material_print_temperature",
|
||||
|
@ -717,3 +921,21 @@ def _indent(elem, level = 0):
|
|||
# before the last }
|
||||
def _tag_without_namespace(element):
|
||||
return element.tag[element.tag.rfind("}") + 1:]
|
||||
|
||||
#While loading XML profiles, some of these profiles don't know what variant
|
||||
#they belong to. We'd like to search by the machine ID and the variant's
|
||||
#name, but we don't know the variant's ID. Not all variants have been loaded
|
||||
#yet so we can't run a filter on the name and machine. The ID is unknown
|
||||
#so we can't lazily load the variant either. So we have to wait until all
|
||||
#the rest is loaded properly and then assign the correct variant to the
|
||||
#material files that were missing it.
|
||||
_with_missing_variants = []
|
||||
def _fillMissingVariants():
|
||||
registry = ContainerRegistry.getInstance()
|
||||
for variant_metadata in _with_missing_variants:
|
||||
variants = registry.findContainersMetadata(definition = variant_metadata["definition"], name = variant_metadata["variant"])
|
||||
if not variants:
|
||||
Logger.log("w", "Could not find variant for variant-specific material {material_id}.".format(material_id = variant_metadata["id"]))
|
||||
continue
|
||||
variant_metadata["variant"] = variants[0]["id"]
|
||||
ContainerRegistry.allMetadataLoaded.connect(_fillMissingVariants)
|
||||
|
|
12
plugins/XmlMaterialProfile/product_to_id.json
Normal file
12
plugins/XmlMaterialProfile/product_to_id.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"Ultimaker 2": "ultimaker2",
|
||||
"Ultimaker 2 Extended": "ultimaker2_extended",
|
||||
"Ultimaker 2 Extended+": "ultimaker2_extended_plus",
|
||||
"Ultimaker 2 Go": "ultimaker2_go",
|
||||
"Ultimaker 2+": "ultimaker2_plus",
|
||||
"Ultimaker 3": "ultimaker3",
|
||||
"Ultimaker 3 Extended": "ultimaker3_extended",
|
||||
"Ultimaker Original": "ultimaker_original",
|
||||
"Ultimaker Original+": "ultimaker_original_plus",
|
||||
"IMADE3D JellyBOX": "imade3d_jellybox"
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "101Hero",
|
||||
"version": 2,
|
||||
"name": "101Hero",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "3Dator",
|
||||
"version": 2,
|
||||
"name": "3Dator",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "PRi3",
|
||||
"name": "ABAX PRi3",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "PRi5",
|
||||
"name": "ABAX PRi5",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "Titan",
|
||||
"name": "ABAX Titan",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "alya3dp",
|
||||
"name": "ALYA",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "bfb",
|
||||
"name": "BFB",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "bq_hephestos",
|
||||
"name": "BQ Prusa i3 Hephestos",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "bq_hephestos_2",
|
||||
"version": 2,
|
||||
"name": "BQ Hephestos 2",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "bq_hephestos_xl",
|
||||
"version": 2,
|
||||
"name": "BQ Prusa i3 Hephestos XL",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "bq_witbox",
|
||||
"version": 2,
|
||||
"name": "BQ Witbox",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "bq_witbox_2",
|
||||
"version": 2,
|
||||
"name": "BQ Witbox 2",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "cartesio",
|
||||
"name": "Cartesio",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "creality_cr10",
|
||||
"name": "Creality CR-10",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "creality_cr10s4",
|
||||
"name": "Creality CR-10 S4",
|
||||
"version": 2,
|
||||
"inherits": "creality_cr10",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "creality_cr10s5",
|
||||
"name": "Creality CR-10 S5",
|
||||
"version": 2,
|
||||
"inherits": "creality_cr10",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "custom",
|
||||
"version": 2,
|
||||
"name": "Custom FDM printer",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "Dagoma_discoeasy200",
|
||||
"name": "Dagoma DiscoEasy200",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "Delta_Go",
|
||||
"name": "Delta Go",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "deltabot",
|
||||
"name": "DeltaBot",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "easyarts_ares",
|
||||
"name": "EasyArts Ares",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "fabtotum",
|
||||
"version": 2,
|
||||
"name": "FABtotum Personal Fabricator",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "fdmextruder",
|
||||
"name": "Extruder",
|
||||
"version": 2,
|
||||
"metadata":
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "fdmprinter",
|
||||
"name": "FDM Printer Base Description",
|
||||
"version": 2,
|
||||
"metadata":
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "grr_neo",
|
||||
"version": 2,
|
||||
"name": "German RepRap Neo",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "BEEVERYCREATIVE-helloBEEprusa",
|
||||
"version": 2,
|
||||
"name": "Hello BEE Prusa",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "imade3d_jellybox",
|
||||
"version": 2,
|
||||
"name": "IMADE3D JellyBOX",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "innovo-inventor",
|
||||
"version": 2,
|
||||
"name": "Innovo INVENTOR",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "julia",
|
||||
"name": "Julia",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "kemiq_q2_beta",
|
||||
"version": 2,
|
||||
"name": "Kemiq Q2 Beta",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "kemiq_q2_gama",
|
||||
"version": 2,
|
||||
"name": "Kemiq Q2 Gama",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "kossel_mini",
|
||||
"version": 2,
|
||||
"name": "Kossel Mini",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "kossel_pro",
|
||||
"version": 2,
|
||||
"name": "Kossel Pro",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "kupido",
|
||||
"name": "Kupido",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "m180",
|
||||
"version": 2,
|
||||
"name": "Malyan M180",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "makeR_pegasus",
|
||||
"version": 2,
|
||||
"name": "makeR Pegasus",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "makeR_prusa_tairona_i3",
|
||||
"version": 2,
|
||||
"name": "makeR Prusa Tairona i3",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "makeit_pro_l",
|
||||
"version": 2,
|
||||
"name": "MAKEiT Pro-L",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "makeit_pro_m",
|
||||
"version": 2,
|
||||
"name": "MAKEiT Pro-M",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "maker_starter",
|
||||
"version": 2,
|
||||
"name": "3DMaker Starter",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "makerbotreplicator",
|
||||
"name": "MakerBotReplicator",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "mankati_fullscale_xt_plus",
|
||||
"version": 2,
|
||||
"name": "Mankati Fullscale XT Plus",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "mendel90",
|
||||
"name": "Mendel90",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "ord",
|
||||
"name": "RoVa3D",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "peopoly_moai",
|
||||
"version": 2,
|
||||
"name": "Peopoly Moai",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "printrbot_play",
|
||||
"version": 2,
|
||||
"name": "Printrbot Play",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "printrbot_play_heated",
|
||||
"version": 2,
|
||||
"name": "Printrbot Play (Heated Bed)",
|
||||
"inherits": "printrbot_play",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "printrbot_simple",
|
||||
"version": 2,
|
||||
"name": "Printrbot Simple",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "printrbot_simple_extended",
|
||||
"version": 2,
|
||||
"name": "Printrbot Simple Metal Extended",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "prusa_i3",
|
||||
"version": 2,
|
||||
"name": "Prusa i3",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "prusa_i3_mk2",
|
||||
"version": 2,
|
||||
"name": "Prusa i3 Mk2",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "prusa_i3_xl",
|
||||
"version": 2,
|
||||
"name": "Prusa i3 xl",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "punchtec_connect_xl",
|
||||
"name": "Punchtec Connect XL",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "raise3D_N2_dual",
|
||||
"version": 2,
|
||||
"name": "Raise3D N2 Dual",
|
||||
"inherits": "fdmprinter",
|
||||
|
@ -55,9 +54,6 @@
|
|||
"machine_min_cool_heat_time_window": {
|
||||
"default_value": 3600
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "raise3D_N2_plus_dual",
|
||||
"version": 2,
|
||||
"name": "Raise3D N2 Plus Dual",
|
||||
"inherits": "fdmprinter",
|
||||
|
@ -55,9 +54,6 @@
|
|||
"machine_min_cool_heat_time_window": {
|
||||
"default_value": 3600
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "raise3D_N2_plus_single",
|
||||
"version": 2,
|
||||
"name": "Raise3D N2 Plus Single",
|
||||
"inherits": "fdmprinter",
|
||||
|
@ -50,9 +49,6 @@
|
|||
"machine_min_cool_heat_time_window": {
|
||||
"default_value": 3600
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "raise3D_N2_single",
|
||||
"version": 2,
|
||||
"name": "Raise3D N2 Single",
|
||||
"inherits": "fdmprinter",
|
||||
|
@ -50,9 +49,6 @@
|
|||
"machine_min_cool_heat_time_window": {
|
||||
"default_value": 3600
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "RF100",
|
||||
"version": 2,
|
||||
"name": "Renkforce RF100",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "rigid3d",
|
||||
"name": "Rigid3D",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "rigid3d_3rdgen",
|
||||
"name": "Rigid3D 3rdGen",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "rigid3d_hobby",
|
||||
"name": "Rigid3D Hobby",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "rigid3d_zero",
|
||||
"name": "Rigid3D Zero",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "rigid3d_zero2",
|
||||
"name": "Rigid3D Zero2",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "rigidbot",
|
||||
"version": 2,
|
||||
"name": "RigidBot",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "rigidbotbig",
|
||||
"version": 2,
|
||||
"name": "RigidBotBig",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "robo_3d_r1",
|
||||
"name": "Robo 3D R1",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "typeamachines",
|
||||
"version": 2,
|
||||
"name": "Type A Machines Series 1 2014",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"id": "tevo_tarantula",
|
||||
"version": 2,
|
||||
"name": "Tevo Tarantula",
|
||||
"inherits": "fdmprinter",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue