diff --git a/cura/API/Backups.py b/cura/API/Backups.py index ba416bd870..a2423bd798 100644 --- a/cura/API/Backups.py +++ b/cura/API/Backups.py @@ -3,30 +3,26 @@ from cura.Backups.BackupsManager import BackupsManager +## The back-ups API provides a version-proof bridge between Cura's +# BackupManager and plug-ins that hook into it. +# +# Usage: +# ``from cura.API import CuraAPI +# api = CuraAPI() +# api.backups.createBackup() +# api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"})`` class Backups: - """ - The backups API provides a version-proof bridge between Cura's BackupManager and plugins that hook into it. - - Usage: - from cura.API import CuraAPI - api = CuraAPI() - api.backups.createBackup() - api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"}) - """ - manager = BackupsManager() # Re-used instance of the backups manager. + ## Create a new back-up using the BackupsManager. + # \return Tuple containing a ZIP file with the back-up data and a dict + # with metadata about the back-up. def createBackup(self) -> (bytes, dict): - """ - Create a new backup using the BackupsManager. - :return: Tuple containing a ZIP file with the backup data and a dict with meta data about the backup. - """ return self.manager.createBackup() + ## Restore a back-up using the BackupsManager. + # \param zip_file A ZIP file containing the actual back-up data. + # \param meta_data Some metadata needed for restoring a back-up, like the + # Cura version number. def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None: - """ - Restore a backup using the BackupManager. - :param zip_file: A ZIP file containing the actual backup data. - :param meta_data: Some meta data needed for restoring a backup, like the Cura version number. - """ return self.manager.restoreBackup(zip_file, meta_data) diff --git a/cura/API/__init__.py b/cura/API/__init__.py index 7dd5d8f79e..13f6722336 100644 --- a/cura/API/__init__.py +++ b/cura/API/__init__.py @@ -3,14 +3,13 @@ from UM.PluginRegistry import PluginRegistry from cura.API.Backups import Backups - +## The official Cura API that plug-ins can use to interact with Cura. +# +# Python does not technically prevent talking to other classes as well, but +# this API provides a version-safe interface with proper deprecation warnings +# etc. Usage of any other methods than the ones provided in this API can cause +# plug-ins to be unstable. class CuraAPI: - """ - The official Cura API that plugins can use to interact with Cura. - Python does not technically prevent talking to other classes as well, - but this API provides a version-safe interface with proper deprecation warnings etc. - Usage of any other methods than the ones provided in this API can cause plugins to be unstable. - """ # For now we use the same API version to be consistent. VERSION = PluginRegistry.APIVersion diff --git a/cura/Backups/Backup.py b/cura/Backups/Backup.py index c4fe720b2b..70807a96d7 100644 --- a/cura/Backups/Backup.py +++ b/cura/Backups/Backup.py @@ -17,12 +17,11 @@ from UM.Resources import Resources from cura.CuraApplication import CuraApplication +## The back-up class holds all data about a back-up. +# +# It is also responsible for reading and writing the zip file to the user data +# folder. class Backup: - """ - The backup class holds all data about a backup. - It is also responsible for reading and writing the zip file to the user data folder. - """ - # These files should be ignored when making a backup. IGNORED_FILES = [r"cura\.log", r"plugins\.json", r"cache", r"__pycache__", r"\.qmlc", r"\.pyc"] @@ -33,10 +32,8 @@ class Backup: self.zip_file = zip_file # type: Optional[bytes] self.meta_data = meta_data # type: Optional[dict] + ## Create a back-up from the current user config folder. def makeFromCurrent(self) -> (bool, Optional[str]): - """ - Create a backup from the current user config folder. - """ cura_release = CuraApplication.getInstance().getVersion() version_data_dir = Resources.getDataStoragePath() @@ -75,12 +72,10 @@ class Backup: "plugin_count": str(plugin_count) } + ## Make a full archive from the given root path with the given name. + # \param root_path The root directory to archive recursively. + # \return The archive as bytes. def _makeArchive(self, buffer: "io.BytesIO", root_path: str) -> Optional[ZipFile]: - """ - Make a full archive from the given root path with the given name. - :param root_path: The root directory to archive recursively. - :return: The archive as bytes. - """ ignore_string = re.compile("|".join(self.IGNORED_FILES)) try: archive = ZipFile(buffer, "w", ZIP_DEFLATED) @@ -99,15 +94,13 @@ class Backup: "Could not create archive from user data directory: {}".format(error))) return None + ## Show a UI message. def _showMessage(self, message: str) -> None: - """Show a UI message""" Message(message, title=self.catalog.i18nc("@info:title", "Backup"), lifetime=30).show() + ## Restore this back-up. + # \return Whether we had success or not. def restore(self) -> bool: - """ - Restore this backups - :return: A boolean whether we had success or not. - """ if not self.zip_file or not self.meta_data or not self.meta_data.get("cura_release", None): # We can restore without the minimum required information. Logger.log("w", "Tried to restore a Cura backup without having proper data or meta data.") @@ -140,14 +133,12 @@ class Backup: return extracted + ## Extract the whole archive to the given target path. + # \param archive The archive as ZipFile. + # \param target_path The target path. + # \return Whether we had success or not. @staticmethod def _extractArchive(archive: "ZipFile", target_path: str) -> bool: - """ - Extract the whole archive to the given target path. - :param archive: The archive as ZipFile. - :param target_path: The target path. - :return: A boolean whether we had success or not. - """ Logger.log("d", "Removing current data in location: %s", target_path) Resources.factoryReset() Logger.log("d", "Extracting backup to location: %s", target_path) diff --git a/cura/Backups/BackupsManager.py b/cura/Backups/BackupsManager.py index fa75ddb587..850b0a2edc 100644 --- a/cura/Backups/BackupsManager.py +++ b/cura/Backups/BackupsManager.py @@ -7,19 +7,18 @@ from cura.Backups.Backup import Backup from cura.CuraApplication import CuraApplication +## The BackupsManager is responsible for managing the creating and restoring of +# back-ups. +# +# Back-ups themselves are represented in a different class. class BackupsManager: - """ - The BackupsManager is responsible for managing the creating and restoring of backups. - Backups themselves are represented in a different class. - """ def __init__(self): self._application = CuraApplication.getInstance() + ## Get a back-up of the current configuration. + # \return A tuple containing a ZipFile (the actual back-up) and a dict + # containing some metadata (like version). def createBackup(self) -> (Optional[bytes], Optional[dict]): - """ - Get a backup of the current configuration. - :return: A Tuple containing a ZipFile (the actual backup) and a dict containing some meta data (like version). - """ self._disableAutoSave() backup = Backup() backup.makeFromCurrent() @@ -27,12 +26,11 @@ class BackupsManager: # We don't return a Backup here because we want plugins only to interact with our API and not full objects. return backup.zip_file, backup.meta_data + ## Restore a back-up from a given ZipFile. + # \param zip_file A bytes object containing the actual back-up. + # \param meta_data A dict containing some metadata that is needed to + # restore the back-up correctly. def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None: - """ - Restore a backup from a given ZipFile. - :param zip_file: A bytes object containing the actual backup. - :param meta_data: A dict containing some meta data that is needed to restore the backup correctly. - """ if not meta_data.get("cura_release", None): # If there is no "cura_release" specified in the meta data, we don't execute a backup restore. Logger.log("w", "Tried to restore a backup without specifying a Cura version number.") @@ -47,10 +45,11 @@ class BackupsManager: # We don't want to store the data at this point as that would override the just-restored backup. self._application.windowClosed(save_data=False) + ## Here we try to disable the auto-save plug-in as it might interfere with + # restoring a back-up. def _disableAutoSave(self): - """Here we try to disable the auto-save plugin as it might interfere with restoring a backup.""" self._application.setSaveDataEnabled(False) + ## Re-enable auto-save after we're done. def _enableAutoSave(self): - """Re-enable auto-save after we're done.""" self._application.setSaveDataEnabled(True) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 6b539a4574..8ddcdbfb2f 100755 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -8,6 +8,7 @@ from UM.Scene.SceneNode import SceneNode from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator from UM.Math.Vector import Vector from UM.Scene.Selection import Selection +from UM.Scene.SceneNodeSettings import SceneNodeSettings from cura.Scene.ConvexHullDecorator import ConvexHullDecorator @@ -80,6 +81,10 @@ class PlatformPhysics: # only push away objects if this node is a printing mesh if not node.callDecoration("isNonPrintingMesh") and Application.getInstance().getPreferences().getValue("physics/automatic_push_free"): + # Do not move locked nodes + if node.getSetting(SceneNodeSettings.LockPosition): + continue + # Check for collisions between convex hulls for other_node in BreadthFirstIterator(root): # Ignore root, ourselves and anything that is not a normal SceneNode. diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 39bda6a4a4..9abdd2cb96 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -306,6 +306,11 @@ class MachineManager(QObject): for position, extruder in global_stack.extruders.items(): material_dict[position] = extruder.material.getMetaDataEntry("base_file") self._current_root_material_id = material_dict + + # Update materials to make sure that the diameters match with the machine's + for position in global_stack.extruders: + self._updateMaterialWithVariant(position) + global_quality = global_stack.quality quality_type = global_quality.getMetaDataEntry("quality_type") global_quality_changes = global_stack.qualityChanges