Browse Source

Merge branch 'WIP_onboarding' of https://github.com/Ultimaker/Cura into WIP_onboarding

Remco Burema 6 years ago
parent
commit
5f335b4401

+ 19 - 0
contributing.md

@@ -0,0 +1,19 @@
+Submitting bug reports
+----------------------
+Please submit bug reports for all of Cura and CuraEngine to the [Cura repository](https://github.com/Ultimaker/Cura/issues). There will be a template there to fill in. Depending on the type of issue, we will usually ask for the [Cura log](Logging Issues) or a project file.
+
+If a bug report would contain private information, such as a proprietary 3D model, you may also e-mail us. Ask for contact information in the issue.
+
+Bugs related to supporting certain types of printers can usually not be solved by the Cura maintainers, since we don't have access to every 3D printer model in the world either. We have to rely on external contributors to fix this. If it's something simple and obvious, such as a mistake in the start g-code, then we can directly fix it for you, but e.g. issues with USB cable connectivity are impossible for us to debug.
+
+Requesting features
+-------------------
+The issue template in the Cura repository does not apply to feature requests. You can ignore it.
+
+When requesting a feature, please describe clearly what you need and why you think this is valuable to users or what problem it solves.
+
+Making pull requests
+--------------------
+If you want to propose a change to Cura's source code, please create a pull request in the appropriate repository (being [Cura](https://github.com/Ultimaker/Cura), [Uranium](https://github.com/Ultimaker/Uranium), [CuraEngine](https://github.com/Ultimaker/CuraEngine), [fdm_materials](https://github.com/Ultimaker/fdm_materials), [libArcus](https://github.com/Ultimaker/libArcus), [cura-build](https://github.com/Ultimaker/cura-build), [cura-build-environment](https://github.com/Ultimaker/cura-build-environment), [libSavitar](https://github.com/Ultimaker/libSavitar), [libCharon](https://github.com/Ultimaker/libCharon) or [cura-binary-data](https://github.com/Ultimaker/cura-binary-data)) and if your change requires changes on multiple of these repositories, please link them together so that we know to merge them together.
+
+Some of these repositories will have automated tests running when you create a pull request, indicated by green check marks or red crosses in the Github web page. If you see a red cross, that means that a test has failed. If the test doesn't fail on the Master branch but does fail on your branch, that indicates that you've probably made a mistake and you need to do that. Click on the cross for more details, or run the test locally by running `cmake . && ctest --verbose`.

+ 1 - 0
cura/CuraApplication.py

@@ -749,6 +749,7 @@ class CuraApplication(QtApplication):
         # Initialize Cura API
         self._cura_API.initialize()
 
+        self._output_device_manager.start()
         self._welcome_pages_model.initialize()
 
         # Detect in which mode to run and execute that mode

+ 8 - 2
cura/OAuth2/AuthorizationHelpers.py

@@ -41,7 +41,10 @@ class AuthorizationHelpers:
             "code_verifier": verification_code,
             "scope": self._settings.CLIENT_SCOPES if self._settings.CLIENT_SCOPES is not None else "",
             }
-        return self.parseTokenResponse(requests.post(self._token_url, data = data))  # type: ignore
+        try:
+            return self.parseTokenResponse(requests.post(self._token_url, data = data))  # type: ignore
+        except requests.exceptions.ConnectionError:
+            return AuthenticationResponse(success=False, err_message="Unable to connect to remote server")
 
     ##  Request the access token from the authorization server using a refresh token.
     #   \param refresh_token:
@@ -54,7 +57,10 @@ class AuthorizationHelpers:
             "refresh_token": refresh_token,
             "scope": self._settings.CLIENT_SCOPES if self._settings.CLIENT_SCOPES is not None else "",
         }
-        return self.parseTokenResponse(requests.post(self._token_url, data = data))  # type: ignore
+        try:
+            return self.parseTokenResponse(requests.post(self._token_url, data = data))  # type: ignore
+        except requests.exceptions.ConnectionError:
+            return AuthenticationResponse(success=False, err_message="Unable to connect to remote server")
 
     @staticmethod
     ##  Parse the token response from the authorization server into an AuthenticationResponse object.

+ 7 - 3
cura/OAuth2/AuthorizationService.py

@@ -108,7 +108,7 @@ class AuthorizationService:
         # We have a fallback on a date far in the past for currently stored auth data in cura.cfg.
         received_at = datetime.strptime(self._auth_data.received_at, TOKEN_TIMESTAMP_FORMAT) \
             if self._auth_data.received_at else datetime(2000, 1, 1)
-        expiry_date = received_at + timedelta(seconds = float(self._auth_data.expires_in or 0))
+        expiry_date = received_at + timedelta(seconds = float(self._auth_data.expires_in or 0) - 60)
         if datetime.now() > expiry_date:
             self.refreshAccessToken()
 
@@ -119,8 +119,12 @@ class AuthorizationService:
         if self._auth_data is None or self._auth_data.refresh_token is None:
             Logger.log("w", "Unable to refresh access token, since there is no refresh token.")
             return
-        self._storeAuthData(self._auth_helpers.getAccessTokenUsingRefreshToken(self._auth_data.refresh_token))
-        self.onAuthStateChanged.emit(logged_in = True)
+        response = self._auth_helpers.getAccessTokenUsingRefreshToken(self._auth_data.refresh_token)
+        if response.success:
+            self._storeAuthData(response)
+            self.onAuthStateChanged.emit(logged_in = True)
+        else:
+            self.onAuthStateChanged(logged_in = False)
 
     ##  Delete the authentication data that we have stored locally (eg; logout)
     def deleteAuthData(self) -> None:

+ 2 - 2
cura/Settings/ExtrudersModel.py

@@ -134,8 +134,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
         self._updateExtruders()  # Since the new extruders may have different properties, update our own model.
 
     def _onExtruderStackContainersChanged(self, container):
-        # Update when there is an empty container or material change
-        if container.getMetaDataEntry("type") == "material" or container.getMetaDataEntry("type") is None:
+        # Update when there is an empty container or material or variant change
+        if container.getMetaDataEntry("type") in ["material", "variant", None]:
             # The ExtrudersModel needs to be updated when the material-name or -color changes, because the user identifies extruders by material-name
             self._updateExtruders()
 

+ 41 - 4
cura/Settings/MachineManager.py

@@ -4,7 +4,7 @@
 import time
 import re
 import unicodedata
-from typing import Any, List, Dict, TYPE_CHECKING, Optional, cast
+from typing import Any, List, Dict, TYPE_CHECKING, Optional, cast, NamedTuple, Callable
 
 from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
 from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@@ -49,6 +49,8 @@ if TYPE_CHECKING:
     from cura.Machines.QualityChangesGroup import QualityChangesGroup
     from cura.Machines.QualityGroup import QualityGroup
 
+DiscoveredPrinter = NamedTuple("DiscoveredPrinter", [("key", str), ("name", str), ("create_callback", Callable[[str], None]), ("machine_type", "str")])
+
 
 class MachineManager(QObject):
     def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None:
@@ -134,6 +136,9 @@ class MachineManager(QObject):
         self.globalContainerChanged.connect(self.printerConnectedStatusChanged)
         self.outputDevicesChanged.connect(self.printerConnectedStatusChanged)
 
+        # This will contain all discovered network printers
+        self._discovered_printers = {}  # type: Dict[str, DiscoveredPrinter]
+
     activeQualityGroupChanged = pyqtSignal()
     activeQualityChangesGroupChanged = pyqtSignal()
 
@@ -157,6 +162,7 @@ class MachineManager(QObject):
     printerConnectedStatusChanged = pyqtSignal() # Emitted every time the active machine change or the outputdevices change
 
     rootMaterialChanged = pyqtSignal()
+    discoveredPrintersChanged = pyqtSignal()
 
     def setInitialActiveMachine(self) -> None:
         active_machine_id = self._application.getPreferences().getValue("cura/active_machine")
@@ -171,7 +177,30 @@ class MachineManager(QObject):
                 self._printer_output_devices.append(printer_output_device)
 
         self.outputDevicesChanged.emit()
-        self.printerConnectedStatusChanged.emit()
+
+    #   Discovered printers are all the printers that were found on the network, which provide a more convenient way
+    #   to add networked printers (Plugin finds a bunch of printers, user can select one from the list, plugin can then
+    #   add that printer to Cura as the active one).
+    def addDiscoveredPrinter(self, key: str, name: str, create_callback: Callable[[str], None], machine_type: str) -> None:
+        if key not in self._discovered_printers:
+            self._discovered_printers[key] = DiscoveredPrinter(key, name, create_callback, machine_type)
+            self.discoveredPrintersChanged.emit()
+        else:
+            Logger.log("e", "Printer with the key %s was already in the discovered printer list", key)
+
+    def removeDiscoveredPrinter(self, key: str) -> None:
+        if key in self._discovered_printers:
+            del self._discovered_printers[key]
+            self.discoveredPrintersChanged.emit()
+
+    @pyqtProperty("QVariantList", notify = discoveredPrintersChanged)
+    def discoveredPrinters(self):
+        return list(self._discovered_printers.values())
+
+    @pyqtSlot(str)
+    def addMachineFromDiscoveredPrinter(self, key: str) -> None:
+        if key in self._discovered_printers:
+            self._discovered_printers[key].create_callback(key)
 
     @pyqtProperty(QObject, notify = currentConfigurationChanged)
     def currentConfiguration(self) -> ConfigurationModel:
@@ -386,9 +415,17 @@ class MachineManager(QObject):
                 return machine
         return None
 
+    @pyqtSlot(str)
     @pyqtSlot(str, str)
-    def addMachine(self, name: str, definition_id: str) -> None:
-        new_stack = CuraStackBuilder.createMachine(name, definition_id)
+    def addMachine(self, definition_id: str, name: Optional[str] = None) -> None:
+        if name is None:
+            definitions = CuraContainerRegistry.getInstance().findDefinitionContainers(id = definition_id)
+            if definitions:
+                name = definitions[0].getName()
+            else:
+                name = definition_id
+
+        new_stack = CuraStackBuilder.createMachine(cast(str, name), definition_id)
         if new_stack:
             # Instead of setting the global container stack here, we set the active machine and so the signals are emitted
             self.setActiveMachine(new_stack.getId())

+ 7 - 1
plugins/CuraDrive/src/DriveApiService.py

@@ -54,7 +54,13 @@ class DriveApiService:
             Logger.log("w", "Could not get backups list from remote: %s", backup_list_request.text)
             Message(catalog.i18nc("@info:backup_status", "There was an error listing your backups."), title = catalog.i18nc("@info:title", "Backup")).show()
             return []
-        return backup_list_request.json()["data"]
+
+        backup_list_response = backup_list_request.json()
+        if "data" not in backup_list_response:
+            Logger.log("w", "Could not get backups from remote, actual response body was: %s", str(backup_list_response))
+            return []
+
+        return backup_list_response["data"]
 
     def createBackup(self) -> None:
         self.creatingStateChanged.emit(is_creating = True)

+ 1 - 0
plugins/PrepareStage/PrepareMenu.qml

@@ -58,6 +58,7 @@ Item
 
             Cura.ConfigurationMenu
             {
+                id: printerSetup
                 Layout.fillHeight: true
                 Layout.fillWidth: true
                 Layout.preferredWidth: itemRow.width - machineSelection.width - printSetupSelectorItem.width - 2 * UM.Theme.getSize("default_lining").width

+ 2 - 2
plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml

@@ -76,12 +76,12 @@ Item
             height: (parent.height * 0.4) | 0
             anchors
             {
-                bottom: parent.bottom
+                bottom: parent.bottomcommi
                 right: parent.right
             }
             sourceSize.height: height
             visible: installedPackages != 0
-            color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
+            color: (installedPackages >= packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
             source: "../images/installed_check.svg"
         }
     }

+ 1 - 1
plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml

@@ -61,7 +61,7 @@ Rectangle
             right: parent.right
         }
         visible: installedPackages != 0
-        color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
+        color: (installedPackages >= packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
         source: "../images/installed_check.svg"
     }
 

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