diff --git a/cura/API/Account.py b/cura/API/Account.py
index 5fab3e4a06..a04f97ef1c 100644
--- a/cura/API/Account.py
+++ b/cura/API/Account.py
@@ -6,7 +6,7 @@ from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty
from UM.i18n import i18nCatalog
from UM.Message import Message
-from cura import CuraConstants
+from cura import UltimakerCloudAuthentication
from cura.OAuth2.AuthorizationService import AuthorizationService
from cura.OAuth2.Models import OAuth2Settings
@@ -38,7 +38,7 @@ class Account(QObject):
self._logged_in = False
self._callback_port = 32118
- self._oauth_root = CuraConstants.CuraCloudAccountAPIRoot
+ self._oauth_root = UltimakerCloudAuthentication.CuraCloudAccountAPIRoot
self._oauth_settings = OAuth2Settings(
OAUTH_SERVER_URL= self._oauth_root,
diff --git a/cura/API/Backups.py b/cura/API/Backups.py
index 8e5cd7b83a..ef74e74be0 100644
--- a/cura/API/Backups.py
+++ b/cura/API/Backups.py
@@ -1,6 +1,6 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
-from typing import Tuple, Optional, TYPE_CHECKING
+from typing import Tuple, Optional, TYPE_CHECKING, Dict, Any
from cura.Backups.BackupsManager import BackupsManager
@@ -24,12 +24,12 @@ class Backups:
## Create a new back-up using the BackupsManager.
# \return Tuple containing a ZIP file with the back-up data and a dict
# with metadata about the back-up.
- def createBackup(self) -> Tuple[Optional[bytes], Optional[dict]]:
+ def createBackup(self) -> Tuple[Optional[bytes], Optional[Dict[str, Any]]]:
return self.manager.createBackup()
## Restore a back-up using the BackupsManager.
# \param zip_file A ZIP file containing the actual back-up data.
# \param meta_data Some metadata needed for restoring a back-up, like the
# Cura version number.
- def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None:
+ def restoreBackup(self, zip_file: bytes, meta_data: Dict[str, Any]) -> None:
return self.manager.restoreBackup(zip_file, meta_data)
diff --git a/cura/CuraConstants.py b/cura/ApplicationMetadata.py
similarity index 50%
rename from cura/CuraConstants.py
rename to cura/ApplicationMetadata.py
index 7ca8ea865b..e2ac4453eb 100644
--- a/cura/CuraConstants.py
+++ b/cura/ApplicationMetadata.py
@@ -1,60 +1,36 @@
-#
-# This file contains all constant values in Cura
-#
-
-# -------------
-# Cura Versions
-# -------------
-DEFAULT_CURA_DISPLAY_NAME = "Ultimaker Cura"
-DEFAULT_CURA_VERSION = "master"
-DEFAULT_CURA_BUILD_TYPE = ""
-DEFAULT_CURA_DEBUG_MODE = False
-DEFAULT_CURA_SDK_VERSION = "5.0.0"
-
-try:
- from cura.CuraVersion import CuraAppDisplayName # type: ignore
-except ImportError:
- CuraAppDisplayName = DEFAULT_CURA_DISPLAY_NAME
-
-try:
- from cura.CuraVersion import CuraVersion # type: ignore
-except ImportError:
- CuraVersion = DEFAULT_CURA_VERSION # [CodeStyle: Reflecting imported value]
-
-try:
- from cura.CuraVersion import CuraBuildType # type: ignore
-except ImportError:
- CuraBuildType = DEFAULT_CURA_BUILD_TYPE
-
-try:
- from cura.CuraVersion import CuraDebugMode # type: ignore
-except ImportError:
- CuraDebugMode = DEFAULT_CURA_DEBUG_MODE
-
-try:
- from cura.CuraVersion import CuraSDKVersion # type: ignore
-except ImportError:
- CuraSDKVersion = DEFAULT_CURA_SDK_VERSION
-
-
-# ---------
-# Cloud API
-# ---------
-DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str
-DEFAULT_CLOUD_API_VERSION = "1" # type: str
-DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str
-
-try:
- from cura.CuraVersion import CuraCloudAPIRoot # type: ignore
-except ImportError:
- CuraCloudAPIRoot = DEFAULT_CLOUD_API_ROOT
-
-try:
- from cura.CuraVersion import CuraCloudAPIVersion # type: ignore
-except ImportError:
- CuraCloudAPIVersion = DEFAULT_CLOUD_API_VERSION
-
-try:
- from cura.CuraVersion import CuraCloudAccountAPIRoot # type: ignore
-except ImportError:
- CuraCloudAccountAPIRoot = DEFAULT_CLOUD_ACCOUNT_API_ROOT
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+# ---------
+# Genearl constants used in Cura
+# ---------
+DEFAULT_CURA_DISPLAY_NAME = "Ultimaker Cura"
+DEFAULT_CURA_VERSION = "master"
+DEFAULT_CURA_BUILD_TYPE = ""
+DEFAULT_CURA_DEBUG_MODE = False
+DEFAULT_CURA_SDK_VERSION = "6.0.0"
+
+try:
+ from cura.CuraVersion import CuraAppDisplayName # type: ignore
+except ImportError:
+ CuraAppDisplayName = DEFAULT_CURA_DISPLAY_NAME
+
+try:
+ from cura.CuraVersion import CuraVersion # type: ignore
+except ImportError:
+ CuraVersion = DEFAULT_CURA_VERSION # [CodeStyle: Reflecting imported value]
+
+try:
+ from cura.CuraVersion import CuraBuildType # type: ignore
+except ImportError:
+ CuraBuildType = DEFAULT_CURA_BUILD_TYPE
+
+try:
+ from cura.CuraVersion import CuraDebugMode # type: ignore
+except ImportError:
+ CuraDebugMode = DEFAULT_CURA_DEBUG_MODE
+
+try:
+ from cura.CuraVersion import CuraSDKVersion # type: ignore
+except ImportError:
+ CuraSDKVersion = DEFAULT_CURA_SDK_VERSION
diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py
index ee1df5fb2e..cf4e85f850 100755
--- a/cura/CuraApplication.py
+++ b/cura/CuraApplication.py
@@ -117,7 +117,7 @@ from cura.ObjectsModel import ObjectsModel
from cura.PrinterOutputDevice import PrinterOutputDevice
from cura.PrinterOutput.NetworkMJPGImage import NetworkMJPGImage
-from cura import CuraConstants
+from cura import ApplicationMetadata
from UM.FlameProfiler import pyqtSlot
from UM.Decorators import override
@@ -167,11 +167,11 @@ class CuraApplication(QtApplication):
def __init__(self, *args, **kwargs):
super().__init__(name = "cura",
- app_display_name = CuraConstants.CuraAppDisplayName,
- version = CuraConstants.CuraVersion,
- api_version = CuraConstants.CuraSDKVersion,
- buildtype = CuraConstants.CuraBuildType,
- is_debug_mode = CuraConstants.CuraDebugMode,
+ app_display_name = ApplicationMetadata.CuraAppDisplayName,
+ version = ApplicationMetadata.CuraVersion,
+ api_version = ApplicationMetadata.CuraSDKVersion,
+ buildtype = ApplicationMetadata.CuraBuildType,
+ is_debug_mode = ApplicationMetadata.CuraDebugMode,
tray_icon_name = "cura-icon-32.png",
**kwargs)
@@ -957,7 +957,7 @@ class CuraApplication(QtApplication):
engine.rootContext().setContextProperty("CuraApplication", self)
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
- engine.rootContext().setContextProperty("CuraSDKVersion", CuraConstants.CuraSDKVersion)
+ engine.rootContext().setContextProperty("CuraSDKVersion", ApplicationMetadata.CuraSDKVersion)
qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
diff --git a/cura/CuraVersion.py.in b/cura/CuraVersion.py.in
index 7c6304231d..770a0efd7b 100644
--- a/cura/CuraVersion.py.in
+++ b/cura/CuraVersion.py.in
@@ -8,3 +8,4 @@ CuraDebugMode = True if "@_cura_debugmode@" == "ON" else False
CuraSDKVersion = "@CURA_SDK_VERSION@"
CuraCloudAPIRoot = "@CURA_CLOUD_API_ROOT@"
CuraCloudAPIVersion = "@CURA_CLOUD_API_VERSION@"
+CuraCloudAccountAPIRoot = "@CURA_CLOUD_ACCOUNT_API_ROOT@"
diff --git a/cura/UltimakerCloudAuthentication.py b/cura/UltimakerCloudAuthentication.py
new file mode 100644
index 0000000000..ac752231b9
--- /dev/null
+++ b/cura/UltimakerCloudAuthentication.py
@@ -0,0 +1,24 @@
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+# ---------
+# Constants used for the Cloud API
+# ---------
+DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str
+DEFAULT_CLOUD_API_VERSION = 1 # type: int
+DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str
+
+try:
+ from cura.CuraVersion import CuraCloudAPIRoot # type: ignore
+except ImportError:
+ CuraCloudAPIRoot = DEFAULT_CLOUD_API_ROOT
+
+try:
+ from cura.CuraVersion import CuraCloudAPIVersion # type: ignore
+except ImportError:
+ CuraCloudAPIVersion = DEFAULT_CLOUD_API_VERSION
+
+try:
+ from cura.CuraVersion import CuraCloudAccountAPIRoot # type: ignore
+except ImportError:
+ CuraCloudAccountAPIRoot = DEFAULT_CLOUD_ACCOUNT_API_ROOT
diff --git a/plugins/CuraDrive/__init__.py b/plugins/CuraDrive/__init__.py
index 6612a5d614..eeb6b78689 100644
--- a/plugins/CuraDrive/__init__.py
+++ b/plugins/CuraDrive/__init__.py
@@ -1,14 +1,12 @@
-# Copyright (c) 2017 Ultimaker B.V.
-import os
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
-is_testing = os.getenv('ENV_NAME', "development") == "testing"
+from .src.DrivePluginExtension import DrivePluginExtension
-# Only load the whole plugin when not running tests as __init__.py is automatically loaded by PyTest
-if not is_testing:
- from .src.DrivePluginExtension import DrivePluginExtension
- def getMetaData():
- return {}
+def getMetaData():
+ return {}
- def register(app):
- return {"extension": DrivePluginExtension(app)}
+
+def register(app):
+ return {"extension": DrivePluginExtension()}
diff --git a/plugins/CuraDrive/plugin.json b/plugins/CuraDrive/plugin.json
index 6cf1fa273c..d1cab39ca5 100644
--- a/plugins/CuraDrive/plugin.json
+++ b/plugins/CuraDrive/plugin.json
@@ -3,6 +3,6 @@
"author": "Ultimaker B.V.",
"description": "Backup and restore your configuration.",
"version": "1.2.0",
- "api": 5,
+ "api": 6,
"i18n-catalog": "cura"
}
diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py
index 98199c91cf..7c1f8faa83 100644
--- a/plugins/CuraDrive/src/DriveApiService.py
+++ b/plugins/CuraDrive/src/DriveApiService.py
@@ -1,4 +1,6 @@
-# Copyright (c) 2017 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
import base64
import hashlib
from datetime import datetime
@@ -9,56 +11,55 @@ import requests
from UM.Logger import Logger
from UM.Message import Message
-from UM.Signal import Signal
+from UM.Signal import Signal, signalemitter
+from cura.CuraApplication import CuraApplication
from .UploadBackupJob import UploadBackupJob
from .Settings import Settings
+from UM.i18n import i18nCatalog
+catalog = i18nCatalog("cura")
+
+## The DriveApiService is responsible for interacting with the CuraDrive API and Cura's backup handling.
+@signalemitter
class DriveApiService:
- """
- The DriveApiService is responsible for interacting with the CuraDrive API and Cura's backup handling.
- """
-
- GET_BACKUPS_URL = "{}/backups".format(Settings.DRIVE_API_URL)
- PUT_BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL)
- DELETE_BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL)
+ BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL)
# Emit signal when restoring backup started or finished.
- onRestoringStateChanged = Signal()
+ restoringStateChanged = Signal()
# Emit signal when creating backup started or finished.
- onCreatingStateChanged = Signal()
+ creatingStateChanged = Signal()
- def __init__(self, cura_api) -> None:
- """Create a new instance of the Drive API service and set the cura_api object."""
- self._cura_api = cura_api
+ def __init__(self) -> None:
+ self._cura_api = CuraApplication.getInstance().getCuraAPI()
def getBackups(self) -> List[Dict[str, Any]]:
- """Get all backups from the API."""
access_token = self._cura_api.account.accessToken
if not access_token:
Logger.log("w", "Could not get access token.")
return []
- backup_list_request = requests.get(self.GET_BACKUPS_URL, headers={
+ backup_list_request = requests.get(self.BACKUP_URL, headers = {
"Authorization": "Bearer {}".format(access_token)
})
- if backup_list_request.status_code > 299:
+
+ # HTTP status 300s mean redirection. 400s and 500s are errors.
+ # Technically 300s are not errors, but the use case here relies on "requests" to handle redirects automatically.
+ if backup_list_request.status_code >= 300:
Logger.log("w", "Could not get backups list from remote: %s", backup_list_request.text)
- Message(Settings.translatable_messages["get_backups_error"], title = Settings.MESSAGE_TITLE,
- lifetime = 10).show()
+ 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"]
def createBackup(self) -> None:
- """Create a backup and upload it to CuraDrive cloud storage."""
- self.onCreatingStateChanged.emit(is_creating=True)
+ self.creatingStateChanged.emit(is_creating = True)
# Create the backup.
backup_zip_file, backup_meta_data = self._cura_api.backups.createBackup()
if not backup_zip_file or not backup_meta_data:
- self.onCreatingStateChanged.emit(is_creating=False, error_message="Could not create backup.")
+ self.creatingStateChanged.emit(is_creating = False, error_message ="Could not create backup.")
return
# Create an upload entry for the backup.
@@ -66,7 +67,7 @@ class DriveApiService:
backup_meta_data["description"] = "{}.backup.{}.cura.zip".format(timestamp, backup_meta_data["cura_release"])
backup_upload_url = self._requestBackupUpload(backup_meta_data, len(backup_zip_file))
if not backup_upload_url:
- self.onCreatingStateChanged.emit(is_creating=False, error_message="Could not upload backup.")
+ self.creatingStateChanged.emit(is_creating = False, error_message ="Could not upload backup.")
return
# Upload the backup to storage.
@@ -75,35 +76,27 @@ class DriveApiService:
upload_backup_job.start()
def _onUploadFinished(self, job: "UploadBackupJob") -> None:
- """
- Callback handler for the upload job.
- :param job: The executed job.
- """
if job.backup_upload_error_message != "":
# If the job contains an error message we pass it along so the UI can display it.
- self.onCreatingStateChanged.emit(is_creating=False, error_message=job.backup_upload_error_message)
+ self.creatingStateChanged.emit(is_creating = False, error_message = job.backup_upload_error_message)
else:
- self.onCreatingStateChanged.emit(is_creating=False)
+ self.creatingStateChanged.emit(is_creating = False)
def restoreBackup(self, backup: Dict[str, Any]) -> None:
- """
- Restore a previously exported backup from cloud storage.
- :param backup: A dict containing an entry from the API list response.
- """
- self.onRestoringStateChanged.emit(is_restoring=True)
+ self.restoringStateChanged.emit(is_restoring = True)
download_url = backup.get("download_url")
if not download_url:
# If there is no download URL, we can't restore the backup.
return self._emitRestoreError()
- download_package = requests.get(download_url, stream=True)
- if download_package.status_code != 200:
+ download_package = requests.get(download_url, stream = True)
+ if download_package.status_code >= 300:
# Something went wrong when attempting to download the backup.
Logger.log("w", "Could not download backup from url %s: %s", download_url, download_package.text)
return self._emitRestoreError()
# We store the file in a temporary path fist to ensure integrity.
- temporary_backup_file = NamedTemporaryFile(delete=False)
+ temporary_backup_file = NamedTemporaryFile(delete = False)
with open(temporary_backup_file.name, "wb") as write_backup:
for chunk in download_package:
write_backup.write(chunk)
@@ -116,69 +109,59 @@ class DriveApiService:
# Tell Cura to place the backup back in the user data folder.
with open(temporary_backup_file.name, "rb") as read_backup:
- self._cura_api.backups.restoreBackup(read_backup.read(), backup.get("data"))
- self.onRestoringStateChanged.emit(is_restoring=False)
+ self._cura_api.backups.restoreBackup(read_backup.read(), backup.get("metadata", {}))
+ self.restoringStateChanged.emit(is_restoring = False)
- def _emitRestoreError(self, error_message: str = Settings.translatable_messages["backup_restore_error_message"]):
- """Helper method for emitting a signal when restoring failed."""
- self.onRestoringStateChanged.emit(
- is_restoring=False,
- error_message=error_message
- )
+ def _emitRestoreError(self) -> None:
+ self.restoringStateChanged.emit(is_restoring = False,
+ error_message = catalog.i18nc("@info:backup_status",
+ "There was an error trying to restore your backup."))
+ # Verify the MD5 hash of a file.
+ # \param file_path Full path to the file.
+ # \param known_hash The known MD5 hash of the file.
+ # \return: Success or not.
@staticmethod
def _verifyMd5Hash(file_path: str, known_hash: str) -> bool:
- """
- Verify the MD5 hash of a file.
- :param file_path: Full path to the file.
- :param known_hash: The known MD5 hash of the file.
- :return: Success or not.
- """
with open(file_path, "rb") as read_backup:
- local_md5_hash = base64.b64encode(hashlib.md5(read_backup.read()).digest(), altchars=b"_-").decode("utf-8")
+ local_md5_hash = base64.b64encode(hashlib.md5(read_backup.read()).digest(), altchars = b"_-").decode("utf-8")
return known_hash == local_md5_hash
def deleteBackup(self, backup_id: str) -> bool:
- """
- Delete a backup from the server by ID.
- :param backup_id: The ID of the backup to delete.
- :return: Success bool.
- """
access_token = self._cura_api.account.accessToken
if not access_token:
Logger.log("w", "Could not get access token.")
return False
- delete_backup = requests.delete("{}/{}".format(self.DELETE_BACKUP_URL, backup_id), headers = {
+ delete_backup = requests.delete("{}/{}".format(self.BACKUP_URL, backup_id), headers = {
"Authorization": "Bearer {}".format(access_token)
})
- if delete_backup.status_code > 299:
+ if delete_backup.status_code >= 300:
Logger.log("w", "Could not delete backup: %s", delete_backup.text)
return False
return True
+ # Request a backup upload slot from the API.
+ # \param backup_metadata: A dict containing some meta data about the backup.
+ # \param backup_size The size of the backup file in bytes.
+ # \return: The upload URL for the actual backup file if successful, otherwise None.
def _requestBackupUpload(self, backup_metadata: Dict[str, Any], backup_size: int) -> Optional[str]:
- """
- Request a backup upload slot from the API.
- :param backup_metadata: A dict containing some meta data about the backup.
- :param backup_size: The size of the backup file in bytes.
- :return: The upload URL for the actual backup file if successful, otherwise None.
- """
access_token = self._cura_api.account.accessToken
if not access_token:
Logger.log("w", "Could not get access token.")
return None
- backup_upload_request = requests.put(self.PUT_BACKUP_URL, json={
+ backup_upload_request = requests.put(self.BACKUP_URL, json = {
"data": {
"backup_size": backup_size,
"metadata": backup_metadata
}
- }, headers={
+ }, headers = {
"Authorization": "Bearer {}".format(access_token)
})
-
- if backup_upload_request.status_code > 299:
+
+ # Any status code of 300 or above indicates an error.
+ if backup_upload_request.status_code >= 300:
Logger.log("w", "Could not request backup upload: %s", backup_upload_request.text)
return None
diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py
index 7e1472b988..060f1496f1 100644
--- a/plugins/CuraDrive/src/DrivePluginExtension.py
+++ b/plugins/CuraDrive/src/DrivePluginExtension.py
@@ -1,22 +1,26 @@
-# Copyright (c) 2017 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
import os
from datetime import datetime
-from typing import Optional
+from typing import Optional, List, Dict, Any
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
from UM.Extension import Extension
+from UM.Logger import Logger
from UM.Message import Message
+from cura.CuraApplication import CuraApplication
from .Settings import Settings
from .DriveApiService import DriveApiService
-from .models.BackupListModel import BackupListModel
+
+from UM.i18n import i18nCatalog
+catalog = i18nCatalog("cura")
+# The DivePluginExtension provides functionality to backup and restore your Cura configuration to Ultimaker's cloud.
class DrivePluginExtension(QObject, Extension):
- """
- The DivePluginExtension provides functionality to backup and restore your Cura configuration to Ultimaker's cloud.
- """
# Signal emitted when the list of backups changed.
backupsChanged = pyqtSignal()
@@ -29,173 +33,130 @@ class DrivePluginExtension(QObject, Extension):
# Signal emitted when preferences changed (like auto-backup).
preferencesChanged = pyqtSignal()
-
+
DATE_FORMAT = "%d/%m/%Y %H:%M:%S"
- def __init__(self, application):
- super(DrivePluginExtension, self).__init__()
-
- # Re-usable instance of application.
- self._application = application
+ def __init__(self) -> None:
+ QObject.__init__(self, None)
+ Extension.__init__(self)
# Local data caching for the UI.
self._drive_window = None # type: Optional[QObject]
- self._backups_list_model = BackupListModel()
+ self._backups = [] # type: List[Dict[str, Any]]
self._is_restoring_backup = False
self._is_creating_backup = False
# Initialize services.
- self._preferences = self._application.getPreferences()
- self._cura_api = self._application.getCuraAPI()
- self._drive_api_service = DriveApiService(self._cura_api)
+ preferences = CuraApplication.getInstance().getPreferences()
+ self._drive_api_service = DriveApiService()
# Attach signals.
- self._cura_api.account.loginStateChanged.connect(self._onLoginStateChanged)
- self._drive_api_service.onRestoringStateChanged.connect(self._onRestoringStateChanged)
- self._drive_api_service.onCreatingStateChanged.connect(self._onCreatingStateChanged)
+ CuraApplication.getInstance().getCuraAPI().account.loginStateChanged.connect(self._onLoginStateChanged)
+ self._drive_api_service.restoringStateChanged.connect(self._onRestoringStateChanged)
+ self._drive_api_service.creatingStateChanged.connect(self._onCreatingStateChanged)
# Register preferences.
- self._preferences.addPreference(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, False)
- self._preferences.addPreference(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, datetime.now()
- .strftime(self.DATE_FORMAT))
-
- # Register menu items.
- self._updateMenuItems()
+ preferences.addPreference(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, False)
+ preferences.addPreference(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY,
+ datetime.now().strftime(self.DATE_FORMAT))
+
+ # Register the menu item
+ self.addMenuItem(catalog.i18nc("@item:inmenu", "Manage backups"), self.showDriveWindow)
# Make auto-backup on boot if required.
- self._application.engineCreatedSignal.connect(self._autoBackup)
+ CuraApplication.getInstance().engineCreatedSignal.connect(self._autoBackup)
def showDriveWindow(self) -> None:
- """Show the Drive UI popup window."""
if not self._drive_window:
- self._drive_window = self.createDriveWindow()
+ plugin_dir_path = CuraApplication.getInstance().getPluginRegistry().getPluginPath("CuraDrive")
+ path = os.path.join(plugin_dir_path, "src", "qml", "main.qml")
+ self._drive_window = CuraApplication.getInstance().createQmlComponent(path, {"CuraDrive": self})
self.refreshBackups()
if self._drive_window:
self._drive_window.show()
- def createDriveWindow(self) -> Optional["QObject"]:
- """
- Create an instance of the Drive UI popup window.
- :return: The popup window object.
- """
- path = os.path.join(os.path.dirname(__file__), "qml", "main.qml")
- return self._application.createQmlComponent(path, {"CuraDrive": self})
-
- def _updateMenuItems(self) -> None:
- """Update the menu items."""
- self.addMenuItem(Settings.translatable_messages["extension_menu_entry"], self.showDriveWindow)
-
def _autoBackup(self) -> None:
- """Automatically make a backup on boot if enabled."""
- if self._preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY) and self._lastBackupTooLongAgo():
+ preferences = CuraApplication.getInstance().getPreferences()
+ if preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY) and self._isLastBackupTooLongAgo():
self.createBackup()
-
- def _lastBackupTooLongAgo(self) -> bool:
- """Check if the last backup was longer than 1 day ago."""
+
+ def _isLastBackupTooLongAgo(self) -> bool:
current_date = datetime.now()
last_backup_date = self._getLastBackupDate()
date_diff = current_date - last_backup_date
return date_diff.days > 1
def _getLastBackupDate(self) -> "datetime":
- """Get the last backup date as datetime object."""
- last_backup_date = self._preferences.getValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY)
+ preferences = CuraApplication.getInstance().getPreferences()
+ last_backup_date = preferences.getValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY)
return datetime.strptime(last_backup_date, self.DATE_FORMAT)
def _storeBackupDate(self) -> None:
- """Store the current date as last backup date."""
backup_date = datetime.now().strftime(self.DATE_FORMAT)
- self._preferences.setValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, backup_date)
+ preferences = CuraApplication.getInstance().getPreferences()
+ preferences.setValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, backup_date)
def _onLoginStateChanged(self, logged_in: bool = False) -> None:
- """Callback handler for changes in the login state."""
if logged_in:
self.refreshBackups()
def _onRestoringStateChanged(self, is_restoring: bool = False, error_message: str = None) -> None:
- """Callback handler for changes in the restoring state."""
self._is_restoring_backup = is_restoring
self.restoringStateChanged.emit()
if error_message:
- Message(error_message, title = Settings.MESSAGE_TITLE, lifetime = 5).show()
+ Message(error_message, title = catalog.i18nc("@info:title", "Backup")).show()
def _onCreatingStateChanged(self, is_creating: bool = False, error_message: str = None) -> None:
- """Callback handler for changes in the creation state."""
self._is_creating_backup = is_creating
self.creatingStateChanged.emit()
if error_message:
- Message(error_message, title = Settings.MESSAGE_TITLE, lifetime = 5).show()
+ Message(error_message, title = catalog.i18nc("@info:title", "Backup")).show()
else:
self._storeBackupDate()
- if not is_creating:
+ if not is_creating and not error_message:
# We've finished creating a new backup, to the list has to be updated.
self.refreshBackups()
@pyqtSlot(bool, name = "toggleAutoBackup")
def toggleAutoBackup(self, enabled: bool) -> None:
- """Enable or disable the auto-backup feature."""
- self._preferences.setValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, enabled)
- self.preferencesChanged.emit()
+ preferences = CuraApplication.getInstance().getPreferences()
+ preferences.setValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, enabled)
@pyqtProperty(bool, notify = preferencesChanged)
def autoBackupEnabled(self) -> bool:
- """Check if auto-backup is enabled or not."""
- return bool(self._preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY))
+ preferences = CuraApplication.getInstance().getPreferences()
+ return bool(preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY))
- @pyqtProperty(QObject, notify = backupsChanged)
- def backups(self) -> BackupListModel:
- """
- Get a list of the backups.
- :return: The backups as Qt List Model.
- """
- return self._backups_list_model
+ @pyqtProperty("QVariantList", notify = backupsChanged)
+ def backups(self) -> List[Dict[str, Any]]:
+ return self._backups
@pyqtSlot(name = "refreshBackups")
def refreshBackups(self) -> None:
- """
- Forcefully refresh the backups list.
- """
- self._backups_list_model.loadBackups(self._drive_api_service.getBackups())
+ self._backups = self._drive_api_service.getBackups()
self.backupsChanged.emit()
@pyqtProperty(bool, notify = restoringStateChanged)
def isRestoringBackup(self) -> bool:
- """
- Get the current restoring state.
- :return: Boolean if we are restoring or not.
- """
return self._is_restoring_backup
@pyqtProperty(bool, notify = creatingStateChanged)
def isCreatingBackup(self) -> bool:
- """
- Get the current creating state.
- :return: Boolean if we are creating or not.
- """
return self._is_creating_backup
@pyqtSlot(str, name = "restoreBackup")
def restoreBackup(self, backup_id: str) -> None:
- """
- Download and restore a backup by ID.
- :param backup_id: The ID of the backup.
- """
- index = self._backups_list_model.find("backup_id", backup_id)
- backup = self._backups_list_model.getItem(index)
- self._drive_api_service.restoreBackup(backup)
+ for backup in self._backups:
+ if backup.get("backup_id") == backup_id:
+ self._drive_api_service.restoreBackup(backup)
+ return
+ Logger.log("w", "Unable to find backup with the ID %s", backup_id)
@pyqtSlot(name = "createBackup")
def createBackup(self) -> None:
- """
- Create a new backup.
- """
self._drive_api_service.createBackup()
@pyqtSlot(str, name = "deleteBackup")
def deleteBackup(self, backup_id: str) -> None:
- """
- Delete a backup by ID.
- :param backup_id: The ID of the backup.
- """
self._drive_api_service.deleteBackup(backup_id)
self.refreshBackups()
diff --git a/plugins/CuraDrive/src/Settings.py b/plugins/CuraDrive/src/Settings.py
index c0df66b950..abe64e0acd 100644
--- a/plugins/CuraDrive/src/Settings.py
+++ b/plugins/CuraDrive/src/Settings.py
@@ -1,37 +1,13 @@
# Copyright (c) 2018 Ultimaker B.V.
-from UM import i18nCatalog
+# Cura is released under the terms of the LGPLv3 or higher.
-from cura import CuraConstants
+from cura import UltimakerCloudAuthentication
class Settings:
- """
- Keeps the application settings.
- """
+ # Keeps the plugin settings.
DRIVE_API_VERSION = 1
- DRIVE_API_URL = "{}/cura-drive/v{}".format(CuraConstants.CuraCloudAPIRoot, str(DRIVE_API_VERSION))
+ DRIVE_API_URL = "{}/cura-drive/v{}".format(UltimakerCloudAuthentication.CuraCloudAPIRoot, str(DRIVE_API_VERSION))
AUTO_BACKUP_ENABLED_PREFERENCE_KEY = "cura_drive/auto_backup_enabled"
AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date"
-
- I18N_CATALOG_ID = "cura"
- I18N_CATALOG = i18nCatalog(I18N_CATALOG_ID)
-
- MESSAGE_TITLE = I18N_CATALOG.i18nc("@info:title", "Backups"),
-
- # Translatable messages for the entire plugin.
- translatable_messages = {
-
- # Menu items.
- "extension_menu_entry": I18N_CATALOG.i18nc("@item:inmenu", "Manage backups"),
-
- # Notification messages.
- "backup_failed": I18N_CATALOG.i18nc("@info:backup_status", "There was an error while creating your backup."),
- "uploading_backup": I18N_CATALOG.i18nc("@info:backup_status", "Uploading your backup..."),
- "uploading_backup_success": I18N_CATALOG.i18nc("@info:backup_status", "Your backup has finished uploading."),
- "uploading_backup_error": I18N_CATALOG.i18nc("@info:backup_status",
- "There was an error while uploading your backup."),
- "get_backups_error": I18N_CATALOG.i18nc("@info:backup_status", "There was an error listing your backups."),
- "backup_restore_error_message": I18N_CATALOG.i18nc("@info:backup_status",
- "There was an error trying to restore your backup.")
- }
diff --git a/plugins/CuraDrive/src/UploadBackupJob.py b/plugins/CuraDrive/src/UploadBackupJob.py
index bcecce554a..2e76ed9b4b 100644
--- a/plugins/CuraDrive/src/UploadBackupJob.py
+++ b/plugins/CuraDrive/src/UploadBackupJob.py
@@ -1,19 +1,21 @@
# Copyright (c) 2018 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
import requests
from UM.Job import Job
from UM.Logger import Logger
from UM.Message import Message
-from .Settings import Settings
+from UM.i18n import i18nCatalog
+catalog = i18nCatalog("cura")
class UploadBackupJob(Job):
- """
- This job is responsible for uploading the backup file to cloud storage.
- As it can take longer than some other tasks, we schedule this using a Cura Job.
- """
+ MESSAGE_TITLE = catalog.i18nc("@info:title", "Backups")
+ # This job is responsible for uploading the backup file to cloud storage.
+ # As it can take longer than some other tasks, we schedule this using a Cura Job.
def __init__(self, signed_upload_url: str, backup_zip: bytes) -> None:
super().__init__()
self._signed_upload_url = signed_upload_url
@@ -22,18 +24,18 @@ class UploadBackupJob(Job):
self.backup_upload_error_message = ""
def run(self) -> None:
- Message(Settings.translatable_messages["uploading_backup"], title = Settings.MESSAGE_TITLE,
- lifetime = 10).show()
+ upload_message = Message(catalog.i18nc("@info:backup_status", "Uploading your backup..."), title = self.MESSAGE_TITLE, progress = -1)
+ upload_message.show()
backup_upload = requests.put(self._signed_upload_url, data = self._backup_zip)
- if backup_upload.status_code not in (200, 201):
+ upload_message.hide()
+
+ if backup_upload.status_code >= 300:
self.backup_upload_error_message = backup_upload.text
Logger.log("w", "Could not upload backup file: %s", backup_upload.text)
- Message(Settings.translatable_messages["uploading_backup_error"], title = Settings.MESSAGE_TITLE,
- lifetime = 10).show()
+ Message(catalog.i18nc("@info:backup_status", "There was an error while uploading your backup."), title = self.MESSAGE_TITLE).show()
else:
self._upload_success = True
- Message(Settings.translatable_messages["uploading_backup_success"], title = Settings.MESSAGE_TITLE,
- lifetime = 10).show()
+ Message(catalog.i18nc("@info:backup_status", "Your backup has finished uploading."), title = self.MESSAGE_TITLE).show()
self.finished.emit(self)
diff --git a/plugins/CuraDrive/src/models/BackupListModel.py b/plugins/CuraDrive/src/models/BackupListModel.py
deleted file mode 100644
index 93b0c4c48c..0000000000
--- a/plugins/CuraDrive/src/models/BackupListModel.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (c) 2018 Ultimaker B.V.
-from typing import Any, List, Dict
-
-from UM.Qt.ListModel import ListModel
-
-from PyQt5.QtCore import Qt
-
-
-class BackupListModel(ListModel):
- """
- The BackupListModel transforms the backups data that came from the server so it can be served to the Qt UI.
- """
-
- def __init__(self, parent = None) -> None:
- super().__init__(parent)
- self.addRoleName(Qt.UserRole + 1, "backup_id")
- self.addRoleName(Qt.UserRole + 2, "download_url")
- self.addRoleName(Qt.UserRole + 3, "generated_time")
- self.addRoleName(Qt.UserRole + 4, "md5_hash")
- self.addRoleName(Qt.UserRole + 5, "data")
-
- def loadBackups(self, data: List[Dict[str, Any]]) -> None:
- """
- Populate the model with server data.
- :param data:
- """
- items = []
- for backup in data:
- # We do this loop because we only want to append these specific fields.
- # Without this, ListModel will break.
- items.append({
- "backup_id": backup["backup_id"],
- "download_url": backup["download_url"],
- "generated_time": backup["generated_time"],
- "md5_hash": backup["md5_hash"],
- "data": backup["metadata"]
- })
- self.setItems(items)
diff --git a/plugins/CuraDrive/src/models/__init__.py b/plugins/CuraDrive/src/models/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/plugins/CuraDrive/src/qml/components/ActionButton.qml b/plugins/CuraDrive/src/qml/components/ActionButton.qml
deleted file mode 100644
index 843079ed88..0000000000
--- a/plugins/CuraDrive/src/qml/components/ActionButton.qml
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-import QtQuick 2.7
-import QtQuick.Controls 2.1
-import QtQuick.Layouts 1.3
-
-import UM 1.1 as UM
-
-Button
-{
- id: button
- property alias cursorShape: mouseArea.cursorShape
- property var iconSource: ""
- property var busy: false
- property var color: UM.Theme.getColor("primary")
- property var hoverColor: UM.Theme.getColor("primary_hover")
- property var disabledColor: color
- property var textColor: UM.Theme.getColor("button_text")
- property var textHoverColor: UM.Theme.getColor("button_text_hover")
- property var textDisabledColor: textColor
- property var textFont: UM.Theme.getFont("action_button")
-
- contentItem: RowLayout
- {
- Icon
- {
- id: buttonIcon
- iconSource: button.iconSource
- width: 16 * screenScaleFactor
- color: button.hovered ? button.textHoverColor : button.textColor
- visible: button.iconSource != "" && !loader.visible
- }
-
- Icon
- {
- id: loader
- iconSource: "../images/loading.gif"
- width: 16 * screenScaleFactor
- color: button.hovered ? button.textHoverColor : button.textColor
- visible: button.busy
- animated: true
- }
-
- Label
- {
- id: buttonText
- text: button.text
- color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor): button.textDisabledColor
- font: button.textFont
- visible: button.text != ""
- renderType: Text.NativeRendering
- }
- }
-
- background: Rectangle
- {
- color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor
- }
-
- MouseArea
- {
- id: mouseArea
- anchors.fill: parent
- onPressed: mouse.accepted = false
- hoverEnabled: true
- cursorShape: button.enabled ? (hovered ? Qt.PointingHandCursor : Qt.ArrowCursor) : Qt.ForbiddenCursor
- }
-}
diff --git a/plugins/CuraDrive/src/qml/components/ActionCheckBox.qml b/plugins/CuraDrive/src/qml/components/ActionCheckBox.qml
deleted file mode 100644
index 71f5e6035d..0000000000
--- a/plugins/CuraDrive/src/qml/components/ActionCheckBox.qml
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-import QtQuick 2.7
-import QtQuick.Controls 2.1
-import QtQuick.Layouts 1.3
-
-import UM 1.3 as UM
-
-CheckBox
-{
- id: checkbox
- hoverEnabled: true
-
- property var label: ""
-
- indicator: Rectangle {
- implicitWidth: 30 * screenScaleFactor
- implicitHeight: 30 * screenScaleFactor
- x: 0
- y: Math.round(parent.height / 2 - height / 2)
- color: UM.Theme.getColor("sidebar")
- border.color: UM.Theme.getColor("text")
-
- Rectangle {
- width: 14 * screenScaleFactor
- height: 14 * screenScaleFactor
- x: 8 * screenScaleFactor
- y: 8 * screenScaleFactor
- color: UM.Theme.getColor("primary")
- visible: checkbox.checked
- }
- }
-
- contentItem: Label {
- anchors
- {
- left: checkbox.indicator.right
- leftMargin: 5 * screenScaleFactor
- }
- text: catalog.i18nc("@checkbox:description", "Auto Backup")
- color: UM.Theme.getColor("text")
- renderType: Text.NativeRendering
- verticalAlignment: Text.AlignVCenter
- }
-
- ActionToolTip
- {
- text: checkbox.label
- }
-}
diff --git a/plugins/CuraDrive/src/qml/components/ActionToolTip.qml b/plugins/CuraDrive/src/qml/components/ActionToolTip.qml
deleted file mode 100644
index 93b92bc2df..0000000000
--- a/plugins/CuraDrive/src/qml/components/ActionToolTip.qml
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-import QtQuick 2.7
-import QtQuick.Controls 2.1
-import QtQuick.Layouts 1.3
-
-import UM 1.1 as UM
-
-ToolTip
-{
- id: tooltip
- visible: parent.hovered
- opacity: 0.9
- delay: 500
-
- background: Rectangle
- {
- color: UM.Theme.getColor("sidebar")
- border.color: UM.Theme.getColor("primary")
- border.width: 1 * screenScaleFactor
- }
-
- contentItem: Label
- {
- text: tooltip.text
- color: UM.Theme.getColor("text")
- font: UM.Theme.getFont("very_small")
- renderType: Text.NativeRendering
- }
-}
diff --git a/plugins/CuraDrive/src/qml/components/BackupList.qml b/plugins/CuraDrive/src/qml/components/BackupList.qml
index 231f25afc8..afa9538486 100644
--- a/plugins/CuraDrive/src/qml/components/BackupList.qml
+++ b/plugins/CuraDrive/src/qml/components/BackupList.qml
@@ -1,31 +1,37 @@
// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
import QtQuick 2.7
-import QtQuick.Controls 2.1
+import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import UM 1.1 as UM
-ListView
+ScrollView
{
- id: backupList
+ property alias model: backupList.model
width: parent.width
- clip: true
- delegate: Item
+ ListView
{
+ id: backupList
width: parent.width
- height: childrenRect.height
-
- BackupListItem
- {
- id: backupListItem
- width: parent.width
- }
-
- Divider
+ delegate: Item
{
width: parent.width
- anchors.top: backupListItem.bottom
+ height: childrenRect.height
+
+ BackupListItem
+ {
+ id: backupListItem
+ width: parent.width
+ }
+
+ Rectangle
+ {
+ id: divider
+ color: UM.Theme.getColor("lining")
+ height: UM.Theme.getSize("default_lining").height
+ }
}
}
- ScrollBar.vertical: RightSideScrollBar {}
}
diff --git a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml
index 80f47d6cba..56706b9990 100644
--- a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml
+++ b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml
@@ -1,9 +1,12 @@
// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import UM 1.3 as UM
+import Cura 1.0 as Cura
import "../components"
@@ -13,30 +16,31 @@ RowLayout
width: parent.width
property bool showInfoButton: false
- ActionButton
+ Cura.PrimaryButton
{
id: infoButton
text: catalog.i18nc("@button", "Want more?")
- iconSource: "../images/info.svg"
+ iconSource: UM.Theme.getIcon("info")
onClicked: Qt.openUrlExternally("https://goo.gl/forms/QACEP8pP3RV60QYG2")
visible: backupListFooter.showInfoButton
}
- ActionButton
+ Cura.PrimaryButton
{
id: createBackupButton
text: catalog.i18nc("@button", "Backup Now")
- iconSource: "../images/backup.svg"
- enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup
+ iconSource: UM.Theme.getIcon("plus")
+ enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup && !backupListFooter.showInfoButton
onClicked: CuraDrive.createBackup()
busy: CuraDrive.isCreatingBackup
}
- ActionCheckBox
+ Cura.CheckBoxWithTooltip
{
id: autoBackupEnabled
checked: CuraDrive.autoBackupEnabled
onClicked: CuraDrive.toggleAutoBackup(autoBackupEnabled.checked)
- label: catalog.i18nc("@checkbox:description", "Automatically create a backup each day that Cura is started.")
+ text: catalog.i18nc("@checkbox:description", "Auto Backup")
+ tooltip: catalog.i18nc("@checkbox:description", "Automatically create a backup each day that Cura is started.")
}
}
diff --git a/plugins/CuraDrive/src/qml/components/BackupListItem.qml b/plugins/CuraDrive/src/qml/components/BackupListItem.qml
index abe9a1acf9..5cdb500b4e 100644
--- a/plugins/CuraDrive/src/qml/components/BackupListItem.qml
+++ b/plugins/CuraDrive/src/qml/components/BackupListItem.qml
@@ -1,10 +1,13 @@
// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.1
import UM 1.1 as UM
+import Cura 1.0 as Cura
Item
{
@@ -25,60 +28,58 @@ Item
RowLayout
{
id: dataRow
- spacing: UM.Theme.getSize("default_margin").width * 2
+ spacing: UM.Theme.getSize("wide_margin").width
width: parent.width
height: 50 * screenScaleFactor
- ActionButton
+ UM.SimpleButton
{
- color: "transparent"
- hoverColor: "transparent"
- textColor: UM.Theme.getColor("text")
- textHoverColor: UM.Theme.getColor("primary")
- iconSource: "../images/info.svg"
+ width: UM.Theme.getSize("section_icon").width
+ height: UM.Theme.getSize("section_icon").height
+ color: UM.Theme.getColor("small_button_text")
+ hoverColor: UM.Theme.getColor("small_button_text_hover")
+ iconSource: UM.Theme.getIcon("info")
onClicked: backupListItem.showDetails = !backupListItem.showDetails
}
Label
{
- text: new Date(model["generated_time"]).toLocaleString(UM.Preferences.getValue("general/language"))
+ text: new Date(modelData.generated_time).toLocaleString(UM.Preferences.getValue("general/language"))
color: UM.Theme.getColor("text")
elide: Text.ElideRight
Layout.minimumWidth: 100 * screenScaleFactor
Layout.maximumWidth: 500 * screenScaleFactor
Layout.fillWidth: true
+ font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
}
Label
{
- text: model["data"]["description"]
+ text: modelData.metadata.description
color: UM.Theme.getColor("text")
elide: Text.ElideRight
Layout.minimumWidth: 100 * screenScaleFactor
Layout.maximumWidth: 500 * screenScaleFactor
Layout.fillWidth: true
+ font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
}
- ActionButton
+ Cura.SecondaryButton
{
text: catalog.i18nc("@button", "Restore")
- color: "transparent"
- hoverColor: "transparent"
- textColor: UM.Theme.getColor("text")
- textHoverColor: UM.Theme.getColor("text_link")
enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup
onClicked: confirmRestoreDialog.visible = true
}
- ActionButton
+ UM.SimpleButton
{
- color: "transparent"
- hoverColor: "transparent"
- textColor: UM.Theme.getColor("setting_validation_error")
- textHoverColor: UM.Theme.getColor("setting_validation_error")
- iconSource: "../images/delete.svg"
+ width: UM.Theme.getSize("message_close").width
+ height: UM.Theme.getSize("message_close").height
+ color: UM.Theme.getColor("small_button_text")
+ hoverColor: UM.Theme.getColor("small_button_text_hover")
+ iconSource: UM.Theme.getIcon("cross1")
onClicked: confirmDeleteDialog.visible = true
}
}
@@ -86,7 +87,7 @@ Item
BackupListItemDetails
{
id: backupDetails
- backupDetailsData: model
+ backupDetailsData: modelData
width: parent.width
visible: parent.showDetails
anchors.top: dataRow.bottom
@@ -98,7 +99,7 @@ Item
title: catalog.i18nc("@dialog:title", "Delete Backup")
text: catalog.i18nc("@dialog:info", "Are you sure you want to delete this backup? This cannot be undone.")
standardButtons: StandardButton.Yes | StandardButton.No
- onYes: CuraDrive.deleteBackup(model["backup_id"])
+ onYes: CuraDrive.deleteBackup(modelData.backup_id)
}
MessageDialog
@@ -107,6 +108,6 @@ Item
title: catalog.i18nc("@dialog:title", "Restore Backup")
text: catalog.i18nc("@dialog:info", "You will need to restart Cura before your backup is restored. Do you want to close Cura now?")
standardButtons: StandardButton.Yes | StandardButton.No
- onYes: CuraDrive.restoreBackup(model["backup_id"])
+ onYes: CuraDrive.restoreBackup(modelData.backup_id)
}
}
diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml
index 74d4c5ab57..4da15c6f16 100644
--- a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml
+++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml
@@ -1,4 +1,6 @@
// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
@@ -9,53 +11,53 @@ ColumnLayout
{
id: backupDetails
width: parent.width
- spacing: 10 * screenScaleFactor
+ spacing: UM.Theme.getSize("default_margin").width
property var backupDetailsData
// Cura version
BackupListItemDetailsRow
{
- iconSource: "../images/cura.svg"
+ iconSource: UM.Theme.getIcon("application")
label: catalog.i18nc("@backuplist:label", "Cura Version")
- value: backupDetailsData["data"]["cura_release"]
+ value: backupDetailsData.metadata.cura_release
}
// Machine count.
BackupListItemDetailsRow
{
- iconSource: "../images/printer.svg"
+ iconSource: UM.Theme.getIcon("printer_single")
label: catalog.i18nc("@backuplist:label", "Machines")
- value: backupDetailsData["data"]["machine_count"]
+ value: backupDetailsData.metadata.machine_count
}
- // Meterial count.
+ // Material count
BackupListItemDetailsRow
{
- iconSource: "../images/material.svg"
+ iconSource: UM.Theme.getIcon("category_material")
label: catalog.i18nc("@backuplist:label", "Materials")
- value: backupDetailsData["data"]["material_count"]
+ value: backupDetailsData.metadata.material_count
}
- // Meterial count.
+ // Profile count.
BackupListItemDetailsRow
{
- iconSource: "../images/profile.svg"
+ iconSource: UM.Theme.getIcon("settings")
label: catalog.i18nc("@backuplist:label", "Profiles")
- value: backupDetailsData["data"]["profile_count"]
+ value: backupDetailsData.metadata.profile_count
}
- // Meterial count.
+ // Plugin count.
BackupListItemDetailsRow
{
- iconSource: "../images/plugin.svg"
+ iconSource: UM.Theme.getIcon("plugin")
label: catalog.i18nc("@backuplist:label", "Plugins")
- value: backupDetailsData["data"]["plugin_count"]
+ value: backupDetailsData.metadata.plugin_count
}
// Spacer.
Item
{
width: parent.width
- height: 10 * screenScaleFactor
+ height: UM.Theme.getSize("default_margin").height
}
}
diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml
index dad1674fe7..9e4612fcf8 100644
--- a/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml
+++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml
@@ -1,4 +1,6 @@
// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
@@ -11,42 +13,40 @@ RowLayout
width: parent.width
height: 40 * screenScaleFactor
- property var iconSource
- property var label
- property var value
+ property alias iconSource: icon.source
+ property alias label: detailName.text
+ property alias value: detailValue.text
- // Spacing.
- Item
- {
- width: 40 * screenScaleFactor
- }
-
- Icon
+ UM.RecolorImage
{
+ id: icon
width: 18 * screenScaleFactor
- iconSource: detailsRow.iconSource
+ height: width
+ source: ""
color: UM.Theme.getColor("text")
}
Label
{
- text: detailsRow.label
+ id: detailName
color: UM.Theme.getColor("text")
elide: Text.ElideRight
Layout.minimumWidth: 50 * screenScaleFactor
Layout.maximumWidth: 100 * screenScaleFactor
Layout.fillWidth: true
+ font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
}
Label
{
- text: detailsRow.value
+ id: detailValue
color: UM.Theme.getColor("text")
elide: Text.ElideRight
Layout.minimumWidth: 50 * screenScaleFactor
Layout.maximumWidth: 100 * screenScaleFactor
Layout.fillWidth: true
+ font: UM.Theme.getFont("default")
renderType: Text.NativeRendering
}
}
diff --git a/plugins/CuraDrive/src/qml/components/Divider.qml b/plugins/CuraDrive/src/qml/components/Divider.qml
deleted file mode 100644
index bba2f2f29c..0000000000
--- a/plugins/CuraDrive/src/qml/components/Divider.qml
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-import QtQuick 2.7
-
-import UM 1.3 as UM
-
-Rectangle
-{
- id: divider
- color: UM.Theme.getColor("lining")
- height: UM.Theme.getSize("default_lining").height
-}
diff --git a/plugins/CuraDrive/src/qml/components/Icon.qml b/plugins/CuraDrive/src/qml/components/Icon.qml
deleted file mode 100644
index 3cb822bf82..0000000000
--- a/plugins/CuraDrive/src/qml/components/Icon.qml
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-import QtQuick 2.7
-import QtQuick.Controls 2.1
-import QtGraphicalEffects 1.0
-
-Item
-{
- id: icon
- width: parent.height
- height: width
- property var color: "transparent"
- property var iconSource
- property bool animated: false
-
- Image
- {
- id: iconImage
- width: parent.height
- height: width
- smooth: true
- source: icon.iconSource
- sourceSize.width: width
- sourceSize.height: height
- antialiasing: true
- visible: !icon.animated
- }
-
- AnimatedImage
- {
- id: animatedIconImage
- width: parent.height
- height: width
- smooth: true
- antialiasing: true
- source: "../images/loading.gif"
- visible: icon.animated
- }
-
- ColorOverlay
- {
- anchors.fill: iconImage
- source: iconImage
- color: icon.color
- antialiasing: true
- visible: !icon.animated
- }
-
- ColorOverlay
- {
- anchors.fill: animatedIconImage
- source: animatedIconImage
- color: icon.color
- antialiasing: true
- visible: icon.animated
- }
-}
diff --git a/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml b/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml
deleted file mode 100644
index 5ac5df15ff..0000000000
--- a/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-import QtQuick 2.7
-import QtQuick.Controls 2.1
-import QtQuick.Layouts 1.3
-
-ScrollBar
-{
- active: true
- size: parent.height
- anchors.top: parent.top
- anchors.right: parent.right
- anchors.bottom: parent.bottom
-}
diff --git a/plugins/CuraDrive/src/qml/images/avatar_default.png b/plugins/CuraDrive/src/qml/images/avatar_default.png
deleted file mode 100644
index 0c306680f7..0000000000
Binary files a/plugins/CuraDrive/src/qml/images/avatar_default.png and /dev/null differ
diff --git a/plugins/CuraDrive/src/qml/images/background.svg b/plugins/CuraDrive/src/qml/images/background.svg
deleted file mode 100644
index cbcfdbaa2d..0000000000
--- a/plugins/CuraDrive/src/qml/images/background.svg
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
\ No newline at end of file
diff --git a/plugins/CuraDrive/src/qml/images/backup.svg b/plugins/CuraDrive/src/qml/images/backup.svg
deleted file mode 100644
index 51f6be4cba..0000000000
--- a/plugins/CuraDrive/src/qml/images/backup.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/plugins/CuraDrive/src/qml/images/cura.svg b/plugins/CuraDrive/src/qml/images/cura.svg
deleted file mode 100644
index 6b1b6c0c79..0000000000
--- a/plugins/CuraDrive/src/qml/images/cura.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
\ No newline at end of file
diff --git a/plugins/CuraDrive/src/qml/images/cura_logo.jpg b/plugins/CuraDrive/src/qml/images/cura_logo.jpg
deleted file mode 100644
index 621c03f035..0000000000
Binary files a/plugins/CuraDrive/src/qml/images/cura_logo.jpg and /dev/null differ
diff --git a/plugins/CuraDrive/src/qml/images/cura_logo.png b/plugins/CuraDrive/src/qml/images/cura_logo.png
deleted file mode 100644
index f846f2a0f0..0000000000
Binary files a/plugins/CuraDrive/src/qml/images/cura_logo.png and /dev/null differ
diff --git a/plugins/CuraDrive/src/qml/images/delete.svg b/plugins/CuraDrive/src/qml/images/delete.svg
deleted file mode 100644
index 2f6190ad43..0000000000
--- a/plugins/CuraDrive/src/qml/images/delete.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
\ No newline at end of file
diff --git a/plugins/CuraDrive/src/qml/images/folder.svg b/plugins/CuraDrive/src/qml/images/folder.svg
deleted file mode 100644
index f66f83a888..0000000000
--- a/plugins/CuraDrive/src/qml/images/folder.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
\ No newline at end of file
diff --git a/plugins/CuraDrive/src/qml/images/home.svg b/plugins/CuraDrive/src/qml/images/home.svg
deleted file mode 100644
index 9d0e4d802c..0000000000
--- a/plugins/CuraDrive/src/qml/images/home.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/plugins/CuraDrive/src/qml/images/info.svg b/plugins/CuraDrive/src/qml/images/info.svg
deleted file mode 100644
index 36154d6729..0000000000
--- a/plugins/CuraDrive/src/qml/images/info.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
\ No newline at end of file
diff --git a/plugins/CuraDrive/src/qml/images/inverted_circle.png b/plugins/CuraDrive/src/qml/images/inverted_circle.png
deleted file mode 100644
index 3612b37d4d..0000000000
Binary files a/plugins/CuraDrive/src/qml/images/inverted_circle.png and /dev/null differ
diff --git a/plugins/CuraDrive/src/qml/images/material.svg b/plugins/CuraDrive/src/qml/images/material.svg
deleted file mode 100644
index eac724e471..0000000000
--- a/plugins/CuraDrive/src/qml/images/material.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
\ No newline at end of file
diff --git a/plugins/CuraDrive/src/qml/images/plugin.svg b/plugins/CuraDrive/src/qml/images/plugin.svg
deleted file mode 100644
index 674eb99a54..0000000000
--- a/plugins/CuraDrive/src/qml/images/plugin.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
\ No newline at end of file
diff --git a/plugins/CuraDrive/src/qml/images/preview_banner.png b/plugins/CuraDrive/src/qml/images/preview_banner.png
deleted file mode 100644
index 414019531b..0000000000
Binary files a/plugins/CuraDrive/src/qml/images/preview_banner.png and /dev/null differ
diff --git a/plugins/CuraDrive/src/qml/images/printer.svg b/plugins/CuraDrive/src/qml/images/printer.svg
deleted file mode 100644
index f7dc83987d..0000000000
--- a/plugins/CuraDrive/src/qml/images/printer.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
\ No newline at end of file
diff --git a/plugins/CuraDrive/src/qml/images/profile.svg b/plugins/CuraDrive/src/qml/images/profile.svg
deleted file mode 100644
index ec2130f3d6..0000000000
--- a/plugins/CuraDrive/src/qml/images/profile.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/plugins/CuraDrive/src/qml/images/restore.svg b/plugins/CuraDrive/src/qml/images/restore.svg
deleted file mode 100644
index 803215eada..0000000000
--- a/plugins/CuraDrive/src/qml/images/restore.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
\ No newline at end of file
diff --git a/plugins/CuraDrive/src/qml/main.qml b/plugins/CuraDrive/src/qml/main.qml
index 4a2219cf1f..48bf3b6ea4 100644
--- a/plugins/CuraDrive/src/qml/main.qml
+++ b/plugins/CuraDrive/src/qml/main.qml
@@ -1,4 +1,6 @@
// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Window 2.2
@@ -14,18 +16,18 @@ Window
id: curaDriveDialog
minimumWidth: Math.round(UM.Theme.getSize("modal_window_minimum").width)
minimumHeight: Math.round(UM.Theme.getSize("modal_window_minimum").height)
- maximumWidth: minimumWidth * 1.2
- maximumHeight: minimumHeight * 1.2
+ maximumWidth: Math.round(minimumWidth * 1.2)
+ maximumHeight: Math.round(minimumHeight * 1.2)
width: minimumWidth
height: minimumHeight
- color: UM.Theme.getColor("sidebar")
+ color: UM.Theme.getColor("main_background")
title: catalog.i18nc("@title:window", "Cura Backups")
// Globally available.
UM.I18nCatalog
{
id: catalog
- name: "cura_drive"
+ name: "cura"
}
WelcomePage
diff --git a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml
index 88ce766383..0ba0cae09b 100644
--- a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml
+++ b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml
@@ -1,4 +1,6 @@
// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
@@ -12,11 +14,11 @@ Item
{
id: backupsPage
anchors.fill: parent
- anchors.margins: UM.Theme.getSize("default_margin").width * 3
+ anchors.margins: UM.Theme.getSize("wide_margin").width
ColumnLayout
{
- spacing: UM.Theme.getSize("default_margin").height * 2
+ spacing: UM.Theme.getSize("wide_margin").height
width: parent.width
anchors.fill: parent
diff --git a/plugins/CuraDrive/src/qml/pages/WelcomePage.qml b/plugins/CuraDrive/src/qml/pages/WelcomePage.qml
index 882656dc4a..0b207bc170 100644
--- a/plugins/CuraDrive/src/qml/pages/WelcomePage.qml
+++ b/plugins/CuraDrive/src/qml/pages/WelcomePage.qml
@@ -1,4 +1,6 @@
// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Window 2.2
@@ -8,12 +10,14 @@ import Cura 1.1 as Cura
import "../components"
+
Column
{
id: welcomePage
spacing: UM.Theme.getSize("wide_margin").height
width: parent.width
- topPadding: 150 * screenScaleFactor
+ height: childrenRect.height
+ anchors.centerIn: parent
Image
{
@@ -38,11 +42,15 @@ Column
renderType: Text.NativeRendering
}
- ActionButton
+ Cura.PrimaryButton
{
id: loginButton
- onClicked: Cura.API.account.login()
- text: catalog.i18nc("@button", "Sign In")
+ width: UM.Theme.getSize("account_button").width
+ height: UM.Theme.getSize("account_button").height
anchors.horizontalCenter: parent.horizontalCenter
+ text: catalog.i18nc("@button", "Sign in")
+ onClicked: Cura.API.account.login()
+ fixedWidthMode: true
}
}
+
diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py
index 2229ab1f67..192471a357 100644
--- a/plugins/Toolbox/src/Toolbox.py
+++ b/plugins/Toolbox/src/Toolbox.py
@@ -16,8 +16,8 @@ from UM.Extension import Extension
from UM.i18n import i18nCatalog
from UM.Version import Version
-import cura
-from cura import CuraConstants
+from cura import ApplicationMetadata
+from cura import UltimakerCloudAuthentication
from cura.CuraApplication import CuraApplication
from .AuthorsModel import AuthorsModel
@@ -31,17 +31,14 @@ i18n_catalog = i18nCatalog("cura")
## The Toolbox class is responsible of communicating with the server through the API
class Toolbox(QObject, Extension):
- DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str
- DEFAULT_CLOUD_API_VERSION = 1 # type: int
-
def __init__(self, application: CuraApplication) -> None:
super().__init__()
self._application = application # type: CuraApplication
- self._sdk_version = CuraConstants.CuraSDKVersion # type: Union[str, int]
- self._cloud_api_version = CuraConstants.CuraCloudAPIVersion # type: int
- self._cloud_api_root = CuraConstants.CuraCloudAPIRoot # type: str
+ self._sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int]
+ self._cloud_api_version = UltimakerCloudAuthentication.CuraCloudAPIVersion # type: int
+ self._cloud_api_root = UltimakerCloudAuthentication.CuraCloudAPIRoot # type: str
self._api_url = None # type: Optional[str]
# Network:
diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py
index 8f10d02802..2ff323555a 100644
--- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py
+++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py
@@ -9,7 +9,7 @@ from PyQt5.QtCore import QUrl
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager
from UM.Logger import Logger
-from cura import CuraConstants
+from cura import UltimakerCloudAuthentication
from cura.API import Account
from .MeshUploader import MeshUploader
from ..Models import BaseModel
@@ -30,7 +30,7 @@ CloudApiClientModel = TypeVar("Model", bound = BaseModel)
class CloudApiClient:
# The cloud URL to use for this remote cluster.
- ROOT_PATH = CuraConstants.CuraCloudAPIRoot
+ ROOT_PATH = UltimakerCloudAuthentication.CuraCloudAPIRoot
CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH)
CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH)
diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json
index dee436f584..172b27b452 100644
--- a/resources/bundled_packages/cura.json
+++ b/resources/bundled_packages/cura.json
@@ -57,7 +57,7 @@
"display_name": "Cura Backups",
"description": "Backup and restore your configuration.",
"package_version": "1.2.0",
- "sdk_version": 5,
+ "sdk_version": 6,
"website": "https://ultimaker.com",
"author": {
"author_id": "UltimakerPackages",
diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml
index 6cab04e5ec..fabdcebc64 100644
--- a/resources/qml/ActionButton.qml
+++ b/resources/qml/ActionButton.qml
@@ -4,6 +4,7 @@
import QtQuick 2.7
import QtQuick.Controls 2.1
import QtGraphicalEffects 1.0 // For the dropshadow
+
import UM 1.1 as UM
import Cura 1.0 as Cura
@@ -30,6 +31,7 @@ Button
property color outlineDisabledColor: outlineColor
property alias shadowColor: shadow.color
property alias shadowEnabled: shadow.visible
+ property alias busy: busyIndicator.visible
property alias toolTipContentAlignment: tooltip.contentAlignment
@@ -55,7 +57,7 @@ Button
width: visible ? height : 0
sourceSize.width: width
sourceSize.height: height
- color: button.hovered ? button.textHoverColor : button.textColor
+ color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor) : button.textDisabledColor
visible: source != "" && !button.isIconOnRightSide
anchors.verticalCenter: parent.verticalCenter
}
@@ -117,4 +119,16 @@ Button
id: tooltip
visible: button.hovered
}
+
+ BusyIndicator
+ {
+ id: busyIndicator
+
+ anchors.centerIn: parent
+
+ width: height
+ height: parent.height
+
+ visible: false
+ }
}
\ No newline at end of file
diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml
index 1695be8748..0f415a6a2d 100644
--- a/resources/qml/ActionPanel/SliceProcessWidget.qml
+++ b/resources/qml/ActionPanel/SliceProcessWidget.qml
@@ -194,7 +194,7 @@ Column
shortcut: "Ctrl+P"
onTriggered:
{
- if (prepareButton.enabled)
+ if (sliceButton.enabled)
{
sliceOrStopSlicing()
}
diff --git a/resources/qml/CheckBoxWithTooltip.qml b/resources/qml/CheckBoxWithTooltip.qml
new file mode 100644
index 0000000000..403efb4d7b
--- /dev/null
+++ b/resources/qml/CheckBoxWithTooltip.qml
@@ -0,0 +1,63 @@
+// Copyright (c) 2018 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
+import QtQuick 2.7
+import QtQuick.Controls 2.1
+
+import UM 1.3 as UM
+
+CheckBox
+{
+ id: checkbox
+ hoverEnabled: true
+
+ property alias tooltip: tooltip.text
+
+ indicator: Rectangle
+ {
+ implicitWidth: UM.Theme.getSize("checkbox").width
+ implicitHeight: UM.Theme.getSize("checkbox").height
+ x: 0
+ anchors.verticalCenter: parent.verticalCenter
+ color: UM.Theme.getColor("main_background")
+ radius: UM.Theme.getSize("checkbox_radius").width
+ border.width: UM.Theme.getSize("default_lining").width
+ border.color: checkbox.hovered ? UM.Theme.getColor("checkbox_border_hover") : UM.Theme.getColor("checkbox_border")
+
+ UM.RecolorImage
+ {
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: Math.round(parent.width / 2.5)
+ height: Math.round(parent.height / 2.5)
+ sourceSize.height: width
+ color: UM.Theme.getColor("checkbox_mark")
+ source: UM.Theme.getIcon("check")
+ opacity: checkbox.checked
+ Behavior on opacity { NumberAnimation { duration: 100; } }
+ }
+ }
+
+ contentItem: Label
+ {
+ anchors
+ {
+ left: checkbox.indicator.right
+ leftMargin: UM.Theme.getSize("narrow_margin").width
+ }
+ text: checkbox.text
+ color: UM.Theme.getColor("checkbox_text")
+ font: UM.Theme.getFont("default")
+ renderType: Text.NativeRendering
+ elide: Text.ElideRight
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ ToolTip
+ {
+ id: tooltip
+ text: ""
+ delay: 500
+ visible: text != "" && checkbox.hovered
+ }
+}
diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml
index a78295e7fa..4a031e33fa 100644
--- a/resources/qml/Cura.qml
+++ b/resources/qml/Cura.qml
@@ -124,16 +124,16 @@ UM.MainWindow
}
}
- // This is a placehoder for adding a pattern in the header
- Image
- {
- id: backgroundPattern
- anchors.fill: parent
- fillMode: Image.Tile
- source: UM.Theme.getImage("header_pattern")
- horizontalAlignment: Image.AlignLeft
- verticalAlignment: Image.AlignTop
- }
+ // This is a placehoder for adding a pattern in the header
+ Image
+ {
+ id: backgroundPattern
+ anchors.fill: parent
+ fillMode: Image.Tile
+ source: UM.Theme.getImage("header_pattern")
+ horizontalAlignment: Image.AlignLeft
+ verticalAlignment: Image.AlignTop
+ }
}
MainWindowHeader
diff --git a/resources/qml/qmldir b/resources/qml/qmldir
index 80e0f8be46..62997cc27a 100644
--- a/resources/qml/qmldir
+++ b/resources/qml/qmldir
@@ -15,4 +15,5 @@ ViewsSelector 1.0 ViewsSelector.qml
ToolbarButton 1.0 ToolbarButton.qml
SettingView 1.0 SettingView.qml
ProfileMenu 1.0 ProfileMenu.qml
-ToolTip 1.0 ToolTip.qml
\ No newline at end of file
+CheckBoxWithTooltip 1.0 CheckBoxWithTooltip.qml
+ToolTip 1.0 ToolTip.qml
diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml
index b314190e24..121f604362 100755
--- a/resources/themes/cura-light/styles.qml
+++ b/resources/themes/cura-light/styles.qml
@@ -478,7 +478,7 @@ QtObject
color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : (control.enabled ? Theme.getColor("checkbox") : Theme.getColor("checkbox_disabled"))
Behavior on color { ColorAnimation { duration: 50; } }
- radius: control.exclusiveGroup ? Math.round(Theme.getSize("checkbox").width / 2) : UM.Theme.getSize("checkbox_radius").width
+ radius: control.exclusiveGroup ? Math.round(Theme.getSize("checkbox").width / 2) : Theme.getSize("checkbox_radius").width
border.width: Theme.getSize("default_lining").width
border.color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_border_hover") : Theme.getColor("checkbox_border")