mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-09 23:05:01 -06:00
Merge pull request #3579 from Ultimaker/feature_show_config_errors
Show configuration errors
This commit is contained in:
commit
3108741b54
14 changed files with 135 additions and 164 deletions
|
@ -18,7 +18,6 @@ from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, Qt, QUrl
|
|||
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox, QCheckBox, QPushButton
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
|
||||
from UM.Resources import Resources
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
|
@ -131,66 +130,13 @@ class CrashHandler:
|
|||
self._sendCrashReport()
|
||||
os._exit(1)
|
||||
|
||||
## Backup the current resource directories and create clean ones.
|
||||
def _backupAndStartClean(self):
|
||||
# backup the current cura directories and create clean ones
|
||||
from cura.CuraVersion import CuraVersion
|
||||
from UM.Resources import Resources
|
||||
# The early crash may happen before those information is set in Resources, so we need to set them here to
|
||||
# make sure that Resources can find the correct place.
|
||||
Resources.ApplicationIdentifier = "cura"
|
||||
Resources.ApplicationVersion = CuraVersion
|
||||
config_path = Resources.getConfigStoragePath()
|
||||
data_path = Resources.getDataStoragePath()
|
||||
cache_path = Resources.getCacheStoragePath()
|
||||
|
||||
folders_to_backup = []
|
||||
folders_to_remove = [] # only cache folder needs to be removed
|
||||
|
||||
folders_to_backup.append(config_path)
|
||||
if data_path != config_path:
|
||||
folders_to_backup.append(data_path)
|
||||
|
||||
# Only remove the cache folder if it's not the same as data or config
|
||||
if cache_path not in (config_path, data_path):
|
||||
folders_to_remove.append(cache_path)
|
||||
|
||||
for folder in folders_to_remove:
|
||||
shutil.rmtree(folder, ignore_errors = True)
|
||||
for folder in folders_to_backup:
|
||||
base_name = os.path.basename(folder)
|
||||
root_dir = os.path.dirname(folder)
|
||||
|
||||
import datetime
|
||||
date_now = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
idx = 0
|
||||
file_name = base_name + "_" + date_now
|
||||
zip_file_path = os.path.join(root_dir, file_name + ".zip")
|
||||
while os.path.exists(zip_file_path):
|
||||
idx += 1
|
||||
file_name = base_name + "_" + date_now + "_" + idx
|
||||
zip_file_path = os.path.join(root_dir, file_name + ".zip")
|
||||
try:
|
||||
# only create the zip backup when the folder exists
|
||||
if os.path.exists(folder):
|
||||
# remove the .zip extension because make_archive() adds it
|
||||
zip_file_path = zip_file_path[:-4]
|
||||
shutil.make_archive(zip_file_path, "zip", root_dir = root_dir, base_dir = base_name)
|
||||
|
||||
# remove the folder only when the backup is successful
|
||||
shutil.rmtree(folder, ignore_errors = True)
|
||||
|
||||
# create an empty folder so Resources will not try to copy the old ones
|
||||
os.makedirs(folder, 0o0755, exist_ok=True)
|
||||
|
||||
except Exception as e:
|
||||
Logger.logException("e", "Failed to backup [%s] to file [%s]", folder, zip_file_path)
|
||||
if not self.has_started:
|
||||
print("Failed to backup [%s] to file [%s]: %s", folder, zip_file_path, e)
|
||||
|
||||
Resources.factoryReset()
|
||||
self.early_crash_dialog.close()
|
||||
|
||||
def _showConfigurationFolder(self):
|
||||
path = Resources.getConfigStoragePath();
|
||||
path = Resources.getConfigStoragePath()
|
||||
QDesktopServices.openUrl(QUrl.fromLocalFile( path ))
|
||||
|
||||
def _showDetailedReport(self):
|
||||
|
|
|
@ -5,6 +5,7 @@ from typing import Optional
|
|||
|
||||
from collections import OrderedDict
|
||||
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
|
||||
|
@ -30,17 +31,20 @@ class ContainerNode:
|
|||
def getChildNode(self, child_key: str) -> Optional["ContainerNode"]:
|
||||
return self.children_map.get(child_key)
|
||||
|
||||
def getContainer(self) -> "InstanceContainer":
|
||||
def getContainer(self) -> Optional["InstanceContainer"]:
|
||||
if self.metadata is None:
|
||||
raise RuntimeError("Cannot get container for a ContainerNode without metadata")
|
||||
Logger.log("e", "Cannot get container for a ContainerNode without metadata.")
|
||||
return None
|
||||
|
||||
if self.container is None:
|
||||
container_id = self.metadata["id"]
|
||||
Logger.log("i", "Lazy-loading container [%s]", container_id)
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
container_list = ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
|
||||
if not container_list:
|
||||
raise RuntimeError("Failed to lazy-load container [%s], cannot find it" % container_id)
|
||||
Logger.log("e", "Failed to lazy-load container [{container_id}]. Cannot find it.".format(container_id = container_id))
|
||||
error_message = ConfigurationErrorMessage.getInstance()
|
||||
error_message.addFaultyContainers(container_id)
|
||||
return None
|
||||
self.container = container_list[0]
|
||||
|
||||
return self.container
|
||||
|
|
|
@ -9,6 +9,7 @@ from typing import Optional, TYPE_CHECKING
|
|||
from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.SettingFunction import SettingFunction
|
||||
|
@ -205,12 +206,10 @@ class MaterialManager(QObject):
|
|||
machine_node.children_map[variant_name] = MaterialNode()
|
||||
|
||||
variant_node = machine_node.children_map[variant_name]
|
||||
if root_material_id not in variant_node.material_map:
|
||||
variant_node.material_map[root_material_id] = MaterialNode(material_metadata)
|
||||
else:
|
||||
# Sanity check: make sure we don't have duplicated variant-specific materials for the same machine
|
||||
raise RuntimeError("Found duplicate variant name [%s] for machine [%s] in material [%s]" %
|
||||
(variant_name, definition, material_metadata["id"]))
|
||||
if root_material_id in variant_node.material_map: #We shouldn't have duplicated variant-specific materials for the same machine.
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(root_material_id)
|
||||
continue
|
||||
variant_node.material_map[root_material_id] = MaterialNode(material_metadata)
|
||||
|
||||
self.materialsUpdated.emit()
|
||||
|
||||
|
@ -423,7 +422,8 @@ class MaterialManager(QObject):
|
|||
return
|
||||
|
||||
material_group = self.getMaterialGroup(root_material_id)
|
||||
material_group.root_material_node.getContainer().setName(name)
|
||||
if material_group:
|
||||
material_group.root_material_node.getContainer().setName(name)
|
||||
|
||||
#
|
||||
# Removes the given material.
|
||||
|
@ -447,6 +447,8 @@ class MaterialManager(QObject):
|
|||
return None
|
||||
|
||||
base_container = material_group.root_material_node.getContainer()
|
||||
if not base_container:
|
||||
return None
|
||||
|
||||
# Ensure all settings are saved.
|
||||
self._application.saveSettings()
|
||||
|
@ -466,6 +468,8 @@ class MaterialManager(QObject):
|
|||
# Clone all of them.
|
||||
for node in material_group.derived_material_node_list:
|
||||
container_to_copy = node.getContainer()
|
||||
if not container_to_copy:
|
||||
continue
|
||||
# Create unique IDs for every clone.
|
||||
new_id = new_base_id
|
||||
if container_to_copy.getMetaDataEntry("definition") != "fdmprinter":
|
||||
|
|
|
@ -38,6 +38,9 @@ class QualityManagementModel(ListModel):
|
|||
Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
|
||||
|
||||
global_stack = self._machine_manager.activeMachine
|
||||
if not global_stack:
|
||||
self.setItems([])
|
||||
return
|
||||
|
||||
quality_group_dict = self._quality_manager.getQualityGroups(global_stack)
|
||||
quality_changes_group_dict = self._quality_manager.getQualityChangesGroups(global_stack)
|
||||
|
|
|
@ -90,7 +90,7 @@ class QualitySettingsModel(ListModel):
|
|||
quality_node = quality_group.nodes_for_extruders.get(str(self._selected_position))
|
||||
settings_keys = quality_group.getAllKeys()
|
||||
quality_containers = []
|
||||
if quality_node is not None:
|
||||
if quality_node is not None and quality_node.getContainer() is not None:
|
||||
quality_containers.append(quality_node.getContainer())
|
||||
|
||||
# Here, if the user has selected a quality changes, then "quality_changes_group" will not be None, and we fetch
|
||||
|
@ -100,13 +100,8 @@ class QualitySettingsModel(ListModel):
|
|||
quality_changes_node = quality_changes_group.node_for_global
|
||||
else:
|
||||
quality_changes_node = quality_changes_group.nodes_for_extruders.get(str(self._selected_position))
|
||||
if quality_changes_node is not None: # it can be None if number of extruders are changed during runtime
|
||||
try:
|
||||
quality_containers.insert(0, quality_changes_node.getContainer())
|
||||
except RuntimeError:
|
||||
# FIXME: This is to prevent incomplete update of QualityManager
|
||||
Logger.logException("d", "Failed to get container for quality changes node %s", quality_changes_node)
|
||||
return
|
||||
if quality_changes_node is not None and quality_changes_node.getContainer() is not None: # it can be None if number of extruders are changed during runtime
|
||||
quality_containers.insert(0, quality_changes_node.getContainer())
|
||||
settings_keys.update(quality_changes_group.getAllKeys())
|
||||
|
||||
# We iterate over all definitions instead of settings in a quality/qualtiy_changes group is because in the GUI,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
|
||||
from .QualityGroup import QualityGroup
|
||||
|
||||
|
@ -13,14 +14,14 @@ class QualityChangesGroup(QualityGroup):
|
|||
|
||||
def addNode(self, node: "QualityNode"):
|
||||
extruder_position = node.metadata.get("position")
|
||||
|
||||
if extruder_position is None and self.node_for_global is not None or extruder_position in self.nodes_for_extruders: #We would be overwriting another node.
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(node.metadata["id"])
|
||||
return
|
||||
|
||||
if extruder_position is None: #Then we're a global quality changes profile.
|
||||
if self.node_for_global is not None:
|
||||
raise RuntimeError("{group} tries to overwrite the existing node_for_global {original_global} with {new_global}".format(group = self, original_global = self.node_for_global, new_global = node))
|
||||
self.node_for_global = node
|
||||
else: #This is an extruder's quality changes profile.
|
||||
if extruder_position in self.nodes_for_extruders:
|
||||
raise RuntimeError("%s tries to overwrite the existing nodes_for_extruders position [%s] %s with %s" %
|
||||
(self, extruder_position, self.node_for_global, node))
|
||||
self.nodes_for_extruders[extruder_position] = node
|
||||
|
||||
def __str__(self) -> str:
|
||||
|
|
|
@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Optional
|
|||
from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtSlot
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
from UM.Logger import Logger
|
||||
from UM.Util import parseBool
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
|
@ -84,7 +85,8 @@ class QualityManager(QObject):
|
|||
|
||||
# Sanity check: material+variant and is_global_quality cannot be present at the same time
|
||||
if is_global_quality and (root_material_id or variant_name):
|
||||
raise RuntimeError("Quality profile [%s] contains invalid data: it is a global quality but contains 'material' and 'nozzle' info." % metadata["id"])
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(metadata["id"])
|
||||
continue
|
||||
|
||||
if definition_id not in self._machine_variant_material_quality_type_to_quality_dict:
|
||||
self._machine_variant_material_quality_type_to_quality_dict[definition_id] = QualityNode()
|
||||
|
@ -393,6 +395,8 @@ class QualityManager(QObject):
|
|||
new_name = self._container_registry.uniqueName(quality_changes_name)
|
||||
for node in quality_changes_group.getAllNodes():
|
||||
container = node.getContainer()
|
||||
if not container:
|
||||
continue
|
||||
new_id = self._container_registry.uniqueName(container.getId())
|
||||
self._container_registry.addContainer(container.duplicate(new_id, new_name))
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ from enum import Enum
|
|||
from collections import OrderedDict
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Util import parseBool
|
||||
|
@ -78,8 +79,8 @@ class VariantManager:
|
|||
variant_dict = self._machine_to_variant_dict_map[variant_definition][variant_type]
|
||||
if variant_name in variant_dict:
|
||||
# ERROR: duplicated variant name.
|
||||
raise RuntimeError("Found duplicated variant name [%s], type [%s] for machine [%s]" %
|
||||
(variant_name, variant_type, variant_definition))
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(variant_metadata["id"])
|
||||
continue #Then ignore this variant. This now chooses one of the two variants arbitrarily and deletes the other one! No guarantees!
|
||||
|
||||
variant_dict[variant_name] = ContainerNode(metadata = variant_metadata)
|
||||
|
||||
|
@ -88,12 +89,8 @@ class VariantManager:
|
|||
if variant_definition not in self._machine_to_buildplate_dict_map:
|
||||
self._machine_to_buildplate_dict_map[variant_definition] = OrderedDict()
|
||||
|
||||
variant_container = self._container_registry.findContainers(type = "variant", id = variant_metadata["id"])
|
||||
if not variant_container:
|
||||
# ERROR: not variant container. This should never happen
|
||||
raise RuntimeError("Not variant found [%s], type [%s] for machine [%s]" %
|
||||
(variant_name, variant_type, variant_definition))
|
||||
buildplate_type = variant_container[0].getProperty("machine_buildplate_type", "value")
|
||||
variant_container = self._container_registry.findContainers(type = "variant", id = variant_metadata["id"])[0]
|
||||
buildplate_type = variant_container.getProperty("machine_buildplate_type", "value")
|
||||
if buildplate_type not in self._machine_to_buildplate_dict_map[variant_definition]:
|
||||
self._machine_to_variant_dict_map[variant_definition][buildplate_type] = dict()
|
||||
|
||||
|
|
|
@ -98,9 +98,10 @@ class ContainerManager(QObject):
|
|||
entry_value = root
|
||||
|
||||
container = material_group.root_material_node.getContainer()
|
||||
container.setMetaDataEntry(entry_name, entry_value)
|
||||
if sub_item_changed: #If it was only a sub-item that has changed then the setMetaDataEntry won't correctly notice that something changed, and we must manually signal that the metadata changed.
|
||||
container.metaDataChanged.emit(container)
|
||||
if container is not None:
|
||||
container.setMetaDataEntry(entry_name, entry_value)
|
||||
if sub_item_changed: #If it was only a sub-item that has changed then the setMetaDataEntry won't correctly notice that something changed, and we must manually signal that the metadata changed.
|
||||
container.metaDataChanged.emit(container)
|
||||
|
||||
## Set a setting property of the specified container.
|
||||
#
|
||||
|
@ -377,7 +378,8 @@ class ContainerManager(QObject):
|
|||
# NOTE: We only need to set the root material container because XmlMaterialProfile.setMetaDataEntry() will
|
||||
# take care of the derived containers too
|
||||
container = material_group.root_material_node.getContainer()
|
||||
container.setMetaDataEntry("GUID", new_guid)
|
||||
if container is not None:
|
||||
container.setMetaDataEntry("GUID", new_guid)
|
||||
|
||||
## Get the singleton instance for this class.
|
||||
@classmethod
|
||||
|
@ -466,5 +468,5 @@ class ContainerManager(QObject):
|
|||
if not path:
|
||||
return
|
||||
|
||||
container_list = [n.getContainer() for n in quality_changes_group.getAllNodes()]
|
||||
container_list = [n.getContainer() for n in quality_changes_group.getAllNodes() if n.getContainer() is not None]
|
||||
self._container_registry.exportQualityProfile(container_list, path, file_type)
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
|
||||
from typing import Optional
|
||||
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.Interfaces import DefinitionContainerInterface
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.SettingFunction import SettingFunction
|
||||
from UM.Util import parseBool
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
from .GlobalStack import GlobalStack
|
||||
|
@ -34,6 +33,7 @@ class CuraStackBuilder:
|
|||
|
||||
definitions = registry.findDefinitionContainers(id = definition_id)
|
||||
if not definitions:
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(definition_id)
|
||||
Logger.log("w", "Definition {definition} was not found!", definition = definition_id)
|
||||
return None
|
||||
|
||||
|
@ -44,6 +44,8 @@ class CuraStackBuilder:
|
|||
global_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.BUILD_PLATE)
|
||||
if global_variant_node:
|
||||
global_variant_container = global_variant_node.getContainer()
|
||||
if not global_variant_container:
|
||||
global_variant_container = application.empty_variant_container
|
||||
|
||||
# get variant container for extruders
|
||||
extruder_variant_container = application.empty_variant_container
|
||||
|
@ -51,6 +53,8 @@ class CuraStackBuilder:
|
|||
extruder_variant_name = None
|
||||
if extruder_variant_node:
|
||||
extruder_variant_container = extruder_variant_node.getContainer()
|
||||
if not extruder_variant_container:
|
||||
extruder_variant_container = application.empty_variant_container
|
||||
extruder_variant_name = extruder_variant_container.getName()
|
||||
|
||||
generated_name = registry.createUniqueName("machine", "", name, machine_definition.getName())
|
||||
|
@ -72,7 +76,7 @@ class CuraStackBuilder:
|
|||
# get material container for extruders
|
||||
material_container = application.empty_material_container
|
||||
material_node = material_manager.getDefaultMaterial(new_global_stack, extruder_variant_name)
|
||||
if material_node:
|
||||
if material_node and material_node.getContainer():
|
||||
material_container = material_node.getContainer()
|
||||
|
||||
# Create ExtruderStacks
|
||||
|
@ -84,8 +88,8 @@ class CuraStackBuilder:
|
|||
extruder_definition = registry.findDefinitionContainers(id = extruder_definition_id)[0]
|
||||
position_in_extruder_def = extruder_definition.getMetaDataEntry("position")
|
||||
if position_in_extruder_def != position:
|
||||
raise RuntimeError("Extruder position [%s] defined in extruder definition [%s] is not the same as in machine definition [%s] position [%s]" %
|
||||
(position_in_extruder_def, extruder_definition_id, definition_id, position))
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(extruder_definition_id)
|
||||
return None #Don't return any container stack then, not the rest of the extruders either.
|
||||
|
||||
new_extruder_id = registry.uniqueName(extruder_definition_id)
|
||||
new_extruder = cls.createExtruderStack(
|
||||
|
@ -100,6 +104,8 @@ class CuraStackBuilder:
|
|||
)
|
||||
new_extruder.setNextStack(new_global_stack)
|
||||
new_global_stack.addExtruder(new_extruder)
|
||||
|
||||
for new_extruder in new_global_stack.extruders.values(): #Only register the extruders if we're sure that all of them are correct.
|
||||
registry.addContainer(new_extruder)
|
||||
|
||||
preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type")
|
||||
|
@ -107,8 +113,10 @@ class CuraStackBuilder:
|
|||
quality_group = quality_group_dict.get(preferred_quality_type)
|
||||
|
||||
new_global_stack.quality = quality_group.node_for_global.getContainer()
|
||||
if not new_global_stack.quality:
|
||||
new_global_stack.quality = application.empty_quality_container
|
||||
for position, extruder_stack in new_global_stack.extruders.items():
|
||||
if position in quality_group.nodes_for_extruders:
|
||||
if position in quality_group.nodes_for_extruders and quality_group.nodes_for_extruders[position].getContainer():
|
||||
extruder_stack.quality = quality_group.nodes_for_extruders[position].getContainer()
|
||||
else:
|
||||
extruder_stack.quality = application.empty_quality_container
|
||||
|
|
|
@ -366,7 +366,7 @@ class ExtruderManager(QObject):
|
|||
def getActiveExtruderStacks(self) -> List["ExtruderStack"]:
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if not global_stack:
|
||||
return None
|
||||
return []
|
||||
|
||||
result = []
|
||||
if global_stack.getId() in self._extruder_trains:
|
||||
|
|
|
@ -331,14 +331,16 @@ class MachineManager(QObject):
|
|||
container_registry = ContainerRegistry.getInstance()
|
||||
|
||||
containers = container_registry.findContainerStacks(id = stack_id)
|
||||
if containers:
|
||||
global_stack = containers[0]
|
||||
ExtruderManager.getInstance().setActiveExtruderIndex(0) # Switch to first extruder
|
||||
self._global_container_stack = global_stack
|
||||
Application.getInstance().setGlobalContainerStack(global_stack)
|
||||
ExtruderManager.getInstance()._globalContainerStackChanged()
|
||||
self._initMachineState(containers[0])
|
||||
self._onGlobalContainerChanged()
|
||||
if not containers:
|
||||
return
|
||||
|
||||
global_stack = containers[0]
|
||||
ExtruderManager.getInstance().setActiveExtruderIndex(0) # Switch to first extruder
|
||||
self._global_container_stack = global_stack
|
||||
Application.getInstance().setGlobalContainerStack(global_stack)
|
||||
ExtruderManager.getInstance()._globalContainerStackChanged()
|
||||
self._initMachineState(containers[0])
|
||||
self._onGlobalContainerChanged()
|
||||
|
||||
self.__emitChangedSignals()
|
||||
|
||||
|
@ -518,12 +520,11 @@ class MachineManager(QObject):
|
|||
result = {}
|
||||
|
||||
active_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
|
||||
if active_stacks is not None: # If we have extruder stacks
|
||||
for stack in active_stacks:
|
||||
material_container = stack.material
|
||||
if not material_container:
|
||||
continue
|
||||
result[stack.getId()] = material_container.getId()
|
||||
for stack in active_stacks:
|
||||
material_container = stack.material
|
||||
if not material_container:
|
||||
continue
|
||||
result[stack.getId()] = material_container.getId()
|
||||
|
||||
return result
|
||||
|
||||
|
@ -935,6 +936,8 @@ class MachineManager(QObject):
|
|||
self.activeQualityChanged.emit()
|
||||
|
||||
def _getContainerChangedSignals(self) -> List[Signal]:
|
||||
if self._global_container_stack is None:
|
||||
return []
|
||||
stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
|
||||
stacks.append(self._global_container_stack)
|
||||
return [ s.containersChanged for s in stacks ]
|
||||
|
@ -970,12 +973,11 @@ class MachineManager(QObject):
|
|||
result = {}
|
||||
|
||||
active_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
|
||||
if active_stacks is not None:
|
||||
for stack in active_stacks:
|
||||
variant_container = stack.variant
|
||||
position = stack.getMetaDataEntry("position")
|
||||
if variant_container and variant_container != self._empty_variant_container:
|
||||
result[position] = variant_container.getName()
|
||||
for stack in active_stacks:
|
||||
variant_container = stack.variant
|
||||
position = stack.getMetaDataEntry("position")
|
||||
if variant_container and variant_container != self._empty_variant_container:
|
||||
result[position] = variant_container.getName()
|
||||
|
||||
return result
|
||||
|
||||
|
@ -996,6 +998,12 @@ class MachineManager(QObject):
|
|||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
||||
def _setQualityGroup(self, quality_group, empty_quality_changes = True):
|
||||
if quality_group.node_for_global.getContainer() is None:
|
||||
return
|
||||
for node in quality_group.nodes_for_extruders.values():
|
||||
if node.getContainer() is None:
|
||||
return
|
||||
|
||||
self._current_quality_group = quality_group
|
||||
if empty_quality_changes:
|
||||
self._current_quality_changes_group = None
|
||||
|
@ -1015,6 +1023,8 @@ class MachineManager(QObject):
|
|||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
||||
def _setQualityChangesGroup(self, quality_changes_group):
|
||||
if self._global_container_stack is None:
|
||||
return #Can't change that.
|
||||
quality_type = quality_changes_group.quality_type
|
||||
# A custom quality can be created based on "not supported".
|
||||
# In that case, do not set quality containers to empty.
|
||||
|
@ -1025,12 +1035,11 @@ class MachineManager(QObject):
|
|||
quality_group = quality_group_dict[quality_type]
|
||||
|
||||
quality_changes_container = self._empty_quality_changes_container
|
||||
if quality_changes_group.node_for_global:
|
||||
quality_changes_container = quality_changes_group.node_for_global.getContainer()
|
||||
quality_container = self._empty_quality_container
|
||||
if quality_group is not None:
|
||||
if quality_group.node_for_global:
|
||||
quality_container = quality_group.node_for_global.getContainer()
|
||||
if quality_changes_group.node_for_global and quality_changes_group.node_for_global.getContainer():
|
||||
quality_changes_container = quality_changes_group.node_for_global.getContainer()
|
||||
if quality_group is not None and quality_group.node_for_global and quality_group.node_for_global.getContainer():
|
||||
quality_container = quality_group.node_for_global.getContainer()
|
||||
|
||||
self._global_container_stack.quality = quality_container
|
||||
self._global_container_stack.qualityChanges = quality_changes_container
|
||||
|
@ -1042,10 +1051,10 @@ class MachineManager(QObject):
|
|||
quality_node = quality_group.nodes_for_extruders.get(position)
|
||||
|
||||
quality_changes_container = self._empty_quality_changes_container
|
||||
if quality_changes_node:
|
||||
quality_changes_container = quality_changes_node.getContainer()
|
||||
quality_container = self._empty_quality_container
|
||||
if quality_node:
|
||||
if quality_changes_node and quality_changes_node.getContainer():
|
||||
quality_changes_container = quality_changes_node.getContainer()
|
||||
if quality_node and quality_node.getContainer():
|
||||
quality_container = quality_node.getContainer()
|
||||
|
||||
extruder.quality = quality_container
|
||||
|
@ -1057,14 +1066,18 @@ class MachineManager(QObject):
|
|||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
||||
def _setVariantNode(self, position, container_node):
|
||||
if container_node.getContainer() is None:
|
||||
return
|
||||
self._global_container_stack.extruders[position].variant = container_node.getContainer()
|
||||
self.activeVariantChanged.emit()
|
||||
|
||||
def _setGlobalVariant(self, container_node):
|
||||
self._global_container_stack.variant = container_node.getContainer()
|
||||
if not self._global_container_stack.variant:
|
||||
self._global_container_stack.variant = Application.getInstance().empty_variant_container
|
||||
|
||||
def _setMaterial(self, position, container_node = None):
|
||||
if container_node:
|
||||
if container_node and container_node.getContainer():
|
||||
self._global_container_stack.extruders[position].material = container_node.getContainer()
|
||||
root_material_id = container_node.metadata["base_file"]
|
||||
else:
|
||||
|
@ -1087,6 +1100,8 @@ class MachineManager(QObject):
|
|||
|
||||
## Update current quality type and machine after setting material
|
||||
def _updateQualityWithMaterial(self, *args):
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
Logger.log("i", "Updating quality/quality_changes due to material change")
|
||||
current_quality_type = None
|
||||
if self._current_quality_group:
|
||||
|
@ -1122,6 +1137,8 @@ class MachineManager(QObject):
|
|||
self._setQualityGroup(candidate_quality_groups[quality_type], empty_quality_changes = True)
|
||||
|
||||
def _updateMaterialWithVariant(self, position: Optional[str]):
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
if position is None:
|
||||
position_list = list(self._global_container_stack.extruders.keys())
|
||||
else:
|
||||
|
@ -1283,6 +1300,8 @@ class MachineManager(QObject):
|
|||
|
||||
@pyqtSlot(str)
|
||||
def setQualityGroupByQualityType(self, quality_type):
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
# Get all the quality groups for this global stack and filter out by quality_type
|
||||
quality_group_dict = self._quality_manager.getQualityGroups(self._global_container_stack)
|
||||
quality_group = quality_group_dict[quality_type]
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from configparser import ConfigParser
|
||||
import zipfile
|
||||
import os
|
||||
import threading
|
||||
from typing import List, Tuple
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
@ -160,7 +161,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
#
|
||||
# In old versions, extruder stack files have the same suffix as container stack files ".stack.cfg".
|
||||
#
|
||||
def _determineGlobalAndExtruderStackFiles(self, project_file_name, file_list):
|
||||
def _determineGlobalAndExtruderStackFiles(self, project_file_name: str, file_list: List[str]) -> Tuple[str, List[str]]:
|
||||
archive = zipfile.ZipFile(project_file_name, "r")
|
||||
|
||||
global_stack_file_list = [name for name in file_list if name.endswith(self._global_stack_suffix)]
|
||||
|
@ -191,8 +192,12 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
Logger.log("w", "Unknown container stack type '%s' from %s in %s",
|
||||
stack_type, file_name, project_file_name)
|
||||
|
||||
if len(global_stack_file_list) != 1:
|
||||
raise RuntimeError("More than one global stack file found: [%s]" % str(global_stack_file_list))
|
||||
if len(global_stack_file_list) > 1:
|
||||
Logger.log("e", "More than one global stack file found: [{file_list}]".format(file_list = global_stack_file_list))
|
||||
#But we can recover by just getting the first global stack file.
|
||||
if len(global_stack_file_list) == 0:
|
||||
Logger.log("e", "No global stack file found!")
|
||||
raise FileNotFoundError("No global stack file found!")
|
||||
|
||||
return global_stack_file_list[0], extruder_stack_file_list
|
||||
|
||||
|
@ -346,8 +351,11 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
self._machine_info.quality_changes_info = None
|
||||
|
||||
# Load ContainerStack files and ExtruderStack files
|
||||
global_stack_file, extruder_stack_files = self._determineGlobalAndExtruderStackFiles(
|
||||
file_name, cura_file_names)
|
||||
try:
|
||||
global_stack_file, extruder_stack_files = self._determineGlobalAndExtruderStackFiles(
|
||||
file_name, cura_file_names)
|
||||
except FileNotFoundError:
|
||||
return WorkspaceReader.PreReadResult.failed
|
||||
machine_conflict = False
|
||||
# Because there can be cases as follows:
|
||||
# - the global stack exists but some/all of the extruder stacks DON'T exist
|
||||
|
@ -549,28 +557,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
|
||||
return WorkspaceReader.PreReadResult.accepted
|
||||
|
||||
## Overrides an ExtruderStack in the given GlobalStack and returns the new ExtruderStack.
|
||||
def _overrideExtruderStack(self, global_stack, extruder_file_content, extruder_stack_file):
|
||||
# Get extruder position first
|
||||
extruder_config = ConfigParser(interpolation = None)
|
||||
extruder_config.read_string(extruder_file_content)
|
||||
if not extruder_config.has_option("metadata", "position"):
|
||||
msg = "Could not find 'metadata/position' in extruder stack file"
|
||||
Logger.log("e", "Could not find 'metadata/position' in extruder stack file")
|
||||
raise RuntimeError(msg)
|
||||
extruder_position = extruder_config.get("metadata", "position")
|
||||
try:
|
||||
extruder_stack = global_stack.extruders[extruder_position]
|
||||
except KeyError:
|
||||
Logger.log("w", "Could not find the matching extruder stack to override for position %s", extruder_position)
|
||||
return None
|
||||
|
||||
# Override the given extruder stack
|
||||
extruder_stack.deserialize(extruder_file_content, file_name = extruder_stack_file)
|
||||
|
||||
# return the new ExtruderStack
|
||||
return extruder_stack
|
||||
|
||||
## Read the project file
|
||||
# Add all the definitions / materials / quality changes that do not exist yet. Then it loads
|
||||
# all the stacks into the container registry. In some cases it will reuse the container for the global stack.
|
||||
|
@ -897,7 +883,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
variant_type = VariantType.BUILD_PLATE
|
||||
|
||||
node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
|
||||
if node is not None:
|
||||
if node is not None and node.getContainer() is not None:
|
||||
global_stack.variant = node.getContainer()
|
||||
|
||||
for position, extruder_stack in extruder_stack_dict.items():
|
||||
|
@ -913,7 +899,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
variant_type = VariantType.NOZZLE
|
||||
|
||||
node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
|
||||
if node is not None:
|
||||
if node is not None and node.getContainer() is not None:
|
||||
extruder_stack.variant = node.getContainer()
|
||||
|
||||
def _applyMaterials(self, global_stack, extruder_stack_dict):
|
||||
|
@ -939,7 +925,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
extruder_stack.variant.getName(),
|
||||
machine_material_diameter,
|
||||
root_material_id)
|
||||
if material_node is not None:
|
||||
if material_node is not None and material_node.getContainer() is not None:
|
||||
extruder_stack.material = material_node.getContainer()
|
||||
|
||||
def _applyChangesToMachine(self, global_stack, extruder_stack_dict):
|
||||
|
|
|
@ -71,12 +71,14 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
|
||||
# Update the root material container
|
||||
root_material_container = material_group.root_material_node.getContainer()
|
||||
root_material_container.setMetaDataEntry(key, value, apply_to_all = False)
|
||||
if root_material_container is not None:
|
||||
root_material_container.setMetaDataEntry(key, value, apply_to_all = False)
|
||||
|
||||
# Update all containers derived from it
|
||||
for node in material_group.derived_material_node_list:
|
||||
container = node.getContainer()
|
||||
container.setMetaDataEntry(key, value, apply_to_all = False)
|
||||
if container is not None:
|
||||
container.setMetaDataEntry(key, value, apply_to_all = False)
|
||||
|
||||
## Overridden from InstanceContainer, similar to setMetaDataEntry.
|
||||
# without this function the setName would only set the name of the specific nozzle / material / machine combination container
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue