diff --git a/cura/Backups/Backup.py b/cura/Backups/Backup.py index 5fad2ccf19..6128dac320 100644 --- a/cura/Backups/Backup.py +++ b/cura/Backups/Backup.py @@ -166,6 +166,9 @@ class Backup: Logger.log("d", "Moving preferences file from %s to %s", backup_preferences_file, preferences_file) shutil.move(backup_preferences_file, preferences_file) + # Read the preferences from the newly restored configuration (or else the cached Preferences will override the restored ones) + self._application.readPreferencesFromConfiguration() + # Restore the obfuscated settings self._illuminate(**secrets) @@ -191,7 +194,10 @@ class Backup: Resources.factoryReset() Logger.log("d", "Extracting backup to location: %s", target_path) try: - archive.extractall(target_path) + name_list = archive.namelist() + for archive_filename in name_list: + archive.extract(archive_filename, target_path) + CuraApplication.getInstance().processEvents() except (PermissionError, EnvironmentError): Logger.logException("e", "Unable to extract the backup due to permission or file system errors.") return False diff --git a/cura/Machines/Models/QualitySettingsModel.py b/cura/Machines/Models/QualitySettingsModel.py index b046b7546d..89a996fba1 100644 --- a/cura/Machines/Models/QualitySettingsModel.py +++ b/cura/Machines/Models/QualitySettingsModel.py @@ -114,6 +114,7 @@ class QualitySettingsModel(ListModel): global_container = None if len(global_containers) == 0 else global_containers[0] extruders_containers = {pos: container_registry.findContainers(id = quality_changes_group.metadata_per_extruder[pos]["id"]) for pos in quality_changes_group.metadata_per_extruder} extruders_container = {pos: None if not containers else containers[0] for pos, containers in extruders_containers.items()} + quality_changes_metadata = None if self._selected_position == self.GLOBAL_STACK_POSITION and global_container: quality_changes_metadata = global_container.getMetaData() else: diff --git a/cura/OAuth2/KeyringAttribute.py b/cura/OAuth2/KeyringAttribute.py index 12cdb94dd5..1d6f329ea9 100644 --- a/cura/OAuth2/KeyringAttribute.py +++ b/cura/OAuth2/KeyringAttribute.py @@ -1,10 +1,10 @@ # Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Type, TYPE_CHECKING, Optional, List +from typing import Type, TYPE_CHECKING, Optional, List, Union import keyring from keyring.backend import KeyringBackend -from keyring.errors import NoKeyringError, PasswordSetError +from keyring.errors import NoKeyringError, PasswordSetError, KeyringError from UM.Logger import Logger @@ -14,13 +14,24 @@ if TYPE_CHECKING: # Need to do some extra workarounds on windows: import sys from UM.Platform import Platform + + +class _KeychainDenied(Exception): + pass + + if Platform.isWindows() and hasattr(sys, "frozen"): import win32timezone from keyring.backends.Windows import WinVaultKeyring keyring.set_keyring(WinVaultKeyring()) if Platform.isOSX() and hasattr(sys, "frozen"): from keyring.backends.macOS import Keyring + from keyring.backends.macOS.api import KeychainDenied as _KeychainDeniedMacOS + KeychainDenied: Union[Type[_KeychainDenied], Type[_KeychainDeniedMacOS]] = _KeychainDeniedMacOS keyring.set_keyring(Keyring()) +else: + KeychainDenied = _KeychainDenied + # Even if errors happen, we don't want this stored locally: DONT_EVER_STORE_LOCALLY: List[str] = ["refresh_token"] @@ -39,6 +50,10 @@ class KeyringAttribute: self._store_secure = False Logger.logException("w", "No keyring backend present") return getattr(instance, self._name) + except KeychainDenied: + self._store_secure = False + Logger.log("i", "Access to the keyring was denied.") + return getattr(instance, self._name) else: return getattr(instance, self._name) diff --git a/plugins/CuraDrive/src/CreateBackupJob.py b/plugins/CuraDrive/src/CreateBackupJob.py index d427f03573..ab1989f382 100644 --- a/plugins/CuraDrive/src/CreateBackupJob.py +++ b/plugins/CuraDrive/src/CreateBackupJob.py @@ -5,7 +5,6 @@ import threading from datetime import datetime from typing import Any, Dict, Optional -import sentry_sdk from PyQt5.QtNetwork import QNetworkReply from UM.Job import Job @@ -99,13 +98,7 @@ class CreateBackupJob(Job): if HttpRequestManager.safeHttpStatus(reply) == 400: errors = json.loads(replyText)["errors"] if "moreThanMaximum" in [error["code"] for error in errors if error["meta"] and error["meta"]["field_name"] == "backup_size"]: - if self._backup_zip is None: # will never happen; keep mypy happy - zip_error = "backup is None." - else: - zip_error = "{} exceeds max size.".format(str(len(self._backup_zip))) - sentry_sdk.capture_message("backup failed: {}".format(zip_error), level ="warning") self.backup_upload_error_message = catalog.i18nc("@error:file_size", "The backup exceeds the maximum file size.") - from sentry_sdk import capture_message self._job_done.set() return diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index 754069dc9b..6dd6f02b97 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -93,7 +93,7 @@ class DriveApiService: def _onRestoreFinished(self, job: "RestoreBackupJob") -> None: if job.restore_backup_error_message != "": # If the job contains an error message we pass it along so the UI can display it. - self.restoringStateChanged.emit(is_restoring=False) + self.restoringStateChanged.emit(is_restoring = False) else: self.restoringStateChanged.emit(is_restoring = False, error_message = job.restore_backup_error_message) diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index 8de4876f52..6fd55d172e 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -34,6 +34,9 @@ class DrivePluginExtension(QObject, Extension): # Signal emitted when preferences changed (like auto-backup). preferencesChanged = pyqtSignal() + # Signal emitted when the id of the backup-to-be-restored is changed + backupIdBeingRestoredChanged = pyqtSignal(arguments = ["backup_id_being_restored"]) + DATE_FORMAT = "%d/%m/%Y %H:%M:%S" def __init__(self) -> None: @@ -45,6 +48,7 @@ class DrivePluginExtension(QObject, Extension): self._backups = [] # type: List[Dict[str, Any]] self._is_restoring_backup = False self._is_creating_backup = False + self._backup_id_being_restored = "" # Initialize services. preferences = CuraApplication.getInstance().getPreferences() @@ -52,6 +56,7 @@ class DrivePluginExtension(QObject, Extension): # Attach signals. CuraApplication.getInstance().getCuraAPI().account.loginStateChanged.connect(self._onLoginStateChanged) + CuraApplication.getInstance().applicationShuttingDown.connect(self._onApplicationShuttingDown) self._drive_api_service.restoringStateChanged.connect(self._onRestoringStateChanged) self._drive_api_service.creatingStateChanged.connect(self._onCreatingStateChanged) @@ -75,6 +80,9 @@ class DrivePluginExtension(QObject, Extension): if self._drive_window: self._drive_window.show() + def _onApplicationShuttingDown(self): + self._drive_window.hide() + def _autoBackup(self) -> None: preferences = CuraApplication.getInstance().getPreferences() if preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY) and self._isLastBackupTooLongAgo(): @@ -100,10 +108,11 @@ class DrivePluginExtension(QObject, Extension): if logged_in: self.refreshBackups() - def _onRestoringStateChanged(self, is_restoring: bool = False, error_message: str = None) -> None: + def _onRestoringStateChanged(self, is_restoring: bool = False, error_message: Optional[str] = None) -> None: self._is_restoring_backup = is_restoring self.restoringStateChanged.emit() if error_message: + self.backupIdBeingRestored = "" Message(error_message, title = catalog.i18nc("@info:title", "Backup")).show() def _onCreatingStateChanged(self, is_creating: bool = False, error_message: str = None) -> None: @@ -152,6 +161,7 @@ class DrivePluginExtension(QObject, Extension): for backup in self._backups: if backup.get("backup_id") == backup_id: self._drive_api_service.restoreBackup(backup) + self.setBackupIdBeingRestored(backup_id) return Logger.log("w", "Unable to find backup with the ID %s", backup_id) @@ -166,3 +176,12 @@ class DrivePluginExtension(QObject, Extension): def _backupDeletedCallback(self, success: bool): if success: self.refreshBackups() + + def setBackupIdBeingRestored(self, backup_id_being_restored: str) -> None: + if backup_id_being_restored != self._backup_id_being_restored: + self._backup_id_being_restored = backup_id_being_restored + self.backupIdBeingRestoredChanged.emit() + + @pyqtProperty(str, fset = setBackupIdBeingRestored, notify = backupIdBeingRestoredChanged) + def backupIdBeingRestored(self) -> str: + return self._backup_id_being_restored diff --git a/plugins/CuraDrive/src/qml/components/BackupListItem.qml b/plugins/CuraDrive/src/qml/components/BackupListItem.qml index 5cdb500b4e..7b539b03b9 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItem.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItem.qml @@ -71,6 +71,7 @@ Item text: catalog.i18nc("@button", "Restore") enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup onClicked: confirmRestoreDialog.visible = true + busy: CuraDrive.backupIdBeingRestored == modelData.backup_id && CuraDrive.isRestoringBackup } UM.SimpleButton diff --git a/resources/i18n/cs_CZ/cura.po b/resources/i18n/cs_CZ/cura.po index b5b5e8379b..cd7b770253 100644 --- a/resources/i18n/cs_CZ/cura.po +++ b/resources/i18n/cs_CZ/cura.po @@ -174,7 +174,7 @@ msgstr "Pokusil se obnovit zálohu Cura, která je vyšší než aktuální verz #: /home/trin/Gedeeld/Projects/Cura/cura/Backups/Backup.py:157 msgctxt "@info:backup_failed" msgid "The following error occurred while trying to restore a Cura backup:" -msgstr "" +msgstr "Během obnovení zálohy Cura se vyskytly následující chyby:" #: /home/trin/Gedeeld/Projects/Cura/cura/BuildVolume.py:98 msgctxt "@info:status" @@ -2268,7 +2268,7 @@ msgstr "Zobrazit online manuály" #: /home/trin/Gedeeld/Projects/Cura/plugins/MonitorStage/MonitorMain.qml:172 msgctxt "@info" msgid "In order to monitor your print from Cura, please connect the printer." -msgstr "" +msgstr "Abyste mohli monitorovat tisk z Cury, připojte prosím tiskárnu." #: /home/trin/Gedeeld/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:42 msgctxt "@label" @@ -2393,7 +2393,7 @@ msgstr "Šířka čáry" #: /home/trin/Gedeeld/Projects/Cura/plugins/SimulationView/SimulationViewMenuComponent.qml:130 msgctxt "@label:listbox" msgid "Flow" -msgstr "" +msgstr "Průtok" #: /home/trin/Gedeeld/Projects/Cura/plugins/SimulationView/SimulationViewMenuComponent.qml:171 msgctxt "@label" @@ -3385,12 +3385,12 @@ msgstr "Přední pohled" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:147 msgctxt "@action:inmenu menubar:view" msgid "Top View" -msgstr "Pohled ze shora" +msgstr "Pohled seshora" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:154 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" -msgstr "" +msgstr "Pohled zezdola" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:161 msgctxt "@action:inmenu menubar:view" @@ -4627,7 +4627,7 @@ msgstr "Export" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Preferences/Materials/MaterialsPage.qml:199 msgctxt "@action:button Sending materials to printers" msgid "Sync with Printers" -msgstr "" +msgstr "Synchronizovat s tiskárnami" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Preferences/Materials/MaterialsPage.qml:248 msgctxt "@action:label" @@ -4681,7 +4681,7 @@ msgstr "Úspěšné exportování materiálu do %1" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Preferences/Materials/MaterialsPage.qml:388 msgctxt "@title:window" msgid "Export All Materials" -msgstr "" +msgstr "Exportovat všechny materiály" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Preferences/Materials/MaterialsView.qml:72 msgctxt "@title" @@ -5252,7 +5252,7 @@ msgstr "Přední pohled" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/ViewOrientationControls.qml:53 msgctxt "@info:tooltip" msgid "Top View" -msgstr "Pohled ze shora" +msgstr "Pohled seshora" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/ViewOrientationControls.qml:66 msgctxt "@info:tooltip" @@ -5593,12 +5593,12 @@ msgstr "Zapisovač Cura profilu" #: DigitalLibrary/plugin.json msgctxt "description" msgid "Connects to the Digital Library, allowing Cura to open files from and save files to the Digital Library." -msgstr "" +msgstr "Připojuje k Digitální knihovně. Umožňuje Cuře otevírat a ukládat soubory z a do Digitální knihovny." #: DigitalLibrary/plugin.json msgctxt "name" msgid "Ultimaker Digital Library" -msgstr "" +msgstr "Digitální knihovna Ultimaker" #: FirmwareUpdateChecker/plugin.json msgctxt "description" @@ -6073,22 +6073,22 @@ msgstr "Aktualizace verze 4.7 na 4.8" #: VersionUpgrade/VersionUpgrade48to49/plugin.json msgctxt "description" msgid "Upgrades configurations from Cura 4.8 to Cura 4.9." -msgstr "" +msgstr "Aktualizuje konfigurace z Cura 4.8 na Cura 4.9." #: VersionUpgrade/VersionUpgrade48to49/plugin.json msgctxt "name" msgid "Version Upgrade 4.8 to 4.9" -msgstr "" +msgstr "Aktualizace verze 4.8 na 4.9" #: VersionUpgrade/VersionUpgrade49to410/plugin.json msgctxt "description" msgid "Upgrades configurations from Cura 4.9 to Cura 4.10." -msgstr "" +msgstr "Aktualizuje konfigurace z Cura 4.9 na Cura 4.10." #: VersionUpgrade/VersionUpgrade49to410/plugin.json msgctxt "name" msgid "Version Upgrade 4.9 to 4.10" -msgstr "" +msgstr "Aktualizace verze 4.9 na 4.10" #: X3DReader/plugin.json msgctxt "description" diff --git a/resources/i18n/cs_CZ/fdmprinter.def.json.po b/resources/i18n/cs_CZ/fdmprinter.def.json.po index 8a120467be..fca828a2a6 100644 --- a/resources/i18n/cs_CZ/fdmprinter.def.json.po +++ b/resources/i18n/cs_CZ/fdmprinter.def.json.po @@ -3200,7 +3200,7 @@ msgstr "Maximální vzdálenost Combing-u bez retrakce" #: fdmprinter.def.json msgctxt "retraction_combing_max_distance description" msgid "When greater than zero, combing travel moves that are longer than this distance will use retraction. If set to zero, there is no maximum and combing moves will not use retraction." -msgstr "" +msgstr "Pokud je větší než nula, combingové pohyby delší než tato vzdálenost budou používat retrakci. Nula znamená, že se při combingových pohybech retrakce provádět nebudou." #: fdmprinter.def.json msgctxt "travel_retract_before_outer_wall label" diff --git a/resources/images/whats_new/0.png b/resources/images/whats_new/0.png index 58fcb10d29..77c7d6cf46 100644 Binary files a/resources/images/whats_new/0.png and b/resources/images/whats_new/0.png differ diff --git a/resources/qml/ToolTip.qml b/resources/qml/ToolTip.qml index b8873439f1..3157f81d89 100644 --- a/resources/qml/ToolTip.qml +++ b/resources/qml/ToolTip.qml @@ -59,6 +59,7 @@ ToolTip color: UM.Theme.getColor("tooltip") target: Qt.point(targetPoint.x - tooltip.x, targetPoint.y - tooltip.y) arrowSize: UM.Theme.getSize("default_arrow").width + visible: tooltip.height != 0 } contentItem: Label