Browse Source

Merge branch 'master' into libArachne_rebased

Conflicts:
	resources/texts/change_log.txt -> Adding 4.10 release notes in between 4.9 and Arachne.
Ghostkeeper 3 years ago
parent
commit
88954922d4

+ 12 - 7
cura/Backups/Backup.py

@@ -7,7 +7,7 @@ import re
 import shutil
 from copy import deepcopy
 from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile
-from typing import Dict, Optional, TYPE_CHECKING
+from typing import Dict, Optional, TYPE_CHECKING, List
 
 from UM import i18nCatalog
 from UM.Logger import Logger
@@ -29,7 +29,7 @@ class Backup:
     IGNORED_FILES = [r"cura\.log", r"plugins\.json", r"cache", r"__pycache__", r"\.qmlc", r"\.pyc"]
     """These files should be ignored when making a backup."""
 
-    IGNORED_FOLDERS = [r"plugins"]
+    IGNORED_FOLDERS = []  # type: List[str]
 
     SECRETS_SETTINGS = ["general/ultimaker_auth_data"]
     """Secret preferences that need to obfuscated when making a backup of Cura"""
@@ -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)
 
@@ -190,11 +193,13 @@ class Backup:
         Logger.log("d", "Removing current data in location: %s", target_path)
         Resources.factoryReset()
         Logger.log("d", "Extracting backup to location: %s", target_path)
-        try:
-            archive.extractall(target_path)
-        except (PermissionError, EnvironmentError):
-            Logger.logException("e", "Unable to extract the backup due to permission or file system errors.")
-            return False
+        name_list = archive.namelist()
+        for archive_filename in name_list:
+            try:
+                archive.extract(archive_filename, target_path)
+            except (PermissionError, EnvironmentError):
+                Logger.logException("e", f"Unable to extract the file {archive_filename} from the backup due to permission or file system errors.")
+            CuraApplication.getInstance().processEvents()
         return True
 
     def _obfuscate(self) -> Dict[str, str]:

+ 0 - 11
cura/Backups/BackupsManager.py

@@ -54,17 +54,6 @@ class BackupsManager:
         backup = Backup(self._application, zip_file = zip_file, meta_data = meta_data)
         restored = backup.restore()
 
-        package_manager = self._application.getPackageManager()
-
-        # If the backup was made with Cura 4.10 (or higher), we no longer store plugins.
-        # Since the restored backup doesn't have those plugins anymore, we should remove it from the list
-        # of installed plugins.
-        if Version(meta_data.get("cura_release")) >= Version("4.10.0"):
-            for package_id in package_manager.getAllInstalledPackageIDs():
-                package_data = package_manager.getInstalledPackageInfo(package_id)
-                if package_data.get("package_type") == "plugin" and not package_data.get("is_bundled"):
-                    package_manager.removePackage(package_id)
-
         if restored:
             # At this point, Cura will need to restart for the changes to take effect.
             # We don't want to store the data at this point as that would override the just-restored backup.

+ 3 - 1
cura/BuildVolume.py

@@ -1,4 +1,4 @@
-# Copyright (c) 2020 Ultimaker B.V.
+# Copyright (c) 2021 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 
 import numpy
@@ -916,6 +916,8 @@ class BuildVolume(SceneNode):
             return {}
 
         for area in self._global_container_stack.getProperty("machine_disallowed_areas", "value"):
+            if len(area) == 0:
+                continue  # Numpy doesn't deal well with 0-length arrays, since it can't determine the dimensionality of them.
             polygon = Polygon(numpy.array(area, numpy.float32))
             polygon = polygon.getMinkowskiHull(Polygon.approximatedCircle(border_size))
             machine_disallowed_polygons.append(polygon)

+ 2 - 0
cura/CuraApplication.py

@@ -708,6 +708,8 @@ class CuraApplication(QtApplication):
     @pyqtSlot(str)
     def discardOrKeepProfileChangesClosed(self, option: str) -> None:
         global_stack = self.getGlobalContainerStack()
+        if global_stack is None:
+            return
         if option == "discard":
             for extruder in global_stack.extruderList:
                 extruder.userChanges.clear()

+ 7 - 1
cura/Machines/Models/ExtrudersModel.py

@@ -53,6 +53,9 @@ class ExtrudersModel(ListModel):
     EnabledRole = Qt.UserRole + 11
     """Is the extruder enabled?"""
 
+    MaterialTypeRole = Qt.UserRole + 12
+    """The type of the material (e.g. PLA, ABS, PETG, etc.)."""
+
     defaultColors = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"]
     """List of colours to display if there is no material or the material has no known colour. """
 
@@ -75,6 +78,7 @@ class ExtrudersModel(ListModel):
         self.addRoleName(self.StackRole, "stack")
         self.addRoleName(self.MaterialBrandRole, "material_brand")
         self.addRoleName(self.ColorNameRole, "color_name")
+        self.addRoleName(self.MaterialTypeRole, "material_type")
         self._update_extruder_timer = QTimer()
         self._update_extruder_timer.setInterval(100)
         self._update_extruder_timer.setSingleShot(True)
@@ -193,7 +197,8 @@ class ExtrudersModel(ListModel):
                     "variant": extruder.variant.getName() if extruder.variant else "",  # e.g. print core
                     "stack": extruder,
                     "material_brand": material_brand,
-                    "color_name": color_name
+                    "color_name": color_name,
+                    "material_type": extruder.material.getMetaDataEntry("material") if extruder.material else "",
                 }
 
                 items.append(item)
@@ -218,6 +223,7 @@ class ExtrudersModel(ListModel):
                     "stack": None,
                     "material_brand": "",
                     "color_name": "",
+                    "material_type": "",
                 }
                 items.append(item)
             if self._items != items:

+ 1 - 56
cura/Machines/Models/MaterialManagementModel.py

@@ -2,10 +2,9 @@
 # Cura is released under the terms of the LGPLv3 or higher.
 
 import copy  # To duplicate materials.
-from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl
+from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot  # To allow the preference page proxy to be used from the actual preferences page.
 from typing import Any, Dict, Optional, TYPE_CHECKING
 import uuid  # To generate new GUIDs for new materials.
-import zipfile  # To export all materials in a .zip archive.
 
 from UM.i18n import i18nCatalog
 from UM.Logger import Logger
@@ -25,11 +24,6 @@ class MaterialManagementModel(QObject):
 
     This class handles the actions in that page, such as creating new materials, renaming them, etc.
     """
-    def __init__(self, parent: QObject) -> None:
-        super().__init__(parent)
-        cura_application = cura.CuraApplication.CuraApplication.getInstance()
-        self._preferred_export_all_path = None  # type: Optional[QUrl]  # Path to export all materials to. None if not yet initialised.
-        cura_application.getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
 
     favoritesChanged = pyqtSignal(str)
     """Triggered when a favorite is added or removed.
@@ -270,52 +264,3 @@ class MaterialManagementModel(QObject):
             self.favoritesChanged.emit(material_base_file)
         except ValueError:  # Material was not in the favorites list.
             Logger.log("w", "Material {material_base_file} was already not a favorite material.".format(material_base_file = material_base_file))
-
-    def _onOutputDevicesChanged(self) -> None:
-        """
-        When the list of output devices changes, we may want to update the
-        preferred export path.
-        """
-        cura_application = cura.CuraApplication.CuraApplication.getInstance()
-        device_manager = cura_application.getOutputDeviceManager()
-        devices = device_manager.getOutputDevices()
-        for device in devices:
-            if device.__class__.__name__ == "RemovableDriveOutputDevice":
-                self._preferred_export_all_path = QUrl.fromLocalFile(device.getId())
-                break
-        else:  # No removable drives? Use local path.
-            self._preferred_export_all_path = cura_application.getDefaultPath("dialog_material_path")
-        self.outputDevicesChanged.emit()
-
-    outputDevicesChanged = pyqtSignal()  # Triggered when adding or removing removable drives.
-    @pyqtProperty(QUrl, notify = outputDevicesChanged)
-    def preferredExportAllPath(self) -> QUrl:
-        """
-        Get the preferred path to export materials to.
-
-        If there is a removable drive, that should be the preferred path. Otherwise it should be the most recent local
-        file path.
-        :return: The preferred path to export all materials to.
-        """
-        if self._preferred_export_all_path is None:  # Not initialised yet. Can happen when output devices changed before class got created.
-            self._onOutputDevicesChanged()
-        return self._preferred_export_all_path
-
-    @pyqtSlot(QUrl)
-    def exportAll(self, file_path: QUrl) -> None:
-        """
-        Export all materials to a certain file path.
-        :param file_path: The path to export the materials to.
-        """
-        registry = CuraContainerRegistry.getInstance()
-
-        archive = zipfile.ZipFile(file_path.toLocalFile(), "w", compression = zipfile.ZIP_DEFLATED)
-        for metadata in registry.findInstanceContainersMetadata(type = "material"):
-            if metadata["base_file"] != metadata["id"]:  # Only process base files.
-                continue
-            if metadata["id"] == "empty_material":  # Don't export the empty material.
-                continue
-            material = registry.findContainers(id = metadata["id"])[0]
-            suffix = registry.getMimeTypeForContainer(type(material)).preferredSuffix
-            filename = metadata["id"] + "." + suffix
-            archive.writestr(filename, material.serialize())

+ 1 - 0
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:

+ 6 - 2
cura/OAuth2/KeyringAttribute.py

@@ -4,7 +4,7 @@ from typing import Type, TYPE_CHECKING, Optional, List
 
 import keyring
 from keyring.backend import KeyringBackend
-from keyring.errors import NoKeyringError, PasswordSetError
+from keyring.errors import NoKeyringError, PasswordSetError, KeyringLocked
 
 from UM.Logger import Logger
 
@@ -39,6 +39,10 @@ class KeyringAttribute:
                 self._store_secure = False
                 Logger.logException("w", "No keyring backend present")
                 return getattr(instance, self._name)
+            except KeyringLocked:
+                self._store_secure = False
+                Logger.log("i", "Access to the keyring was denied.")
+                return getattr(instance, self._name)
         else:
             return getattr(instance, self._name)
 
@@ -48,7 +52,7 @@ class KeyringAttribute:
             if value is not None:
                 try:
                     keyring.set_password("cura", self._keyring_name, value)
-                except PasswordSetError:
+                except (PasswordSetError, KeyringLocked):
                     self._store_secure = False
                     if self._name not in DONT_EVER_STORE_LOCALLY:
                         setattr(instance, self._name, value)

+ 0 - 8
cura/Settings/GlobalStack.py

@@ -86,14 +86,6 @@ class GlobalStack(CuraContainerStack):
     def supportsNetworkConnection(self):
         return self.getMetaDataEntry("supports_network_connection", False)
 
-    @pyqtProperty(bool, constant = True)
-    def supportsMaterialExport(self):
-        """
-        Whether the printer supports Cura's export format of material profiles.
-        :return: ``True`` if it supports it, or ``False`` if not.
-        """
-        return self.getMetaDataEntry("supports_material_export", False)
-
     @classmethod
     def getLoadingPriority(cls) -> int:
         return 2

+ 1 - 1
cura/UI/WhatsNewPagesModel.py

@@ -29,7 +29,7 @@ class WhatsNewPagesModel(WelcomePagesModel):
                 for filename in files:
                     basename = os.path.basename(filename)
                     base, ext = os.path.splitext(basename)
-                    if ext not in include or not base.isdigit():
+                    if ext.lower() not in include or not base.isdigit():
                         continue
                     page_no = int(base)
                     highest = max(highest, page_no)

Some files were not shown because too many files changed in this diff