diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index c5d46f9a79..2527d761bd 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -5,7 +5,7 @@ import os import re import configparser -from typing import Any, cast, Dict, Optional, List, Union +from typing import Any, cast, Dict, Optional, List, Union, Tuple from PyQt5.QtWidgets import QMessageBox from UM.Decorators import override @@ -179,7 +179,7 @@ class CuraContainerRegistry(ContainerRegistry): """Imports a profile from a file :param file_name: The full path and filename of the profile to import. - :return: Dict with a 'status' key containing the string 'ok' or 'error', + :return: Dict with a 'status' key containing the string 'ok', 'warning' or 'error', and a 'message' key containing a message for the user. """ @@ -305,6 +305,7 @@ class CuraContainerRegistry(ContainerRegistry): # Import all profiles profile_ids_added = [] # type: List[str] + additional_message = None for profile_index, profile in enumerate(profile_or_list): if profile_index == 0: # This is assumed to be the global profile @@ -323,18 +324,26 @@ class CuraContainerRegistry(ContainerRegistry): else: # More extruders in the imported file than in the machine. continue # Delete the additional profiles. - result = self._configureProfile(profile, profile_id, new_name, expected_machine_definition) - if result is not None: - # Remove any profiles that did got added. - for profile_id in profile_ids_added: + configuration_successful, message = self._configureProfile(profile, profile_id, new_name, expected_machine_definition) + if configuration_successful: + additional_message = message + else: + # Remove any profiles that were added. + for profile_id in profile_ids_added + [profile.getId()]: self.removeContainer(profile_id) - + if not message: + message = "" return {"status": "error", "message": catalog.i18nc( - "@info:status Don't translate the XML tag !", - "Failed to import profile from {0}:", - file_name) + " " + result} + "@info:status Don't translate the XML tag !", + "Failed to import profile from {0}:", + file_name) + " " + message} profile_ids_added.append(profile.getId()) - return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())} + result_status = "ok" + success_message = catalog.i18nc("@info:status", "Successfully imported profile {0}.", profile_or_list[0].getName()) + if additional_message: + result_status = "warning" + success_message += additional_message + return {"status": result_status, "message": success_message} # This message is throw when the profile reader doesn't find any profile in the file return {"status": "error", "message": catalog.i18nc("@info:status", "File {0} does not contain any valid profile.", file_name)} @@ -395,14 +404,18 @@ class CuraContainerRegistry(ContainerRegistry): return False return True - def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str, machine_definition_id: str) -> Optional[str]: + def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str, machine_definition_id: str) -> Tuple[bool, Optional[str]]: """Update an imported profile to match the current machine configuration. :param profile: The profile to configure. :param id_seed: The base ID for the profile. May be changed so it does not conflict with existing containers. :param new_name: The new name for the profile. - :return: None if configuring was successful or an error message if an error occurred. + :returns: tuple (configuration_successful, message) + WHERE + bool configuration_successful: Whether the process of configuring the profile was successful + optional str message: A message indicating the outcome of configuring the profile. If the configuration + is successful, this message can be None or contain a warning """ profile.setDirty(True) # Ensure the profiles are correctly saved @@ -423,26 +436,39 @@ class CuraContainerRegistry(ContainerRegistry): quality_type = profile.getMetaDataEntry("quality_type") if not quality_type: - return catalog.i18nc("@info:status", "Profile is missing a quality type.") + return False, catalog.i18nc("@info:status", "Profile is missing a quality type.") global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() - if global_stack is None: - return None + if not global_stack: + return False, catalog.i18nc("@info:status", "Global stack is missing.") + definition_id = ContainerTree.getInstance().machines[global_stack.definition.getId()].quality_definition profile.setDefinition(definition_id) + if not self.addContainer(profile): + return False, catalog.i18nc("@info:status", "Unable to add the profile.") + + # "not_supported" profiles can be imported. + if quality_type == empty_quality_container.getMetaDataEntry("quality_type"): + return True, None + # Check to make sure the imported profile actually makes sense in context of the current configuration. # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as # successfully imported but then fail to show up. - quality_group_dict = ContainerTree.getInstance().getCurrentQualityGroups() - # "not_supported" profiles can be imported. - if quality_type != empty_quality_container.getMetaDataEntry("quality_type") and quality_type not in quality_group_dict: - return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type) + available_quality_groups_dict = {name: quality_group for name, quality_group in ContainerTree.getInstance().getCurrentQualityGroups().items() if quality_group.is_available} + all_quality_groups_dict = ContainerTree.getInstance().getCurrentQualityGroups() - if not self.addContainer(profile): - return catalog.i18nc("@info:status", "Unable to add the profile.") + # If the quality type doesn't exist at all in the quality_groups of this machine, reject the profile + if quality_type not in all_quality_groups_dict: + return False, catalog.i18nc("@info:status", "Quality type '{0}' is not compatible with the current active machine definition '{1}'.", quality_type, definition_id) - return None + # If the quality_type exists in the quality_groups of this printer but it is not available with the current + # machine configuration (e.g. not available for the selected nozzles), accept it with a warning + if quality_type not in available_quality_groups_dict: + return True, "\n\n" + catalog.i18nc("@info:status", "Warning: The profile is not visible because its quality type '{0}' is not available for the current configuration. " + "Switch to a material/nozzle combination that can use this quality type.", quality_type) + + return True, None @override(ContainerRegistry) def saveDirtyContainers(self) -> None: diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index fdb961ad21..5ee6dc32ce 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -313,7 +313,7 @@ Item { messageDialog.icon = StandardIcon.Information; } - else if (result.status == "duplicate") + else if (result.status == "warning" || result.status == "duplicate") { messageDialog.icon = StandardIcon.Warning; }