diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 3533ca1fa4..4dc1f297f7 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -153,7 +153,6 @@ class CuraApplication(QtApplication): if not hasattr(sys, "frozen"): resource_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources") Resources.addSearchPath(resource_path) - Resources.setBundledResourcesPath(resource_path) self._use_gui = True self._open_file_queue = [] # Files to open when plug-ins are loaded. diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index 2b91081e4d..ec75174db4 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -28,15 +28,18 @@ class CuraPackageManager(QObject): self._container_registry = self._application.getContainerRegistry() self._plugin_registry = self._application.getPluginRegistry() - # JSON file that keeps track of all installed packages. - self._bundled_package_management_file_path = os.path.join( - os.path.abspath(Resources.getBundledResourcesPath()), - "packages.json" - ) - self._user_package_management_file_path = os.path.join( - os.path.abspath(Resources.getDataStoragePath()), - "packages.json" - ) + #JSON files that keep track of all installed packages. + self._user_package_management_file_path = None + self._bundled_package_management_file_path = None + for search_path in Resources.getSearchPaths(): + candidate_bundled_path = os.path.join(search_path, "bundled_packages.json") + if os.path.exists(candidate_bundled_path): + self._bundled_package_management_file_path = candidate_bundled_path + candidate_user_path = os.path.join(search_path, "packages.json") + if os.path.exists(candidate_user_path): + self._user_package_management_file_path = candidate_user_path + if self._user_package_management_file_path is None: #Doesn't exist yet. + self._user_package_management_file_path = os.path.join(Resources.getDataStoragePath(), "packages.json") self._bundled_package_dict = {} # A dict of all bundled packages self._installed_package_dict = {} # A dict of all installed packages @@ -136,14 +139,14 @@ class CuraPackageManager(QObject): all_installed_ids = all_installed_ids.union(set(self._bundled_package_dict.keys())) if self._installed_package_dict.keys(): all_installed_ids = all_installed_ids.union(set(self._installed_package_dict.keys())) + all_installed_ids = all_installed_ids.difference(self._to_remove_package_set) + # If it's going to be installed and to be removed, then the package is being updated and it should be listed. if self._to_install_package_dict.keys(): all_installed_ids = all_installed_ids.union(set(self._to_install_package_dict.keys())) - all_installed_ids = all_installed_ids.difference(self._to_remove_package_set) # map of -> -> installed_packages_dict = {} for package_id in all_installed_ids: - # Skip required plugins as they should not be tampered with if package_id in Application.getInstance().getRequiredPlugins(): continue @@ -194,11 +197,6 @@ class CuraPackageManager(QObject): return package_id = package_info["package_id"] - # Check the delayed installation and removal lists first - if package_id in self._to_remove_package_set: - self._to_remove_package_set.remove(package_id) - has_changes = True - # Check if it is installed installed_package_info = self.getInstalledPackageInfo(package_info["package_id"]) to_install_package = installed_package_info is None # Install if the package has not been installed @@ -235,19 +233,27 @@ class CuraPackageManager(QObject): self.installedPackagesChanged.emit() # Schedules the given package to be removed upon the next start. + # \param package_id id of the package + # \param force_add is used when updating. In that case you actually want to uninstall & install @pyqtSlot(str) - def removePackage(self, package_id: str) -> None: + def removePackage(self, package_id: str, force_add: bool = False) -> None: # Check the delayed installation and removal lists first if not self.isPackageInstalled(package_id): Logger.log("i", "Attempt to remove package [%s] that is not installed, do nothing.", package_id) return - # Remove from the delayed installation list if present - if package_id in self._to_install_package_dict: - del self._to_install_package_dict[package_id] + # Temp hack + if package_id not in self._installed_package_dict and package_id in self._bundled_package_dict: + Logger.log("i", "Not uninstalling [%s] because it is a bundled package.") + return - # Schedule for a delayed removal: - self._to_remove_package_set.add(package_id) + if package_id not in self._to_install_package_dict or force_add: + # Schedule for a delayed removal: + self._to_remove_package_set.add(package_id) + else: + if package_id in self._to_install_package_dict: + # Remove from the delayed installation list if present + del self._to_install_package_dict[package_id] self._saveManagementData() self.installedPackagesChanged.emit() diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index a59f235741..01d0b43f5b 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -346,7 +346,7 @@ class PrintInformation(QObject): Logger.log("w", "Unsupported Mime Type Database file extension") if data is not None and check_name is not None: - self._base_name = check_name + self._base_name = data else: self._base_name = '' diff --git a/plugins/Toolbox/resources/images/loading.gif b/plugins/Toolbox/resources/images/loading.gif new file mode 100644 index 0000000000..43cc1ed6d7 Binary files /dev/null and b/plugins/Toolbox/resources/images/loading.gif differ diff --git a/plugins/Toolbox/resources/images/loading.svg b/plugins/Toolbox/resources/images/loading.svg new file mode 100644 index 0000000000..1ceb4a8d7f --- /dev/null +++ b/plugins/Toolbox/resources/images/loading.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml index 80d50616e8..739dc4ccfe 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml @@ -20,7 +20,7 @@ Item { left: parent.left right: controls.left - rightMargin: UM.Theme.getSize("default_margin").width + rightMargin: UM.Theme.getSize("default_margin").width * 2 + UM.Theme.getSize("toolbox_loader").width top: parent.top } Label @@ -53,60 +53,25 @@ Item anchors.top: tile.top width: childrenRect.width height: childrenRect.height - Button + + ToolboxProgressButton { id: installButton - text: - { - if (installed) - { - return catalog.i18nc("@action:button", "Installed") - } - else - { - if (toolbox.isDownloading && toolbox.activePackage == model) - { - return catalog.i18nc("@action:button", "Cancel") - } - else - { - return catalog.i18nc("@action:button", "Install") - } - } + active: toolbox.isDownloading && toolbox.activePackage == model + complete: tile.installed + readyAction: function() { + toolbox.activePackage = model + toolbox.startDownload(model.download_url) } - enabled: installed || !(toolbox.isDownloading && toolbox.activePackage != model) //Don't allow installing while another download is running. + activeAction: function() { + toolbox.cancelDownload() + } + completeAction: function() { + toolbox.viewCategory = "installed" + } + // Don't allow installing while another download is running + enabled: installed || !(toolbox.isDownloading && toolbox.activePackage != model) opacity: enabled ? 1.0 : 0.5 - - property alias installed: tile.installed - style: UM.Theme.styles.toolbox_action_button - onClicked: - { - if (installed) - { - toolbox.viewCategory = "installed" - } - else - { - // if ( toolbox.isDownloading && toolbox.activePackage == model ) - if ( toolbox.isDownloading ) - { - toolbox.cancelDownload(); - } - else - { - toolbox.activePackage = model - // toolbox.activePackage = model; - if ( model.can_upgrade ) - { - // toolbox.downloadAndInstallPlugin( model.update_url ); - } - else - { - toolbox.startDownload( model.download_url ); - } - } - } - } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml index b585a084b3..435319b5e9 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml @@ -112,4 +112,4 @@ Item onMetadataChanged: canUpdate = toolbox.canUpdate(model.id) } } -} \ No newline at end of file +} diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml index 1921bcb58e..5bbed2351c 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml @@ -11,56 +11,26 @@ Column width: UM.Theme.getSize("toolbox_action_button").width spacing: UM.Theme.getSize("narrow_margin").height - Item + ToolboxProgressButton { - width: parent.width - height: childrenRect.height + id: updateButton + active: toolbox.isDownloading && toolbox.activePackage == model + readyLabel: catalog.i18nc("@action:button", "Update") + activeLabel: catalog.i18nc("@action:button", "Updating") + completeLabel: catalog.i18nc("@action:button", "Updated") + readyAction: function() { + toolbox.activePackage = model + toolbox.update(model.id) + } + activeAction: function() { + toolbox.cancelDownload() + } + // Don't allow installing while another download is running + enabled: !(toolbox.isDownloading && toolbox.activePackage != model) + opacity: enabled ? 1.0 : 0.5 visible: canUpdate - Button - { - id: updateButton - text: catalog.i18nc("@action:button", "Update") - style: ButtonStyle - { - background: Rectangle - { - implicitWidth: UM.Theme.getSize("toolbox_action_button").width - implicitHeight: UM.Theme.getSize("toolbox_action_button").height - color: control.hovered ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary") - } - label: Label - { - text: control.text - color: control.hovered ? UM.Theme.getColor("button_text") : UM.Theme.getColor("button_text_hover") - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - font: UM.Theme.getFont("default_bold") - } - } - onClicked: toolbox.update(model.id) - } - ProgressBar - { - id: progressbar - width: parent.width - value: toolbox.isDownloading ? toolbox.downloadProgress : 0 - visible: toolbox.isDownloading - style: ProgressBarStyle - { - background: Rectangle - { - color: "transparent" - implicitHeight: UM.Theme.getSize("toolbox_action_button").height - } - progress: Rectangle - { - // TODO Define a good color that fits the purpuse - color: "blue" - opacity: 0.5 - } - } - } } + Button { id: removeButton @@ -90,4 +60,4 @@ Column } onClicked: toolbox.uninstall(model.id) } -} \ No newline at end of file +} diff --git a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml new file mode 100644 index 0000000000..d3aafae987 --- /dev/null +++ b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml @@ -0,0 +1,125 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Toolbox is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import UM 1.1 as UM + + +Item +{ + id: base + + property var active: false + property var complete: false + + property var readyLabel: "Install" + property var activeLabel: "Installing" + property var completeLabel: "Installed" + + property var readyAction: null // Action when button is ready and clicked (likely install) + property var activeAction: null // Action when button is active and clicked (likely cancel) + property var completeAction: null // Action when button is complete and clicked (likely go to installed) + + width: UM.Theme.getSize("toolbox_action_button").width + height: UM.Theme.getSize("toolbox_action_button").height + + Button + { + id: button + text: + { + if (complete) + { + return catalog.i18nc("@action:button", "Installed") + } + else if (active) + { + return catalog.i18nc("@action:button", "Cancel") + } + else + { + return catalog.i18nc("@action:button", "Install") + } + } + onClicked: + { + if (complete) + { + return completeAction() + } + else if (active) + { + return activeAction() + } + else + { + return readyAction() + } + } + style: ButtonStyle + { + background: Rectangle + { + implicitWidth: UM.Theme.getSize("toolbox_action_button").width + implicitHeight: UM.Theme.getSize("toolbox_action_button").height + color: + { + if (base.complete) + { + return UM.Theme.getColor("action_button_disabled") + } + else + { + if (control.hovered) + { + return UM.Theme.getColor("primary_hover") + } + else + { + return UM.Theme.getColor("primary") + } + } + } + } + label: Label + { + text: control.text + color: + { + if (base.complete) + { + return UM.Theme.getColor("action_button_disabled_text") + } + else + { + if (control.hovered) + { + return UM.Theme.getColor("button_text_hover") + } + else + { + return UM.Theme.getColor("button_text") + } + } + } + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font: UM.Theme.getFont("default_bold") + } + } + } + + AnimatedImage + { + id: loader + visible: active + source: "../images/loading.gif" + width: UM.Theme.getSize("toolbox_loader").width + height: UM.Theme.getSize("toolbox_loader").height + anchors.right: button.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: button.verticalCenter + } +} diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index c5b7ababc5..cb5272446d 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -70,6 +70,7 @@ class Toolbox(QObject, Extension): "plugins_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)), "materials_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)) } + self._to_update = [] # Package_ids that are waiting to be updated # Data: self._metadata = { @@ -234,13 +235,35 @@ class Toolbox(QObject, Extension): @pyqtSlot(str) def uninstall(self, plugin_id: str) -> None: - self._package_manager.removePackage(plugin_id) + self._package_manager.removePackage(plugin_id, force_add = True) self.installChanged.emit() self._updateInstalledModels() self.metadataChanged.emit() self._restart_required = True self.restartRequiredChanged.emit() + ## Actual update packages that are in self._to_update + def _update(self) -> None: + if self._to_update: + plugin_id = self._to_update.pop(0) + remote_package = self.getRemotePackage(plugin_id) + if remote_package: + download_url = remote_package["download_url"] + Logger.log("d", "Updating package [%s]..." % plugin_id) + self.uninstall(plugin_id) + self.startDownload(download_url) + else: + Logger.log("e", "Could not update package [%s] because there is no remote package info available.", plugin_id) + + if self._to_update: + self._application.callLater(self._update) + + ## Update a plugin by plugin_id + @pyqtSlot(str) + def update(self, plugin_id: str) -> None: + self._to_update.append(plugin_id) + self._application.callLater(self._update) + @pyqtSlot(str) def enable(self, plugin_id: str) -> None: self._plugin_registry.enablePlugin(plugin_id) @@ -269,6 +292,15 @@ class Toolbox(QObject, Extension): def restart(self): CuraApplication.getInstance().windowClosed() + def getRemotePackage(self, package_id: str) -> Optional[Dict]: + # TODO: make the lookup in a dict, not a loop. canUpdate is called for every item. + remote_package = None + for package in self._metadata["packages"]: + if package["package_id"] == package_id: + remote_package = package + break + return remote_package + # Checks # -------------------------------------------------------------------------- @pyqtSlot(str, result = bool) @@ -277,10 +309,7 @@ class Toolbox(QObject, Extension): if local_package is None: return False - remote_package = None - for package in self._metadata["packages"]: - if package["package_id"] == package_id: - remote_package = package + remote_package = self.getRemotePackage(package_id) if remote_package is None: return False @@ -339,8 +368,8 @@ class Toolbox(QObject, Extension): def resetDownload(self) -> None: if self._download_reply: - self._download_reply.abort() self._download_reply.downloadProgress.disconnect(self._onDownloadProgress) + self._download_reply.abort() self._download_reply = None self._download_request = None self.setDownloadProgress(0) diff --git a/resources/packages.json b/resources/bundled_packages.json similarity index 100% rename from resources/packages.json rename to resources/bundled_packages.json diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index f2309fb4a9..4d75ecc1f1 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -458,6 +458,7 @@ "toolbox_header": [1.0, 4.0], "toolbox_progress_bar": [8.0, 0.5], "toolbox_chart_row": [1.0, 2.0], - "toolbox_action_button": [8.0, 2.5] + "toolbox_action_button": [8.0, 2.5], + "toolbox_loader": [2.0, 2.0] } } diff --git a/resources/variants/ultimaker_s5_aluminum.inst.cfg b/resources/variants/ultimaker_s5_aluminum.inst.cfg new file mode 100644 index 0000000000..ea349d05f5 --- /dev/null +++ b/resources/variants/ultimaker_s5_aluminum.inst.cfg @@ -0,0 +1,13 @@ +[general] +name = Aluminum +version = 4 +definition = ultimaker_s5 + +[metadata] +setting_version = 4 +type = variant +hardware_type = buildplate + +[values] +material_bed_temperature = =default_material_bed_temperature + 10 +machine_buildplate_type = aluminum