From 0df21e6073cc9fc525e47fe7d300ed32b9407331 Mon Sep 17 00:00:00 2001 From: "j.delarago" Date: Wed, 1 Jun 2022 16:50:53 +0200 Subject: [PATCH] Refactor missing packages dialog into model (MissingPackageList) and controller (InstallMissingPackagesDialog.qml) Rename classes and files to have unified naming. CURA-6990 --- plugins/3MFReader/WorkspaceDialog.py | 7 + plugins/3MFReader/WorkspaceDialog.qml | 2 +- .../InstallMissingPackagesDialog.py | 60 +++++++ plugins/Marketplace/MissingPackageList.py | 32 ++++ .../qml/InstallMissingPackagesDialog.qml | 158 ++++++++++++++++++ .../resources/qml/MissingPackages.qml | 15 ++ 6 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 plugins/Marketplace/InstallMissingPackagesDialog.py create mode 100644 plugins/Marketplace/MissingPackageList.py create mode 100644 plugins/Marketplace/resources/qml/InstallMissingPackagesDialog.qml create mode 100644 plugins/Marketplace/resources/qml/MissingPackages.qml diff --git a/plugins/3MFReader/WorkspaceDialog.py b/plugins/3MFReader/WorkspaceDialog.py index 04c9494ee7..56bc9e2fcc 100644 --- a/plugins/3MFReader/WorkspaceDialog.py +++ b/plugins/3MFReader/WorkspaceDialog.py @@ -9,6 +9,7 @@ from UM.Application import Application from UM.i18n import i18nCatalog from UM.Settings.ContainerRegistry import ContainerRegistry from cura.Settings.GlobalStack import GlobalStack +from plugins.Marketplace.InstallMissingPackagesDialog import InstallMissingPackageDialog from .UpdatableMachinesModel import UpdatableMachinesModel import os @@ -60,6 +61,8 @@ class WorkspaceDialog(QObject): self._is_printer_group = False self._updatable_machines_model = UpdatableMachinesModel(self) self._missing_package_metadata: List[Dict[str, str]] = [] + self._plugin_registry: PluginRegistry = CuraApplication.getInstance().getPluginRegistry() + self._install_missing_package_dialog: Optional[QObject] = None machineConflictChanged = pyqtSignal() qualityChangesConflictChanged = pyqtSignal() @@ -284,6 +287,10 @@ class WorkspaceDialog(QObject): def missingPackages(self): return self._missing_package_metadata + @pyqtSlot() + def installMissingPackages(self): + self._install_missing_package_dialog = InstallMissingPackageDialog(self._missing_package_metadata) + self._install_missing_package_dialog.show() def getResult(self) -> Dict[str, Optional[str]]: if "machine" in self._result and self.updatableMachinesModel.count <= 1: diff --git a/plugins/3MFReader/WorkspaceDialog.qml b/plugins/3MFReader/WorkspaceDialog.qml index 19f682b631..f6048f8324 100644 --- a/plugins/3MFReader/WorkspaceDialog.qml +++ b/plugins/3MFReader/WorkspaceDialog.qml @@ -469,7 +469,7 @@ UM.Dialog { visible: buttonWarning text: catalog.i18nc("@action:button", "Install missing Material") - onClicked: accept() + onClicked: manager.installMissingPackages() } ] diff --git a/plugins/Marketplace/InstallMissingPackagesDialog.py b/plugins/Marketplace/InstallMissingPackagesDialog.py new file mode 100644 index 0000000000..eb6c03f81c --- /dev/null +++ b/plugins/Marketplace/InstallMissingPackagesDialog.py @@ -0,0 +1,60 @@ +import os + +from PyQt6.QtCore import QObject, pyqtSignal, pyqtProperty +from typing import Optional, List, Dict, cast +from cura.CuraApplication import CuraApplication +from UM.PluginRegistry import PluginRegistry +from cura.CuraPackageManager import CuraPackageManager + +from plugins.Marketplace.MissingPackageList import MissingPackageList + + +class InstallMissingPackageDialog(QObject): + """Dialog used to display packages that need to be installed to load 3mf file materials""" + def __init__(self, packages_metadata: List[Dict[str, str]]): + """Initialize + + :param packages_metadata: List of dictionaries containing information about missing packages. + """ + super().__init__() + + self._plugin_registry: PluginRegistry = CuraApplication.getInstance().getPluginRegistry() + self._package_manager: CuraPackageManager = cast(CuraPackageManager, CuraApplication.getInstance().getPackageManager()) + self._package_manager.installedPackagesChanged.connect(self.checkIfRestartNeeded) + + self._dialog: Optional[QObject] = None + self._restart_needed = False + self._package_metadata: List[Dict[str, str]] = packages_metadata + + self._package_model = MissingPackageList() + self._package_model.setPackageIds(packages_metadata) + + def show(self): + plugin_path = self._plugin_registry.getPluginPath("Marketplace") + if plugin_path is None: + plugin_path = os.path.dirname(__file__) + + # create a QML component for the license dialog + license_dialog_component_path = os.path.join(plugin_path, "resources", "qml", "InstallMissingPackagesDialog.qml") + self._dialog = CuraApplication.getInstance().createQmlComponent(license_dialog_component_path, {"manager": self}) + self._dialog.show() + + def checkIfRestartNeeded(self) -> None: + if self._dialog is None: + return + + if self._package_manager.hasPackagesToRemoveOrInstall: + self._restart_needed = True + else: + self._restart_needed = False + self.showRestartChanged.emit() + + showRestartChanged = pyqtSignal() + + @pyqtProperty(bool, notify=showRestartChanged) + def showRestartNotification(self) -> bool: + return self._restart_needed + + @pyqtProperty(QObject) + def model(self): + return self._package_model diff --git a/plugins/Marketplace/MissingPackageList.py b/plugins/Marketplace/MissingPackageList.py new file mode 100644 index 0000000000..b036e8b966 --- /dev/null +++ b/plugins/Marketplace/MissingPackageList.py @@ -0,0 +1,32 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from typing import Optional, TYPE_CHECKING, Dict, List + +from .Constants import PACKAGES_URL +from .PackageModel import PackageModel +from .RemotePackageList import RemotePackageList +from PyQt6.QtCore import pyqtSignal, QObject, pyqtProperty, QCoreApplication + +from UM.TaskManagement.HttpRequestManager import HttpRequestManager # To request the package list from the API. +from UM.i18n import i18nCatalog + +if TYPE_CHECKING: + from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal + +catalog = i18nCatalog("cura") + +class MissingPackageList(RemotePackageList): + def __init__(self, parent: Optional["QObject"] = None) -> None: + super().__init__(parent) + self._package_metadata: List[Dict[str, str]] = [] + # self.packageTypeFilter = None # This will be our new filter + self._package_type_filter = "material" + + def setPackageIds(self, packages: List[Dict[str, str]]) -> None: + self._package_metadata = packages + search_string = ", ".join(map(lambda package: package["id"], packages)) + # self.setSearchString(search_string) + self.setSearchString("ABS") + + diff --git a/plugins/Marketplace/resources/qml/InstallMissingPackagesDialog.qml b/plugins/Marketplace/resources/qml/InstallMissingPackagesDialog.qml new file mode 100644 index 0000000000..a5fbe2c965 --- /dev/null +++ b/plugins/Marketplace/resources/qml/InstallMissingPackagesDialog.qml @@ -0,0 +1,158 @@ +// Copyright (c) 2021 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Window 2.2 + +import UM 1.5 as UM +import Cura 1.6 as Cura + +Window +{ + id: marketplaceDialog + + property variant catalog: UM.I18nCatalog { name: "cura" } + + signal searchStringChanged(string new_search) + + minimumWidth: UM.Theme.getSize("modal_window_minimum").width + minimumHeight: UM.Theme.getSize("modal_window_minimum").height + width: minimumWidth + height: minimumHeight + + onVisibleChanged: + { + while(contextStack.depth > 1) + { + contextStack.pop(); //Do NOT use the StackView.Immediate transition here, since it causes the window to stay empty. Seemingly a Qt bug: https://bugreports.qt.io/browse/QTBUG-60670? + } + } + + Connections + { + target: Cura.API.account + function onLoginStateChanged() + { + close(); + } + } + + title: catalog.i18nc("@title", "Install missing Materials") + modality: Qt.ApplicationModal + + // Background color + Rectangle + { + anchors.fill: parent + color: UM.Theme.getColor("main_background") + } + //The Marketplace can have a page in front of everything with package details. The stack view controls its visibility. + StackView + { + id: contextStack + anchors.fill: parent + + initialItem: packageBrowse + + ColumnLayout + { + id: packageBrowse + + spacing: UM.Theme.getSize("narrow_margin").height + + // Page title. + Item + { + Layout.preferredWidth: parent.width + Layout.preferredHeight: childrenRect.height + UM.Theme.getSize("default_margin").height + + UM.Label + { + id: pageTitle + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + bottom: parent.bottom + } + + font: UM.Theme.getFont("large") + text: content.item ? content.item.pageTitle: catalog.i18nc("@title", "Loading...") + } + } + + // Page contents. + Rectangle + { + Layout.preferredWidth: parent.width + Layout.fillHeight: true + color: UM.Theme.getColor("detail_background") + + // Page contents. + Loader + { + id: content + anchors.fill: parent + anchors.margins: UM.Theme.getSize("default_margin").width + source: "MissingPackages.qml" + } + } + } + } + + Rectangle + { + height: quitButton.height + 2 * UM.Theme.getSize("default_margin").width + color: UM.Theme.getColor("primary") + visible: manager.showRestartNotification + anchors + { + left: parent.left + right: parent.right + bottom: parent.bottom + } + + RowLayout + { + anchors + { + left: parent.left + right: parent.right + verticalCenter: parent.verticalCenter + margins: UM.Theme.getSize("default_margin").width + } + spacing: UM.Theme.getSize("default_margin").width + UM.ColorImage + { + id: bannerIcon + source: UM.Theme.getIcon("Plugin") + + color: UM.Theme.getColor("primary_button_text") + implicitWidth: UM.Theme.getSize("banner_icon_size").width + implicitHeight: UM.Theme.getSize("banner_icon_size").height + } + Text + { + color: UM.Theme.getColor("primary_button_text") + text: catalog.i18nc("@button", "In order to use the package you will need to restart Cura") + font: UM.Theme.getFont("default") + renderType: Text.NativeRendering + Layout.fillWidth: true + } + Cura.SecondaryButton + { + id: quitButton + text: catalog.i18nc("@info:button, %1 is the application name", "Quit %1").arg(CuraApplication.applicationDisplayName) + onClicked: + { + marketplaceDialog.hide(); + CuraApplication.closeApplication(); + } + } + } + } +} diff --git a/plugins/Marketplace/resources/qml/MissingPackages.qml b/plugins/Marketplace/resources/qml/MissingPackages.qml new file mode 100644 index 0000000000..316d048317 --- /dev/null +++ b/plugins/Marketplace/resources/qml/MissingPackages.qml @@ -0,0 +1,15 @@ +// Copyright (c) 2021 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import UM 1.4 as UM + +Packages +{ + pageTitle: catalog.i18nc("@header", "Install Materials") + + bannerVisible: false + showUpdateButton: false + showInstallButton: true + + model: manager.model +}