Browse Source

Merge pull request #5 from Ultimaker/master

Update from master CURA Repo
Gabriele Rossetti 3 years ago
parent
commit
69beba448c

+ 1 - 1
.github/ISSUE_TEMPLATE.md

@@ -1,5 +1,5 @@
 ---
-name: Bug report
+name: Old Bug report
 about: Create a report to help us fix issues.
 title: ''
 labels: 'Type: Bug'

+ 21 - 7
.github/ISSUE_TEMPLATE/bugreport.yaml

@@ -1,7 +1,6 @@
 name: Bug Report
 description: Create a report to help us fix issues.
 labels: "Type: Bug"
-issue_body: true
 body:
 - type: markdown
   attributes:
@@ -15,7 +14,7 @@ body:
   attributes:
     label: Application Version
     description: The version of Cura this issue occurs with.
-    placeholder: 4.8.0
+    placeholder: 4.9.0
   validations:
     required: true
 - type: input
@@ -56,13 +55,28 @@ body:
 - type: markdown
   attributes:
     value: |
-      ## Additional information & file uploads
-      
       Please be sure to add the following files:
         * For slicing issues, upload a **project file** that clearly shows the bug. 
           To save a project file go to `File -> Save project`. Please make sure to .zip your project file. For big files you may need to use WeTransfer or similar file sharing sites. 
           G-code files are not project files!
         * **Screenshots** of showing the problem, perhaps before/after images.
-        * A **log file**, see [here](https://github.com/Ultimaker/Cura#logging-issues) how to find the log file. 
-      
-      You can add these files and additional information that is relevant to the issue in the comments below. 
+        * A **log file** for crashes and similar issues.
+          You can find your log file here:
+          Windows: `%APPDATA%\cura\<Cura version>\cura.log` or usually `C:\Users\\<your username>\AppData\Roaming\cura\<Cura version>\cura.log`
+          MacOS: `$USER/Library/Application Support/cura/<Cura version>/cura.log`
+          Ubuntu/Linus: `$USER/.local/share/cura/<Cura version>/cura.log` 
+          
+          If the Cura user interface still starts, you can also reach this directory from the application menu in Help -> Show settings folder 
+- type: checkboxes
+  attributes:
+    label: Checklist of files to include
+    options:
+      - label: Log file
+      - label: Project file
+- type: textarea
+  attributes:
+    label: Additional information & file uploads  
+    description: You can add these files and additional information that is relevant to the issue in the comments below.
+  validations:
+    required: true
+

+ 5 - 0
.github/ISSUE_TEMPLATE/config.yml

@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+  - name: Have questions or need support?
+    url: https://community.ultimaker.com/
+    about: Please get in touch on our Ultimaker Community Forum!

+ 4 - 6
.github/ISSUE_TEMPLATE/featurerequest.yaml

@@ -1,7 +1,6 @@
 name: Feature Request
 description: Suggest an idea for this project.
 labels: "Type: New Feature"
-issue_body: true
 body:
 - type: markdown
   attributes:
@@ -28,7 +27,7 @@ body:
 - type: textarea
   attributes:
     label: Describe alternatives you've considered
-    description: A clear and concise description of any alternative solutions or features you've considered. Again, if possible, think about why these alternatives are not working out.
+    description: A clear and concise description of any alternative solutions or features you've considered. If possible, think about why these alternatives are not working out.
     placeholder: The alternatives I've considered are...
   validations:
     required: true
@@ -39,8 +38,7 @@ body:
     placeholder: It will affect...
   validations:
     required: true
-- type: markdown
+- type: textarea
   attributes:
-    value: |
-      ## Additional information & file uploads
-      You can add pictures or files to visualize your feature request in the comments below.
+    label: Additional information & file uploads
+    description: You can add pictures or files to visualize your feature request in the comments below.

+ 7 - 5
cura/API/Account.py

@@ -40,7 +40,7 @@ class Account(QObject):
     """
 
     # The interval in which sync services are automatically triggered
-    SYNC_INTERVAL = 30.0  # seconds
+    SYNC_INTERVAL = 60.0  # seconds
     Q_ENUMS(SyncState)
 
     loginStateChanged = pyqtSignal(bool)
@@ -58,6 +58,11 @@ class Account(QObject):
     manualSyncEnabledChanged = pyqtSignal(bool)
     updatePackagesEnabledChanged = pyqtSignal(bool)
 
+    CLIENT_SCOPES = "account.user.read drive.backup.read drive.backup.write packages.download " \
+                    "packages.rating.read packages.rating.write connect.cluster.read connect.cluster.write " \
+                    "library.project.read library.project.write cura.printjob.read cura.printjob.write " \
+                    "cura.mesh.read cura.mesh.write"
+
     def __init__(self, application: "CuraApplication", parent = None) -> None:
         super().__init__(parent)
         self._application = application
@@ -79,10 +84,7 @@ class Account(QObject):
             CALLBACK_PORT=self._callback_port,
             CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port),
             CLIENT_ID="um----------------------------ultimaker_cura",
-            CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download "
-                          "packages.rating.read packages.rating.write connect.cluster.read connect.cluster.write "
-                          "library.project.read library.project.write cura.printjob.read cura.printjob.write "
-                          "cura.mesh.read cura.mesh.write",
+            CLIENT_SCOPES=self.CLIENT_SCOPES,
             AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data",
             AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root),
             AUTH_FAILED_REDIRECT="{}/app/auth-error".format(self._oauth_root)

+ 1 - 1
cura/ApplicationMetadata.py

@@ -13,7 +13,7 @@ DEFAULT_CURA_DEBUG_MODE = False
 # Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for
 # example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the
 # CuraVersion.py.in template.
-CuraSDKVersion = "7.4.0"
+CuraSDKVersion = "7.5.0"
 
 try:
     from cura.CuraVersion import CuraAppName  # type: ignore

+ 4 - 3
cura/Arranging/Nest2DArrange.py

@@ -36,6 +36,7 @@ def findNodePlacement(nodes_to_arrange: List["SceneNode"], build_volume: "BuildV
         found_solution_for_all: Whether the algorithm found a place on the buildplate for all the objects
         node_items: A list of the nodes return by libnest2d, which contain the new positions on the buildplate
     """
+    spacing = int(1.5 * factor)  # 1.5mm spacing.
 
     machine_width = build_volume.getWidth()
     machine_depth = build_volume.getDepth()
@@ -75,7 +76,7 @@ def findNodePlacement(nodes_to_arrange: List["SceneNode"], build_volume: "BuildV
         # Clip the disallowed areas so that they don't overlap the bounding box (The arranger chokes otherwise)
         clipped_area = area.intersectionConvexHulls(build_plate_polygon)
 
-        if clipped_area.getPoints() is not None:  # numpy array has to be explicitly checked against None
+        if clipped_area.getPoints() is not None and len(clipped_area.getPoints()) > 2:  # numpy array has to be explicitly checked against None
             for point in clipped_area.getPoints():
                 converted_points.append(Point(int(point[0] * factor), int(point[1] * factor)))
 
@@ -88,7 +89,7 @@ def findNodePlacement(nodes_to_arrange: List["SceneNode"], build_volume: "BuildV
         converted_points = []
         hull_polygon = node.callDecoration("getConvexHull")
 
-        if hull_polygon is not None and hull_polygon.getPoints() is not None:  # numpy array has to be explicitly checked against None
+        if hull_polygon is not None and hull_polygon.getPoints() is not None and len(hull_polygon.getPoints()) > 2:  # numpy array has to be explicitly checked against None
             for point in hull_polygon.getPoints():
                 converted_points.append(Point(point[0] * factor, point[1] * factor))
             item = Item(converted_points)
@@ -99,7 +100,7 @@ def findNodePlacement(nodes_to_arrange: List["SceneNode"], build_volume: "BuildV
     config = NfpConfig()
     config.accuracy = 1.0
 
-    num_bins = nest(node_items, build_plate_bounding_box, 10000, config)
+    num_bins = nest(node_items, build_plate_bounding_box, spacing, config)
 
     # Strip the fixed items (previously placed) and the disallowed areas from the results again.
     node_items = list(filter(lambda item: not item.isFixed(), node_items))

+ 6 - 6
cura/Arranging/ShapeArray.py

@@ -3,7 +3,7 @@
 
 import numpy
 import copy
-from typing import Optional, Tuple, TYPE_CHECKING
+from typing import Optional, Tuple, TYPE_CHECKING, Union
 
 from UM.Math.Polygon import Polygon
 
@@ -14,14 +14,14 @@ if TYPE_CHECKING:
 class ShapeArray:
     """Polygon representation as an array for use with :py:class:`cura.Arranging.Arrange.Arrange`"""
 
-    def __init__(self, arr: numpy.array, offset_x: float, offset_y: float, scale: float = 1) -> None:
+    def __init__(self, arr: numpy.ndarray, offset_x: float, offset_y: float, scale: float = 1) -> None:
         self.arr = arr
         self.offset_x = offset_x
         self.offset_y = offset_y
         self.scale = scale
 
     @classmethod
-    def fromPolygon(cls, vertices: numpy.array, scale: float = 1) -> "ShapeArray":
+    def fromPolygon(cls, vertices: numpy.ndarray, scale: float = 1) -> "ShapeArray":
         """Instantiate from a bunch of vertices
 
         :param vertices:
@@ -98,7 +98,7 @@ class ShapeArray:
         return offset_shape_arr, hull_shape_arr
 
     @classmethod
-    def arrayFromPolygon(cls, shape: Tuple[int, int], vertices: numpy.array) -> numpy.array:
+    def arrayFromPolygon(cls, shape: Union[Tuple[int, int], numpy.ndarray], vertices: numpy.ndarray) -> numpy.ndarray:
         """Create :py:class:`numpy.ndarray` with dimensions defined by shape
 
         Fills polygon defined by vertices with ones, all other values zero
@@ -110,7 +110,7 @@ class ShapeArray:
         :return: numpy array with dimensions defined by shape
         """
 
-        base_array = numpy.zeros(shape, dtype = numpy.int32)  # Initialize your array of zeros
+        base_array = numpy.zeros(shape, dtype = numpy.int32)  # type: ignore # Initialize your array of zeros
 
         fill = numpy.ones(base_array.shape) * True  # Initialize boolean array defining shape fill
 
@@ -126,7 +126,7 @@ class ShapeArray:
         return base_array
 
     @classmethod
-    def _check(cls, p1: numpy.array, p2: numpy.array, base_array: numpy.array) -> Optional[numpy.array]:
+    def _check(cls, p1: numpy.ndarray, p2: numpy.ndarray, base_array: numpy.ndarray) -> Optional[numpy.ndarray]:
         """Return indices that mark one side of the line, used by arrayFromPolygon
 
         Uses the line defined by p1 and p2 to check array of

+ 5 - 3
cura/AutoSave.py

@@ -1,4 +1,4 @@
-# Copyright (c) 2019 Ultimaker B.V.
+# Copyright (c) 2021 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 
 from PyQt5.QtCore import QTimer
@@ -6,6 +6,8 @@ from typing import Any, TYPE_CHECKING
 
 from UM.Logger import Logger
 
+import time
+
 if TYPE_CHECKING:
     from cura.CuraApplication import CuraApplication
 
@@ -56,8 +58,8 @@ class AutoSave:
 
     def _onTimeout(self) -> None:
         self._saving = True # To prevent the save process from triggering another autosave.
-        Logger.log("d", "Autosaving preferences, instances and profiles")
 
+        save_start_time = time.time()
         self._application.saveSettings()
-
+        Logger.log("d", "Autosaving preferences, instances and profiles took %s seconds", time.time() - save_start_time)
         self._saving = False

+ 46 - 1
cura/Backups/Backup.py

@@ -5,6 +5,7 @@ import io
 import os
 import re
 import shutil
+from copy import deepcopy
 from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile
 from typing import Dict, Optional, TYPE_CHECKING
 
@@ -27,6 +28,9 @@ 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."""
 
+    SECRETS_SETTINGS = ["general/ultimaker_auth_data"]
+    """Secret preferences that need to obfuscated when making a backup of Cura"""
+
     catalog = i18nCatalog("cura")
     """Re-use translation catalog"""
 
@@ -43,6 +47,9 @@ class Backup:
 
         Logger.log("d", "Creating backup for Cura %s, using folder %s", cura_release, version_data_dir)
 
+        # obfuscate sensitive secrets
+        secrets = self._obfuscate()
+
         # Ensure all current settings are saved.
         self._application.saveSettings()
 
@@ -78,6 +85,8 @@ class Backup:
             "profile_count": str(profile_count),
             "plugin_count": str(plugin_count)
         }
+        # Restore the obfuscated settings
+        self._illuminate(**secrets)
 
     def _makeArchive(self, buffer: "io.BytesIO", root_path: str) -> Optional[ZipFile]:
         """Make a full archive from the given root path with the given name.
@@ -134,8 +143,16 @@ class Backup:
                                    "Tried to restore a Cura backup that is higher than the current version."))
             return False
 
+        # Get the current secrets and store since the back-up doesn't contain those
+        secrets = self._obfuscate()
+
         version_data_dir = Resources.getDataStoragePath()
-        archive = ZipFile(io.BytesIO(self.zip_file), "r")
+        try:
+            archive = ZipFile(io.BytesIO(self.zip_file), "r")
+        except LookupError as e:
+            Logger.log("d", f"The following error occurred while trying to restore a Cura backup: {str(e)}")
+            self._showMessage(self.catalog.i18nc("@info:backup_failed", "The following error occurred while trying to restore a Cura backup:") + str(e))
+            return False
         extracted = self._extractArchive(archive, version_data_dir)
 
         # Under Linux, preferences are stored elsewhere, so we copy the file to there.
@@ -146,6 +163,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)
 
+        # Restore the obfuscated settings
+        self._illuminate(**secrets)
+
         return extracted
 
     @staticmethod
@@ -173,3 +193,28 @@ class Backup:
             Logger.logException("e", "Unable to extract the backup due to permission or file system errors.")
             return False
         return True
+
+    def _obfuscate(self) -> Dict[str, str]:
+        """
+        Obfuscate and remove the secret preferences that are specified in SECRETS_SETTINGS
+
+        :return: a dictionary of the removed secrets. Note: the '/' is replaced by '__'
+        """
+        preferences = self._application.getPreferences()
+        secrets = {}
+        for secret in self.SECRETS_SETTINGS:
+            secrets[secret.replace("/", "__")] = deepcopy(preferences.getValue(secret))
+            preferences.setValue(secret, None)
+        self._application.savePreferences()
+        return secrets
+
+    def _illuminate(self, **kwargs) -> None:
+        """
+        Restore the obfuscated settings
+
+        :param kwargs: a dict of obscured preferences. Note: the '__' of the keys will be replaced by '/'
+        """
+        preferences = self._application.getPreferences()
+        for key, value in kwargs.items():
+            preferences.setValue(key.replace("__", "/"), value)
+        self._application.savePreferences()

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