From b048c27238ec267e32137ebddbc6c9916d2c762b Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 16 Dec 2019 09:54:49 +0100 Subject: [PATCH 01/32] Added check for discrepancies between Cloud subscribed packages and Cura installed packages CURA-69679 --- plugins/Toolbox/src/Toolbox.py | 54 ++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 27197275b8..4b44ba7268 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -15,6 +15,7 @@ from UM.PluginRegistry import PluginRegistry from UM.Extension import Extension from UM.i18n import i18nCatalog from UM.Version import Version +from UM.Message import Message from cura import ApplicationMetadata from cura import UltimakerCloudAuthentication @@ -61,6 +62,7 @@ class Toolbox(QObject, Extension): "authors": [], "packages": [], "updates": [], + "installed_plugins": [], } # type: Dict[str, List[Any]] # Models: @@ -68,6 +70,7 @@ class Toolbox(QObject, Extension): "authors": AuthorsModel(self), "packages": PackagesModel(self), "updates": PackagesModel(self), + "installed_plugins": PackagesModel(self), } # type: Dict[str, Union[AuthorsModel, PackagesModel]] self._plugins_showcase_model = PackagesModel(self) @@ -198,6 +201,12 @@ class Toolbox(QObject, Extension): sdk_version = self._sdk_version ) + # https://api.ultimaker.com/cura-packages/v1/ + self._api_url_user_packages = "{cloud_api_root}/cura-packages/v{cloud_api_version}".format( + cloud_api_root=self._cloud_api_root, + cloud_api_version=self._cloud_api_version, + ) + # We need to construct a query like installed_packages=ID:VERSION&installed_packages=ID:VERSION, etc. installed_package_ids_with_versions = [":".join(items) for items in self._package_manager.getAllInstalledPackageIdsAndVersions()] @@ -207,15 +216,18 @@ class Toolbox(QObject, Extension): "authors": QUrl("{base_url}/authors".format(base_url = self._api_url)), "packages": QUrl("{base_url}/packages".format(base_url = self._api_url)), "updates": QUrl("{base_url}/packages/package-updates?installed_packages={query}".format( - base_url = self._api_url, query = installed_packages_query)) + base_url = self._api_url, query = installed_packages_query)), + "installed_plugins": QUrl("{base_url}/user/packages".format(base_url=self._api_url_user_packages)) } self._application.getCuraAPI().account.loginStateChanged.connect(self._restart) + self._application.getCuraAPI().account.loginStateChanged.connect(self._fetchUserInstalledPlugins) # On boot we check which packages have updates. if CuraApplication.getInstance().getPreferences().getValue("info/automatic_update_check") and len(installed_package_ids_with_versions) > 0: # Request the latest and greatest! self._fetchPackageUpdates() + self._fetchUserInstalledPlugins() def _prepareNetworkManager(self): if self._network_manager is not None: @@ -237,6 +249,11 @@ class Toolbox(QObject, Extension): # Gather installed packages: self._updateInstalledModels() + def _fetchUserInstalledPlugins(self): + self._prepareNetworkManager() + if self._application.getCuraAPI().account.isLoggedIn: + self._makeRequestByType("installed_plugins") + # Displays the toolbox @pyqtSlot() def launch(self) -> None: @@ -540,9 +557,7 @@ class Toolbox(QObject, Extension): @pyqtSlot(str, result = bool) def isEnabled(self, package_id: str) -> bool: - if package_id in self._plugin_registry.getActivePlugins(): - return True - return False + return package_id in self._plugin_registry.getActivePlugins() # Check for plugins that were installed with the old plugin browser def isOldPlugin(self, plugin_id: str) -> bool: @@ -561,10 +576,11 @@ class Toolbox(QObject, Extension): # Make API Calls # -------------------------------------------------------------------------- def _makeRequestByType(self, request_type: str) -> None: - Logger.log("d", "Requesting %s metadata from server.", request_type) + Logger.log("d", "Requesting '%s' metadata from server.", request_type) request = QNetworkRequest(self._request_urls[request_type]) for header_name, header_value in self._request_headers: request.setRawHeader(header_name, header_value) + self._updateRequestHeader() if self._network_manager: self._network_manager.get(request) @@ -646,8 +662,10 @@ class Toolbox(QObject, Extension): Logger.log("e", "Could not find the %s model.", response_type) break - self._server_response_data[response_type] = json_data["data"] - self._models[response_type].setMetadata(self._server_response_data[response_type]) + # Workaround: Do not add Metadata for "installed_plugins" check JUST YET + if response_type != "installed_plugins": + self._server_response_data[response_type] = json_data["data"] + self._models[response_type].setMetadata(self._server_response_data[response_type]) if response_type == "packages": self._models[response_type].setFilter({"type": "plugin"}) @@ -661,9 +679,27 @@ class Toolbox(QObject, Extension): # Tell the package manager that there's a new set of updates available. packages = set([pkg["package_id"] for pkg in self._server_response_data[response_type]]) self._package_manager.setPackagesWithUpdate(packages) + elif response_type == "installed_plugins": + user_subscribed = [(plugin['package_id'], plugin['package_version']) for plugin in json_data['data']] + Logger.log("i", "- User is subscribed to {} package(s).".format(len(user_subscribed))) + user_installed = self._package_manager.getUserInstalledPackagesOnMarketplace() + Logger.log("i", "- User has installed locally {} package(s).".format(len(user_installed))) - self.metadataChanged.emit() - + # Check for discrepancies between Cura installed and Cloud subscribed packages + # convert them to set() to check if they are equal + if set(user_installed) != set(user_subscribed): + Logger.log('d', "Mismatch found between Cloud subscribed packages and Cura installed packages") + sync_message = Message(i18n_catalog.i18nc( + "@info:generic", + "\nDo you want to sync material and software packages with your account?"), + lifetime=0, + title=i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", )) + sync_message.addAction("sync", + name=i18n_catalog.i18nc("@action:button", "Sync"), + icon="", + description="Sync your Cloud subscribed packages to your local environment.", + button_align=Message.ActionButtonAlignment.ALIGN_RIGHT) + sync_message.show() if self.isLoadingComplete(): self.setViewPage("overview") From 1f5df8fe790c01766a9fdb21b78bc0a16d5fb341 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 16 Dec 2019 10:43:55 +0100 Subject: [PATCH 02/32] Restore wrongly deleted code CURA-6979 --- plugins/Toolbox/src/Toolbox.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 4b44ba7268..d60b40f91b 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -700,6 +700,9 @@ class Toolbox(QObject, Extension): description="Sync your Cloud subscribed packages to your local environment.", button_align=Message.ActionButtonAlignment.ALIGN_RIGHT) sync_message.show() + + self.metadataChanged.emit() + if self.isLoadingComplete(): self.setViewPage("overview") From ebc0813603004c62addf4a4ebe81ca49d2bab4ba Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 16 Dec 2019 10:53:56 +0100 Subject: [PATCH 03/32] Refactored 'installed_plugins' to 'installed_packages' for brevity CURA-6979 --- plugins/Toolbox/src/Toolbox.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index d60b40f91b..2b244e6432 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -62,7 +62,7 @@ class Toolbox(QObject, Extension): "authors": [], "packages": [], "updates": [], - "installed_plugins": [], + "installed_packages": [], } # type: Dict[str, List[Any]] # Models: @@ -70,7 +70,7 @@ class Toolbox(QObject, Extension): "authors": AuthorsModel(self), "packages": PackagesModel(self), "updates": PackagesModel(self), - "installed_plugins": PackagesModel(self), + "installed_packages": PackagesModel(self), } # type: Dict[str, Union[AuthorsModel, PackagesModel]] self._plugins_showcase_model = PackagesModel(self) @@ -217,7 +217,7 @@ class Toolbox(QObject, Extension): "packages": QUrl("{base_url}/packages".format(base_url = self._api_url)), "updates": QUrl("{base_url}/packages/package-updates?installed_packages={query}".format( base_url = self._api_url, query = installed_packages_query)), - "installed_plugins": QUrl("{base_url}/user/packages".format(base_url=self._api_url_user_packages)) + "installed_packages": QUrl("{base_url}/user/packages".format(base_url=self._api_url_user_packages)) } self._application.getCuraAPI().account.loginStateChanged.connect(self._restart) @@ -252,7 +252,7 @@ class Toolbox(QObject, Extension): def _fetchUserInstalledPlugins(self): self._prepareNetworkManager() if self._application.getCuraAPI().account.isLoggedIn: - self._makeRequestByType("installed_plugins") + self._makeRequestByType("installed_packages") # Displays the toolbox @pyqtSlot() @@ -662,8 +662,8 @@ class Toolbox(QObject, Extension): Logger.log("e", "Could not find the %s model.", response_type) break - # Workaround: Do not add Metadata for "installed_plugins" check JUST YET - if response_type != "installed_plugins": + # Workaround: Do not add Metadata for "installed_packages" check JUST YET + if response_type != "installed_packages": self._server_response_data[response_type] = json_data["data"] self._models[response_type].setMetadata(self._server_response_data[response_type]) @@ -679,7 +679,7 @@ class Toolbox(QObject, Extension): # Tell the package manager that there's a new set of updates available. packages = set([pkg["package_id"] for pkg in self._server_response_data[response_type]]) self._package_manager.setPackagesWithUpdate(packages) - elif response_type == "installed_plugins": + elif response_type == "installed_packages": user_subscribed = [(plugin['package_id'], plugin['package_version']) for plugin in json_data['data']] Logger.log("i", "- User is subscribed to {} package(s).".format(len(user_subscribed))) user_installed = self._package_manager.getUserInstalledPackagesOnMarketplace() From 3c2da64c9479c5ca429bd23baef60efa15fe7192 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 16 Dec 2019 15:39:21 +0100 Subject: [PATCH 04/32] Some refactoring done after code review CURA-6979 --- plugins/Toolbox/src/Toolbox.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 2b244e6432..3f6e3472dc 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -62,7 +62,7 @@ class Toolbox(QObject, Extension): "authors": [], "packages": [], "updates": [], - "installed_packages": [], + "subscribed_packages": [], } # type: Dict[str, List[Any]] # Models: @@ -70,7 +70,7 @@ class Toolbox(QObject, Extension): "authors": AuthorsModel(self), "packages": PackagesModel(self), "updates": PackagesModel(self), - "installed_packages": PackagesModel(self), + "subscribed_packages": PackagesModel(self), } # type: Dict[str, Union[AuthorsModel, PackagesModel]] self._plugins_showcase_model = PackagesModel(self) @@ -217,7 +217,7 @@ class Toolbox(QObject, Extension): "packages": QUrl("{base_url}/packages".format(base_url = self._api_url)), "updates": QUrl("{base_url}/packages/package-updates?installed_packages={query}".format( base_url = self._api_url, query = installed_packages_query)), - "installed_packages": QUrl("{base_url}/user/packages".format(base_url=self._api_url_user_packages)) + "subscribed_packages": QUrl("{base_url}/user/packages".format(base_url=self._api_url_user_packages)) } self._application.getCuraAPI().account.loginStateChanged.connect(self._restart) @@ -250,9 +250,9 @@ class Toolbox(QObject, Extension): self._updateInstalledModels() def _fetchUserInstalledPlugins(self): - self._prepareNetworkManager() if self._application.getCuraAPI().account.isLoggedIn: - self._makeRequestByType("installed_packages") + self._prepareNetworkManager() + self._makeRequestByType("subscribed_packages") # Displays the toolbox @pyqtSlot() @@ -662,8 +662,8 @@ class Toolbox(QObject, Extension): Logger.log("e", "Could not find the %s model.", response_type) break - # Workaround: Do not add Metadata for "installed_packages" check JUST YET - if response_type != "installed_packages": + # Workaround: Do not add Metadata for "subscribed_packages" check JUST YET + if response_type != "subscribed_packages": self._server_response_data[response_type] = json_data["data"] self._models[response_type].setMetadata(self._server_response_data[response_type]) @@ -679,11 +679,11 @@ class Toolbox(QObject, Extension): # Tell the package manager that there's a new set of updates available. packages = set([pkg["package_id"] for pkg in self._server_response_data[response_type]]) self._package_manager.setPackagesWithUpdate(packages) - elif response_type == "installed_packages": + elif response_type == "subscribed_packages": user_subscribed = [(plugin['package_id'], plugin['package_version']) for plugin in json_data['data']] - Logger.log("i", "- User is subscribed to {} package(s).".format(len(user_subscribed))) + Logger.log("d", "User is subscribed to {} package(s).".format(len(user_subscribed))) user_installed = self._package_manager.getUserInstalledPackagesOnMarketplace() - Logger.log("i", "- User has installed locally {} package(s).".format(len(user_installed))) + Logger.log("d", "User has installed locally {} package(s).".format(len(user_installed))) # Check for discrepancies between Cura installed and Cloud subscribed packages # convert them to set() to check if they are equal From 46d3a73e7ecf337f7a8d659f0198573d2b100e0a Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 16 Dec 2019 15:52:00 +0100 Subject: [PATCH 05/32] Some more refactoring CURA-6979 --- plugins/Toolbox/src/Toolbox.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 3f6e3472dc..5cca504fd2 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -59,17 +59,17 @@ class Toolbox(QObject, Extension): # The responses as given by the server parsed to a list. self._server_response_data = { - "authors": [], - "packages": [], - "updates": [], - "subscribed_packages": [], + "authors": [], + "packages": [], + "updates": [], + "subscribed_packages": [], } # type: Dict[str, List[Any]] # Models: self._models = { - "authors": AuthorsModel(self), - "packages": PackagesModel(self), - "updates": PackagesModel(self), + "authors": AuthorsModel(self), + "packages": PackagesModel(self), + "updates": PackagesModel(self), "subscribed_packages": PackagesModel(self), } # type: Dict[str, Union[AuthorsModel, PackagesModel]] @@ -200,9 +200,8 @@ class Toolbox(QObject, Extension): cloud_api_version = self._cloud_api_version, sdk_version = self._sdk_version ) - - # https://api.ultimaker.com/cura-packages/v1/ - self._api_url_user_packages = "{cloud_api_root}/cura-packages/v{cloud_api_version}".format( + # https://api.ultimaker.com/cura-packages/v1/user/packages + self._api_url_user_packages = "{cloud_api_root}/cura-packages/v{cloud_api_version}/user/packages".format( cloud_api_root=self._cloud_api_root, cloud_api_version=self._cloud_api_version, ) @@ -217,17 +216,17 @@ class Toolbox(QObject, Extension): "packages": QUrl("{base_url}/packages".format(base_url = self._api_url)), "updates": QUrl("{base_url}/packages/package-updates?installed_packages={query}".format( base_url = self._api_url, query = installed_packages_query)), - "subscribed_packages": QUrl("{base_url}/user/packages".format(base_url=self._api_url_user_packages)) + "subscribed_packages": QUrl(self._api_url_user_packages) } self._application.getCuraAPI().account.loginStateChanged.connect(self._restart) - self._application.getCuraAPI().account.loginStateChanged.connect(self._fetchUserInstalledPlugins) + self._application.getCuraAPI().account.loginStateChanged.connect(self._fetchUserSubscribedPackages) # On boot we check which packages have updates. if CuraApplication.getInstance().getPreferences().getValue("info/automatic_update_check") and len(installed_package_ids_with_versions) > 0: # Request the latest and greatest! self._fetchPackageUpdates() - self._fetchUserInstalledPlugins() + self._fetchUserSubscribedPackages() def _prepareNetworkManager(self): if self._network_manager is not None: @@ -249,7 +248,7 @@ class Toolbox(QObject, Extension): # Gather installed packages: self._updateInstalledModels() - def _fetchUserInstalledPlugins(self): + def _fetchUserSubscribedPackages(self): if self._application.getCuraAPI().account.isLoggedIn: self._prepareNetworkManager() self._makeRequestByType("subscribed_packages") From d958e74467349f98eaf6595e3ef552cc4c625be3 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 16 Dec 2019 15:57:49 +0100 Subject: [PATCH 06/32] Use the new name of the function from Uranium's PackageManager CURA-6979 --- plugins/Toolbox/src/Toolbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 5cca504fd2..5837de9061 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -681,7 +681,7 @@ class Toolbox(QObject, Extension): elif response_type == "subscribed_packages": user_subscribed = [(plugin['package_id'], plugin['package_version']) for plugin in json_data['data']] Logger.log("d", "User is subscribed to {} package(s).".format(len(user_subscribed))) - user_installed = self._package_manager.getUserInstalledPackagesOnMarketplace() + user_installed = self._package_manager.getUserSubscribedPackagesAndVersions() Logger.log("d", "User has installed locally {} package(s).".format(len(user_installed))) # Check for discrepancies between Cura installed and Cloud subscribed packages From 1155b4bab24b1c838b4d41e1db90f97a542506d5 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 17 Dec 2019 16:10:09 +0100 Subject: [PATCH 07/32] Code style: Double quotes around strings Contributes to issue CURA-6979. --- plugins/Toolbox/src/Toolbox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 5837de9061..61e220fbba 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -679,7 +679,7 @@ class Toolbox(QObject, Extension): packages = set([pkg["package_id"] for pkg in self._server_response_data[response_type]]) self._package_manager.setPackagesWithUpdate(packages) elif response_type == "subscribed_packages": - user_subscribed = [(plugin['package_id'], plugin['package_version']) for plugin in json_data['data']] + user_subscribed = [(plugin["package_id"], plugin["package_version"]) for plugin in json_data["data"]] Logger.log("d", "User is subscribed to {} package(s).".format(len(user_subscribed))) user_installed = self._package_manager.getUserSubscribedPackagesAndVersions() Logger.log("d", "User has installed locally {} package(s).".format(len(user_installed))) @@ -687,7 +687,7 @@ class Toolbox(QObject, Extension): # Check for discrepancies between Cura installed and Cloud subscribed packages # convert them to set() to check if they are equal if set(user_installed) != set(user_subscribed): - Logger.log('d', "Mismatch found between Cloud subscribed packages and Cura installed packages") + Logger.log("d", "Mismatch found between Cloud subscribed packages and Cura installed packages") sync_message = Message(i18n_catalog.i18nc( "@info:generic", "\nDo you want to sync material and software packages with your account?"), From 9870097ab5325401316d7116b24c654611b0a269 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 17 Dec 2019 16:11:47 +0100 Subject: [PATCH 08/32] Code style: Spaces around binary operators Contributes to issue CURA-6979. --- plugins/Toolbox/src/Toolbox.py | 36 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 61e220fbba..52b2456fae 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -164,7 +164,7 @@ class Toolbox(QObject, Extension): @pyqtSlot(str, int) def ratePackage(self, package_id: str, rating: int) -> None: - url = QUrl("{base_url}/packages/{package_id}/ratings".format(base_url=self._api_url, package_id = package_id)) + url = QUrl("{base_url}/packages/{package_id}/ratings".format(base_url = self._api_url, package_id = package_id)) self._rate_request = QNetworkRequest(url) for header_name, header_value in self._request_headers: @@ -202,8 +202,8 @@ class Toolbox(QObject, Extension): ) # https://api.ultimaker.com/cura-packages/v1/user/packages self._api_url_user_packages = "{cloud_api_root}/cura-packages/v{cloud_api_version}/user/packages".format( - cloud_api_root=self._cloud_api_root, - cloud_api_version=self._cloud_api_version, + cloud_api_root = self._cloud_api_root, + cloud_api_version = self._cloud_api_version, ) # We need to construct a query like installed_packages=ID:VERSION&installed_packages=ID:VERSION, etc. @@ -691,13 +691,13 @@ class Toolbox(QObject, Extension): sync_message = Message(i18n_catalog.i18nc( "@info:generic", "\nDo you want to sync material and software packages with your account?"), - lifetime=0, - title=i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", )) + lifetime = 0, + title = i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", )) sync_message.addAction("sync", - name=i18n_catalog.i18nc("@action:button", "Sync"), - icon="", - description="Sync your Cloud subscribed packages to your local environment.", - button_align=Message.ActionButtonAlignment.ALIGN_RIGHT) + name = i18n_catalog.i18nc("@action:button", "Sync"), + icon = "", + description = "Sync your Cloud subscribed packages to your local environment.", + button_align = Message.ActionButtonAlignment.ALIGN_RIGHT) sync_message.show() self.metadataChanged.emit() @@ -810,39 +810,39 @@ class Toolbox(QObject, Extension): # Exposed Models: # -------------------------------------------------------------------------- - @pyqtProperty(QObject, constant=True) + @pyqtProperty(QObject, constant = True) def authorsModel(self) -> AuthorsModel: return cast(AuthorsModel, self._models["authors"]) - @pyqtProperty(QObject, constant=True) + @pyqtProperty(QObject, constant = True) def packagesModel(self) -> PackagesModel: return cast(PackagesModel, self._models["packages"]) - @pyqtProperty(QObject, constant=True) + @pyqtProperty(QObject, constant = True) def pluginsShowcaseModel(self) -> PackagesModel: return self._plugins_showcase_model - @pyqtProperty(QObject, constant=True) + @pyqtProperty(QObject, constant = True) def pluginsAvailableModel(self) -> PackagesModel: return self._plugins_available_model - @pyqtProperty(QObject, constant=True) + @pyqtProperty(QObject, constant = True) def pluginsInstalledModel(self) -> PackagesModel: return self._plugins_installed_model - @pyqtProperty(QObject, constant=True) + @pyqtProperty(QObject, constant = True) def materialsShowcaseModel(self) -> AuthorsModel: return self._materials_showcase_model - @pyqtProperty(QObject, constant=True) + @pyqtProperty(QObject, constant = True) def materialsAvailableModel(self) -> AuthorsModel: return self._materials_available_model - @pyqtProperty(QObject, constant=True) + @pyqtProperty(QObject, constant = True) def materialsInstalledModel(self) -> PackagesModel: return self._materials_installed_model - @pyqtProperty(QObject, constant=True) + @pyqtProperty(QObject, constant = True) def materialsGenericModel(self) -> PackagesModel: return self._materials_generic_model From 799e7fc48c0d4205c05c446730fff8bfdd4b12de Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Tue, 17 Dec 2019 16:26:59 +0100 Subject: [PATCH 09/32] changed to use the new function name from UM CURA-6979 --- plugins/Toolbox/src/Toolbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 52b2456fae..e3b41f9bbc 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -681,7 +681,7 @@ class Toolbox(QObject, Extension): elif response_type == "subscribed_packages": user_subscribed = [(plugin["package_id"], plugin["package_version"]) for plugin in json_data["data"]] Logger.log("d", "User is subscribed to {} package(s).".format(len(user_subscribed))) - user_installed = self._package_manager.getUserSubscribedPackagesAndVersions() + user_installed = self._package_manager.getUserInstalledPackagesAndVersions() Logger.log("d", "User has installed locally {} package(s).".format(len(user_installed))) # Check for discrepancies between Cura installed and Cloud subscribed packages From dc20db393e012ba4ca77a183fd00ef557580185c Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Fri, 20 Dec 2019 13:10:22 +0100 Subject: [PATCH 10/32] Initial work for Compatiility Dialog done CURA-7038 --- cura/CuraApplication.py | 3 + .../qml/dialogs/CompatibilityDialog.qml | 39 +++++ .../Toolbox/src/SubscribedPackagesModel.py | 160 ++++++++++++++++++ plugins/Toolbox/src/Toolbox.py | 56 +++++- 4 files changed, 250 insertions(+), 8 deletions(-) create mode 100644 plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml create mode 100644 plugins/Toolbox/src/SubscribedPackagesModel.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 93f7fa97ff..d0f81920c7 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1062,6 +1062,9 @@ class CuraApplication(QtApplication): qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") qmlRegisterType(GlobalStacksModel, "Cura", 1, 0, "GlobalStacksModel") + # from plugins.Toolbox.src.SubscribedPackagesModel import SubscribedPackagesModel + # qmlRegisterType(SubscribedPackagesModel, "Cura", 1, 6, "SubscribedPackagesModel") ### This might not be needed + qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel") qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel") qmlRegisterType(MaterialBrandsModel, "Cura", 1, 0, "MaterialBrandsModel") diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml new file mode 100644 index 0000000000..ea39cb5e99 --- /dev/null +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -0,0 +1,39 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Toolbox is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Dialogs 1.1 +import QtQuick.Window 2.2 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles + +import UM 1.1 as UM +import Cura 1.6 as Cura + + +UM.Dialog +{ + visible: true + + title: "Some title" + minimumWidth: UM.Theme.getSize("license_window_minimum").width + minimumHeight: UM.Theme.getSize("license_window_minimum").height + width: minimumWidth + height: minimumHeight + + ListView + { + id: listView + anchors.fill: parent + + + model: toolbox.subscribedPackagesModel + + delegate: Label + { + text: "A :)" + } + } +} diff --git a/plugins/Toolbox/src/SubscribedPackagesModel.py b/plugins/Toolbox/src/SubscribedPackagesModel.py new file mode 100644 index 0000000000..1d2e98913b --- /dev/null +++ b/plugins/Toolbox/src/SubscribedPackagesModel.py @@ -0,0 +1,160 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import re +from typing import Dict + +from PyQt5.QtCore import Qt, pyqtProperty + +from UM.Logger import Logger +from UM.Qt.ListModel import ListModel + +from .ConfigsModel import ConfigsModel + + +from UM.PluginRegistry import PluginRegistry + +## Model that holds Cura packages. By setting the filter property the instances held by this model can be changed. +class SubscribedPackagesModel(ListModel): + def __init__(self, parent = None): + super().__init__(parent) + + # self._metadata = None + + self.addRoleName(Qt.UserRole + 1, "name") + self.addRoleName(Qt.UserRole + 2, "icon_url") + self.addRoleName(Qt.UserRole + 3, "is_compatible") + + + # List of filters for queries. The result is the union of the each list of results. + self._filter = {} # type: Dict[str, str] + # + # def setMetadata(self, data): + # if self._metadata != data: + # self._metadata = data + # self._update() + + def update(self): + print("---- in update function") + items1 = [] + items2 = [] + + # if self._metadata is None: + # Logger.logException("w", "Failed to load packages for Marketplace") + # self.setItems(items) + # return + + toolbox = PluginRegistry.getInstance().getPluginObject("Toolbox") + print(toolbox.subscribed_compatible_packages) + print(toolbox.subscribed_incompatible_packages) + + for incompatible in toolbox.subscribed_incompatible_packages: + items1.append({ + "name": incompatible.package_id + }) + + for compatible in toolbox.subscribed_compatible_packages: + items2.append({ + "name": compatible.package_id + }) + print("======================0----------------------") + print(items1) + print(items2) + + # for package in self._metadata: + # has_configs = False + # configs_model = None + # + # links_dict = {} + # if "data" in package: + # if "supported_configs" in package["data"]: + # if len(package["data"]["supported_configs"]) > 0: + # has_configs = True + # configs_model = ConfigsModel() + # configs_model.setConfigs(package["data"]["supported_configs"]) + # + # # Links is a list of dictionaries with "title" and "url". Convert this list into a dict so it's easier + # # to process. + # link_list = package["data"]["links"] if "links" in package["data"] else [] + # links_dict = {d["title"]: d["url"] for d in link_list} + # + # if "author_id" not in package["author"] or "display_name" not in package["author"]: + # package["author"]["author_id"] = "" + # package["author"]["display_name"] = "" + # + # items.append({ + # "id": package["package_id"], + # "type": package["package_type"], + # "name": package["display_name"], + # "version": package["package_version"], + # "author_id": package["author"]["author_id"], + # "author_name": package["author"]["display_name"], + # "author_email": package["author"]["email"] if "email" in package["author"] else None, + # "description": package["description"] if "description" in package else None, + # "icon_url": package["icon_url"] if "icon_url" in package else None, + # "image_urls": package["image_urls"] if "image_urls" in package else None, + # "download_url": package["download_url"] if "download_url" in package else None, + # "last_updated": package["last_updated"] if "last_updated" in package else None, + # "is_bundled": package["is_bundled"] if "is_bundled" in package else False, + # "is_active": package["is_active"] if "is_active" in package else False, + # "is_installed": package["is_installed"] if "is_installed" in package else False, + # "has_configs": has_configs, + # "supported_configs": configs_model, + # "download_count": package["download_count"] if "download_count" in package else 0, + # "tags": package["tags"] if "tags" in package else [], + # "links": links_dict, + # "website": package["website"] if "website" in package else None, + # "login_required": "login-required" in package.get("tags", []), + # "average_rating": float(package.get("rating", {}).get("average", 0)), + # "num_ratings": package.get("rating", {}).get("count", 0), + # "user_rating": package.get("rating", {}).get("user_rating", 0) + # }) + # + # # Filter on all the key-word arguments. + # for key, value in self._filter.items(): + # if key == "tags": + # key_filter = lambda item, v = value: v in item["tags"] + # elif "*" in value: + # key_filter = lambda candidate, k = key, v = value: self._matchRegExp(candidate, k, v) + # else: + # key_filter = lambda candidate, k = key, v = value: self._matchString(candidate, k, v) + # items = filter(key_filter, items) + # + # # Execute all filters. + # filtered_items = list(items) + # + # filtered_items.sort(key = lambda k: k["name"]) + # self.setItems(filtered_items) + final_list = items1 + items2 + print(final_list) + self.setItems(final_list) + + ## Set the filter of this model based on a string. + # \param filter_dict \type{Dict} Dictionary to do the filtering by. + def setFilter(self, filter_dict: Dict[str, str]) -> None: + if filter_dict != self._filter: + self._filter = filter_dict + self._update() + + @pyqtProperty("QVariantMap", fset = setFilter, constant = True) + def filter(self) -> Dict[str, str]: + return self._filter + + # Check to see if a container matches with a regular expression + def _matchRegExp(self, metadata, property_name, value): + if property_name not in metadata: + return False + value = re.escape(value) #Escape for regex patterns. + value = "^" + value.replace("\\*", ".*") + "$" #Instead of (now escaped) asterisks, match on any string. Also add anchors for a complete match. + if self._ignore_case: + value_pattern = re.compile(value, re.IGNORECASE) + else: + value_pattern = re.compile(value) + + return value_pattern.match(str(metadata[property_name])) + + # Check to see if a container matches with a string + def _matchString(self, metadata, property_name, value): + if property_name not in metadata: + return False + return value.lower() == str(metadata[property_name]).lower() diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index e3b41f9bbc..f5c15007e7 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -24,6 +24,9 @@ from cura.Machines.ContainerTree import ContainerTree from .AuthorsModel import AuthorsModel from .PackagesModel import PackagesModel +from .SubscribedPackagesModel import SubscribedPackagesModel + +from PyQt5.QtQml import qmlRegisterType if TYPE_CHECKING: from cura.Settings.GlobalStack import GlobalStack @@ -38,6 +41,9 @@ class Toolbox(QObject, Extension): self._application = application # type: CuraApplication + # self._application.qm + # qmlRegisterType(Toolbox, "Cura", 1, 6, "Toolbox") + self._sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int] self._cloud_api_version = UltimakerCloudAuthentication.CuraCloudAPIVersion # type: str self._cloud_api_root = UltimakerCloudAuthentication.CuraCloudAPIRoot # type: str @@ -57,6 +63,9 @@ class Toolbox(QObject, Extension): self._old_plugin_ids = set() # type: Set[str] self._old_plugin_metadata = dict() # type: Dict[str, Dict[str, Any]] + self.subscribed_compatible_packages = [] # type: List[str] + self.subscribed_incompatible_packages = [] # type: List[str] + # The responses as given by the server parsed to a list. self._server_response_data = { "authors": [], @@ -70,8 +79,8 @@ class Toolbox(QObject, Extension): "authors": AuthorsModel(self), "packages": PackagesModel(self), "updates": PackagesModel(self), - "subscribed_packages": PackagesModel(self), - } # type: Dict[str, Union[AuthorsModel, PackagesModel]] + "subscribed_packages": SubscribedPackagesModel(self), + } # type: Dict[str, Union[AuthorsModel, PackagesModel, SubscribedPackagesModel]] self._plugins_showcase_model = PackagesModel(self) self._plugins_available_model = PackagesModel(self) @@ -679,14 +688,32 @@ class Toolbox(QObject, Extension): packages = set([pkg["package_id"] for pkg in self._server_response_data[response_type]]) self._package_manager.setPackagesWithUpdate(packages) elif response_type == "subscribed_packages": - user_subscribed = [(plugin["package_id"], plugin["package_version"]) for plugin in json_data["data"]] - Logger.log("d", "User is subscribed to {} package(s).".format(len(user_subscribed))) - user_installed = self._package_manager.getUserInstalledPackagesAndVersions() + + import collections + Package = collections.namedtuple("Package", ["package_id", "sdk_versions"]) + + user_subscribed = [Package(plugin['package_id'], plugin['sdk_versions']) for plugin in json_data["data"]] + user_subscribed_list = [plugin["package_id"] for plugin in json_data["data"]] + + self.subscribed_compatible_packages.clear() + self.subscribed_incompatible_packages.clear() + + for subscribed in user_subscribed: + if self._sdk_version not in subscribed.sdk_versions: + self.subscribed_incompatible_packages.append(subscribed) + else: + self.subscribed_compatible_packages.append(subscribed) + + + print("compatible packages: \n {}".format(self.subscribed_compatible_packages)) + print("incompatible packages: \n {}".format(self.subscribed_incompatible_packages)) + + self._models["subscribed_packages"].update() + + user_installed = self._package_manager.getUserInstalledPackages() Logger.log("d", "User has installed locally {} package(s).".format(len(user_installed))) - # Check for discrepancies between Cura installed and Cloud subscribed packages - # convert them to set() to check if they are equal - if set(user_installed) != set(user_subscribed): + if set(user_installed) != set(user_subscribed_list): Logger.log("d", "Mismatch found between Cloud subscribed packages and Cura installed packages") sync_message = Message(i18n_catalog.i18nc( "@info:generic", @@ -699,6 +726,7 @@ class Toolbox(QObject, Extension): description = "Sync your Cloud subscribed packages to your local environment.", button_align = Message.ActionButtonAlignment.ALIGN_RIGHT) sync_message.show() + sync_message.actionTriggered.connect(self.some_function) self.metadataChanged.emit() @@ -716,6 +744,14 @@ class Toolbox(QObject, Extension): # Ignore any operation that is not a get operation pass + def some_function(self, messageId: str, actionId: str) -> None: + print("Clicked the BUTTON") + + compatibilityDialog = "resources/qml/dialogs/CompatibilityDialog.qml" + path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), compatibilityDialog) + self._view = self._application.getInstance().createQmlComponent(path, {"toolbox": self}) # what is toolbox: self + + # This function goes through all known remote versions of a package and notifies the package manager of this change def _notifyPackageManager(self): for package in self._server_response_data["packages"]: @@ -814,6 +850,10 @@ class Toolbox(QObject, Extension): def authorsModel(self) -> AuthorsModel: return cast(AuthorsModel, self._models["authors"]) + @pyqtProperty(QObject, constant = True) + def subscribedPackagesModel(self) -> SubscribedPackagesModel: + return cast(SubscribedPackagesModel, self._models["subscribed_packages"]) + @pyqtProperty(QObject, constant = True) def packagesModel(self) -> PackagesModel: return cast(PackagesModel, self._models["packages"]) From 9cf5a388f3482b6b945cd144f3ad7fb75b85e86f Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Fri, 20 Dec 2019 13:59:45 +0100 Subject: [PATCH 11/32] Change for CURA-6979. Check for packages installed in Cloud MP but not in Cura MP. CURA-7038 --- plugins/Toolbox/src/Toolbox.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index f5c15007e7..dfe6de9726 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -705,15 +705,13 @@ class Toolbox(QObject, Extension): self.subscribed_compatible_packages.append(subscribed) - print("compatible packages: \n {}".format(self.subscribed_compatible_packages)) - print("incompatible packages: \n {}".format(self.subscribed_incompatible_packages)) - self._models["subscribed_packages"].update() user_installed = self._package_manager.getUserInstalledPackages() Logger.log("d", "User has installed locally {} package(s).".format(len(user_installed))) - if set(user_installed) != set(user_subscribed_list): + # We check if there are packages installed in Cloud Marketplace but not in Cura marketplace + if list(set(user_subscribed_list).difference(user_installed)): Logger.log("d", "Mismatch found between Cloud subscribed packages and Cura installed packages") sync_message = Message(i18n_catalog.i18nc( "@info:generic", From bd8c1e4c96c05ba4acbf070c33035e1cba379951 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 23 Dec 2019 15:18:09 +0100 Subject: [PATCH 12/32] Initial implementation for the Compatibility Dialog window CURA-7038 --- .../qml/dialogs/CompatibilityDialog.qml | 70 ++++++--- .../Toolbox/src/SubscribedPackagesModel.py | 144 +++--------------- plugins/Toolbox/src/Toolbox.py | 56 +++++-- 3 files changed, 115 insertions(+), 155 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index ea39cb5e99..894cab532c 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -13,27 +13,63 @@ import UM 1.1 as UM import Cura 1.6 as Cura -UM.Dialog -{ +UM.Dialog{ visible: true + title: "Changes from your account" + Label{ + text: "Some text here" + height: 50 + } +Rectangle +{ + id: compatibleRectangle + width: parent.width + height: 300 + Label{ + text: "Some text here" + height: 50 + } - title: "Some title" - minimumWidth: UM.Theme.getSize("license_window_minimum").width - minimumHeight: UM.Theme.getSize("license_window_minimum").height - width: minimumWidth - height: minimumHeight - ListView - { - id: listView + + // Compatible packages + Column{ + id: compatibleColumn + anchors.fill: parent + spacing: 2 + + Repeater{ + model: toolbox.subscribedPackagesModel + delegate: Rectangle{ + id: someRect + width: parent.width + height: 50 + border.color: "black" + Image{ + source: model.icon_url || "../../images/logobot.svg" + width: 50 + height: parent.height + //anchors.left: parent.left + //anchors.right: packageName.left + anchors.rightMargin: 20 + } + Text{ + id: packageName + text: model.name + anchors.centerIn: parent + } + MouseArea{ anchors.fill: parent - - - model: toolbox.subscribedPackagesModel - - delegate: Label - { - text: "A :)" + onClicked: { + console.log("Clicked!") } } + + } + } + } +} + + + } diff --git a/plugins/Toolbox/src/SubscribedPackagesModel.py b/plugins/Toolbox/src/SubscribedPackagesModel.py index 1d2e98913b..79df620ca8 100644 --- a/plugins/Toolbox/src/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/SubscribedPackagesModel.py @@ -19,142 +19,32 @@ class SubscribedPackagesModel(ListModel): def __init__(self, parent = None): super().__init__(parent) - # self._metadata = None self.addRoleName(Qt.UserRole + 1, "name") self.addRoleName(Qt.UserRole + 2, "icon_url") self.addRoleName(Qt.UserRole + 3, "is_compatible") - - # List of filters for queries. The result is the union of the each list of results. - self._filter = {} # type: Dict[str, str] - # - # def setMetadata(self, data): - # if self._metadata != data: - # self._metadata = data - # self._update() - def update(self): - print("---- in update function") - items1 = [] - items2 = [] - - # if self._metadata is None: - # Logger.logException("w", "Failed to load packages for Marketplace") - # self.setItems(items) - # return - + # items1 = [] + # items2 = [] toolbox = PluginRegistry.getInstance().getPluginObject("Toolbox") - print(toolbox.subscribed_compatible_packages) - print(toolbox.subscribed_incompatible_packages) + # print("Compatible: {}".format(toolbox.subscribed_compatible_packages)) + # print("Incompatible: {}".format(toolbox.subscribed_incompatible_packages)) - for incompatible in toolbox.subscribed_incompatible_packages: - items1.append({ - "name": incompatible.package_id - }) - - for compatible in toolbox.subscribed_compatible_packages: - items2.append({ - "name": compatible.package_id - }) - print("======================0----------------------") - print(items1) - print(items2) - - # for package in self._metadata: - # has_configs = False - # configs_model = None - # - # links_dict = {} - # if "data" in package: - # if "supported_configs" in package["data"]: - # if len(package["data"]["supported_configs"]) > 0: - # has_configs = True - # configs_model = ConfigsModel() - # configs_model.setConfigs(package["data"]["supported_configs"]) - # - # # Links is a list of dictionaries with "title" and "url". Convert this list into a dict so it's easier - # # to process. - # link_list = package["data"]["links"] if "links" in package["data"] else [] - # links_dict = {d["title"]: d["url"] for d in link_list} - # - # if "author_id" not in package["author"] or "display_name" not in package["author"]: - # package["author"]["author_id"] = "" - # package["author"]["display_name"] = "" - # - # items.append({ - # "id": package["package_id"], - # "type": package["package_type"], - # "name": package["display_name"], - # "version": package["package_version"], - # "author_id": package["author"]["author_id"], - # "author_name": package["author"]["display_name"], - # "author_email": package["author"]["email"] if "email" in package["author"] else None, - # "description": package["description"] if "description" in package else None, - # "icon_url": package["icon_url"] if "icon_url" in package else None, - # "image_urls": package["image_urls"] if "image_urls" in package else None, - # "download_url": package["download_url"] if "download_url" in package else None, - # "last_updated": package["last_updated"] if "last_updated" in package else None, - # "is_bundled": package["is_bundled"] if "is_bundled" in package else False, - # "is_active": package["is_active"] if "is_active" in package else False, - # "is_installed": package["is_installed"] if "is_installed" in package else False, - # "has_configs": has_configs, - # "supported_configs": configs_model, - # "download_count": package["download_count"] if "download_count" in package else 0, - # "tags": package["tags"] if "tags" in package else [], - # "links": links_dict, - # "website": package["website"] if "website" in package else None, - # "login_required": "login-required" in package.get("tags", []), - # "average_rating": float(package.get("rating", {}).get("average", 0)), - # "num_ratings": package.get("rating", {}).get("count", 0), - # "user_rating": package.get("rating", {}).get("user_rating", 0) + # for incompatible in toolbox.subscribed_incompatible_packages: + # items1.append({ + # "name": incompatible.package_id, + # "icon_url": incompatible.icon_url # }) # - # # Filter on all the key-word arguments. - # for key, value in self._filter.items(): - # if key == "tags": - # key_filter = lambda item, v = value: v in item["tags"] - # elif "*" in value: - # key_filter = lambda candidate, k = key, v = value: self._matchRegExp(candidate, k, v) - # else: - # key_filter = lambda candidate, k = key, v = value: self._matchString(candidate, k, v) - # items = filter(key_filter, items) - # - # # Execute all filters. - # filtered_items = list(items) - # - # filtered_items.sort(key = lambda k: k["name"]) - # self.setItems(filtered_items) - final_list = items1 + items2 - print(final_list) - self.setItems(final_list) + # for compatible in toolbox.subscribed_compatible_packages: + # items2.append({ + # "name": compatible.package_id, + # "icon_url": compatible.icon_url + # }) - ## Set the filter of this model based on a string. - # \param filter_dict \type{Dict} Dictionary to do the filtering by. - def setFilter(self, filter_dict: Dict[str, str]) -> None: - if filter_dict != self._filter: - self._filter = filter_dict - self._update() + print("self.subscribed_packages: {}".format(toolbox.subscribed_packages)) - @pyqtProperty("QVariantMap", fset = setFilter, constant = True) - def filter(self) -> Dict[str, str]: - return self._filter - - # Check to see if a container matches with a regular expression - def _matchRegExp(self, metadata, property_name, value): - if property_name not in metadata: - return False - value = re.escape(value) #Escape for regex patterns. - value = "^" + value.replace("\\*", ".*") + "$" #Instead of (now escaped) asterisks, match on any string. Also add anchors for a complete match. - if self._ignore_case: - value_pattern = re.compile(value, re.IGNORECASE) - else: - value_pattern = re.compile(value) - - return value_pattern.match(str(metadata[property_name])) - - # Check to see if a container matches with a string - def _matchString(self, metadata, property_name, value): - if property_name not in metadata: - return False - return value.lower() == str(metadata[property_name]).lower() + # final_list = items1 + items2 + self.setItems(toolbox.subscribed_packages) + # self.setItems(final_list) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index dfe6de9726..a79c52dbe8 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -63,8 +63,9 @@ class Toolbox(QObject, Extension): self._old_plugin_ids = set() # type: Set[str] self._old_plugin_metadata = dict() # type: Dict[str, Dict[str, Any]] - self.subscribed_compatible_packages = [] # type: List[str] - self.subscribed_incompatible_packages = [] # type: List[str] + # self.subscribed_compatible_packages = [] # type: List[str] + # self.subscribed_incompatible_packages = [] # type: List[str] + self.subscribed_packages = [] # type: List[Dict[str, str]] # The responses as given by the server parsed to a list. self._server_response_data = { @@ -689,20 +690,53 @@ class Toolbox(QObject, Extension): self._package_manager.setPackagesWithUpdate(packages) elif response_type == "subscribed_packages": - import collections - Package = collections.namedtuple("Package", ["package_id", "sdk_versions"]) + # import collections + # Package = collections.namedtuple("Package", ["package_id", "icon_url", "sdk_versions", "is_compatible"]) + # Package.__new__.__defaults__ = (None, ) * len(Package._fields) - user_subscribed = [Package(plugin['package_id'], plugin['sdk_versions']) for plugin in json_data["data"]] + # There is not always an ICON_URL in the response payload ! + # user_subscribed = [Package(plugin['package_id'], plugin.get("icon_url", ""), plugin['sdk_versions']) for plugin in json_data["data"]] user_subscribed_list = [plugin["package_id"] for plugin in json_data["data"]] - self.subscribed_compatible_packages.clear() - self.subscribed_incompatible_packages.clear() + all_subscribed_packages = [] - for subscribed in user_subscribed: - if self._sdk_version not in subscribed.sdk_versions: - self.subscribed_incompatible_packages.append(subscribed) + self.subscribed_packages.clear() + + for package in json_data["data"]: + packagex = { + "name": package["package_id"], + "sdk_versions": package["sdk_versions"] + } + + # packagex = Package(package["package_id"], package["sdk_versions"], ) + if self._sdk_version not in package["sdk_versions"]: + packagex.update({"is_compatible": False}) + # packagex._replace(is_compatible=0) + # packagex.is_compatible = "1" else: - self.subscribed_compatible_packages.append(subscribed) + # packagex._replace(is_compatible="1") + # packagex.is_compatible = "0" + packagex.update({"is_compatible": True}) + + try: + packagex.update({"icon_url": package["icon_url"]}) + except KeyError: # There is no 'icon_url" in the response payload for this package + packagex.update({"icon_url": ""}) + + self.subscribed_packages.append(packagex) + # all_subscribed_packages.append(packagex) + # print("ALL PACKAGES: {}".format(all_subscribed_packages)) + + # self.subscribed_compatible_packages.clear() + # self.subscribed_incompatible_packages.clear() + + + + # for subscribed in user_subscribed: + # if self._sdk_version not in subscribed.sdk_versions: + # self.subscribed_incompatible_packages.append(subscribed) + # else: + # self.subscribed_compatible_packages.append(subscribed) self._models["subscribed_packages"].update() From 4375118a9f31ce9f670b39cc480a8a120a7ba0b3 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 23 Dec 2019 16:58:44 +0100 Subject: [PATCH 13/32] Refactored most of the code into separate functions CURA-7038 --- .../Toolbox/src/SubscribedPackagesModel.py | 32 +---- plugins/Toolbox/src/Toolbox.py | 124 ++++++------------ 2 files changed, 44 insertions(+), 112 deletions(-) diff --git a/plugins/Toolbox/src/SubscribedPackagesModel.py b/plugins/Toolbox/src/SubscribedPackagesModel.py index 79df620ca8..28fadd3765 100644 --- a/plugins/Toolbox/src/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/SubscribedPackagesModel.py @@ -1,17 +1,9 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import re -from typing import Dict +from PyQt5.QtCore import Qt -from PyQt5.QtCore import Qt, pyqtProperty - -from UM.Logger import Logger from UM.Qt.ListModel import ListModel - -from .ConfigsModel import ConfigsModel - - from UM.PluginRegistry import PluginRegistry ## Model that holds Cura packages. By setting the filter property the instances held by this model can be changed. @@ -19,32 +11,10 @@ class SubscribedPackagesModel(ListModel): def __init__(self, parent = None): super().__init__(parent) - self.addRoleName(Qt.UserRole + 1, "name") self.addRoleName(Qt.UserRole + 2, "icon_url") self.addRoleName(Qt.UserRole + 3, "is_compatible") def update(self): - # items1 = [] - # items2 = [] toolbox = PluginRegistry.getInstance().getPluginObject("Toolbox") - # print("Compatible: {}".format(toolbox.subscribed_compatible_packages)) - # print("Incompatible: {}".format(toolbox.subscribed_incompatible_packages)) - - # for incompatible in toolbox.subscribed_incompatible_packages: - # items1.append({ - # "name": incompatible.package_id, - # "icon_url": incompatible.icon_url - # }) - # - # for compatible in toolbox.subscribed_compatible_packages: - # items2.append({ - # "name": compatible.package_id, - # "icon_url": compatible.icon_url - # }) - - print("self.subscribed_packages: {}".format(toolbox.subscribed_packages)) - - # final_list = items1 + items2 self.setItems(toolbox.subscribed_packages) - # self.setItems(final_list) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index a79c52dbe8..ae8396ad67 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -5,6 +5,7 @@ import json import os import tempfile import platform +import functools from typing import cast, Any, Dict, List, Set, TYPE_CHECKING, Tuple, Optional, Union from PyQt5.QtCore import QUrl, QObject, pyqtProperty, pyqtSignal, pyqtSlot @@ -26,8 +27,6 @@ from .AuthorsModel import AuthorsModel from .PackagesModel import PackagesModel from .SubscribedPackagesModel import SubscribedPackagesModel -from PyQt5.QtQml import qmlRegisterType - if TYPE_CHECKING: from cura.Settings.GlobalStack import GlobalStack @@ -41,9 +40,6 @@ class Toolbox(QObject, Extension): self._application = application # type: CuraApplication - # self._application.qm - # qmlRegisterType(Toolbox, "Cura", 1, 6, "Toolbox") - self._sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int] self._cloud_api_version = UltimakerCloudAuthentication.CuraCloudAPIVersion # type: str self._cloud_api_root = UltimakerCloudAuthentication.CuraCloudAPIRoot # type: str @@ -63,8 +59,6 @@ class Toolbox(QObject, Extension): self._old_plugin_ids = set() # type: Set[str] self._old_plugin_metadata = dict() # type: Dict[str, Dict[str, Any]] - # self.subscribed_compatible_packages = [] # type: List[str] - # self.subscribed_incompatible_packages = [] # type: List[str] self.subscribed_packages = [] # type: List[Dict[str, str]] # The responses as given by the server parsed to a list. @@ -689,76 +683,7 @@ class Toolbox(QObject, Extension): packages = set([pkg["package_id"] for pkg in self._server_response_data[response_type]]) self._package_manager.setPackagesWithUpdate(packages) elif response_type == "subscribed_packages": - - # import collections - # Package = collections.namedtuple("Package", ["package_id", "icon_url", "sdk_versions", "is_compatible"]) - # Package.__new__.__defaults__ = (None, ) * len(Package._fields) - - # There is not always an ICON_URL in the response payload ! - # user_subscribed = [Package(plugin['package_id'], plugin.get("icon_url", ""), plugin['sdk_versions']) for plugin in json_data["data"]] - user_subscribed_list = [plugin["package_id"] for plugin in json_data["data"]] - - all_subscribed_packages = [] - - self.subscribed_packages.clear() - - for package in json_data["data"]: - packagex = { - "name": package["package_id"], - "sdk_versions": package["sdk_versions"] - } - - # packagex = Package(package["package_id"], package["sdk_versions"], ) - if self._sdk_version not in package["sdk_versions"]: - packagex.update({"is_compatible": False}) - # packagex._replace(is_compatible=0) - # packagex.is_compatible = "1" - else: - # packagex._replace(is_compatible="1") - # packagex.is_compatible = "0" - packagex.update({"is_compatible": True}) - - try: - packagex.update({"icon_url": package["icon_url"]}) - except KeyError: # There is no 'icon_url" in the response payload for this package - packagex.update({"icon_url": ""}) - - self.subscribed_packages.append(packagex) - # all_subscribed_packages.append(packagex) - # print("ALL PACKAGES: {}".format(all_subscribed_packages)) - - # self.subscribed_compatible_packages.clear() - # self.subscribed_incompatible_packages.clear() - - - - # for subscribed in user_subscribed: - # if self._sdk_version not in subscribed.sdk_versions: - # self.subscribed_incompatible_packages.append(subscribed) - # else: - # self.subscribed_compatible_packages.append(subscribed) - - - self._models["subscribed_packages"].update() - - user_installed = self._package_manager.getUserInstalledPackages() - Logger.log("d", "User has installed locally {} package(s).".format(len(user_installed))) - - # We check if there are packages installed in Cloud Marketplace but not in Cura marketplace - if list(set(user_subscribed_list).difference(user_installed)): - Logger.log("d", "Mismatch found between Cloud subscribed packages and Cura installed packages") - sync_message = Message(i18n_catalog.i18nc( - "@info:generic", - "\nDo you want to sync material and software packages with your account?"), - lifetime = 0, - title = i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", )) - sync_message.addAction("sync", - name = i18n_catalog.i18nc("@action:button", "Sync"), - icon = "", - description = "Sync your Cloud subscribed packages to your local environment.", - button_align = Message.ActionButtonAlignment.ALIGN_RIGHT) - sync_message.show() - sync_message.actionTriggered.connect(self.some_function) + self._checkCompatibilities(json_data["data"]) self.metadataChanged.emit() @@ -776,13 +701,50 @@ class Toolbox(QObject, Extension): # Ignore any operation that is not a get operation pass - def some_function(self, messageId: str, actionId: str) -> None: - print("Clicked the BUTTON") + def _checkCompatibilities(self, json_data): + user_subscribed_list = [plugin["package_id"] for plugin in json_data] + user_installed_packages = self._package_manager.getUserInstalledPackages() + + # We check if there are packages installed in Cloud Marketplace but not in Cura marketplace + if list(set(user_subscribed_list).difference(user_installed_packages)): + Logger.log("d", "Mismatch found between Cloud subscribed packages and Cura installed packages") + sync_message = Message(i18n_catalog.i18nc( + "@info:generic", + "\nDo you want to sync material and software packages with your account?"), + lifetime=0, + title=i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", )) + sync_message.addAction("sync", + name=i18n_catalog.i18nc("@action:button", "Sync"), + icon="", + description="Sync your Cloud subscribed packages to your local environment.", + button_align=Message.ActionButtonAlignment.ALIGN_RIGHT) + + self._onSyncButtonClickedHelper = functools.partial(self._onSyncButtonClicked, json_data) + sync_message.actionTriggered.connect(self._onSyncButtonClickedHelper) + sync_message.show() + + def _onSyncButtonClicked(self, json_data, messageId: str, actionId: str) -> None: + self.subscribed_packages.clear() + # We create the packages from the HTTP payload + for item in json_data: + package = {"name": item["package_id"], "sdk_versions": item["sdk_versions"]} + + if self._sdk_version not in item["sdk_versions"]: + package.update({"is_compatible": False}) + else: + package.update({"is_compatible": True}) + + try: + package.update({"icon_url": item["icon_url"]}) + except KeyError: # There is no 'icon_url" in the response payload for this package + package.update({"icon_url": ""}) + + self.subscribed_packages.append(package) + self._models["subscribed_packages"].update() compatibilityDialog = "resources/qml/dialogs/CompatibilityDialog.qml" path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), compatibilityDialog) - self._view = self._application.getInstance().createQmlComponent(path, {"toolbox": self}) # what is toolbox: self - + self.compatibility_dialog_view = self._application.getInstance().createQmlComponent(path, {"toolbox": self}) # This function goes through all known remote versions of a package and notifies the package manager of this change def _notifyPackageManager(self): From 12be21e5948ab84258d16612398a753e7c5a6beb Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 23 Dec 2019 17:23:26 +0100 Subject: [PATCH 14/32] Reworked the flow to show only not installed packages for installing in the Compatibility Dialog CURA-7038 --- .../qml/dialogs/CompatibilityDialog.qml | 2 +- plugins/Toolbox/src/Toolbox.py | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index 894cab532c..dd178cc0a4 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -55,7 +55,7 @@ Rectangle } Text{ id: packageName - text: model.name + text: model.name + " (Compatible: " + model.is_compatible + ")" anchors.centerIn: parent } MouseArea{ diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index ae8396ad67..91eb235328 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -702,12 +702,13 @@ class Toolbox(QObject, Extension): pass def _checkCompatibilities(self, json_data): - user_subscribed_list = [plugin["package_id"] for plugin in json_data] + user_subscribed_packages = [plugin["package_id"] for plugin in json_data] user_installed_packages = self._package_manager.getUserInstalledPackages() - # We check if there are packages installed in Cloud Marketplace but not in Cura marketplace - if list(set(user_subscribed_list).difference(user_installed_packages)): - Logger.log("d", "Mismatch found between Cloud subscribed packages and Cura installed packages") + # We check if there are packages installed in Cloud Marketplace but not in Cura marketplace (discrepancy) + package_discrepancy = list(set(user_subscribed_packages).difference(user_installed_packages)) + if package_discrepancy: + Logger.log("d", "Discrepancy found between Cloud subscribed packages and Cura installed packages") sync_message = Message(i18n_catalog.i18nc( "@info:generic", "\nDo you want to sync material and software packages with your account?"), @@ -719,27 +720,28 @@ class Toolbox(QObject, Extension): description="Sync your Cloud subscribed packages to your local environment.", button_align=Message.ActionButtonAlignment.ALIGN_RIGHT) - self._onSyncButtonClickedHelper = functools.partial(self._onSyncButtonClicked, json_data) + self._onSyncButtonClickedHelper = functools.partial(self._onSyncButtonClicked, json_data, package_discrepancy) sync_message.actionTriggered.connect(self._onSyncButtonClickedHelper) sync_message.show() - def _onSyncButtonClicked(self, json_data, messageId: str, actionId: str) -> None: - self.subscribed_packages.clear() - # We create the packages from the HTTP payload + def _onSyncButtonClicked(self, json_data, package_discrepancy, messageId: str, actionId: str) -> None: + # self.subscribed_packages.clear() + # We 'create' the packages from the HTTP payload for item in json_data: + if item["package_id"] not in package_discrepancy: # But we skip packages that the user has locally installed + continue package = {"name": item["package_id"], "sdk_versions": item["sdk_versions"]} - if self._sdk_version not in item["sdk_versions"]: package.update({"is_compatible": False}) else: package.update({"is_compatible": True}) - try: package.update({"icon_url": item["icon_url"]}) except KeyError: # There is no 'icon_url" in the response payload for this package package.update({"icon_url": ""}) self.subscribed_packages.append(package) + Logger.log("d", "Package '{}' scheduled for installing.".format(package['name'])) self._models["subscribed_packages"].update() compatibilityDialog = "resources/qml/dialogs/CompatibilityDialog.qml" From 8c0f64633b7025a01be9bdb4ac088d48a137f4ab Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 23 Dec 2019 18:08:22 +0100 Subject: [PATCH 15/32] Removed comments CURA-7038 --- cura/CuraApplication.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index d0f81920c7..93f7fa97ff 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1062,9 +1062,6 @@ class CuraApplication(QtApplication): qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") qmlRegisterType(GlobalStacksModel, "Cura", 1, 0, "GlobalStacksModel") - # from plugins.Toolbox.src.SubscribedPackagesModel import SubscribedPackagesModel - # qmlRegisterType(SubscribedPackagesModel, "Cura", 1, 6, "SubscribedPackagesModel") ### This might not be needed - qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel") qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel") qmlRegisterType(MaterialBrandsModel, "Cura", 1, 0, "MaterialBrandsModel") From e28a662d7ce3e63929da5e5cb9e89bad64ad9fbb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 30 Dec 2019 13:44:54 +0100 Subject: [PATCH 16/32] Speed up mypy checking Checking Cura takes up most of the time, but during that time we could check for the plugins. --- run_mypy.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/run_mypy.py b/run_mypy.py index 6be424bda8..d93e1cafc8 100644 --- a/run_mypy.py +++ b/run_mypy.py @@ -1,8 +1,9 @@ #!/usr/bin/env python import os import sys -import subprocess - +from multiprocessing.dummy import Pool +from functools import partial +from subprocess import call # A quick Python implementation of unix 'where' command. def where(exe_name: str, search_path: str = os.getenv("PATH")) -> str: @@ -62,21 +63,23 @@ def main(): mods = ["cura"] + plugins + findModules("plugins/VersionUpgrade") success_code = 0 - for mod in mods: - print("------------- Checking module {mod}".format(**locals())) - if sys.platform == "win32": - result = subprocess.run([mypy_module, "-p", mod, "--ignore-missing-imports"]) - else: - result = subprocess.run([sys.executable, mypy_module, "-p", mod, "--ignore-missing-imports"]) - if result.returncode != 0: - print("\nModule {mod} failed checking. :(".format(**locals())) - success_code = 1 - if success_code: - print("\n\nSome modules failed checking!") + + pool = Pool(2) # Run two commands at once + + if sys.platform == "win32": + commands = ["%s -p %s --ignore-missing-imports" % (mypy_module, mod) for mod in mods] else: - print("\n\nDone checking. All is good.") + commands = ["%s %s -p %s --ignore-missing-imports" % (sys.executable, mypy_module, mod) for mod in mods] + + for i, returncode in enumerate(pool.imap(partial(call, shell=True), commands)): + if returncode != 0: + print("\nCommand %s failed checking. :(" % commands[i]) + success_code = 1 + if success_code: + print("MYPY check was compleded, but did not pass") + else: + print("MYPY check was compleded and passed with flying colors") return success_code - if __name__ == "__main__": - sys.exit(main()) + sys.exit(main()) \ No newline at end of file From ada2a776d5faec75fa338c59d93f5609a499a196 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 30 Dec 2019 17:36:46 +0100 Subject: [PATCH 17/32] Reworked the Compatibility Dialog and how it shows the subscribed packages CURA-7038 --- .../qml/dialogs/CompatibilityDialog.qml | 177 ++++++++++++------ plugins/Toolbox/src/Toolbox.py | 8 +- 2 files changed, 128 insertions(+), 57 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index dd178cc0a4..249111b9ad 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -1,13 +1,9 @@ -// Copyright (c) 2018 Ultimaker B.V. +// Copyright (c) 2020 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. import QtQuick 2.10 -import QtQuick.Dialogs 1.1 import QtQuick.Window 2.2 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles +import QtQuick.Controls 2.3 import UM 1.1 as UM import Cura 1.6 as Cura @@ -15,61 +11,136 @@ import Cura 1.6 as Cura UM.Dialog{ visible: true - title: "Changes from your account" - Label{ - text: "Some text here" - height: 50 - } -Rectangle -{ - id: compatibleRectangle - width: parent.width - height: 300 - Label{ - text: "Some text here" - height: 50 - } + title: catalog.i18nc("@title", "Changes from your account") + width: UM.Theme.getSize("popup_dialog").width + height: UM.Theme.getSize("popup_dialog").height + minimumWidth: width + maximumWidth: minimumWidth + minimumHeight: height + maximumHeight: minimumHeight + margin: 0 - - - // Compatible packages - Column{ - id: compatibleColumn + Rectangle + { + id: root anchors.fill: parent - spacing: 2 + color: UM.Theme.getColor("main_background") - Repeater{ - model: toolbox.subscribedPackagesModel - delegate: Rectangle{ - id: someRect - width: parent.width - height: 50 - border.color: "black" - Image{ - source: model.icon_url || "../../images/logobot.svg" - width: 50 - height: parent.height - //anchors.left: parent.left - //anchors.right: packageName.left - anchors.rightMargin: 20 + UM.I18nCatalog + { + id: catalog + name: "cura" + } + + ScrollView + { + width: parent.width + height: parent.height - nextButton.height - nextButton.anchors.margins * 2 // We want some leftover space for the button at the bottom + clip: true + + Column + { + anchors.fill: parent + anchors.margins: UM.Theme.getSize("default_margin").width + + // Compatible packages + Label + { + font: UM.Theme.getFont("default") + text: catalog.i18nc("@label", "The following packages will be added:") + color: UM.Theme.getColor("text") + height: contentHeight + UM.Theme.getSize("default_margin").height } - Text{ - id: packageName - text: model.name + " (Compatible: " + model.is_compatible + ")" - anchors.centerIn: parent - } - MouseArea{ - anchors.fill: parent - onClicked: { - console.log("Clicked!") + Repeater + { + model: toolbox.subscribedPackagesModel + Component + { + id: compatibleDelegate + Item + { + width: parent.width + property var lineHeight: 60 + visible: model.is_compatible == "True" ? true : false + height: visible ? (lineHeight + UM.Theme.getSize("default_margin").height) : 0 // We only show the compatible packages here + Image + { + id: packageIcon + source: model.icon_url || "../../images/logobot.svg" + height: lineHeight + width: height + mipmap: true + fillMode: Image.PreserveAspectFit + } + Label + { + id: compatibleLabel + text: model.name + font: UM.Theme.getFont("medium_bold") + anchors.left: packageIcon.right + anchors.leftMargin: 20 + anchors.verticalCenter: packageIcon.verticalCenter + color: UM.Theme.getColor("text") + elide: Text.ElideRight + } + } } } + // Incompatible packages + Label + { + font: UM.Theme.getFont("default") + text: catalog.i18nc("@label", "The following packages can not be installed because of incompatible Cura version:") + color: UM.Theme.getColor("text") + height: contentHeight + UM.Theme.getSize("default_margin").height + } + Repeater + { + model: toolbox.subscribedPackagesModel + Component + { + id: incompatibleDelegate + Item + { + width: parent.width + property var lineHeight: 60 + visible: model.is_compatible == "True" ? false : true + height: visible ? (lineHeight + UM.Theme.getSize("default_margin").height) : 0 // We only show the incompatible packages here + Image + { + id: packageIcon + source: model.icon_url || "../../images/logobot.svg" + height: lineHeight + width: height + mipmap: true + fillMode: Image.PreserveAspectFit + } + Label + { + id: incompatibleLabel + text: model.name + font: UM.Theme.getFont("medium_bold") + anchors.left: packageIcon.right + anchors.leftMargin: 20 + anchors.verticalCenter: packageIcon.verticalCenter + color: UM.Theme.getColor("text") + elide: Text.ElideRight + } + } + } + } } + + } // End of ScrollView + + Cura.ActionButton + { + id: nextButton + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.margins: UM.Theme.getSize("default_margin").height + text: catalog.i18nc("@button", "Next") } } } - - - -} diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 91eb235328..2c890c8eec 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -701,7 +701,7 @@ class Toolbox(QObject, Extension): # Ignore any operation that is not a get operation pass - def _checkCompatibilities(self, json_data): + def _checkCompatibilities(self, json_data) -> None: user_subscribed_packages = [plugin["package_id"] for plugin in json_data] user_installed_packages = self._package_manager.getUserInstalledPackages() @@ -725,16 +725,16 @@ class Toolbox(QObject, Extension): sync_message.show() def _onSyncButtonClicked(self, json_data, package_discrepancy, messageId: str, actionId: str) -> None: - # self.subscribed_packages.clear() + self.subscribed_packages.clear() # We 'create' the packages from the HTTP payload for item in json_data: if item["package_id"] not in package_discrepancy: # But we skip packages that the user has locally installed continue package = {"name": item["package_id"], "sdk_versions": item["sdk_versions"]} if self._sdk_version not in item["sdk_versions"]: - package.update({"is_compatible": False}) + package.update({"is_compatible": "False"}) else: - package.update({"is_compatible": True}) + package.update({"is_compatible": "True"}) try: package.update({"icon_url": item["icon_url"]}) except KeyError: # There is no 'icon_url" in the response payload for this package From 93c1c1a86eaba24ce13136736703f3a9a0515dc9 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 30 Dec 2019 17:38:21 +0100 Subject: [PATCH 18/32] deleted unused lines CURA-7038 --- plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index 249111b9ad..5685ddd2b5 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -56,7 +56,6 @@ UM.Dialog{ model: toolbox.subscribedPackagesModel Component { - id: compatibleDelegate Item { width: parent.width @@ -74,7 +73,6 @@ UM.Dialog{ } Label { - id: compatibleLabel text: model.name font: UM.Theme.getFont("medium_bold") anchors.left: packageIcon.right @@ -100,7 +98,6 @@ UM.Dialog{ model: toolbox.subscribedPackagesModel Component { - id: incompatibleDelegate Item { width: parent.width @@ -118,7 +115,6 @@ UM.Dialog{ } Label { - id: incompatibleLabel text: model.name font: UM.Theme.getFont("medium_bold") anchors.left: packageIcon.right From c7dbb1477a9cd12cc06475b9e9fa80be49085e38 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 31 Dec 2019 11:07:11 +0100 Subject: [PATCH 19/32] Remove maximum on small feature speed You can make the small features print FASTER if you like, as well. There is no physical or computational constraint there. Discovered during investigation of #6866. --- resources/definitions/fdmprinter.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index e13f3e6f8b..46d194c6a0 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -7650,7 +7650,7 @@ "default_value": 50, "minimum_value": "1", "minimum_value_warning": "25", - "maximum_value": "100", + "maximum_value_warning": "100", "settable_per_mesh": true }, "small_feature_speed_factor_0": @@ -7663,7 +7663,7 @@ "value": "small_feature_speed_factor", "minimum_value": "1", "minimum_value_warning": "25", - "maximum_value": "100", + "maximum_value_warning": "100", "settable_per_mesh": true } } From bf57a61c48bffc8c678ec4f7ae1a7689b250fd87 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 31 Dec 2019 11:35:37 +0100 Subject: [PATCH 20/32] Fix settings overlapping after collapse I can't figure out why we ever set the min size to be negative, but it was ocasonally making the layout stumble --- resources/qml/Settings/SettingView.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 5aea939728..bddd82c802 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -251,7 +251,7 @@ Item id: delegate width: scrollView.width - height: provider.properties.enabled === "True" ? UM.Theme.getSize("section").height : - contents.spacing + height: provider.properties.enabled === "True" ? UM.Theme.getSize("section").height : 0 Behavior on height { NumberAnimation { duration: 100 } } opacity: provider.properties.enabled === "True" ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } From 96311c974b1d961e099e35813ecc770de2c830a9 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Tue, 31 Dec 2019 12:07:42 +0100 Subject: [PATCH 21/32] Fixed failing tests. Hid the sync Message when it's clicked. CURA-7038 --- plugins/Toolbox/src/Toolbox.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 2c890c8eec..b03b11317a 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -724,7 +724,8 @@ class Toolbox(QObject, Extension): sync_message.actionTriggered.connect(self._onSyncButtonClickedHelper) sync_message.show() - def _onSyncButtonClicked(self, json_data, package_discrepancy, messageId: str, actionId: str) -> None: + def _onSyncButtonClicked(self, json_data, package_discrepancy, sync_message: Message, actionId: str) -> None: + sync_message.hide() self.subscribed_packages.clear() # We 'create' the packages from the HTTP payload for item in json_data: @@ -744,9 +745,11 @@ class Toolbox(QObject, Extension): Logger.log("d", "Package '{}' scheduled for installing.".format(package['name'])) self._models["subscribed_packages"].update() - compatibilityDialog = "resources/qml/dialogs/CompatibilityDialog.qml" - path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), compatibilityDialog) - self.compatibility_dialog_view = self._application.getInstance().createQmlComponent(path, {"toolbox": self}) + compatibility_dialog_path = "resources/qml/dialogs/CompatibilityDialog.qml" + plugin_path_prefix = PluginRegistry.getInstance().getPluginPath(self.getPluginId()) + if plugin_path_prefix: + path = os.path.join(plugin_path_prefix, compatibility_dialog_path) + self.compatibility_dialog_view = self._application.getInstance().createQmlComponent(path, {"toolbox": self}) # This function goes through all known remote versions of a package and notifies the package manager of this change def _notifyPackageManager(self): From dde0dd8ce324059269338085ab809660804e8b4a Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Tue, 31 Dec 2019 14:02:41 +0100 Subject: [PATCH 22/32] Removed the 'workaround' that I've added earlier. Simplified the flow. The dialog is now showing display_name instead of package_id CURA-7038 --- .../Toolbox/src/SubscribedPackagesModel.py | 38 ++++++++++++++++--- plugins/Toolbox/src/Toolbox.py | 33 +++------------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/plugins/Toolbox/src/SubscribedPackagesModel.py b/plugins/Toolbox/src/SubscribedPackagesModel.py index 28fadd3765..f8340ab7a0 100644 --- a/plugins/Toolbox/src/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/SubscribedPackagesModel.py @@ -1,20 +1,46 @@ -# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2020 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from PyQt5.QtCore import Qt - from UM.Qt.ListModel import ListModel -from UM.PluginRegistry import PluginRegistry +from cura import ApplicationMetadata + -## Model that holds Cura packages. By setting the filter property the instances held by this model can be changed. class SubscribedPackagesModel(ListModel): def __init__(self, parent = None): super().__init__(parent) + self._metadata = None + self._discrepancies = None + self._sdk_version = ApplicationMetadata.CuraSDKVersion + self.addRoleName(Qt.UserRole + 1, "name") self.addRoleName(Qt.UserRole + 2, "icon_url") self.addRoleName(Qt.UserRole + 3, "is_compatible") + def setMetadata(self, data): + if self._metadata != data: + self._metadata = data + + def addValue(self, discrepancy): + if self._discrepancies != discrepancy: + self._discrepancies = discrepancy + def update(self): - toolbox = PluginRegistry.getInstance().getPluginObject("Toolbox") - self.setItems(toolbox.subscribed_packages) + items = [] + + for item in self._metadata: + if item["package_id"] not in self._discrepancies: + continue + package = {"name": item["display_name"], "sdk_versions": item["sdk_versions"]} + if self._sdk_version not in item["sdk_versions"]: + package.update({"is_compatible": "False"}) + else: + package.update({"is_compatible": "True"}) + try: + package.update({"icon_url": item["icon_url"]}) + except KeyError: # There is no 'icon_url" in the response payload for this package + package.update({"icon_url": ""}) + + items.append(package) + self.setItems(items) \ No newline at end of file diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index b03b11317a..9e785e6225 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -5,7 +5,6 @@ import json import os import tempfile import platform -import functools from typing import cast, Any, Dict, List, Set, TYPE_CHECKING, Tuple, Optional, Union from PyQt5.QtCore import QUrl, QObject, pyqtProperty, pyqtSignal, pyqtSlot @@ -665,10 +664,8 @@ class Toolbox(QObject, Extension): Logger.log("e", "Could not find the %s model.", response_type) break - # Workaround: Do not add Metadata for "subscribed_packages" check JUST YET - if response_type != "subscribed_packages": - self._server_response_data[response_type] = json_data["data"] - self._models[response_type].setMetadata(self._server_response_data[response_type]) + self._server_response_data[response_type] = json_data["data"] + self._models[response_type].setMetadata(self._server_response_data[response_type]) if response_type == "packages": self._models[response_type].setFilter({"type": "plugin"}) @@ -708,6 +705,8 @@ class Toolbox(QObject, Extension): # We check if there are packages installed in Cloud Marketplace but not in Cura marketplace (discrepancy) package_discrepancy = list(set(user_subscribed_packages).difference(user_installed_packages)) if package_discrepancy: + self._models["subscribed_packages"].addValue(package_discrepancy) + self._models["subscribed_packages"].update() Logger.log("d", "Discrepancy found between Cloud subscribed packages and Cura installed packages") sync_message = Message(i18n_catalog.i18nc( "@info:generic", @@ -720,31 +719,11 @@ class Toolbox(QObject, Extension): description="Sync your Cloud subscribed packages to your local environment.", button_align=Message.ActionButtonAlignment.ALIGN_RIGHT) - self._onSyncButtonClickedHelper = functools.partial(self._onSyncButtonClicked, json_data, package_discrepancy) - sync_message.actionTriggered.connect(self._onSyncButtonClickedHelper) + sync_message.actionTriggered.connect(self._onSyncButtonClicked) sync_message.show() - def _onSyncButtonClicked(self, json_data, package_discrepancy, sync_message: Message, actionId: str) -> None: + def _onSyncButtonClicked(self, sync_message: Message, sync_message_action: str) -> None: sync_message.hide() - self.subscribed_packages.clear() - # We 'create' the packages from the HTTP payload - for item in json_data: - if item["package_id"] not in package_discrepancy: # But we skip packages that the user has locally installed - continue - package = {"name": item["package_id"], "sdk_versions": item["sdk_versions"]} - if self._sdk_version not in item["sdk_versions"]: - package.update({"is_compatible": "False"}) - else: - package.update({"is_compatible": "True"}) - try: - package.update({"icon_url": item["icon_url"]}) - except KeyError: # There is no 'icon_url" in the response payload for this package - package.update({"icon_url": ""}) - - self.subscribed_packages.append(package) - Logger.log("d", "Package '{}' scheduled for installing.".format(package['name'])) - self._models["subscribed_packages"].update() - compatibility_dialog_path = "resources/qml/dialogs/CompatibilityDialog.qml" plugin_path_prefix = PluginRegistry.getInstance().getPluginPath(self.getPluginId()) if plugin_path_prefix: From 42851b0851019998a3831ec89a4b52371dcd1a02 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Tue, 31 Dec 2019 14:03:56 +0100 Subject: [PATCH 23/32] Removed unnecessary pass statement from reply.operation CURA-7038 --- plugins/Toolbox/src/Toolbox.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 9e785e6225..50a8d39a90 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -694,9 +694,6 @@ class Toolbox(QObject, Extension): Logger.log("w", "Unable to connect with the server, we got a response code %s while trying to connect to %s", reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url()) self.setViewPage("errored") self.resetDownload() - elif reply.operation() == QNetworkAccessManager.PutOperation: - # Ignore any operation that is not a get operation - pass def _checkCompatibilities(self, json_data) -> None: user_subscribed_packages = [plugin["package_id"] for plugin in json_data] From 802c095c9f0fd585d0e46070dc8aa181604df660 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Tue, 31 Dec 2019 14:07:40 +0100 Subject: [PATCH 24/32] Removed unused list CURA-7038 --- plugins/Toolbox/src/Toolbox.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 50a8d39a90..af0a0748e7 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -58,8 +58,6 @@ class Toolbox(QObject, Extension): self._old_plugin_ids = set() # type: Set[str] self._old_plugin_metadata = dict() # type: Dict[str, Dict[str, Any]] - self.subscribed_packages = [] # type: List[Dict[str, str]] - # The responses as given by the server parsed to a list. self._server_response_data = { "authors": [], From 1ea8145f3957c00d76e57d2e2c6280bf921c95a4 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Tue, 31 Dec 2019 14:35:47 +0100 Subject: [PATCH 25/32] Changed the hardcoded value for leftMargin CURA-7038 --- plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index 5685ddd2b5..f5a20986d1 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -76,7 +76,7 @@ UM.Dialog{ text: model.name font: UM.Theme.getFont("medium_bold") anchors.left: packageIcon.right - anchors.leftMargin: 20 + anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.verticalCenter: packageIcon.verticalCenter color: UM.Theme.getColor("text") elide: Text.ElideRight @@ -118,7 +118,7 @@ UM.Dialog{ text: model.name font: UM.Theme.getFont("medium_bold") anchors.left: packageIcon.right - anchors.leftMargin: 20 + anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.verticalCenter: packageIcon.verticalCenter color: UM.Theme.getColor("text") elide: Text.ElideRight From 81c4c22d6348e645629ecb8259d2b9090cda3abe Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 31 Dec 2019 14:42:10 +0100 Subject: [PATCH 26/32] Fix that invisible items would still add spacing --- resources/qml/Settings/SettingView.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index bddd82c802..419af782cf 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -222,7 +222,6 @@ Item ListView { id: contents - spacing: UM.Theme.getSize("default_lining").height cacheBuffer: 1000000 // Set a large cache to effectively just cache every list item. model: UM.SettingDefinitionsModel @@ -251,7 +250,7 @@ Item id: delegate width: scrollView.width - height: provider.properties.enabled === "True" ? UM.Theme.getSize("section").height : 0 + height: provider.properties.enabled === "True" ? UM.Theme.getSize("section").height + 2 * UM.Theme.getSize("default_lining").height : 0 Behavior on height { NumberAnimation { duration: 100 } } opacity: provider.properties.enabled === "True" ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } From 7cc74c97e37168390c90771dd2521593709126cb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 31 Dec 2019 15:28:29 +0100 Subject: [PATCH 27/32] Ensure setting category uses correct background size --- resources/qml/Settings/SettingCategory.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index 18c5820832..5466aeaeaa 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -19,7 +19,7 @@ Button background: Rectangle { id: backgroundRectangle - implicitHeight: UM.Theme.getSize("section").height + height: UM.Theme.getSize("section").height color: { if (base.color) From cb361e9543941fbcae69deda4c4b089025c1921b Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 31 Dec 2019 16:14:02 +0100 Subject: [PATCH 28/32] Force sim-view to update after view-type change. --- plugins/SimulationView/SimulationView.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 6d6f19c57c..265289c263 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -292,8 +292,12 @@ class SimulationView(CuraView): # # \param layer_view_type integer as in SimulationView.qml and this class def setSimulationViewType(self, layer_view_type: int) -> None: - self._layer_view_type = layer_view_type - self.currentLayerNumChanged.emit() + if layer_view_type != self._layer_view_type: + self._layer_view_type = layer_view_type + self.currentLayerNumChanged.emit() + + scene = Application.getInstance().getController().getScene() + scene.sceneChanged.emit(scene.getRoot()) ## Return the layer view type, integer as in SimulationView.qml and this class def getSimulationViewType(self) -> int: From 1915100118d836c70e6f7a8047063b4bfa6b38eb Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 31 Dec 2019 16:48:50 +0100 Subject: [PATCH 29/32] Fix: Layersliders work again. --- plugins/SimulationView/SimulationView.py | 5 ++--- plugins/SimulationView/SimulationViewProxy.py | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 265289c263..4282806ff5 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -296,9 +296,6 @@ class SimulationView(CuraView): self._layer_view_type = layer_view_type self.currentLayerNumChanged.emit() - scene = Application.getInstance().getController().getScene() - scene.sceneChanged.emit(scene.getRoot()) - ## Return the layer view type, integer as in SimulationView.qml and this class def getSimulationViewType(self) -> int: return self._layer_view_type @@ -575,6 +572,8 @@ class SimulationView(CuraView): def _onCurrentLayerNumChanged(self) -> None: self.calculateMaxPathsOnLayer(self._current_layer_num) + scene = Application.getInstance().getController().getScene() + scene.sceneChanged.emit(scene.getRoot()) def _startUpdateTopLayers(self) -> None: if not self._compatibility_mode: diff --git a/plugins/SimulationView/SimulationViewProxy.py b/plugins/SimulationView/SimulationViewProxy.py index 58a004cc31..1183244ab3 100644 --- a/plugins/SimulationView/SimulationViewProxy.py +++ b/plugins/SimulationView/SimulationViewProxy.py @@ -149,6 +149,9 @@ class SimulationViewProxy(QObject): self.currentPathChanged.emit() self._layerActivityChanged() + scene = Application.getInstance().getController().getScene() + scene.sceneChanged.emit(scene.getRoot()) + def _onMaxLayersChanged(self): self.maxLayersChanged.emit() From 90ded1abf9667d7ef5901cdfe164d5fa8660f4c4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 2 Jan 2020 09:49:52 +0100 Subject: [PATCH 30/32] Remove useless override of bed temperature The Generic PETG material also sets it to 70, as well as the eMotionTech and TiXYZ PETGs. The iMade3D bed temperature was 65, so for that one the temperature will change to 65 then which seems appropriate. I'll change the iMade3D thing in fdm_materials so that the settings are still the same. Discovered during investigation of #6898. --- .../quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg | 1 - .../quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg | 1 - .../anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg | 1 - 3 files changed, 3 deletions(-) diff --git a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg index 9d6fcd0159..df82701b13 100644 --- a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg +++ b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg @@ -12,7 +12,6 @@ material = generic_petg [values] material_print_temperature = =default_material_print_temperature + 35 -material_bed_temperature = 70 cool_fan_enabled = False speed_print = 30 diff --git a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg index 67dbe2c33b..9ab962068b 100644 --- a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg +++ b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg @@ -12,7 +12,6 @@ material = generic_petg [values] material_print_temperature = =default_material_print_temperature + 35 -material_bed_temperature = 70 cool_fan_enabled = False speed_print = 30 diff --git a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg index 5f79e3b1c4..77e5f3dc0e 100644 --- a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg +++ b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg @@ -12,7 +12,6 @@ material = generic_petg [values] material_print_temperature = =default_material_print_temperature + 35 -material_bed_temperature = 70 cool_fan_enabled = False speed_print = 30 From 8b393317ff1ca5024b203f2fe85642a3bf5334f4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 2 Jan 2020 10:51:40 +0100 Subject: [PATCH 31/32] Add "all" as a setting visibility preset CURA-7082 --- .../Models/SettingVisibilityPresetsModel.py | 4 ++++ cura/Settings/MachineManager.py | 12 +++++++----- .../qml/Menus/SettingVisibilityPresetsMenu.qml | 13 ------------- resources/qml/Settings/SettingView.qml | 9 +-------- 4 files changed, 12 insertions(+), 26 deletions(-) diff --git a/cura/Machines/Models/SettingVisibilityPresetsModel.py b/cura/Machines/Models/SettingVisibilityPresetsModel.py index baa8e3ed29..6b5766c127 100644 --- a/cura/Machines/Models/SettingVisibilityPresetsModel.py +++ b/cura/Machines/Models/SettingVisibilityPresetsModel.py @@ -77,6 +77,10 @@ class SettingVisibilityPresetsModel(QObject): items.append(setting_visibility_preset) + # Add the "all" visibility: + all_setting_visibility_preset = SettingVisibilityPreset(preset_id = "all", name = "All", weight = 9001) + all_setting_visibility_preset.setSettings(list(CuraApplication.getInstance().getMachineManager().getAllSettingKeys())) + items.append(all_setting_visibility_preset) # Sort them on weight (and if that fails, use ID) items.sort(key = lambda k: (int(k.weight), k.presetId)) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 876f10ebf8..2a9b2e8f83 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -4,12 +4,11 @@ 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, Set from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer from UM.ConfigurationErrorMessage import ConfigurationErrorMessage -from UM.Decorators import deprecated from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.Interfaces import ContainerInterface @@ -212,10 +211,13 @@ class MachineManager(QObject): @pyqtProperty(int, constant=True) def totalNumberOfSettings(self) -> int: - general_definition_containers = CuraContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter") + return len(self.getAllSettingKeys()) + + def getAllSettingKeys(self) -> Set[str]: + general_definition_containers = CuraContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter") if not general_definition_containers: - return 0 - return len(general_definition_containers[0].getAllKeys()) + return set() + return general_definition_containers[0].getAllKeys() ## Triggered when the global container stack is changed in CuraApplication. def _onGlobalContainerChanged(self) -> None: diff --git a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml index 8116b6def1..97cee67300 100644 --- a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml +++ b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml @@ -14,8 +14,6 @@ Menu property QtObject settingVisibilityPresetsModel: CuraApplication.getSettingVisibilityPresetsModel() - signal showAllSettings() - Instantiator { model: settingVisibilityPresetsModel.items @@ -36,17 +34,6 @@ Menu onObjectRemoved: menu.removeItem(object) } - MenuSeparator {} - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Show All Settings") - checkable: false - exclusiveGroup: group - onTriggered: - { - showAllSettings(); - } - } MenuSeparator {} MenuItem { diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 419af782cf..8617c564f6 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -185,14 +185,7 @@ Item label: Label {} } - menu: SettingVisibilityPresetsMenu - { - onShowAllSettings: - { - definitionsModel.setAllVisible(true) - filter.updateDefinitionModel() - } - } + menu: SettingVisibilityPresetsMenu {} } // Mouse area that gathers the scroll events to not propagate it to the main view. From 9de2b39d38321a4a7646c37a872eb61c15498207 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 2 Jan 2020 11:13:48 +0100 Subject: [PATCH 32/32] Fix the failing tests CURA-6255 --- tests/Settings/TestSettingVisibilityPresets.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/Settings/TestSettingVisibilityPresets.py b/tests/Settings/TestSettingVisibilityPresets.py index b82aa62ea7..017bb6077a 100644 --- a/tests/Settings/TestSettingVisibilityPresets.py +++ b/tests/Settings/TestSettingVisibilityPresets.py @@ -1,4 +1,4 @@ -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch import os.path @@ -28,8 +28,8 @@ def test_createVisibilityPresetFromLocalFile(): def test_visibilityFromPrevious(): # This test checks that all settings in basic are in advanced and all settings in advanced are in expert. - - visibility_model = SettingVisibilityPresetsModel(Preferences()) + with patch("cura.CuraApplication.CuraApplication.getInstance"): + visibility_model = SettingVisibilityPresetsModel(Preferences()) basic_visibility = visibility_model.getVisibilityPresetById("basic") advanced_visibility = visibility_model.getVisibilityPresetById("advanced") @@ -46,7 +46,8 @@ def test_visibilityFromPrevious(): def test_setActivePreset(): preferences = Preferences() - visibility_model = SettingVisibilityPresetsModel(preferences) + with patch("cura.CuraApplication.CuraApplication.getInstance"): + visibility_model = SettingVisibilityPresetsModel(preferences) visibility_model.activePresetChanged = MagicMock() # Ensure that we start off with basic (since we didn't change anyting just yet!) assert visibility_model.activePreset == "basic" @@ -71,13 +72,13 @@ def test_preferenceChanged(): preferences = Preferences() # Set the visible_settings to something silly preferences.addPreference("general/visible_settings", "omgzomg") - visibility_model = SettingVisibilityPresetsModel(preferences) + with patch("cura.CuraApplication.CuraApplication.getInstance"): + visibility_model = SettingVisibilityPresetsModel(preferences) visibility_model.activePresetChanged = MagicMock() assert visibility_model.activePreset == "custom" # This should make the model start at "custom assert visibility_model.activePresetChanged.emit.call_count == 0 - basic_visibility = visibility_model.getVisibilityPresetById("basic") new_visibility_string = ";".join(basic_visibility.settings) preferences.setValue("general/visible_settings", new_visibility_string)