Browse Source

Revert "Revert CURA-8055 in the 4.10 branch. This should not have been merged in there."

This reverts commit 4d29de45796dc42f76c774a216a0601bb57cf790.
Effectively this re-applies the changes for CURA-8055 / #9957.
Ghostkeeper 3 years ago
parent
commit
f82384d93c

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

@@ -2,9 +2,10 @@
 # Cura is released under the terms of the LGPLv3 or higher.
 # Cura is released under the terms of the LGPLv3 or higher.
 
 
 import copy  # To duplicate materials.
 import copy  # To duplicate materials.
-from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot  # To allow the preference page proxy to be used from the actual preferences page.
+from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl
 from typing import Any, Dict, Optional, TYPE_CHECKING
 from typing import Any, Dict, Optional, TYPE_CHECKING
 import uuid  # To generate new GUIDs for new materials.
 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.i18n import i18nCatalog
 from UM.Logger import Logger
 from UM.Logger import Logger
@@ -24,6 +25,11 @@ class MaterialManagementModel(QObject):
 
 
     This class handles the actions in that page, such as creating new materials, renaming them, etc.
     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)
     favoritesChanged = pyqtSignal(str)
     """Triggered when a favorite is added or removed.
     """Triggered when a favorite is added or removed.
@@ -264,3 +270,52 @@ class MaterialManagementModel(QObject):
             self.favoritesChanged.emit(material_base_file)
             self.favoritesChanged.emit(material_base_file)
         except ValueError:  # Material was not in the favorites list.
         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))
             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())

+ 8 - 0
cura/Settings/GlobalStack.py

@@ -86,6 +86,14 @@ class GlobalStack(CuraContainerStack):
     def supportsNetworkConnection(self):
     def supportsNetworkConnection(self):
         return self.getMetaDataEntry("supports_network_connection", False)
         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
     @classmethod
     def getLoadingPriority(cls) -> int:
     def getLoadingPriority(cls) -> int:
         return 2
         return 2

+ 2 - 1
resources/definitions/ultimaker2_plus_connect.def.json

@@ -22,7 +22,8 @@
             "0": "ultimaker2_plus_connect_extruder_0"
             "0": "ultimaker2_plus_connect_extruder_0"
         },
         },
         "supports_usb_connection": false,
         "supports_usb_connection": false,
-        "supports_network_connection": true
+        "supports_network_connection": true,
+        "supports_material_export": true
     },
     },
 
 
     "overrides": {
     "overrides": {

+ 1 - 0
resources/definitions/ultimaker_s3.def.json

@@ -27,6 +27,7 @@
         "first_start_actions": [ "DiscoverUM3Action" ],
         "first_start_actions": [ "DiscoverUM3Action" ],
         "supported_actions": [ "DiscoverUM3Action" ],
         "supported_actions": [ "DiscoverUM3Action" ],
         "supports_usb_connection": false,
         "supports_usb_connection": false,
+        "supports_material_export": true,
         "weight": -1,
         "weight": -1,
         "firmware_update_info": {
         "firmware_update_info": {
             "id": 213482,
             "id": 213482,

+ 1 - 0
resources/definitions/ultimaker_s5.def.json

@@ -28,6 +28,7 @@
         "supported_actions": [ "DiscoverUM3Action" ],
         "supported_actions": [ "DiscoverUM3Action" ],
         "supports_usb_connection": false,
         "supports_usb_connection": false,
         "supports_network_connection": true,
         "supports_network_connection": true,
+        "supports_material_export": true,
         "weight": -2,
         "weight": -2,
         "firmware_update_info": {
         "firmware_update_info": {
             "id": 9051,
             "id": 9051,

+ 30 - 2
resources/qml/Preferences/Materials/MaterialsPage.qml

@@ -1,5 +1,5 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Uranium is released under the terms of the LGPLv3 or higher.
+// Copyright (c) 2021 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
 
 
 import QtQuick 2.7
 import QtQuick 2.7
 import QtQuick.Controls 1.4
 import QtQuick.Controls 1.4
@@ -191,6 +191,20 @@ Item
             }
             }
             enabled: base.hasCurrentItem
             enabled: base.hasCurrentItem
         }
         }
+
+        //Sync button.
+        Button
+        {
+            id: syncMaterialsButton
+            text: catalog.i18nc("@action:button Sending materials to printers", "Sync with Printers")
+            iconName: "sync-synchronizing"
+            onClicked:
+            {
+                forceActiveFocus();
+                exportAllMaterialsDialog.open();
+            }
+            visible: Cura.MachineManager.activeMachine.supportsMaterialExport
+        }
     }
     }
 
 
     Item {
     Item {
@@ -368,6 +382,20 @@ Item
         }
         }
     }
     }
 
 
+    FileDialog
+    {
+        id: exportAllMaterialsDialog
+        title: catalog.i18nc("@title:window", "Export All Materials")
+        selectExisting: false
+        nameFilters: ["Material archives (*.umm)", "All files (*)"]
+        folder: base.materialManagementModel.preferredExportAllPath
+        onAccepted:
+        {
+            base.materialManagementModel.exportAll(fileUrl);
+            CuraApplication.setDefaultPath("dialog_material_path", folder);
+        }
+    }
+
     MessageDialog
     MessageDialog
     {
     {
         id: messageDialog
         id: messageDialog