diff --git a/cura/API/Account.py b/cura/API/Account.py index 93738a78e9..bc1ce8c2b9 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -1,15 +1,18 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Optional, Dict +from typing import Optional, Dict, TYPE_CHECKING from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty +from UM.i18n import i18nCatalog from UM.Message import Message + from cura.OAuth2.AuthorizationService import AuthorizationService from cura.OAuth2.Models import OAuth2Settings -from UM.Application import Application -from UM.i18n import i18nCatalog +if TYPE_CHECKING: + from cura.CuraApplication import CuraApplication + i18n_catalog = i18nCatalog("cura") @@ -26,8 +29,9 @@ class Account(QObject): # Signal emitted when user logged in or out. loginStateChanged = pyqtSignal(bool) - def __init__(self, parent = None) -> None: + def __init__(self, application: "CuraApplication", parent = None) -> None: super().__init__(parent) + self._application = application self._error_message = None # type: Optional[Message] self._logged_in = False @@ -47,7 +51,11 @@ class Account(QObject): AUTH_FAILED_REDIRECT="{}/app/auth-error".format(self._oauth_root) ) - self._authorization_service = AuthorizationService(Application.getInstance().getPreferences(), self._oauth_settings) + self._authorization_service = AuthorizationService(self._oauth_settings) + + def initialize(self) -> None: + self._authorization_service.initialize(self._application.getPreferences()) + self._authorization_service.onAuthStateChanged.connect(self._onLoginStateChanged) self._authorization_service.onAuthenticationError.connect(self._onLoginStateChanged) self._authorization_service.loadAuthDataFromPreferences() diff --git a/cura/API/Backups.py b/cura/API/Backups.py index f31933c844..8e5cd7b83a 100644 --- a/cura/API/Backups.py +++ b/cura/API/Backups.py @@ -1,9 +1,12 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Tuple, Optional +from typing import Tuple, Optional, TYPE_CHECKING from cura.Backups.BackupsManager import BackupsManager +if TYPE_CHECKING: + from cura.CuraApplication import CuraApplication + ## The back-ups API provides a version-proof bridge between Cura's # BackupManager and plug-ins that hook into it. @@ -13,9 +16,10 @@ from cura.Backups.BackupsManager import BackupsManager # api = CuraAPI() # api.backups.createBackup() # api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"})`` - class Backups: - manager = BackupsManager() # Re-used instance of the backups manager. + + def __init__(self, application: "CuraApplication") -> None: + self.manager = BackupsManager(application) ## Create a new back-up using the BackupsManager. # \return Tuple containing a ZIP file with the back-up data and a dict diff --git a/cura/API/Interface/Settings.py b/cura/API/Interface/Settings.py index 2889db7022..371c40c14c 100644 --- a/cura/API/Interface/Settings.py +++ b/cura/API/Interface/Settings.py @@ -1,7 +1,11 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from cura.CuraApplication import CuraApplication +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from cura.CuraApplication import CuraApplication + ## The Interface.Settings API provides a version-proof bridge between Cura's # (currently) sidebar UI and plug-ins that hook into it. @@ -19,8 +23,9 @@ from cura.CuraApplication import CuraApplication # api.interface.settings.addContextMenuItem(data)`` class Settings: - # Re-used instance of Cura: - application = CuraApplication.getInstance() # type: CuraApplication + + def __init__(self, application: "CuraApplication") -> None: + self.application = application ## Add items to the sidebar context menu. # \param menu_item dict containing the menu item to add. @@ -30,4 +35,4 @@ class Settings: ## Get all custom items currently added to the sidebar context menu. # \return List containing all custom context menu items. def getContextMenuItems(self) -> list: - return self.application.getSidebarCustomMenuItems() \ No newline at end of file + return self.application.getSidebarCustomMenuItems() diff --git a/cura/API/Interface/__init__.py b/cura/API/Interface/__init__.py index b38118949b..742254a1a4 100644 --- a/cura/API/Interface/__init__.py +++ b/cura/API/Interface/__init__.py @@ -1,9 +1,15 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import TYPE_CHECKING + from UM.PluginRegistry import PluginRegistry from cura.API.Interface.Settings import Settings +if TYPE_CHECKING: + from cura.CuraApplication import CuraApplication + + ## The Interface class serves as a common root for the specific API # methods for each interface element. # @@ -20,5 +26,6 @@ class Interface: # For now we use the same API version to be consistent. VERSION = PluginRegistry.APIVersion - # API methods specific to the settings portion of the UI - settings = Settings() + def __init__(self, application: "CuraApplication") -> None: + # API methods specific to the settings portion of the UI + self.settings = Settings(application) diff --git a/cura/API/__init__.py b/cura/API/__init__.py index 54f5c1f8b0..e9aba86a41 100644 --- a/cura/API/__init__.py +++ b/cura/API/__init__.py @@ -1,5 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional, TYPE_CHECKING + from PyQt5.QtCore import QObject, pyqtProperty from UM.PluginRegistry import PluginRegistry @@ -7,6 +9,9 @@ from cura.API.Backups import Backups from cura.API.Interface import Interface from cura.API.Account import Account +if TYPE_CHECKING: + from cura.CuraApplication import CuraApplication + ## The official Cura API that plug-ins can use to interact with Cura. # @@ -19,14 +24,30 @@ class CuraAPI(QObject): # For now we use the same API version to be consistent. VERSION = PluginRegistry.APIVersion - # Backups API - backups = Backups() + def __init__(self, application: "CuraApplication") -> None: + super().__init__(parent = application) + self._application = application - # Interface API - interface = Interface() + # Accounts API + self._account = Account(self._application) - _account = Account() + # Backups API + self._backups = Backups(self._application) + + # Interface API + self._interface = Interface(self._application) + + def initialize(self) -> None: + self._account.initialize() @pyqtProperty(QObject, constant = True) - def account(self) -> Account: - return CuraAPI._account + def account(self) -> "Account": + return self._account + + @property + def backups(self) -> "Backups": + return self._backups + + @property + def interface(self) -> "Interface": + return self._interface \ No newline at end of file diff --git a/cura/Backups/BackupsManager.py b/cura/Backups/BackupsManager.py index 67e2a222f1..a4d8528960 100644 --- a/cura/Backups/BackupsManager.py +++ b/cura/Backups/BackupsManager.py @@ -1,11 +1,13 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Dict, Optional, Tuple +from typing import Dict, Optional, Tuple, TYPE_CHECKING from UM.Logger import Logger from cura.Backups.Backup import Backup -from cura.CuraApplication import CuraApplication + +if TYPE_CHECKING: + from cura.CuraApplication import CuraApplication ## The BackupsManager is responsible for managing the creating and restoring of @@ -13,8 +15,8 @@ from cura.CuraApplication import CuraApplication # # Back-ups themselves are represented in a different class. class BackupsManager: - def __init__(self): - self._application = CuraApplication.getInstance() + def __init__(self, application: "CuraApplication") -> None: + self._application = application ## Get a back-up of the current configuration. # \return A tuple containing a ZipFile (the actual back-up) and a dict diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 04c9ea88db..9d6a2361a1 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -44,6 +44,7 @@ from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.SetTransformOperation import SetTransformOperation +from cura.API import CuraAPI from cura.Arranging.Arrange import Arrange from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob from cura.Arranging.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob @@ -204,7 +205,7 @@ class CuraApplication(QtApplication): self._quality_profile_drop_down_menu_model = None self._custom_quality_profile_drop_down_menu_model = None - self._cura_API = None + self._cura_API = CuraAPI(self) self._physics = None self._volume = None @@ -713,6 +714,9 @@ class CuraApplication(QtApplication): default_visibility_profile = self._setting_visibility_presets_model.getItem(0) self.getPreferences().setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"])) + # Initialize Cura API + self._cura_API.initialize() + # Detect in which mode to run and execute that mode if self._is_headless: self.runWithoutGUI() @@ -900,10 +904,7 @@ class CuraApplication(QtApplication): self._custom_quality_profile_drop_down_menu_model = CustomQualityProfilesDropDownMenuModel(self) return self._custom_quality_profile_drop_down_menu_model - def getCuraAPI(self, *args, **kwargs): - if self._cura_API is None: - from cura.API import CuraAPI - self._cura_API = CuraAPI() + def getCuraAPI(self, *args, **kwargs) -> "CuraAPI": return self._cura_API ## Registers objects for the QML engine to use. diff --git a/cura/OAuth2/AuthorizationService.py b/cura/OAuth2/AuthorizationService.py index 16f525625e..df068cc43e 100644 --- a/cura/OAuth2/AuthorizationService.py +++ b/cura/OAuth2/AuthorizationService.py @@ -29,7 +29,7 @@ class AuthorizationService: # Emit signal when authentication failed. onAuthenticationError = Signal() - def __init__(self, preferences: Optional["Preferences"], settings: "OAuth2Settings") -> None: + def __init__(self, settings: "OAuth2Settings", preferences: Optional["Preferences"] = None) -> None: self._settings = settings self._auth_helpers = AuthorizationHelpers(settings) self._auth_url = "{}/authorize".format(self._settings.OAUTH_SERVER_URL) @@ -38,6 +38,8 @@ class AuthorizationService: self._preferences = preferences self._server = LocalAuthorizationServer(self._auth_helpers, self._onAuthStateChanged, daemon=True) + def initialize(self, preferences: Optional["Preferences"] = None) -> None: + self._preferences = preferences if self._preferences: self._preferences.addPreference(self._settings.AUTH_DATA_PREFERENCE_KEY, "{}")