mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-06-04 11:14:21 +08:00
Merge branch 'feature-backup-manager' of https://github.com/Ultimaker/Cura into feature-backup-manager
This commit is contained in:
commit
0723a6a63f
1
.gitignore
vendored
1
.gitignore
vendored
@ -40,6 +40,7 @@ plugins/cura-siemensnx-plugin
|
|||||||
plugins/CuraBlenderPlugin
|
plugins/CuraBlenderPlugin
|
||||||
plugins/CuraCloudPlugin
|
plugins/CuraCloudPlugin
|
||||||
plugins/CuraDrivePlugin
|
plugins/CuraDrivePlugin
|
||||||
|
plugins/CuraDrive
|
||||||
plugins/CuraLiveScriptingPlugin
|
plugins/CuraLiveScriptingPlugin
|
||||||
plugins/CuraOpenSCADPlugin
|
plugins/CuraOpenSCADPlugin
|
||||||
plugins/CuraPrintProfileCreator
|
plugins/CuraPrintProfileCreator
|
||||||
|
@ -19,7 +19,7 @@ class Backup:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# These files should be ignored when making a backup.
|
# These files should be ignored when making a backup.
|
||||||
IGNORED_FILES = {"cura.log"}
|
IGNORED_FILES = {"cura.log", "cache"}
|
||||||
|
|
||||||
def __init__(self, zip_file: bytes = None, meta_data: dict = None):
|
def __init__(self, zip_file: bytes = None, meta_data: dict = None):
|
||||||
self.zip_file = zip_file # type: Optional[bytes]
|
self.zip_file = zip_file # type: Optional[bytes]
|
||||||
@ -34,24 +34,33 @@ class Backup:
|
|||||||
|
|
||||||
Logger.log("d", "Creating backup for Cura %s, using folder %s", cura_release, version_data_dir)
|
Logger.log("d", "Creating backup for Cura %s, using folder %s", cura_release, version_data_dir)
|
||||||
|
|
||||||
|
# TODO: support preferences file in backup under Linux (is in different directory).
|
||||||
|
|
||||||
# Ensure all current settings are saved.
|
# Ensure all current settings are saved.
|
||||||
CuraApplication.getInstance().saveSettings()
|
CuraApplication.getInstance().saveSettings()
|
||||||
|
|
||||||
# We're using an easy to parse filename for when we're restoring edge cases:
|
# Create an empty buffer and write the archive to it.
|
||||||
# TIMESTAMP.backup.VERSION.cura.zip
|
buffer = io.BytesIO()
|
||||||
archive = self._makeArchive(version_data_dir)
|
archive = self._makeArchive(buffer, version_data_dir)
|
||||||
|
files = archive.namelist()
|
||||||
|
|
||||||
self.zip_file = archive
|
# Count the metadata items. We do this in a rather naive way at the moment.
|
||||||
|
machine_count = len([s for s in files if "machine_instances/" in s]) - 1
|
||||||
|
material_count = len([s for s in files if "materials/" in s]) - 1
|
||||||
|
profile_count = len([s for s in files if "quality_changes/" in s]) - 1
|
||||||
|
plugin_count = len([s for s in files if "plugin.json" in s])
|
||||||
|
|
||||||
|
# Store the archive and metadata so the BackupManager can fetch them when needed.
|
||||||
|
self.zip_file = buffer.getvalue()
|
||||||
self.meta_data = {
|
self.meta_data = {
|
||||||
"cura_release": cura_release,
|
"cura_release": cura_release,
|
||||||
"machine_count": 0,
|
"machine_count": str(machine_count),
|
||||||
"material_count": 0,
|
"material_count": str(material_count),
|
||||||
"profile_count": 0,
|
"profile_count": str(profile_count),
|
||||||
"plugin_count": 0
|
"plugin_count": str(plugin_count)
|
||||||
}
|
}
|
||||||
# TODO: fill meta data with real machine/material/etc counts.
|
|
||||||
|
|
||||||
def _makeArchive(self, root_path: str) -> Optional[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.
|
Make a full archive from the given root path with the given name.
|
||||||
:param root_path: The root directory to archive recursively.
|
:param root_path: The root directory to archive recursively.
|
||||||
@ -59,52 +68,57 @@ class Backup:
|
|||||||
"""
|
"""
|
||||||
contents = os.walk(root_path)
|
contents = os.walk(root_path)
|
||||||
try:
|
try:
|
||||||
buffer = io.BytesIO()
|
|
||||||
archive = ZipFile(buffer, "w", ZIP_DEFLATED)
|
archive = ZipFile(buffer, "w", ZIP_DEFLATED)
|
||||||
for root, folders, files in contents:
|
for root, folders, files in contents:
|
||||||
for folder_name in folders:
|
for folder_name in folders:
|
||||||
# Add all folders, even empty ones.
|
if folder_name in self.IGNORED_FILES:
|
||||||
|
continue
|
||||||
absolute_path = os.path.join(root, folder_name)
|
absolute_path = os.path.join(root, folder_name)
|
||||||
relative_path = absolute_path[len(root_path) + len(os.sep):]
|
relative_path = absolute_path[len(root_path) + len(os.sep):]
|
||||||
archive.write(absolute_path, relative_path)
|
archive.write(absolute_path, relative_path)
|
||||||
for file_name in files:
|
for file_name in files:
|
||||||
# Add all files except the ignored ones.
|
|
||||||
if file_name in self.IGNORED_FILES:
|
if file_name in self.IGNORED_FILES:
|
||||||
continue
|
continue
|
||||||
absolute_path = os.path.join(root, file_name)
|
absolute_path = os.path.join(root, file_name)
|
||||||
relative_path = absolute_path[len(root_path) + len(os.sep):]
|
relative_path = absolute_path[len(root_path) + len(os.sep):]
|
||||||
archive.write(absolute_path, relative_path)
|
archive.write(absolute_path, relative_path)
|
||||||
archive.close()
|
archive.close()
|
||||||
return buffer.getvalue()
|
return archive
|
||||||
except (IOError, OSError, BadZipfile) as error:
|
except (IOError, OSError, BadZipfile) as error:
|
||||||
Logger.log("e", "Could not create archive from user data directory: %s", error)
|
Logger.log("e", "Could not create archive from user data directory: %s", error)
|
||||||
|
# TODO: show message.
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def restore(self) -> bool:
|
def restore(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Restore this backup.
|
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):
|
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.
|
# We can restore without the minimum required information.
|
||||||
Logger.log("w", "Tried to restore a Cura backup without having proper data or meta data.")
|
Logger.log("w", "Tried to restore a Cura backup without having proper data or meta data.")
|
||||||
|
# TODO: show message.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# global_data_dir = os.path.dirname(version_data_dir)
|
|
||||||
# TODO: handle restoring older data version.
|
# TODO: handle restoring older data version.
|
||||||
|
# TODO: support preferences file in backup under Linux (is in different directory).
|
||||||
|
# global_data_dir = os.path.dirname(version_data_dir)
|
||||||
|
|
||||||
version_data_dir = Resources.getDataStoragePath()
|
version_data_dir = Resources.getDataStoragePath()
|
||||||
archive = ZipFile(io.BytesIO(self.zip_file), "r")
|
archive = ZipFile(io.BytesIO(self.zip_file), "r")
|
||||||
extracted = self._extractArchive(archive, version_data_dir)
|
extracted = self._extractArchive(archive, version_data_dir)
|
||||||
if not extracted:
|
return extracted
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _extractArchive(archive: "ZipFile", target_path: str) -> bool:
|
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)
|
Logger.log("d", "Removing current data in location: %s", target_path)
|
||||||
shutil.rmtree(target_path)
|
shutil.rmtree(target_path)
|
||||||
|
|
||||||
Logger.log("d", "Extracting backup to location: %s", target_path)
|
Logger.log("d", "Extracting backup to location: %s", target_path)
|
||||||
archive.extractall(target_path)
|
archive.extractall(target_path)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -54,7 +54,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin):
|
|||||||
self._check_updates = True
|
self._check_updates = True
|
||||||
self._update_thread.start()
|
self._update_thread.start()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self, store_data: bool = True):
|
||||||
self._check_updates = False
|
self._check_updates = False
|
||||||
|
|
||||||
def _onConnectionStateChanged(self, serial_port):
|
def _onConnectionStateChanged(self, serial_port):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user