Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Ultimaker/Cura.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/cura/API
diff options
context:
space:
mode:
authorNino van Hooff <ninovanhooff@gmail.com>2020-05-28 18:31:24 +0300
committerNino van Hooff <ninovanhooff@gmail.com>2020-05-28 18:31:24 +0300
commit58ffc9dcae0020d7dd4f3c32b41922dfdbef37d3 (patch)
tree54902883427fa76a9dffb5068afd99532563cb4b /cura/API
parentc2c96faf5fcbad942f8cf257e75c94a623ac5eaa (diff)
parent2a70813d030c678181b5c37fc82cb513d689187b (diff)
Merge remote-tracking branch 'origin/master' into doxygen_to_restructuredtext_comments
# Conflicts: # cura/API/__init__.py # cura/Settings/CuraContainerRegistry.py # cura/Settings/ExtruderManager.py # plugins/PostProcessingPlugin/scripts/PauseAtHeight.py # plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py # plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py # plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDeviceManager.py
Diffstat (limited to 'cura/API')
-rw-r--r--cura/API/Account.py57
-rw-r--r--cura/API/ConnectionStatus.py64
-rw-r--r--cura/API/__init__.py10
3 files changed, 117 insertions, 14 deletions
diff --git a/cura/API/Account.py b/cura/API/Account.py
index 27adb1a0c8..581a91da04 100644
--- a/cura/API/Account.py
+++ b/cura/API/Account.py
@@ -23,6 +23,7 @@ class SyncState:
SYNCING = 0
SUCCESS = 1
ERROR = 2
+ IDLE = 3
class Account(QObject):
"""The account API provides a version-proof bridge to use Ultimaker Accounts
@@ -54,6 +55,7 @@ class Account(QObject):
"""
lastSyncDateTimeChanged = pyqtSignal()
syncStateChanged = pyqtSignal(int) # because SyncState is an int Enum
+ manualSyncEnabledChanged = pyqtSignal(bool)
def __init__(self, application: "CuraApplication", parent = None) -> None:
super().__init__(parent)
@@ -62,7 +64,8 @@ class Account(QObject):
self._error_message = None # type: Optional[Message]
self._logged_in = False
- self._sync_state = SyncState.SUCCESS
+ self._sync_state = SyncState.IDLE
+ self._manual_sync_enabled = False
self._last_sync_str = "-"
self._callback_port = 32118
@@ -110,16 +113,21 @@ class Account(QObject):
:param state: One of SyncState
"""
+ Logger.info("Service {service} enters sync state {state}", service = service_name, state = state)
+
prev_state = self._sync_state
self._sync_services[service_name] = state
if any(val == SyncState.SYNCING for val in self._sync_services.values()):
self._sync_state = SyncState.SYNCING
+ self._setManualSyncEnabled(False)
elif any(val == SyncState.ERROR for val in self._sync_services.values()):
self._sync_state = SyncState.ERROR
+ self._setManualSyncEnabled(True)
else:
self._sync_state = SyncState.SUCCESS
+ self._setManualSyncEnabled(False)
if self._sync_state != prev_state:
self.syncStateChanged.emit(self._sync_state)
@@ -162,11 +170,31 @@ class Account(QObject):
self._logged_in = logged_in
self.loginStateChanged.emit(logged_in)
if logged_in:
- self.sync()
+ self._setManualSyncEnabled(False)
+ self._sync()
else:
if self._update_timer.isActive():
self._update_timer.stop()
+ def _sync(self) -> None:
+ """Signals all sync services to start syncing
+
+ This can be considered a forced sync: even when a
+ sync is currently running, a sync will be requested.
+ """
+
+ if self._update_timer.isActive():
+ self._update_timer.stop()
+ elif self._sync_state == SyncState.SYNCING:
+ Logger.warning("Starting a new sync while previous sync was not completed\n{}", str(self._sync_services))
+
+ self.syncRequested.emit()
+
+ def _setManualSyncEnabled(self, enabled: bool) -> None:
+ if self._manual_sync_enabled != enabled:
+ self._manual_sync_enabled = enabled
+ self.manualSyncEnabledChanged.emit(enabled)
+
@pyqtSlot()
@pyqtSlot(bool)
def login(self, force_logout_before_login: bool = False) -> None:
@@ -217,20 +245,23 @@ class Account(QObject):
def lastSyncDateTime(self) -> str:
return self._last_sync_str
- @pyqtSlot()
- def sync(self) -> None:
- """Signals all sync services to start syncing
+ @pyqtProperty(bool, notify=manualSyncEnabledChanged)
+ def manualSyncEnabled(self) -> bool:
+ return self._manual_sync_enabled
- This can be considered a forced sync: even when a
- sync is currently running, a sync will be requested.
- """
+ @pyqtSlot()
+ @pyqtSlot(bool)
+ def sync(self, user_initiated: bool = False) -> None:
+ if user_initiated:
+ self._setManualSyncEnabled(False)
- if self._update_timer.isActive():
- self._update_timer.stop()
- elif self._sync_state == SyncState.SYNCING:
- Logger.warning("Starting a new sync while previous sync was not completed\n{}", str(self._sync_services))
+ self._sync()
- self.syncRequested.emit()
+ @pyqtSlot()
+ def popupOpened(self) -> None:
+ self._setManualSyncEnabled(True)
+ self._sync_state = SyncState.IDLE
+ self.syncStateChanged.emit(self._sync_state)
@pyqtSlot()
def logout(self) -> None:
diff --git a/cura/API/ConnectionStatus.py b/cura/API/ConnectionStatus.py
new file mode 100644
index 0000000000..332e519ca9
--- /dev/null
+++ b/cura/API/ConnectionStatus.py
@@ -0,0 +1,64 @@
+from typing import Optional
+
+from PyQt5.QtCore import QObject, pyqtSignal, QTimer, pyqtProperty
+from PyQt5.QtNetwork import QNetworkReply
+
+from UM.TaskManagement.HttpRequestManager import HttpRequestManager
+from cura.UltimakerCloud import UltimakerCloudAuthentication
+
+
+class ConnectionStatus(QObject):
+ """Status info for some web services"""
+
+ UPDATE_INTERVAL = 10.0 # seconds
+ ULTIMAKER_CLOUD_STATUS_URL = UltimakerCloudAuthentication.CuraCloudAPIRoot + "/connect/v1/"
+
+ __instance = None # type: Optional[ConnectionStatus]
+
+ internetReachableChanged = pyqtSignal()
+ umCloudReachableChanged = pyqtSignal()
+
+ @classmethod
+ def getInstance(cls, *args, **kwargs) -> "ConnectionStatus":
+ if cls.__instance is None:
+ cls.__instance = cls(*args, **kwargs)
+ return cls.__instance
+
+ def __init__(self, parent: Optional["QObject"] = None):
+ super().__init__(parent)
+
+ self._http = HttpRequestManager.getInstance()
+ self._statuses = {
+ self.ULTIMAKER_CLOUD_STATUS_URL: True,
+ "http://example.com": True
+ }
+
+ # Create a timer for automatic updates
+ self._update_timer = QTimer()
+ self._update_timer.setInterval(int(self.UPDATE_INTERVAL * 1000))
+ # The timer is restarted automatically
+ self._update_timer.setSingleShot(False)
+ self._update_timer.timeout.connect(self._update)
+ self._update_timer.start()
+
+ @pyqtProperty(bool, notify=internetReachableChanged)
+ def isInternetReachable(self) -> bool:
+ # Is any of the test urls reachable?
+ return any(self._statuses.values())
+
+ def _update(self):
+ for url in self._statuses.keys():
+ self._http.get(
+ url = url,
+ callback = self._statusCallback,
+ error_callback = self._statusCallback,
+ timeout = 5
+ )
+
+ def _statusCallback(self, reply: QNetworkReply, error: QNetworkReply.NetworkError = None):
+ url = reply.request().url().toString()
+ prev_statuses = self._statuses.copy()
+ self._statuses[url] = HttpRequestManager.replyIndicatesSuccess(reply, error)
+
+ if any(self._statuses.values()) != any(prev_statuses.values()):
+ self.internetReachableChanged.emit()
diff --git a/cura/API/__init__.py b/cura/API/__init__.py
index 97d0797430..447be98e4b 100644
--- a/cura/API/__init__.py
+++ b/cura/API/__init__.py
@@ -5,6 +5,7 @@ from typing import Optional, TYPE_CHECKING
from PyQt5.QtCore import QObject, pyqtProperty
from cura.API.Backups import Backups
+from cura.API.ConnectionStatus import ConnectionStatus
from cura.API.Interface import Interface
from cura.API.Account import Account
@@ -14,7 +15,7 @@ if TYPE_CHECKING:
class CuraAPI(QObject):
"""The official Cura API that plug-ins can use to interact with Cura.
-
+
Python does not technically prevent talking to other classes as well, but this API provides a version-safe
interface with proper deprecation warnings etc. Usage of any other methods than the ones provided in this API can
cause plug-ins to be unstable.
@@ -44,6 +45,9 @@ class CuraAPI(QObject):
self._backups = Backups(self._application)
+ self._connectionStatus = ConnectionStatus()
+
+ # Interface API
self._interface = Interface(self._application)
def initialize(self) -> None:
@@ -55,6 +59,10 @@ class CuraAPI(QObject):
return self._account
+ @pyqtProperty(QObject, constant = True)
+ def connectionStatus(self) -> "ConnectionStatus":
+ return self._connectionStatus
+
@property
def backups(self) -> "Backups":
"""Backups API"""