diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py
index aa36f77878..e7e9675082 100755
--- a/cura/CuraApplication.py
+++ b/cura/CuraApplication.py
@@ -494,7 +494,7 @@ class CuraApplication(QtApplication):
"CuraEngineBackend", #Cura is useless without this one since you can't slice.
"FileLogger", #You want to be able to read the log if something goes wrong.
"XmlMaterialProfile", #Cura crashes without this one.
- "Toolbox", #This contains the interface to enable/disable plug-ins, so if you disable it you can't enable it back.
+ "Marketplace", #This contains the interface to enable/disable plug-ins, so if you disable it you can't enable it back.
"PrepareStage", #Cura is useless without this one since you can't load models.
"PreviewStage", #This shows the list of the plugin views that are installed in Cura.
"MonitorStage", #Major part of Cura's functionality.
diff --git a/plugins/DigitalLibrary/resources/qml/ProjectSummaryCard.qml b/plugins/DigitalLibrary/resources/qml/ProjectSummaryCard.qml
index 4374b2f998..ba2abf22a9 100644
--- a/plugins/DigitalLibrary/resources/qml/ProjectSummaryCard.qml
+++ b/plugins/DigitalLibrary/resources/qml/ProjectSummaryCard.qml
@@ -44,7 +44,7 @@ Cura.RoundedRectangle
{
id: projectImage
anchors.verticalCenter: parent.verticalCenter
- width: UM.Theme.getSize("toolbox_thumbnail_small").width
+ width: UM.Theme.getSize("card_icon").width
height: Math.round(width * 3/4)
sourceSize.width: width
sourceSize.height: height
diff --git a/plugins/DigitalLibrary/resources/qml/SelectProjectPage.qml b/plugins/DigitalLibrary/resources/qml/SelectProjectPage.qml
index 9ebf264e0f..8732c03c4a 100644
--- a/plugins/DigitalLibrary/resources/qml/SelectProjectPage.qml
+++ b/plugins/DigitalLibrary/resources/qml/SelectProjectPage.qml
@@ -201,7 +201,7 @@ Item
LoadMoreProjectsCard
{
id: loadMoreProjectsCard
- height: UM.Theme.getSize("toolbox_thumbnail_small").height
+ height: UM.Theme.getSize("card_icon").height
width: parent.width
visible: manager.digitalFactoryProjectModel.count > 0
hasMoreProjectsToLoad: manager.hasMoreProjectsToLoad
diff --git a/plugins/Marketplace/LocalPackageList.py b/plugins/Marketplace/LocalPackageList.py
index ab28e634c2..a609e72d33 100644
--- a/plugins/Marketplace/LocalPackageList.py
+++ b/plugins/Marketplace/LocalPackageList.py
@@ -1,17 +1,11 @@
-# Copyright (c) 2021 Ultimaker B.V.
+# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Any, Dict, List, Optional, TYPE_CHECKING
-from operator import attrgetter
from PyQt5.QtCore import pyqtSlot, QObject
from UM.Version import Version
-
-if TYPE_CHECKING:
- from PyQt5.QtCore import QObject
- from PyQt5.QtNetwork import QNetworkReply
-
from UM.i18n import i18nCatalog
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from UM.Logger import Logger
@@ -20,6 +14,10 @@ from .PackageList import PackageList
from .PackageModel import PackageModel
from .Constants import PACKAGE_UPDATES_URL
+if TYPE_CHECKING:
+ from PyQt5.QtCore import QObject
+ from PyQt5.QtNetwork import QNetworkReply
+
catalog = i18nCatalog("cura")
@@ -54,7 +52,8 @@ class LocalPackageList(PackageList):
be updated, it is in the to remove list and isn't in the to be installed list
"""
package = self.getPackageModel(package_id)
- if not package.canUpdate and \
+
+ if package and not package.canUpdate and \
package_id in self._package_manager.getToRemovePackageIDs() and \
package_id not in self._package_manager.getPackagesToInstall():
index = self.find("package", package_id)
diff --git a/plugins/Marketplace/Marketplace.py b/plugins/Marketplace/Marketplace.py
index 143469d82e..8ce062394e 100644
--- a/plugins/Marketplace/Marketplace.py
+++ b/plugins/Marketplace/Marketplace.py
@@ -1,10 +1,9 @@
-# Copyright (c) 2021 Ultimaker B.V.
+# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os.path
-from PyQt5.QtCore import pyqtSlot
-from PyQt5.QtQml import qmlRegisterType
-from typing import Optional, TYPE_CHECKING
+from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
+from typing import Optional, cast
from cura.CuraApplication import CuraApplication # Creating QML objects and managing packages.
@@ -13,25 +12,62 @@ from UM.PluginRegistry import PluginRegistry # To find out where we are stored
from .RemotePackageList import RemotePackageList # To register this type with QML.
from .LocalPackageList import LocalPackageList # To register this type with QML.
-from .RestartManager import RestartManager # To register this type with QML.
-
-if TYPE_CHECKING:
- from PyQt5.QtCore import QObject
-class Marketplace(Extension):
+class Marketplace(Extension, QObject):
"""
The main managing object for the Marketplace plug-in.
"""
-
- def __init__(self) -> None:
- super().__init__()
+ def __init__(self, parent: Optional[QObject] = None) -> None:
+ QObject.__init__(self, parent)
+ Extension.__init__(self)
self._window: Optional["QObject"] = None # If the window has been loaded yet, it'll be cached in here.
self._plugin_registry: Optional[PluginRegistry] = None
+ self._package_manager = CuraApplication.getInstance().getPackageManager()
- qmlRegisterType(RemotePackageList, "Marketplace", 1, 0, "RemotePackageList")
- qmlRegisterType(LocalPackageList, "Marketplace", 1, 0, "LocalPackageList")
- qmlRegisterType(RestartManager, "Marketplace", 1, 0, "RestartManager")
+ self._material_package_list: Optional[RemotePackageList] = None
+ self._plugin_package_list: Optional[RemotePackageList] = None
+
+ # Not entirely the cleanest code, since the localPackage list also checks the server if there are updates
+ # Since that in turn will trigger notifications to be shown, we do need to construct it here and make sure
+ # that it checks for updates...
+ self._local_package_list = LocalPackageList(self)
+ self._local_package_list.checkForUpdates(self._package_manager.local_packages)
+
+ self._package_manager.installedPackagesChanged.connect(self.checkIfRestartNeeded)
+
+ self._tab_shown: int = 0
+ self._restart_needed = False
+
+ def getTabShown(self) -> int:
+ return self._tab_shown
+
+ def setTabShown(self, tab_shown: int) -> None:
+ if tab_shown != self._tab_shown:
+ self._tab_shown = tab_shown
+ self.tabShownChanged.emit()
+
+ tabShownChanged = pyqtSignal()
+ tabShown = pyqtProperty(int, fget=getTabShown, fset=setTabShown, notify=tabShownChanged)
+
+ @pyqtProperty(QObject, constant=True)
+ def MaterialPackageList(self):
+ if self._material_package_list is None:
+ self._material_package_list = RemotePackageList()
+ self._material_package_list.packageTypeFilter = "material"
+
+ return self._material_package_list
+
+ @pyqtProperty(QObject, constant=True)
+ def PluginPackageList(self):
+ if self._plugin_package_list is None:
+ self._plugin_package_list = RemotePackageList()
+ self._plugin_package_list.packageTypeFilter = "plugin"
+ return self._plugin_package_list
+
+ @pyqtProperty(QObject, constant=True)
+ def LocalPackageList(self):
+ return self._local_package_list
@pyqtSlot()
def show(self) -> None:
@@ -42,12 +78,36 @@ class Marketplace(Extension):
"""
if self._window is None:
self._plugin_registry = PluginRegistry.getInstance()
+ self._plugin_registry.pluginsEnabledOrDisabledChanged.connect(self.checkIfRestartNeeded)
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
if plugin_path is None:
plugin_path = os.path.dirname(__file__)
path = os.path.join(plugin_path, "resources", "qml", "Marketplace.qml")
- self._window = CuraApplication.getInstance().createQmlComponent(path, {})
+ self._window = CuraApplication.getInstance().createQmlComponent(path, {"manager": self})
if self._window is None: # Still None? Failed to load the QML then.
return
+ self.setTabShown(0)
self._window.show()
self._window.requestActivate() # Bring window into focus, if it was already open in the background.
+
+ @pyqtSlot()
+ def setVisibleTabToMaterials(self) -> None:
+ """
+ Set the tab shown to the remote materials one.
+ Not implemented in a more generic way because it needs the ability to be called with 'callExtensionMethod'.
+ """
+ self.setTabShown(1)
+
+ def checkIfRestartNeeded(self) -> None:
+ if self._package_manager.hasPackagesToRemoveOrInstall or \
+ cast(PluginRegistry, self._plugin_registry).getCurrentSessionActivationChangedPlugins():
+ self._restart_needed = True
+ else:
+ self._restart_needed = False
+ self.showRestartNotificationChanged.emit()
+
+ showRestartNotificationChanged = pyqtSignal()
+
+ @pyqtProperty(bool, notify=showRestartNotificationChanged)
+ def showRestartNotification(self) -> bool:
+ return self._restart_needed
diff --git a/plugins/Marketplace/PackageList.py b/plugins/Marketplace/PackageList.py
index ddc39e0c94..04b602002c 100644
--- a/plugins/Marketplace/PackageList.py
+++ b/plugins/Marketplace/PackageList.py
@@ -53,7 +53,6 @@ class PackageList(ListModel):
def __del__(self) -> None:
""" When this object is deleted it will loop through all registered API requests and aborts them """
-
try:
self.isLoadingChanged.disconnect()
self.hasMoreChanged.disconnect()
@@ -132,9 +131,12 @@ class PackageList(ListModel):
:return: ``True`` if a Footer should be displayed in the ListView, e.q.: paginated lists, ``False`` Otherwise"""
return self._has_footer
- def getPackageModel(self, package_id: str) -> PackageModel:
+ def getPackageModel(self, package_id: str) -> Optional[PackageModel]:
index = self.find("package", package_id)
- return self.getItem(index)["package"]
+ data = self.getItem(index)
+ if data:
+ return data.get("package")
+ return None
def _openLicenseDialog(self, package_id: str, license_content: str) -> None:
plugin_path = self._plugin_registry.getPluginPath("Marketplace")
@@ -189,7 +191,10 @@ class PackageList(ListModel):
Logger.warning(f"Could not install {package_id}")
return
package = self.getPackageModel(package_id)
- self.subscribeUserToPackage(package_id, str(package.sdk_version))
+ if package:
+ self.subscribeUserToPackage(package_id, str(package.sdk_version))
+ else:
+ Logger.log("w", f"Unable to get data on package {package_id}")
def download(self, package_id: str, url: str, update: bool = False) -> None:
"""Initiate the download request
@@ -280,7 +285,8 @@ class PackageList(ListModel):
self.download(package_id, url, False)
else:
package = self.getPackageModel(package_id)
- self.subscribeUserToPackage(package_id, str(package.sdk_version))
+ if package:
+ self.subscribeUserToPackage(package_id, str(package.sdk_version))
def uninstallPackage(self, package_id: str) -> None:
"""Uninstall a package from the Marketplace
diff --git a/plugins/Marketplace/RestartManager.py b/plugins/Marketplace/RestartManager.py
deleted file mode 100644
index 9fe52b4116..0000000000
--- a/plugins/Marketplace/RestartManager.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (c) 2021 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-from typing import Optional, TYPE_CHECKING
-
-from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject
-
-from cura.CuraApplication import CuraApplication
-
-if TYPE_CHECKING:
- from UM.PluginRegistry import PluginRegistry
- from cura.CuraPackageManager import CuraPackageManager
-
-
-class RestartManager(QObject):
- def __init__(self, parent: Optional[QObject] = None) -> None:
- super().__init__(parent = parent)
- self._manager: "CuraPackageManager" = CuraApplication.getInstance().getPackageManager()
- self._plugin_registry: "PluginRegistry" = CuraApplication.getInstance().getPluginRegistry()
-
- self._manager.installedPackagesChanged.connect(self.checkIfRestartNeeded)
- self._plugin_registry.pluginsEnabledOrDisabledChanged.connect(self.checkIfRestartNeeded)
-
- self._restart_needed = False
-
- def checkIfRestartNeeded(self) -> None:
- if self._manager.hasPackagesToRemoveOrInstall or len(self._plugin_registry.getCurrentSessionActivationChangedPlugins()) > 0:
- self._restart_needed = True
- else:
- self._restart_needed = False
- self.showRestartNotificationChanged.emit()
-
- showRestartNotificationChanged = pyqtSignal()
-
- @pyqtProperty(bool, notify = showRestartNotificationChanged)
- def showRestartNotification(self) -> bool:
- return self._restart_needed
diff --git a/plugins/Marketplace/resources/qml/ManagedPackages.qml b/plugins/Marketplace/resources/qml/ManagedPackages.qml
index dbdc04bf52..8ccaacea46 100644
--- a/plugins/Marketplace/resources/qml/ManagedPackages.qml
+++ b/plugins/Marketplace/resources/qml/ManagedPackages.qml
@@ -4,7 +4,6 @@ import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
-import Marketplace 1.0 as Marketplace
import UM 1.4 as UM
Packages
@@ -22,7 +21,5 @@ Packages
searchInBrowserUrl: "https://marketplace.ultimaker.com/app/cura/plugins?utm_source=cura&utm_medium=software&utm_campaign=marketplace-search-plugins-browser"
packagesManageableInListView: true
- model: Marketplace.LocalPackageList
- {
- }
+ model: manager.LocalPackageList
}
diff --git a/plugins/Marketplace/resources/qml/Marketplace.qml b/plugins/Marketplace/resources/qml/Marketplace.qml
index c04b24cc13..fc6d3cd755 100644
--- a/plugins/Marketplace/resources/qml/Marketplace.qml
+++ b/plugins/Marketplace/resources/qml/Marketplace.qml
@@ -8,13 +8,11 @@ import QtQuick.Window 2.2
import UM 1.2 as UM
import Cura 1.6 as Cura
-import Marketplace 1.0 as Marketplace
Window
{
id: marketplaceDialog
property variant catalog: UM.I18nCatalog { name: "cura" }
- property variant restartManager: Marketplace.RestartManager { }
signal searchStringChanged(string new_search)
@@ -25,7 +23,6 @@ Window
onVisibleChanged:
{
- pageSelectionTabBar.currentIndex = 0; //Go back to the initial tab.
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?
@@ -131,9 +128,11 @@ Window
height: UM.Theme.getSize("button_icon").height
spacing: 0
background: Rectangle { color: "transparent" }
+ currentIndex: manager.tabShown
onCurrentIndexChanged:
{
+ manager.tabShown = currentIndex
searchBar.text = "";
searchBar.visible = currentItem.hasSearch;
content.source = currentItem.sourcePage;
@@ -250,7 +249,7 @@ Window
{
height: quitButton.height + 2 * UM.Theme.getSize("default_margin").width
color: UM.Theme.getColor("primary")
- visible: restartManager.showRestartNotification
+ visible: manager.showRestartNotification
anchors
{
left: parent.left
diff --git a/plugins/Marketplace/resources/qml/Materials.qml b/plugins/Marketplace/resources/qml/Materials.qml
index d19f3a4b04..39fae7042a 100644
--- a/plugins/Marketplace/resources/qml/Materials.qml
+++ b/plugins/Marketplace/resources/qml/Materials.qml
@@ -1,7 +1,6 @@
// Copyright (c) 2021 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
-import Marketplace 1.0 as Marketplace
import UM 1.4 as UM
Packages
@@ -19,8 +18,5 @@ Packages
searchInBrowserUrl: "https://marketplace.ultimaker.com/app/cura/materials?utm_source=cura&utm_medium=software&utm_campaign=marketplace-search-materials-browser"
packagesManageableInListView: false
- model: Marketplace.RemotePackageList
- {
- packageTypeFilter: "material"
- }
+ model: manager.MaterialPackageList
}
diff --git a/plugins/Marketplace/resources/qml/PackageCardHeader.qml b/plugins/Marketplace/resources/qml/PackageCardHeader.qml
index 0bf93fc67c..3a76f7a959 100644
--- a/plugins/Marketplace/resources/qml/PackageCardHeader.qml
+++ b/plugins/Marketplace/resources/qml/PackageCardHeader.qml
@@ -135,21 +135,23 @@ Item
}
// clickable author name
- Cura.TertiaryButton
+ Item
{
Layout.fillWidth: true
- Layout.preferredHeight: authorBy.height
- Layout.alignment: Qt.AlignCenter
+ implicitHeight: authorBy.height
+ Layout.alignment: Qt.AlignTop
+ Cura.TertiaryButton
+ {
+ text: packageData.authorName
+ textFont: UM.Theme.getFont("default_bold")
+ textColor: UM.Theme.getColor("text") // override normal link color
+ leftPadding: 0
+ rightPadding: 0
+ iconSource: UM.Theme.getIcon("LinkExternal")
+ isIconOnRightSide: true
- text: packageData.authorName
- textFont: UM.Theme.getFont("default_bold")
- textColor: UM.Theme.getColor("text") // override normal link color
- leftPadding: 0
- rightPadding: 0
- iconSource: UM.Theme.getIcon("LinkExternal")
- isIconOnRightSide: true
-
- onClicked: Qt.openUrlExternally(packageData.authorInfoUrl)
+ onClicked: Qt.openUrlExternally(packageData.authorInfoUrl)
+ }
}
ManageButton
diff --git a/plugins/Marketplace/resources/qml/Plugins.qml b/plugins/Marketplace/resources/qml/Plugins.qml
index 3cfa92d134..9983a827d8 100644
--- a/plugins/Marketplace/resources/qml/Plugins.qml
+++ b/plugins/Marketplace/resources/qml/Plugins.qml
@@ -1,7 +1,6 @@
// Copyright (c) 2021 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
-import Marketplace 1.0 as Marketplace
import UM 1.4 as UM
Packages
@@ -19,8 +18,5 @@ Packages
searchInBrowserUrl: "https://marketplace.ultimaker.com/app/cura/plugins?utm_source=cura&utm_medium=software&utm_campaign=marketplace-search-plugins-browser"
packagesManageableInListView: false
- model: Marketplace.RemotePackageList
- {
- packageTypeFilter: "plugin"
- }
+ model: manager.PluginPackageList
}
diff --git a/plugins/Toolbox/__init__.py b/plugins/Toolbox/__init__.py
deleted file mode 100644
index 51f1b643d0..0000000000
--- a/plugins/Toolbox/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2018 Ultimaker B.V.
-# Toolbox is released under the terms of the LGPLv3 or higher.
-
-from .src import Toolbox
-from .src.CloudSync.SyncOrchestrator import SyncOrchestrator
-
-
-def getMetaData():
- return {}
-
-
-def register(app):
- return {
- "extension": [Toolbox.Toolbox(app), SyncOrchestrator(app)]
- }
diff --git a/plugins/Toolbox/plugin.json b/plugins/Toolbox/plugin.json
deleted file mode 100644
index ed4a3eae97..0000000000
--- a/plugins/Toolbox/plugin.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "Toolbox",
- "author": "Ultimaker B.V.",
- "version": "1.0.1",
- "api": 7,
- "description": "Find, manage and install new Cura packages."
-}
diff --git a/plugins/Toolbox/resources/images/Shop.svg b/plugins/Toolbox/resources/images/Shop.svg
deleted file mode 100755
index 5056a25c51..0000000000
--- a/plugins/Toolbox/resources/images/Shop.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
diff --git a/plugins/Toolbox/resources/images/installed_check.svg b/plugins/Toolbox/resources/images/installed_check.svg
deleted file mode 100644
index 1f1302770b..0000000000
--- a/plugins/Toolbox/resources/images/installed_check.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
diff --git a/plugins/Toolbox/resources/images/placeholder.svg b/plugins/Toolbox/resources/images/placeholder.svg
deleted file mode 100644
index cc674a4b38..0000000000
--- a/plugins/Toolbox/resources/images/placeholder.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/plugins/Toolbox/resources/qml/Toolbox.qml b/plugins/Toolbox/resources/qml/Toolbox.qml
deleted file mode 100644
index b67d175194..0000000000
--- a/plugins/Toolbox/resources/qml/Toolbox.qml
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-// Main window for the Toolbox
-
-import QtQuick 2.2
-import QtQuick.Dialogs 1.1
-import QtQuick.Window 2.2
-import UM 1.1 as UM
-
-import "./pages"
-import "./dialogs"
-import "./components"
-
-Window
-{
- id: base
- property var selection: null
- title: catalog.i18nc("@title", "Marketplace")
- modality: Qt.ApplicationModal
- flags: Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
-
- width: UM.Theme.getSize("large_popup_dialog").width
- height: UM.Theme.getSize("large_popup_dialog").height
- minimumWidth: width
- maximumWidth: minimumWidth
- minimumHeight: height
- maximumHeight: minimumHeight
- color: UM.Theme.getColor("main_background")
- UM.I18nCatalog
- {
- id: catalog
- name: "cura"
- }
- Item
- {
- anchors.fill: parent
-
- WelcomePage
- {
- visible: toolbox.viewPage === "welcome"
- }
-
- ToolboxHeader
- {
- id: header
- visible: toolbox.viewPage !== "welcome"
- }
-
- Item
- {
- id: mainView
- width: parent.width
- z: parent.z - 1
- anchors
- {
- top: header.bottom
- bottom: footer.top
- }
- // TODO: This could be improved using viewFilter instead of viewCategory
- ToolboxLoadingPage
- {
- id: viewLoading
- visible: toolbox.viewCategory !== "installed" && toolbox.viewPage === "loading"
- }
- ToolboxErrorPage
- {
- id: viewErrored
- visible: toolbox.viewCategory !== "installed" && toolbox.viewPage === "errored"
- }
- ToolboxDownloadsPage
- {
- id: viewDownloads
- visible: toolbox.viewCategory !== "installed" && toolbox.viewPage === "overview"
- }
- ToolboxDetailPage
- {
- id: viewDetail
- visible: toolbox.viewCategory !== "installed" && toolbox.viewPage === "detail"
- }
- ToolboxAuthorPage
- {
- id: viewAuthor
- visible: toolbox.viewCategory !== "installed" && toolbox.viewPage === "author"
- }
- ToolboxInstalledPage
- {
- id: installedPluginList
- visible: toolbox.viewCategory === "installed"
- }
- }
-
- ToolboxFooter
- {
- id: footer
- visible: toolbox.restartRequired
- height: visible ? UM.Theme.getSize("toolbox_footer").height : 0
- }
-
- Connections
- {
- target: toolbox
- function onShowLicenseDialog() { licenseDialog.show() }
- function onCloseLicenseDialog() { licenseDialog.close() }
- }
-
- ToolboxLicenseDialog
- {
- id: licenseDialog
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxActionButtonStyle.qml b/plugins/Toolbox/resources/qml/components/ToolboxActionButtonStyle.qml
deleted file mode 100644
index eff74278c9..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxActionButtonStyle.qml
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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
-
-ButtonStyle
-{
- background: Rectangle
- {
- implicitWidth: UM.Theme.getSize("toolbox_action_button").width
- implicitHeight: UM.Theme.getSize("toolbox_action_button").height
- color: "transparent"
- border
- {
- width: UM.Theme.getSize("default_lining").width
- color: UM.Theme.getColor("lining")
- }
- }
- label: Label
- {
- text: control.text
- color: UM.Theme.getColor("text")
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignHCenter
- }
-}
\ No newline at end of file
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxBackColumn.qml b/plugins/Toolbox/resources/qml/components/ToolboxBackColumn.qml
deleted file mode 100644
index 9874a977f5..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxBackColumn.qml
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4
-import UM 1.1 as UM
-
-Item
-{
- id: sidebar
- height: parent.height
- width: UM.Theme.getSize("toolbox_back_column").width
- anchors
- {
- top: parent.top
- left: parent.left
- topMargin: UM.Theme.getSize("wide_margin").height
- leftMargin: UM.Theme.getSize("default_margin").width
- rightMargin: UM.Theme.getSize("default_margin").width
- }
- Button
- {
- id: button
- text: catalog.i18nc("@action:button", "Back")
- enabled: !toolbox.isDownloading
- UM.RecolorImage
- {
- id: backArrow
- anchors
- {
- verticalCenter: parent.verticalCenter
- left: parent.left
- rightMargin: UM.Theme.getSize("default_margin").width
- }
- width: UM.Theme.getSize("standard_arrow").width
- height: UM.Theme.getSize("standard_arrow").height
- sourceSize
- {
- width: width
- height: height
- }
- color: button.enabled ? (button.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text")) : UM.Theme.getColor("text_inactive")
- source: UM.Theme.getIcon("ChevronSingleLeft")
- }
- width: UM.Theme.getSize("toolbox_back_button").width
- height: UM.Theme.getSize("toolbox_back_button").height
- onClicked:
- {
- toolbox.viewPage = "overview"
- if (toolbox.viewCategory == "material")
- {
- toolbox.filterModelByProp("authors", "package_types", "material")
- }
- else if (toolbox.viewCategory == "plugin")
- {
- toolbox.filterModelByProp("packages", "type", "plugin")
- }
-
- }
- style: ButtonStyle
- {
- background: Rectangle
- {
- color: "transparent"
- }
- label: Label
- {
- id: labelStyle
- text: control.text
- color: control.enabled ? (control.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text")) : UM.Theme.getColor("text_inactive")
- font: UM.Theme.getFont("medium_bold")
- horizontalAlignment: Text.AlignLeft
- anchors
- {
- left: parent.left
- leftMargin: UM.Theme.getSize("default_margin").width
- }
- width: control.width
- renderType: Text.NativeRendering
- }
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/components/ToolboxCompatibilityChart.qml
deleted file mode 100644
index e1f88a473f..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxCompatibilityChart.qml
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright (c) 2019 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-
-import UM 1.5 as UM
-
-Item
-{
- id: base
-
- property var packageData
- property var technicalDataSheetUrl: packageData.links.technicalDataSheet
- property var safetyDataSheetUrl: packageData.links.safetyDataSheet
- property var printingGuidelinesUrl: packageData.links.printingGuidelines
- property var materialWebsiteUrl: packageData.links.website
-
- height: childrenRect.height
- onVisibleChanged: packageData.type === "material" && (compatibilityItem.visible || dataSheetLinks.visible)
-
- Column
- {
- id: compatibilityItem
- visible: packageData.has_configs
- width: parent.width
- // This is a bit of a hack, but the whole QML is pretty messy right now. This needs a big overhaul.
- height: visible ? heading.height + table.height: 0
-
- Label
- {
- id: heading
- width: parent.width
- text: catalog.i18nc("@label", "Compatibility")
- wrapMode: Text.WordWrap
- color: UM.Theme.getColor("text_medium")
- font: UM.Theme.getFont("medium")
- renderType: Text.NativeRendering
- }
-
- TableView
- {
- id: table
- width: parent.width
- frameVisible: false
-
- // Workaround for scroll issues (QTBUG-49652)
- flickableItem.interactive: false
- Component.onCompleted:
- {
- for (var i = 0; i < flickableItem.children.length; ++i)
- {
- flickableItem.children[i].enabled = false
- }
- }
- selectionMode: 0
- model: packageData.supported_configs
- headerDelegate: Rectangle
- {
- color: UM.Theme.getColor("main_background")
- height: UM.Theme.getSize("toolbox_chart_row").height
- Label
- {
- anchors.verticalCenter: parent.verticalCenter
- elide: Text.ElideRight
- text: styleData.value || ""
- color: UM.Theme.getColor("text")
- font: UM.Theme.getFont("default_bold")
- renderType: Text.NativeRendering
- }
- Rectangle
- {
- anchors.bottom: parent.bottom
- height: UM.Theme.getSize("default_lining").height
- width: parent.width
- color: "black"
- }
- }
- rowDelegate: Item
- {
- height: UM.Theme.getSize("toolbox_chart_row").height
- Label
- {
- anchors.verticalCenter: parent.verticalCenter
- elide: Text.ElideRight
- text: styleData.value || ""
- color: UM.Theme.getColor("text_medium")
- font: UM.Theme.getFont("default")
- renderType: Text.NativeRendering
- }
- }
- itemDelegate: Item
- {
- height: UM.Theme.getSize("toolbox_chart_row").height
- Label
- {
- anchors.verticalCenter: parent.verticalCenter
- elide: Text.ElideRight
- text: styleData.value || ""
- color: UM.Theme.getColor("text_medium")
- font: UM.Theme.getFont("default")
- renderType: Text.NativeRendering
- }
- }
-
- Component
- {
- id: columnTextDelegate
- Label
- {
- anchors.fill: parent
- verticalAlignment: Text.AlignVCenter
- text: styleData.value || ""
- elide: Text.ElideRight
- color: UM.Theme.getColor("text_medium")
- font: UM.Theme.getFont("default")
- renderType: Text.NativeRendering
- }
- }
-
- TableViewColumn
- {
- role: "machine"
- title: catalog.i18nc("@label:table_header", "Machine")
- width: Math.floor(table.width * 0.25)
- delegate: columnTextDelegate
- }
- TableViewColumn
- {
- role: "print_core"
- title: "Print Core" //This term should not be translated.
- width: Math.floor(table.width * 0.2)
- }
- TableViewColumn
- {
- role: "build_plate"
- title: catalog.i18nc("@label:table_header", "Build Plate")
- width: Math.floor(table.width * 0.225)
- }
- TableViewColumn
- {
- role: "support_material"
- title: catalog.i18nc("@label:table_header", "Support")
- width: Math.floor(table.width * 0.225)
- }
- TableViewColumn
- {
- role: "quality"
- title: catalog.i18nc("@label:table_header", "Quality")
- width: Math.floor(table.width * 0.1)
- }
- }
- }
-
- Label
- {
- id: dataSheetLinks
- anchors.top: compatibilityItem.bottom
- anchors.topMargin: UM.Theme.getSize("narrow_margin").height
- visible: base.technicalDataSheetUrl !== undefined ||
- base.safetyDataSheetUrl !== undefined ||
- base.printingGuidelinesUrl !== undefined ||
- base.materialWebsiteUrl !== undefined
-
- text:
- {
- var result = ""
- if (base.technicalDataSheetUrl !== undefined)
- {
- var tds_name = catalog.i18nc("@action:label", "Technical Data Sheet")
- result += "%2".arg(base.technicalDataSheetUrl).arg(tds_name)
- }
- if (base.safetyDataSheetUrl !== undefined)
- {
- if (result.length > 0)
- {
- result += "
"
- }
- var sds_name = catalog.i18nc("@action:label", "Safety Data Sheet")
- result += "%2".arg(base.safetyDataSheetUrl).arg(sds_name)
- }
- if (base.printingGuidelinesUrl !== undefined)
- {
- if (result.length > 0)
- {
- result += "
"
- }
- var pg_name = catalog.i18nc("@action:label", "Printing Guidelines")
- result += "%2".arg(base.printingGuidelinesUrl).arg(pg_name)
- }
- if (base.materialWebsiteUrl !== undefined)
- {
- if (result.length > 0)
- {
- result += "
"
- }
- var pg_name = catalog.i18nc("@action:label", "Website")
- result += "%2".arg(base.materialWebsiteUrl).arg(pg_name)
- }
-
- return result
- }
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- linkColor: UM.Theme.getColor("text_link")
- onLinkActivated: UM.UrlUtil.openUrl(link, ["http", "https"])
- renderType: Text.NativeRendering
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxDetailList.qml b/plugins/Toolbox/resources/qml/components/ToolboxDetailList.qml
deleted file mode 100644
index 22c6b6045f..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxDetailList.qml
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2019 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 2.3
-import UM 1.1 as UM
-
-Item
-{
- id: detailList
- ScrollView
- {
- clip: true
- anchors.fill: detailList
-
- Column
- {
- anchors
- {
- right: parent.right
- topMargin: UM.Theme.getSize("wide_margin").height
- bottomMargin: UM.Theme.getSize("wide_margin").height
- top: parent.top
- }
- height: childrenRect.height + 2 * UM.Theme.getSize("wide_margin").height
- spacing: UM.Theme.getSize("default_margin").height
-
- Repeater
- {
- model: toolbox.packagesModel
- delegate: Loader
- {
- // FIXME: When using asynchronous loading, on Mac and Windows, the tile may fail to load complete,
- // leaving an empty space below the title part. We turn it off for now to make it work on Mac and
- // Windows.
- // Can be related to this QT bug: https://bugreports.qt.io/browse/QTBUG-50992
- asynchronous: false
- source: "ToolboxDetailTile.qml"
- }
- }
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxDetailTile.qml b/plugins/Toolbox/resources/qml/components/ToolboxDetailTile.qml
deleted file mode 100644
index 5badc6b66d..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxDetailTile.qml
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2019 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 2.3
-
-import UM 1.1 as UM
-
-Item
-{
- id: tile
- width: detailList.width - UM.Theme.getSize("wide_margin").width
- height: normalData.height + 2 * UM.Theme.getSize("wide_margin").height
- Column
- {
- id: normalData
-
- anchors
- {
- top: parent.top
- left: parent.left
- right: controls.left
- rightMargin: UM.Theme.getSize("wide_margin").width
- }
-
- Label
- {
- width: parent.width
- height: UM.Theme.getSize("toolbox_property_label").height
- text: model.name
- wrapMode: Text.WordWrap
- color: UM.Theme.getColor("text")
- font: UM.Theme.getFont("medium_bold")
- renderType: Text.NativeRendering
- }
-
- Label
- {
- width: parent.width
- text: model.description
- maximumLineCount: 25
- elide: Text.ElideRight
- wrapMode: Text.WordWrap
- color: UM.Theme.getColor("text")
- font: UM.Theme.getFont("default")
- renderType: Text.NativeRendering
- }
-
- ToolboxCompatibilityChart
- {
- width: parent.width
- packageData: model
- }
- }
-
- ToolboxDetailTileActions
- {
- id: controls
- anchors.right: tile.right
- anchors.top: tile.top
- width: childrenRect.width
- height: childrenRect.height
- packageData: model
- }
-
- Rectangle
- {
- color: UM.Theme.getColor("lining")
- width: tile.width
- height: UM.Theme.getSize("default_lining").height
- anchors.top: normalData.bottom
- anchors.topMargin: UM.Theme.getSize("default_margin").height + UM.Theme.getSize("wide_margin").height //Normal margin for spacing after chart, wide margin between items.
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxDetailTileActions.qml b/plugins/Toolbox/resources/qml/components/ToolboxDetailTileActions.qml
deleted file mode 100644
index d683877605..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxDetailTileActions.qml
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4
-import UM 1.5 as UM
-import Cura 1.1 as Cura
-
-Column
-{
- property bool installed: toolbox.isInstalled(model.id)
- property bool canUpdate: CuraApplication.getPackageManager().packagesWithUpdate.indexOf(model.id) != -1
- property bool loginRequired: model.login_required && !Cura.API.account.isLoggedIn
- property var packageData
-
- width: UM.Theme.getSize("toolbox_action_button").width
- spacing: UM.Theme.getSize("narrow_margin").height
-
- Item
- {
- width: installButton.width
- height: installButton.height
- ToolboxProgressButton
- {
- id: installButton
- active: toolbox.isDownloading && toolbox.activePackage == model
- onReadyAction:
- {
- toolbox.activePackage = model
- toolbox.startDownload(model.download_url)
- }
- onActiveAction: toolbox.cancelDownload()
-
- // Don't allow installing while another download is running
- enabled: installed || (!(toolbox.isDownloading && toolbox.activePackage != model) && !loginRequired)
- opacity: enabled ? 1.0 : 0.5
- visible: !updateButton.visible && !installed // Don't show when the update button is visible
- }
-
- Cura.SecondaryButton
- {
- id: installedButton
- visible: installed
- onClicked: toolbox.viewCategory = "installed"
- text: catalog.i18nc("@action:button", "Installed")
- fixedWidthMode: true
- width: installButton.width
- height: installButton.height
- }
- }
-
- Label
- {
- wrapMode: Text.WordWrap
- text: catalog.i18nc("@label:The string between and is the highlighted link", "Log in is required to install or update")
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- linkColor: UM.Theme.getColor("text_link")
- visible: loginRequired
- width: installButton.width
- renderType: Text.NativeRendering
-
- MouseArea
- {
- anchors.fill: parent
- onClicked: Cura.API.account.login()
- }
- }
-
- Label
- {
- property var whereToBuyUrl:
- {
- var pg_name = "whereToBuy"
- return (pg_name in packageData.links) ? packageData.links[pg_name] : undefined
- }
-
- renderType: Text.NativeRendering
- text: catalog.i18nc("@label:The string between and is the highlighted link", "Buy material spools")
- linkColor: UM.Theme.getColor("text_link")
- visible: whereToBuyUrl != undefined
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- MouseArea
- {
- anchors.fill: parent
- onClicked: UM.UrlUtil.openUrl(parent.whereToBuyUrl, ["https", "http"])
- }
- }
-
- ToolboxProgressButton
- {
- 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")
-
- onReadyAction:
- {
- toolbox.activePackage = model
- toolbox.update(model.id)
- }
- onActiveAction: toolbox.cancelDownload()
- // Don't allow installing while another download is running
- enabled: !(toolbox.isDownloading && toolbox.activePackage != model) && !loginRequired
- opacity: enabled ? 1.0 : 0.5
- visible: canUpdate
- }
-
- Connections
- {
- target: toolbox
- function onInstallChanged() { installed = toolbox.isInstalled(model.id) }
- function onFilterChanged()
- {
- installed = toolbox.isInstalled(model.id)
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxDownloadsGrid.qml b/plugins/Toolbox/resources/qml/components/ToolboxDownloadsGrid.qml
deleted file mode 100644
index 6682281a31..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxDownloadsGrid.qml
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 2.3
-import UM 1.1 as UM
-
-Column
-{
- property var heading: ""
- property var model
- id: gridArea
- height: childrenRect.height + 2 * padding
- width: parent.width
- spacing: UM.Theme.getSize("default_margin").height
- padding: UM.Theme.getSize("wide_margin").height
- Label
- {
- id: heading
- text: gridArea.heading
- width: parent.width
- color: UM.Theme.getColor("text_medium")
- font: UM.Theme.getFont("large")
- renderType: Text.NativeRendering
- }
- Grid
- {
- id: grid
- width: parent.width - 2 * parent.padding
- columns: 2
- columnSpacing: UM.Theme.getSize("default_margin").height
- rowSpacing: UM.Theme.getSize("default_margin").width
- Repeater
- {
- model: gridArea.model
- delegate: Loader
- {
- asynchronous: true
- width: Math.round((grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns)
- height: UM.Theme.getSize("toolbox_thumbnail_small").height
- source: "ToolboxDownloadsGridTile.qml"
- }
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/components/ToolboxDownloadsGridTile.qml
deleted file mode 100644
index c310bd7121..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxDownloadsGridTile.qml
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4
-import QtQuick.Layouts 1.3
-import UM 1.1 as UM
-import Cura 1.1 as Cura
-
-Item
-{
- id: toolboxDownloadsGridTile
- property int packageCount: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getTotalNumberOfMaterialPackagesByAuthor(model.id) : 1
- property int installedPackages: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
- height: childrenRect.height
- Layout.alignment: Qt.AlignTop | Qt.AlignLeft
-
- MouseArea
- {
- anchors.fill: parent
- hoverEnabled: true
- onEntered: thumbnail.border.color = UM.Theme.getColor("primary")
- onExited: thumbnail.border.color = UM.Theme.getColor("lining")
- onClicked:
- {
- base.selection = model
- switch(toolbox.viewCategory)
- {
- case "material":
-
- // If model has a type, it must be a package
- if (model.type !== undefined)
- {
- toolbox.viewPage = "detail"
- toolbox.filterModelByProp("packages", "id", model.id)
- }
- else
- {
- toolbox.viewPage = "author"
- toolbox.setFilters("packages", {
- "author_id": model.id,
- "type": "material"
- })
- }
- break
- default:
- toolbox.viewPage = "detail"
- toolbox.filterModelByProp("packages", "id", model.id)
- break
- }
- }
- }
-
- Rectangle
- {
- id: thumbnail
- width: UM.Theme.getSize("toolbox_thumbnail_small").width
- height: UM.Theme.getSize("toolbox_thumbnail_small").height
- color: UM.Theme.getColor("main_background")
- border.width: UM.Theme.getSize("default_lining").width
- border.color: UM.Theme.getColor("lining")
-
- Image
- {
- anchors.centerIn: parent
- width: UM.Theme.getSize("toolbox_thumbnail_small").width - UM.Theme.getSize("wide_margin").width
- height: UM.Theme.getSize("toolbox_thumbnail_small").height - UM.Theme.getSize("wide_margin").width
- sourceSize.width: width
- sourceSize.height: height
- fillMode: Image.PreserveAspectFit
- source: model.icon_url || "../../images/placeholder.svg"
- mipmap: true
- }
- UM.RecolorImage
- {
- width: (parent.width * 0.4) | 0
- height: (parent.height * 0.4) | 0
- anchors
- {
- bottom: parent.bottom
- right: parent.right
- }
- sourceSize.height: height
- visible: installedPackages != 0
- color: (installedPackages >= packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
- source: "../../images/installed_check.svg"
- }
- }
- Item
- {
- anchors
- {
- left: thumbnail.right
- leftMargin: Math.floor(UM.Theme.getSize("narrow_margin").width)
- right: parent.right
- top: parent.top
- bottom: parent.bottom
- }
-
- Label
- {
- id: name
- text: model.name
- width: parent.width
- elide: Text.ElideRight
- color: UM.Theme.getColor("text")
- font: UM.Theme.getFont("default_bold")
- }
- Label
- {
- id: info
- text: model.description
- elide: Text.ElideRight
- width: parent.width
- wrapMode: Text.WordWrap
- color: UM.Theme.getColor("text")
- font: UM.Theme.getFont("default")
- anchors.top: name.bottom
- anchors.bottom: parent.bottom
- verticalAlignment: Text.AlignVCenter
- maximumLineCount: 2
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxDownloadsShowcase.qml b/plugins/Toolbox/resources/qml/components/ToolboxDownloadsShowcase.qml
deleted file mode 100644
index a42a10aa29..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxDownloadsShowcase.qml
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4
-import UM 1.1 as UM
-
-Rectangle
-{
- color: UM.Theme.getColor("toolbox_premium_packages_background")
- height: childrenRect.height
- width: parent.width
- Column
- {
- height: childrenRect.height + 2 * padding
- spacing: UM.Theme.getSize("default_margin").height
- width: parent.width
- padding: UM.Theme.getSize("wide_margin").height
- Item
- {
- width: parent.width - parent.padding * 2
- height: childrenRect.height
- Label
- {
- id: heading
- text: catalog.i18nc("@label", "Premium")
- width: contentWidth
- height: contentHeight
- color: UM.Theme.getColor("text_medium")
- font: UM.Theme.getFont("large")
- renderType: Text.NativeRendering
- }
- UM.TooltipArea
- {
- width: childrenRect.width
- height: childrenRect.height
- anchors.right: parent.right
- text: catalog.i18nc("@info:tooltip", "Go to Web Marketplace")
- Label
- {
- text: "".arg(toolbox.getWebMarketplaceUrl("materials") + "?utm_source=cura&utm_medium=software&utm_campaign=marketplace-search") + catalog.i18nc("@label", "Search materials") + ""
- width: contentWidth
- height: contentHeight
- horizontalAlignment: Text.AlignRight
- font: UM.Theme.getFont("default")
- renderType: Text.NativeRendering
-
- linkColor: UM.Theme.getColor("text_link")
- onLinkActivated: Qt.openUrlExternally(link)
-
- visible: toolbox.viewCategory === "material"
- }
- }
- }
- Grid
- {
- height: childrenRect.height
- spacing: UM.Theme.getSize("wide_margin").width
- columns: 3
- anchors.horizontalCenter: parent.horizontalCenter
-
- Repeater
- {
- model:
- {
- if (toolbox.viewCategory == "plugin")
- {
- return toolbox.pluginsShowcaseModel
- }
- if (toolbox.viewCategory == "material")
- {
- return toolbox.materialsShowcaseModel
- }
- }
- delegate: Loader
- {
- asynchronous: true
- source: "ToolboxDownloadsShowcaseTile.qml"
- }
- }
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/components/ToolboxDownloadsShowcaseTile.qml
deleted file mode 100644
index 6695921126..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxDownloadsShowcaseTile.qml
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Cura is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4
-
-import UM 1.1 as UM
-
-Rectangle
-{
- property int packageCount: toolbox.viewCategory == "material" ? toolbox.getTotalNumberOfMaterialPackagesByAuthor(model.id) : 1
- property int installedPackages: toolbox.viewCategory == "material" ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
- id: tileBase
- width: UM.Theme.getSize("toolbox_thumbnail_large").width + (2 * UM.Theme.getSize("default_lining").width)
- height: thumbnail.height + packageName.height + UM.Theme.getSize("default_margin").width
- border.width: UM.Theme.getSize("default_lining").width
- border.color: UM.Theme.getColor("lining")
- color: UM.Theme.getColor("main_background")
- Image
- {
- id: thumbnail
- height: UM.Theme.getSize("toolbox_thumbnail_large").height - 4 * UM.Theme.getSize("default_margin").height
- width: UM.Theme.getSize("toolbox_thumbnail_large").height - 4 * UM.Theme.getSize("default_margin").height
- sourceSize.height: height
- sourceSize.width: width
- fillMode: Image.PreserveAspectFit
- source: model.icon_url || "../../images/placeholder.svg"
- mipmap: true
- anchors
- {
- top: parent.top
- topMargin: UM.Theme.getSize("default_margin").height
- horizontalCenter: parent.horizontalCenter
- }
- }
- Label
- {
- id: packageName
- text: model.name
- anchors
- {
- horizontalCenter: parent.horizontalCenter
- top: thumbnail.bottom
- }
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignHCenter
- renderType: Text.NativeRendering
- height: UM.Theme.getSize("toolbox_heading_label").height
- width: parent.width - UM.Theme.getSize("default_margin").width
- wrapMode: Text.WordWrap
- elide: Text.ElideRight
- font: UM.Theme.getFont("medium_bold")
- color: UM.Theme.getColor("text")
- }
- UM.RecolorImage
- {
- width: (parent.width * 0.20) | 0
- height: width
- anchors
- {
- bottom: bottomBorder.top
- right: parent.right
- }
- visible: installedPackages != 0
- color: (installedPackages >= packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
- source: "../../images/installed_check.svg"
- }
-
- Rectangle
- {
- id: bottomBorder
- color: UM.Theme.getColor("primary")
- anchors.bottom: parent.bottom
- width: parent.width
- height: UM.Theme.getSize("toolbox_header_highlight").height
- }
-
- MouseArea
- {
- anchors.fill: parent
- hoverEnabled: true
- onEntered: tileBase.border.color = UM.Theme.getColor("primary")
- onExited: tileBase.border.color = UM.Theme.getColor("lining")
- onClicked:
- {
- base.selection = model
- switch(toolbox.viewCategory)
- {
- case "material":
-
- // If model has a type, it must be a package
- if (model.type !== undefined)
- {
- toolbox.viewPage = "detail"
- toolbox.filterModelByProp("packages", "id", model.id)
- }
- else
- {
- toolbox.viewPage = "author"
- toolbox.setFilters("packages", {
- "author_id": model.id,
- "type": "material"
- })
- }
- break
- default:
- toolbox.viewPage = "detail"
- toolbox.filterModelByProp("packages", "id", model.id)
- break
- }
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxFooter.qml b/plugins/Toolbox/resources/qml/components/ToolboxFooter.qml
deleted file mode 100644
index 9863bd9a93..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxFooter.qml
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 2.3
-
-import UM 1.1 as UM
-import Cura 1.0 as Cura
-
-Item
-{
- id: footer
- width: parent.width
- anchors.bottom: parent.bottom
- height: visible ? UM.Theme.getSize("toolbox_footer").height : 0
-
- Label
- {
- text: catalog.i18nc("@info", "You will need to restart Cura before changes in packages have effect.")
- color: UM.Theme.getColor("text")
- height: UM.Theme.getSize("toolbox_footer_button").height
- verticalAlignment: Text.AlignVCenter
- wrapMode: Text.WordWrap
- anchors
- {
- top: restartButton.top
- left: parent.left
- leftMargin: UM.Theme.getSize("wide_margin").width
- right: restartButton.left
- rightMargin: UM.Theme.getSize("default_margin").width
- }
- renderType: Text.NativeRendering
- }
-
- Cura.PrimaryButton
- {
- id: restartButton
- anchors
- {
- top: parent.top
- topMargin: UM.Theme.getSize("default_margin").height
- right: parent.right
- rightMargin: UM.Theme.getSize("wide_margin").width
- }
- height: UM.Theme.getSize("toolbox_footer_button").height
- text: catalog.i18nc("@info:button, %1 is the application name", "Quit %1").arg(CuraApplication.applicationDisplayName)
- onClicked:
- {
- base.hide()
- toolbox.restart()
- }
- }
-
- ToolboxShadow
- {
- visible: footer.visible
- anchors.bottom: footer.top
- reversed: true
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxHeader.qml b/plugins/Toolbox/resources/qml/components/ToolboxHeader.qml
deleted file mode 100644
index 2c43110af9..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxHeader.qml
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2020 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-
-import UM 1.4 as UM
-import Cura 1.0 as Cura
-
-Item
-{
- id: header
- width: parent.width
- height: UM.Theme.getSize("toolbox_header").height
- Row
- {
- id: bar
- spacing: UM.Theme.getSize("default_margin").width
- height: childrenRect.height
- width: childrenRect.width
- anchors
- {
- left: parent.left
- leftMargin: UM.Theme.getSize("default_margin").width
- }
-
- ToolboxTabButton
- {
- id: pluginsTabButton
- text: catalog.i18nc("@title:tab", "Plugins")
- active: toolbox.viewCategory == "plugin" && enabled
- enabled: !toolbox.isDownloading && toolbox.viewPage != "loading" && toolbox.viewPage != "errored"
- onClicked:
- {
- toolbox.filterModelByProp("packages", "type", "plugin")
- toolbox.viewCategory = "plugin"
- toolbox.viewPage = "overview"
- }
- }
-
- ToolboxTabButton
- {
- id: materialsTabButton
- text: catalog.i18nc("@title:tab", "Materials")
- active: toolbox.viewCategory == "material" && enabled
- enabled: !toolbox.isDownloading && toolbox.viewPage != "loading" && toolbox.viewPage != "errored"
- onClicked:
- {
- toolbox.filterModelByProp("authors", "package_types", "material")
- toolbox.viewCategory = "material"
- toolbox.viewPage = "overview"
- }
- }
-
- ToolboxTabButton
- {
- id: installedTabButton
- text: catalog.i18nc("@title:tab", "Installed")
- active: toolbox.viewCategory == "installed"
- enabled: !toolbox.isDownloading
- onClicked: toolbox.viewCategory = "installed"
- width: UM.Theme.getSize("toolbox_header_tab").width + marketplaceNotificationIcon.width - UM.Theme.getSize("default_margin").width
- }
-
-
- }
-
- Cura.NotificationIcon
- {
- id: marketplaceNotificationIcon
- visible: CuraApplication.getPackageManager().packagesWithUpdate.length > 0
- anchors.right: bar.right
- labelText:
- {
- const itemCount = CuraApplication.getPackageManager().packagesWithUpdate.length
- return itemCount > 9 ? "9+" : itemCount
- }
- }
-
-
- UM.TooltipArea
- {
- id: webMarketplaceButtonTooltipArea
- width: childrenRect.width
- height: parent.height
- text: catalog.i18nc("@info:tooltip", "Go to Web Marketplace")
- anchors
- {
- right: parent.right
- rightMargin: UM.Theme.getSize("default_margin").width
- verticalCenter: parent.verticalCenter
- }
- acceptedButtons: Qt.LeftButton
- onClicked: Qt.openUrlExternally(toolbox.getWebMarketplaceUrl("plugins") + "?utm_source=cura&utm_medium=software&utm_campaign=marketplace-button")
- UM.RecolorImage
- {
- id: cloudMarketplaceButton
- source: "../../images/Shop.svg"
- color: UM.Theme.getColor(webMarketplaceButtonTooltipArea.containsMouse ? "primary" : "text")
- height: parent.height / 2
- width: height
- anchors.verticalCenter: parent.verticalCenter
- sourceSize.width: width
- sourceSize.height: height
- }
- }
-
- ToolboxShadow
- {
- anchors.top: bar.bottom
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxInstalledTile.qml b/plugins/Toolbox/resources/qml/components/ToolboxInstalledTile.qml
deleted file mode 100644
index e5c94fc996..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxInstalledTile.qml
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4
-import UM 1.1 as UM
-
-Item
-{
- height: UM.Theme.getSize("toolbox_installed_tile").height
- width: parent.width
- property bool isEnabled: true
-
- Rectangle
- {
- color: UM.Theme.getColor("lining")
- width: parent.width
- height: Math.floor(UM.Theme.getSize("default_lining").height)
- anchors.bottom: parent.top
- visible: index != 0
- }
- Row
- {
- id: tileRow
- height: parent.height
- width: parent.width
- spacing: UM.Theme.getSize("default_margin").width
- topPadding: UM.Theme.getSize("default_margin").height
-
- CheckBox
- {
- id: disableButton
- anchors.verticalCenter: pluginInfo.verticalCenter
- checked: isEnabled
- visible: model.type == "plugin"
- width: visible ? UM.Theme.getSize("checkbox").width : 0
- enabled: !toolbox.isDownloading
- style: UM.Theme.styles.checkbox
- onClicked: toolbox.isEnabled(model.id) ? toolbox.disable(model.id) : toolbox.enable(model.id)
- }
- Column
- {
- id: pluginInfo
- topPadding: UM.Theme.getSize("narrow_margin").height
- property var color: model.type === "plugin" && !isEnabled ? UM.Theme.getColor("lining") : UM.Theme.getColor("text")
- width: Math.floor(tileRow.width - (authorInfo.width + pluginActions.width + 2 * tileRow.spacing + ((disableButton.visible) ? disableButton.width + tileRow.spacing : 0)))
- Label
- {
- text: model.name
- width: parent.width
- maximumLineCount: 1
- elide: Text.ElideRight
- wrapMode: Text.WordWrap
- font: UM.Theme.getFont("large_bold")
- color: pluginInfo.color
- renderType: Text.NativeRendering
- }
- Label
- {
- text: model.description
- font: UM.Theme.getFont("default")
- maximumLineCount: 3
- elide: Text.ElideRight
- width: parent.width
- wrapMode: Text.WordWrap
- color: pluginInfo.color
- renderType: Text.NativeRendering
- }
- }
- Column
- {
- id: authorInfo
- width: Math.floor(UM.Theme.getSize("toolbox_action_button").width * 1.25)
-
- Label
- {
- text:
- {
- if (model.author_email)
- {
- return "" + model.author_name + ""
- }
- else
- {
- return model.author_name
- }
- }
- font: UM.Theme.getFont("medium")
- width: parent.width
- height: Math.floor(UM.Theme.getSize("toolbox_property_label").height)
- wrapMode: Text.WordWrap
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignLeft
- onLinkActivated: Qt.openUrlExternally("mailto:" + model.author_email + "?Subject=Cura: " + model.name + " Plugin")
- color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining")
- linkColor: UM.Theme.getColor("text_link")
- renderType: Text.NativeRendering
- }
-
- Label
- {
- text: model.version
- font: UM.Theme.getFont("default")
- width: parent.width
- height: UM.Theme.getSize("toolbox_property_label").height
- color: UM.Theme.getColor("text")
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignLeft
- renderType: Text.NativeRendering
- }
- }
- ToolboxInstalledTileActions
- {
- id: pluginActions
- }
- Connections
- {
- target: toolbox
- function onToolboxEnabledChanged() { isEnabled = toolbox.isEnabled(model.id) }
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxInstalledTileActions.qml b/plugins/Toolbox/resources/qml/components/ToolboxInstalledTileActions.qml
deleted file mode 100644
index 1726497c00..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxInstalledTileActions.qml
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4
-import UM 1.1 as UM
-
-import Cura 1.1 as Cura
-
-Column
-{
- property bool canUpdate: CuraApplication.getPackageManager().packagesWithUpdate.indexOf(model.id) != -1
- property bool canDowngrade: false
- property bool loginRequired: model.login_required && !Cura.API.account.isLoggedIn
- width: UM.Theme.getSize("toolbox_action_button").width
- spacing: UM.Theme.getSize("narrow_margin").height
-
- Label
- {
- visible: !model.is_installed
- text: catalog.i18nc("@label", "Will install upon restarting")
- color: UM.Theme.getColor("lining")
- font: UM.Theme.getFont("default")
- wrapMode: Text.WordWrap
- width: parent.width
- renderType: Text.NativeRendering
- }
-
- ToolboxProgressButton
- {
- 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")
- onReadyAction:
- {
- toolbox.activePackage = model
- toolbox.update(model.id)
- }
- onActiveAction: toolbox.cancelDownload()
-
- // Don't allow installing while another download is running
- enabled: !(toolbox.isDownloading && toolbox.activePackage != model) && !loginRequired
- opacity: enabled ? 1.0 : 0.5
- visible: canUpdate
- }
-
- Label
- {
- wrapMode: Text.WordWrap
- text: catalog.i18nc("@label:The string between and is the highlighted link", "Log in is required to update")
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- linkColor: UM.Theme.getColor("text_link")
- visible: loginRequired
- width: updateButton.width
- renderType: Text.NativeRendering
-
- MouseArea
- {
- anchors.fill: parent
- onClicked: Cura.API.account.login()
- }
- }
-
- Cura.SecondaryButton
- {
- id: removeButton
- text: canDowngrade ? catalog.i18nc("@action:button", "Downgrade") : catalog.i18nc("@action:button", "Uninstall")
- visible: !model.is_bundled && model.is_installed
- enabled: !toolbox.isDownloading
-
- width: UM.Theme.getSize("toolbox_action_button").width
- height: UM.Theme.getSize("toolbox_action_button").height
-
- fixedWidthMode: true
-
- onClicked: toolbox.checkPackageUsageAndUninstall(model.id)
- Connections
- {
- target: toolbox
- function onMetadataChanged()
- {
- canDowngrade = toolbox.canDowngrade(model.id)
- }
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxProgressButton.qml b/plugins/Toolbox/resources/qml/components/ToolboxProgressButton.qml
deleted file mode 100644
index 40d6c1af47..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxProgressButton.qml
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2019 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 2.3
-
-import UM 1.1 as UM
-import Cura 1.0 as Cura
-
-
-Cura.PrimaryButton
-{
- id: button
-
- property var active: false
- property var complete: false
-
- property var readyLabel: catalog.i18nc("@action:button", "Install")
- property var activeLabel: catalog.i18nc("@action:button", "Cancel")
- property var completeLabel: catalog.i18nc("@action:button", "Installed")
-
- signal readyAction() // Action when button is ready and clicked (likely install)
- signal activeAction() // Action when button is active and clicked (likely cancel)
- signal completeAction() // 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
- fixedWidthMode: true
- text:
- {
- if (complete)
- {
- return completeLabel
- }
- else if (active)
- {
- return activeLabel
- }
- else
- {
- return readyLabel
- }
- }
- onClicked:
- {
- if (complete)
- {
- completeAction()
- }
- else if (active)
- {
- activeAction()
- }
- else
- {
- readyAction()
- }
- }
- busy: active
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxShadow.qml b/plugins/Toolbox/resources/qml/components/ToolboxShadow.qml
deleted file mode 100644
index 0f2f98beb9..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxShadow.qml
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.2
-
-Rectangle
-{
- property bool reversed: false
- width: parent.width
- height: 8
- gradient: Gradient
- {
- GradientStop
- {
- position: reversed ? 1.0 : 0.0
- color: reversed ? Qt.rgba(0,0,0,0.05) : Qt.rgba(0,0,0,0.2)
- }
- GradientStop
- {
- position: reversed ? 0.0 : 1.0
- color: Qt.rgba(0,0,0,0)
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/components/ToolboxTabButton.qml b/plugins/Toolbox/resources/qml/components/ToolboxTabButton.qml
deleted file mode 100644
index 7a7d2be48a..0000000000
--- a/plugins/Toolbox/resources/qml/components/ToolboxTabButton.qml
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 2.3
-import UM 1.1 as UM
-
-Button
-{
- id: control
- property bool active: false
-
- implicitWidth: UM.Theme.getSize("toolbox_header_tab").width
- implicitHeight: UM.Theme.getSize("toolbox_header_tab").height
-
- background: Item
- {
- id: backgroundItem
- Rectangle
- {
- id: highlight
-
- visible: control.active
- color: UM.Theme.getColor("primary")
- anchors.bottom: parent.bottom
- width: parent.width
- height: UM.Theme.getSize("toolbox_header_highlight").height
- }
- }
-
- contentItem: Label
- {
- id: label
- text: control.text
- color: UM.Theme.getColor("toolbox_header_button_text_inactive")
- font: UM.Theme.getFont("medium")
-
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignHCenter
-
- renderType: Text.NativeRendering
- }
-
- states:
- [
- State
- {
- name: "disabled"
- when: !control.enabled
- PropertyChanges
- {
- target: label
- font: UM.Theme.getFont("default_italic")
- }
- },
- State
- {
- name: "active"
- when: control.active
- PropertyChanges
- {
- target: label
- font: UM.Theme.getFont("medium_bold")
- color: UM.Theme.getColor("action_button_text")
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml
deleted file mode 100644
index b33036847b..0000000000
--- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (c) 2020 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Window 2.2
-import QtQuick.Controls 2.3
-
-import UM 1.1 as UM
-import Cura 1.6 as Cura
-
-
-UM.Dialog{
- visible: true
- 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
-
- property string actionButtonText: subscribedPackagesModel.hasIncompatiblePackages && !subscribedPackagesModel.hasCompatiblePackages ? catalog.i18nc("@button", "Dismiss") : catalog.i18nc("@button", "Next")
-
- Rectangle
- {
- id: root
- anchors.fill: parent
- color: UM.Theme.getColor("main_background")
-
- 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:")
- visible: subscribedPackagesModel.hasCompatiblePackages
- color: UM.Theme.getColor("text")
- height: contentHeight + UM.Theme.getSize("default_margin").height
- }
- Repeater
- {
- model: subscribedPackagesModel
- Component
- {
- Item
- {
- width: parent.width
- property int lineHeight: 60
- visible: model.is_compatible
- 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/placeholder.svg"
- height: lineHeight
- width: height
- sourceSize.height: height
- sourceSize.width: width
- mipmap: true
- fillMode: Image.PreserveAspectFit
- }
- Label
- {
- text: model.display_name
- font: UM.Theme.getFont("medium_bold")
- anchors.left: packageIcon.right
- anchors.leftMargin: UM.Theme.getSize("default_margin").width
- 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 an incompatible Cura version:")
- visible: subscribedPackagesModel.hasIncompatiblePackages
- color: UM.Theme.getColor("text")
- height: contentHeight + UM.Theme.getSize("default_margin").height
- }
- Repeater
- {
- model: subscribedPackagesModel
- Component
- {
- Item
- {
- width: parent.width
- property int lineHeight: 60
- visible: !model.is_compatible && !model.is_dismissed
- 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/placeholder.svg"
- height: lineHeight
- width: height
- sourceSize.height: height
- sourceSize.width: width
- mipmap: true
- fillMode: Image.PreserveAspectFit
- }
- Label
- {
- text: model.display_name
- font: UM.Theme.getFont("medium_bold")
- anchors.left: packageIcon.right
- anchors.leftMargin: UM.Theme.getSize("default_margin").width
- anchors.verticalCenter: packageIcon.verticalCenter
- color: UM.Theme.getColor("text")
- elide: Text.ElideRight
- }
- }
- }
- }
- }
-
- } // End of ScrollView
-
- Cura.PrimaryButton
- {
- id: nextButton
- anchors.bottom: parent.bottom
- anchors.right: parent.right
- anchors.margins: UM.Theme.getSize("default_margin").height
- text: actionButtonText
- onClicked: accept()
- leftPadding: UM.Theme.getSize("dialog_primary_button_padding").width
- rightPadding: UM.Theme.getSize("dialog_primary_button_padding").width
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/dialogs/ToolboxConfirmUninstallResetDialog.qml b/plugins/Toolbox/resources/qml/dialogs/ToolboxConfirmUninstallResetDialog.qml
deleted file mode 100644
index 1b5e4d1d46..0000000000
--- a/plugins/Toolbox/resources/qml/dialogs/ToolboxConfirmUninstallResetDialog.qml
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Cura is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.1
-import QtQuick.Controls.Styles 1.1
-import QtQuick.Layouts 1.1
-import QtQuick.Dialogs 1.1
-import QtQuick.Window 2.1
-
-import UM 1.3 as UM
-import Cura 1.0 as Cura
-
-
-UM.Dialog
-{
- // This dialog asks the user to confirm he/she wants to uninstall materials/pprofiles which are currently in use
- id: base
-
- title: catalog.i18nc("@title:window", "Confirm uninstall") + toolbox.pluginToUninstall
- width: 450 * screenScaleFactor
- height: 50 * screenScaleFactor + dialogText.height + buttonBar.height
-
- maximumWidth: 450 * screenScaleFactor
- maximumHeight: 450 * screenScaleFactor
- minimumWidth: 450 * screenScaleFactor
- minimumHeight: 150 * screenScaleFactor
-
- modality: Qt.WindowModal
-
- Column
- {
- UM.I18nCatalog { id: catalog; name: "cura" }
-
- anchors
- {
- fill: parent
- leftMargin: Math.round(20 * screenScaleFactor)
- rightMargin: Math.round(20 * screenScaleFactor)
- topMargin: Math.round(10 * screenScaleFactor)
- bottomMargin: Math.round(10 * screenScaleFactor)
- }
- spacing: Math.round(15 * screenScaleFactor)
-
- Label
- {
- id: dialogText
- text:
- {
- var base_text = catalog.i18nc("@text:window", "You are uninstalling materials and/or profiles that are still in use. Confirming will reset the following materials/profiles to their defaults.")
- var materials_text = catalog.i18nc("@text:window", "Materials")
- var qualities_text = catalog.i18nc("@text:window", "Profiles")
- var machines_with_materials = toolbox.uninstallUsedMaterials
- var machines_with_qualities = toolbox.uninstallUsedQualities
- if (machines_with_materials != "")
- {
- base_text += "\n\n" + materials_text +": \n" + machines_with_materials
- }
- if (machines_with_qualities != "")
- {
- base_text += "\n\n" + qualities_text + ": \n" + machines_with_qualities
- }
- return base_text
- }
- anchors.left: parent.left
- anchors.right: parent.right
- font: UM.Theme.getFont("default")
- wrapMode: Text.WordWrap
- renderType: Text.NativeRendering
- }
-
- // Buttons
- Item {
- id: buttonBar
- anchors.right: parent.right
- anchors.left: parent.left
- height: childrenRect.height
-
- Button {
- id: cancelButton
- text: catalog.i18nc("@action:button", "Cancel")
- anchors.right: confirmButton.left
- anchors.rightMargin: UM.Theme.getSize("default_margin").width
- isDefault: true
- onClicked: toolbox.closeConfirmResetDialog()
- }
-
- Button {
- id: confirmButton
- text: catalog.i18nc("@action:button", "Confirm")
- anchors.right: parent.right
- onClicked: toolbox.resetMaterialsQualitiesAndUninstall()
- }
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml b/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml
deleted file mode 100644
index 9219f4ed32..0000000000
--- a/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml
+++ /dev/null
@@ -1,110 +0,0 @@
-// 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 2.3
-import QtQuick.Layouts 1.3
-import QtQuick.Controls.Styles 1.4
-
-import UM 1.1 as UM
-import Cura 1.6 as Cura
-
-UM.Dialog
-{
- id: licenseDialog
- title: licenseModel.dialogTitle
- minimumWidth: UM.Theme.getSize("license_window_minimum").width
- minimumHeight: UM.Theme.getSize("license_window_minimum").height
- width: minimumWidth
- height: minimumHeight
- backgroundColor: UM.Theme.getColor("main_background")
- margin: screenScaleFactor * 10
-
- ColumnLayout
- {
- anchors.fill: parent
- spacing: UM.Theme.getSize("thick_margin").height
-
- UM.I18nCatalog{id: catalog; name: "cura"}
-
- Label
- {
- id: licenseHeader
- Layout.fillWidth: true
- text: catalog.i18nc("@label", "You need to accept the license to install the package")
- color: UM.Theme.getColor("text")
- wrapMode: Text.Wrap
- renderType: Text.NativeRendering
- }
-
- Row {
- id: packageRow
-
- Layout.fillWidth: true
- height: childrenRect.height
- spacing: UM.Theme.getSize("default_margin").width
- leftPadding: UM.Theme.getSize("narrow_margin").width
-
- Image
- {
- id: icon
- width: 30 * screenScaleFactor
- height: width
- sourceSize.width: width
- sourceSize.height: height
- fillMode: Image.PreserveAspectFit
- source: licenseModel.iconUrl || "../../images/placeholder.svg"
- mipmap: true
- }
-
- Label
- {
- id: packageName
- text: licenseModel.packageName
- color: UM.Theme.getColor("text")
- font.bold: true
- anchors.verticalCenter: icon.verticalCenter
- height: contentHeight
- wrapMode: Text.Wrap
- renderType: Text.NativeRendering
- }
-
-
- }
-
- Cura.ScrollableTextArea
- {
-
- Layout.fillWidth: true
- Layout.fillHeight: true
- anchors.topMargin: UM.Theme.getSize("default_margin").height
-
- textArea.text: licenseModel.licenseText
- textArea.readOnly: true
- }
-
- }
- rightButtons:
- [
- Cura.PrimaryButton
- {
- leftPadding: UM.Theme.getSize("dialog_primary_button_padding").width
- rightPadding: UM.Theme.getSize("dialog_primary_button_padding").width
-
- text: licenseModel.acceptButtonText
- onClicked: { handler.onLicenseAccepted() }
- }
- ]
-
- leftButtons:
- [
- Cura.SecondaryButton
- {
- id: declineButton
- text: licenseModel.declineButtonText
- onClicked: { handler.onLicenseDeclined() }
- }
- ]
-}
diff --git a/plugins/Toolbox/resources/qml/pages/ToolboxAuthorPage.qml b/plugins/Toolbox/resources/qml/pages/ToolboxAuthorPage.qml
deleted file mode 100644
index 2fa4224388..0000000000
--- a/plugins/Toolbox/resources/qml/pages/ToolboxAuthorPage.qml
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4
-import UM 1.5 as UM
-
-import "../components"
-
-Item
-{
- id: page
- property var details: base.selection || {}
- anchors.fill: parent
- ToolboxBackColumn
- {
- id: sidebar
- }
- Item
- {
- id: header
- anchors
- {
- left: sidebar.right
- right: parent.right
- rightMargin: UM.Theme.getSize("wide_margin").width
- }
- height: UM.Theme.getSize("toolbox_detail_header").height
- Image
- {
- id: thumbnail
- width: UM.Theme.getSize("toolbox_thumbnail_medium").width
- height: UM.Theme.getSize("toolbox_thumbnail_medium").height
- fillMode: Image.PreserveAspectFit
- source: details && details.icon_url ? details.icon_url : "../../images/placeholder.svg"
- mipmap: true
- anchors
- {
- top: parent.top
- left: parent.left
- leftMargin: UM.Theme.getSize("wide_margin").width
- topMargin: UM.Theme.getSize("wide_margin").height
- }
- }
-
- Label
- {
- id: title
- anchors
- {
- top: thumbnail.top
- left: thumbnail.right
- leftMargin: UM.Theme.getSize("default_margin").width
- right: parent.right
- rightMargin: UM.Theme.getSize("wide_margin").width
- bottomMargin: UM.Theme.getSize("default_margin").height
- }
- text: details && details.name ? details.name : ""
- font: UM.Theme.getFont("large_bold")
- color: UM.Theme.getColor("text_medium")
- wrapMode: Text.WordWrap
- width: parent.width
- height: UM.Theme.getSize("toolbox_property_label").height
- renderType: Text.NativeRendering
- }
- Label
- {
- id: description
- text: details && details.description ? details.description : ""
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text_medium")
- anchors
- {
- top: title.bottom
- left: title.left
- topMargin: UM.Theme.getSize("default_margin").height
- }
- renderType: Text.NativeRendering
- }
- Column
- {
- id: properties
- anchors
- {
- top: description.bottom
- left: description.left
- topMargin: UM.Theme.getSize("default_margin").height
- }
- spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
- width: childrenRect.width
-
- Label
- {
- text: catalog.i18nc("@label", "Website") + ":"
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text_medium")
- renderType: Text.NativeRendering
- }
- Label
- {
- text: catalog.i18nc("@label", "Email") + ":"
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text_medium")
- renderType: Text.NativeRendering
- }
- }
- Column
- {
- id: values
- anchors
- {
- top: description.bottom
- left: properties.right
- leftMargin: UM.Theme.getSize("default_margin").width
- right: parent.right
- rightMargin: UM.Theme.getSize("default_margin").width
- topMargin: UM.Theme.getSize("default_margin").height
- }
- spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
-
- Label
- {
- text:
- {
- if (details && details.website)
- {
- return "" + details.website + ""
- }
- return ""
- }
- width: parent.width
- elide: Text.ElideRight
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- linkColor: UM.Theme.getColor("text_link")
- onLinkActivated: UM.UrlUtil.openUrl(link, ["https", "http"])
- renderType: Text.NativeRendering
- }
-
- Label
- {
- text:
- {
- if (details && details.email)
- {
- return "" + details.email + ""
- }
- return ""
- }
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- linkColor: UM.Theme.getColor("text_link")
- onLinkActivated: Qt.openUrlExternally(link)
- renderType: Text.NativeRendering
- }
- }
- Rectangle
- {
- color: UM.Theme.getColor("lining")
- width: parent.width
- height: UM.Theme.getSize("default_lining").height
- anchors.bottom: parent.bottom
- }
- }
- ToolboxDetailList
- {
- anchors
- {
- top: header.bottom
- bottom: page.bottom
- left: header.left
- right: page.right
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/pages/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/pages/ToolboxDetailPage.qml
deleted file mode 100644
index 645b77a8c9..0000000000
--- a/plugins/Toolbox/resources/qml/pages/ToolboxDetailPage.qml
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4
-import UM 1.5 as UM
-
-import Cura 1.1 as Cura
-
-import "../components"
-
-Item
-{
- id: page
- property var details: base.selection || {}
- anchors.fill: parent
- ToolboxBackColumn
- {
- id: sidebar
- }
- Item
- {
- id: header
- anchors
- {
- left: sidebar.right
- right: parent.right
- rightMargin: UM.Theme.getSize("wide_margin").width
- }
- height: childrenRect.height + 3 * UM.Theme.getSize("default_margin").width
- Rectangle
- {
- id: thumbnail
- width: UM.Theme.getSize("toolbox_thumbnail_medium").width
- height: UM.Theme.getSize("toolbox_thumbnail_medium").height
- anchors
- {
- top: parent.top
- left: parent.left
- leftMargin: UM.Theme.getSize("wide_margin").width
- topMargin: UM.Theme.getSize("wide_margin").height
- }
- color: UM.Theme.getColor("main_background")
- Image
- {
- anchors.fill: parent
- fillMode: Image.PreserveAspectFit
- source: details === null ? "" : (details.icon_url || "../../images/placeholder.svg")
- mipmap: true
- height: UM.Theme.getSize("toolbox_thumbnail_large").height - 4 * UM.Theme.getSize("default_margin").height
- width: UM.Theme.getSize("toolbox_thumbnail_large").height - 4 * UM.Theme.getSize("default_margin").height
- sourceSize.height: height
- sourceSize.width: width
- }
- }
-
- Label
- {
- id: title
- anchors
- {
- top: thumbnail.top
- left: thumbnail.right
- leftMargin: UM.Theme.getSize("default_margin").width
- }
- text: details === null ? "" : (details.name || "")
- font: UM.Theme.getFont("large_bold")
- color: UM.Theme.getColor("text")
- width: contentWidth
- height: contentHeight
- renderType: Text.NativeRendering
- }
-
- Column
- {
- id: properties
- anchors
- {
- top: title.bottom
- left: title.left
- topMargin: UM.Theme.getSize("default_margin").height
- }
- spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
- width: childrenRect.width
- height: childrenRect.height
- Label
- {
- text: catalog.i18nc("@label", "Version") + ":"
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text_medium")
- renderType: Text.NativeRendering
- }
- Label
- {
- text: catalog.i18nc("@label", "Last updated") + ":"
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text_medium")
- renderType: Text.NativeRendering
- }
- Label
- {
- text: catalog.i18nc("@label", "Brand") + ":"
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text_medium")
- renderType: Text.NativeRendering
- }
- Label
- {
- text: catalog.i18nc("@label", "Downloads") + ":"
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text_medium")
- renderType: Text.NativeRendering
- }
- }
- Column
- {
- id: values
- anchors
- {
- top: title.bottom
- left: properties.right
- leftMargin: UM.Theme.getSize("default_margin").width
- topMargin: UM.Theme.getSize("default_margin").height
- }
- spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
- height: childrenRect.height
- Label
- {
- text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown"))
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- renderType: Text.NativeRendering
- }
- Label
- {
- text:
- {
- if (details === null)
- {
- return ""
- }
- var date = new Date(details.last_updated)
- return date.toLocaleString(UM.Preferences.getValue("general/language"))
- }
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- renderType: Text.NativeRendering
- }
- Label
- {
- text:
- {
- if (details === null)
- {
- return ""
- }
- else
- {
- return "" + details.author_name + ""
- }
- }
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- linkColor: UM.Theme.getColor("text_link")
- onLinkActivated: UM.UrlUtil.openUrl(link, ["http", "https"])
- renderType: Text.NativeRendering
- }
- Label
- {
- text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown"))
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- renderType: Text.NativeRendering
- }
- }
- }
- ToolboxDetailList
- {
- anchors
- {
- top: header.bottom
- bottom: page.bottom
- left: header.left
- right: page.right
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/pages/ToolboxDownloadsPage.qml b/plugins/Toolbox/resources/qml/pages/ToolboxDownloadsPage.qml
deleted file mode 100644
index 9be8cbe2b9..0000000000
--- a/plugins/Toolbox/resources/qml/pages/ToolboxDownloadsPage.qml
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2019 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 2.3
-import UM 1.1 as UM
-
-import "../components"
-
-ScrollView
-{
- clip: true
- width: parent.width
- height: parent.height
- contentHeight: mainColumn.height
-
- Column
- {
- id: mainColumn
- width: base.width
- spacing: UM.Theme.getSize("default_margin").height
-
- ToolboxDownloadsShowcase
- {
- id: showcase
- width: parent.width
- }
-
- ToolboxDownloadsGrid
- {
- id: allPlugins
- width: parent.width
- heading: toolbox.viewCategory === "material" ? catalog.i18nc("@label", "Community Contributions") : catalog.i18nc("@label", "Community Plugins")
- model: toolbox.viewCategory === "material" ? toolbox.materialsAvailableModel : toolbox.pluginsAvailableModel
- }
-
- ToolboxDownloadsGrid
- {
- id: genericMaterials
- visible: toolbox.viewCategory === "material"
- width: parent.width
- heading: catalog.i18nc("@label", "Generic Materials")
- model: toolbox.materialsGenericModel
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/pages/ToolboxErrorPage.qml b/plugins/Toolbox/resources/qml/pages/ToolboxErrorPage.qml
deleted file mode 100644
index e57e63dbb9..0000000000
--- a/plugins/Toolbox/resources/qml/pages/ToolboxErrorPage.qml
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4
-
-Rectangle
-{
- id: page
- width: parent.width
- height: parent.height
- color: "transparent"
- Label
- {
- text: catalog.i18nc("@info", "Could not connect to the Cura Package database. Please check your connection.")
- anchors
- {
- centerIn: parent
- }
- renderType: Text.NativeRendering
- }
-}
diff --git a/plugins/Toolbox/resources/qml/pages/ToolboxInstalledPage.qml b/plugins/Toolbox/resources/qml/pages/ToolboxInstalledPage.qml
deleted file mode 100644
index fa7bd24c9d..0000000000
--- a/plugins/Toolbox/resources/qml/pages/ToolboxInstalledPage.qml
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright (c) 2019 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 2.3
-
-import UM 1.1 as UM
-
-import "../components"
-
-ScrollView
-{
- id: page
- clip: true
- width: parent.width
- height: parent.height
-
- Column
- {
- width: page.width
- spacing: UM.Theme.getSize("default_margin").height
- padding: UM.Theme.getSize("wide_margin").width
- height: childrenRect.height + 2 * UM.Theme.getSize("wide_margin").height
-
- Label
- {
- anchors
- {
- left: parent.left
- right: parent.right
- margins: parent.padding
- }
- text: catalog.i18nc("@title:tab", "Installed plugins")
- color: UM.Theme.getColor("text_medium")
- font: UM.Theme.getFont("medium")
- renderType: Text.NativeRendering
- }
-
- Rectangle
- {
- anchors
- {
- left: parent.left
- right: parent.right
- margins: parent.padding
- }
- id: installedPlugins
- color: "transparent"
- height: childrenRect.height + UM.Theme.getSize("default_margin").width
- border.color: UM.Theme.getColor("lining")
- border.width: UM.Theme.getSize("default_lining").width
- Column
- {
- anchors
- {
- top: parent.top
- right: parent.right
- left: parent.left
- margins: UM.Theme.getSize("default_margin").width
- }
- Repeater
- {
- id: pluginList
- model: toolbox.pluginsInstalledModel
- delegate: ToolboxInstalledTile { }
- }
- }
- Label
- {
- visible: toolbox.pluginsInstalledModel.count < 1
- padding: UM.Theme.getSize("default_margin").width
- text: catalog.i18nc("@info", "No plugin has been installed.")
- font: UM.Theme.getFont("medium")
- color: UM.Theme.getColor("lining")
- renderType: Text.NativeRendering
- }
- }
-
- Label
- {
- anchors
- {
- left: parent.left
- right: parent.right
- margins: parent.padding
- }
- text: catalog.i18nc("@title:tab", "Installed materials")
- color: UM.Theme.getColor("text_medium")
- font: UM.Theme.getFont("medium")
- renderType: Text.NativeRendering
- }
-
- Rectangle
- {
- anchors
- {
- left: parent.left
- right: parent.right
- margins: parent.padding
- }
- id: installedMaterials
- color: "transparent"
- height: childrenRect.height + UM.Theme.getSize("default_margin").width
- border.color: UM.Theme.getColor("lining")
- border.width: UM.Theme.getSize("default_lining").width
- Column
- {
- anchors
- {
- top: parent.top
- right: parent.right
- left: parent.left
- margins: UM.Theme.getSize("default_margin").width
- }
- Repeater
- {
- id: installedMaterialsList
- model: toolbox.materialsInstalledModel
- delegate: ToolboxInstalledTile { }
- }
- }
- Label
- {
- visible: toolbox.materialsInstalledModel.count < 1
- padding: UM.Theme.getSize("default_margin").width
- text: catalog.i18nc("@info", "No material has been installed.")
- color: UM.Theme.getColor("lining")
- font: UM.Theme.getFont("medium")
- renderType: Text.NativeRendering
- }
- }
-
- Label
- {
- anchors
- {
- left: parent.left
- right: parent.right
- margins: parent.padding
- }
- text: catalog.i18nc("@title:tab", "Bundled plugins")
- color: UM.Theme.getColor("text_medium")
- font: UM.Theme.getFont("medium")
- renderType: Text.NativeRendering
- }
-
- Rectangle
- {
- anchors
- {
- left: parent.left
- right: parent.right
- margins: parent.padding
- }
- id: bundledPlugins
- color: "transparent"
- height: childrenRect.height + UM.Theme.getSize("default_margin").width
- border.color: UM.Theme.getColor("lining")
- border.width: UM.Theme.getSize("default_lining").width
- Column
- {
- anchors
- {
- top: parent.top
- right: parent.right
- left: parent.left
- margins: UM.Theme.getSize("default_margin").width
- }
- Repeater
- {
- id: bundledPluginsList
- model: toolbox.pluginsBundledModel
- delegate: ToolboxInstalledTile { }
- }
- }
- }
-
- Label
- {
- anchors
- {
- left: parent.left
- right: parent.right
- margins: parent.padding
- }
- text: catalog.i18nc("@title:tab", "Bundled materials")
- color: UM.Theme.getColor("text_medium")
- font: UM.Theme.getFont("medium")
- renderType: Text.NativeRendering
- }
-
- Rectangle
- {
- anchors
- {
- left: parent.left
- right: parent.right
- margins: parent.padding
- }
- id: bundledMaterials
- color: "transparent"
- height: childrenRect.height + UM.Theme.getSize("default_margin").width
- border.color: UM.Theme.getColor("lining")
- border.width: UM.Theme.getSize("default_lining").width
- Column
- {
- anchors
- {
- top: parent.top
- right: parent.right
- left: parent.left
- margins: UM.Theme.getSize("default_margin").width
- }
- Repeater
- {
- id: bundledMaterialsList
- model: toolbox.materialsBundledModel
- delegate: ToolboxInstalledTile {}
- }
- }
- }
- }
-}
diff --git a/plugins/Toolbox/resources/qml/pages/ToolboxLoadingPage.qml b/plugins/Toolbox/resources/qml/pages/ToolboxLoadingPage.qml
deleted file mode 100644
index a30af6b335..0000000000
--- a/plugins/Toolbox/resources/qml/pages/ToolboxLoadingPage.qml
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.10
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4
-import UM 1.3 as UM
-
-Rectangle
-{
- id: page
- width: parent.width
- height: parent.height
- color: "transparent"
- Label
- {
- text: catalog.i18nc("@info", "Fetching packages...")
- color: UM.Theme.getColor("text")
- anchors
- {
- centerIn: parent
- }
- renderType: Text.NativeRendering
- }
-}
diff --git a/plugins/Toolbox/resources/qml/pages/WelcomePage.qml b/plugins/Toolbox/resources/qml/pages/WelcomePage.qml
deleted file mode 100644
index 04110cbc0f..0000000000
--- a/plugins/Toolbox/resources/qml/pages/WelcomePage.qml
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Cura is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.7
-import QtQuick.Controls 2.1
-import QtQuick.Window 2.2
-
-import UM 1.3 as UM
-import Cura 1.1 as Cura
-
-Column
-{
- id: welcomePage
- spacing: UM.Theme.getSize("wide_margin").height
- width: parent.width
- height: childrenRect.height
- anchors.centerIn: parent
-
- Label
- {
- id: welcomeTextLabel
- text: catalog.i18nc("@description", "Please sign in to get verified plugins and materials for Ultimaker Cura Enterprise")
- width: Math.round(parent.width / 2)
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("text")
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignHCenter
- anchors.horizontalCenter: parent.horizontalCenter
- wrapMode: Label.WordWrap
- renderType: Text.NativeRendering
- }
-
- Cura.PrimaryButton
- {
- id: loginButton
- width: UM.Theme.getSize("account_button").width
- height: UM.Theme.getSize("account_button").height
- anchors.horizontalCenter: parent.horizontalCenter
- text: catalog.i18nc("@button", "Sign in")
- onClicked: Cura.API.account.login()
- fixedWidthMode: true
- }
-}
-
diff --git a/plugins/Toolbox/src/AuthorsModel.py b/plugins/Toolbox/src/AuthorsModel.py
deleted file mode 100644
index 04c8ed3a40..0000000000
--- a/plugins/Toolbox/src/AuthorsModel.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# Copyright (c) 2018 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-import re
-from typing import Dict, List, Optional, Union, cast
-
-from PyQt5.QtCore import Qt, pyqtProperty
-
-from UM.Qt.ListModel import ListModel
-
-
-class AuthorsModel(ListModel):
- """Model that holds cura packages.
-
- By setting the filter property the instances held by this model can be changed.
- """
-
- def __init__(self, parent = None) -> None:
- super().__init__(parent)
-
- self._metadata = None # type: Optional[List[Dict[str, Union[str, List[str], int]]]]
-
- self.addRoleName(Qt.UserRole + 1, "id")
- self.addRoleName(Qt.UserRole + 2, "name")
- self.addRoleName(Qt.UserRole + 3, "email")
- self.addRoleName(Qt.UserRole + 4, "website")
- self.addRoleName(Qt.UserRole + 5, "package_count")
- self.addRoleName(Qt.UserRole + 6, "package_types")
- self.addRoleName(Qt.UserRole + 7, "icon_url")
- self.addRoleName(Qt.UserRole + 8, "description")
-
- # 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: List[Dict[str, Union[str, List[str], int]]]):
- if self._metadata != data:
- self._metadata = data
- self._update()
-
- def _update(self) -> None:
- items = [] # type: List[Dict[str, Union[str, List[str], int, None]]]
- if not self._metadata:
- self.setItems(items)
- return
-
- for author in self._metadata:
- items.append({
- "id": author.get("author_id"),
- "name": author.get("display_name"),
- "email": author.get("email"),
- "website": author.get("website"),
- "package_count": author.get("package_count", 0),
- "package_types": author.get("package_types", []),
- "icon_url": author.get("icon_url"),
- "description": "Material and quality profiles from {author_name}".format(author_name = author.get("display_name", ""))
- })
-
- # Filter on all the key-word arguments.
- for key, value in self._filter.items():
- if key == "package_types":
- key_filter = lambda item, value = value: value in item["package_types"] # type: ignore
- elif "*" in value:
- key_filter = lambda item, key = key, value = value: self._matchRegExp(item, key, value) # type: ignore
- else:
- key_filter = lambda item, key = key, value = value: self._matchString(item, key, value) # type: ignore
- items = filter(key_filter, items) # type: ignore
-
- # Execute all filters.
- filtered_items = list(items)
-
- filtered_items.sort(key = lambda k: cast(str, k["name"]))
- self.setItems(filtered_items)
-
- def setFilter(self, filter_dict: Dict[str, str]) -> None:
- """Set the filter of this model based on a string.
-
- :param filter_dict: Dictionary to do the filtering by.
- """
- 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/CloudApiModel.py b/plugins/Toolbox/src/CloudApiModel.py
deleted file mode 100644
index bef37d8173..0000000000
--- a/plugins/Toolbox/src/CloudApiModel.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from typing import Union
-
-from cura import ApplicationMetadata
-from cura.UltimakerCloud import UltimakerCloudConstants
-
-
-class CloudApiModel:
- sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int]
- cloud_api_version = UltimakerCloudConstants.CuraCloudAPIVersion # type: str
- cloud_api_root = UltimakerCloudConstants.CuraCloudAPIRoot # type: str
- api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format(
- cloud_api_root = cloud_api_root,
- cloud_api_version = cloud_api_version,
- sdk_version = sdk_version
- ) # type: str
-
- # https://api.ultimaker.com/cura-packages/v1/user/packages
- api_url_user_packages = "{cloud_api_root}/cura-packages/v{cloud_api_version}/user/packages".format(
- cloud_api_root=cloud_api_root,
- cloud_api_version=cloud_api_version,
- )
-
- @classmethod
- def userPackageUrl(cls, package_id: str) -> str:
- """https://api.ultimaker.com/cura-packages/v1/user/packages/{package_id}"""
-
- return (CloudApiModel.api_url_user_packages + "/{package_id}").format(
- package_id=package_id
- )
diff --git a/plugins/Toolbox/src/CloudSync/CloudApiClient.py b/plugins/Toolbox/src/CloudSync/CloudApiClient.py
deleted file mode 100644
index 9543ec012e..0000000000
--- a/plugins/Toolbox/src/CloudSync/CloudApiClient.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from UM.Logger import Logger
-from UM.TaskManagement.HttpRequestManager import HttpRequestManager
-from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
-from cura.CuraApplication import CuraApplication
-from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
-from ..CloudApiModel import CloudApiModel
-
-
-class CloudApiClient:
- """Manages Cloud subscriptions
-
- When a package is added to a user's account, the user is 'subscribed' to that package.
- Whenever the user logs in on another instance of Cura, these subscriptions can be used to sync the user's plugins
-
- Singleton: use CloudApiClient.getInstance() instead of CloudApiClient()
- """
-
- __instance = None
-
- @classmethod
- def getInstance(cls, app: CuraApplication):
- if not cls.__instance:
- cls.__instance = CloudApiClient(app)
- return cls.__instance
-
- def __init__(self, app: CuraApplication) -> None:
- if self.__instance is not None:
- raise RuntimeError("This is a Singleton. use getInstance()")
-
- self._scope = JsonDecoratorScope(UltimakerCloudScope(app)) # type: JsonDecoratorScope
-
- app.getPackageManager().packageInstalled.connect(self._onPackageInstalled)
-
- def unsubscribe(self, package_id: str) -> None:
- url = CloudApiModel.userPackageUrl(package_id)
- HttpRequestManager.getInstance().delete(url = url, scope = self._scope)
-
- def _subscribe(self, package_id: str) -> None:
- """You probably don't want to use this directly. All installed packages will be automatically subscribed."""
-
- Logger.debug("Subscribing to using the Old Toolbox {}", package_id)
- data = "{\"data\": {\"package_id\": \"%s\", \"sdk_version\": \"%s\"}}" % (package_id, CloudApiModel.sdk_version)
- HttpRequestManager.getInstance().put(
- url = CloudApiModel.api_url_user_packages,
- data = data.encode(),
- scope = self._scope
- )
-
- def _onPackageInstalled(self, package_id: str):
- if CuraApplication.getInstance().getCuraAPI().account.isLoggedIn:
- # We might already be subscribed, but checking would take one extra request. Instead, simply subscribe
- self._subscribe(package_id)
diff --git a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py
deleted file mode 100644
index 6d2ed1dcbd..0000000000
--- a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# Copyright (c) 2020 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-import json
-from typing import List, Dict, Any, Set
-from typing import Optional
-
-from PyQt5.QtCore import QObject
-from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
-
-from UM import i18nCatalog
-from UM.Logger import Logger
-from UM.Message import Message
-from UM.Signal import Signal
-from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
-from cura.API.Account import SyncState
-from cura.CuraApplication import CuraApplication, ApplicationMetadata
-from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
-from .SubscribedPackagesModel import SubscribedPackagesModel
-from ..CloudApiModel import CloudApiModel
-
-
-class CloudPackageChecker(QObject):
-
- SYNC_SERVICE_NAME = "CloudPackageChecker"
-
- def __init__(self, application: CuraApplication) -> None:
- super().__init__()
-
- self.discrepancies = Signal() # Emits SubscribedPackagesModel
- self._application = application # type: CuraApplication
- self._scope = JsonDecoratorScope(UltimakerCloudScope(application))
- self._model = SubscribedPackagesModel()
- self._message = None # type: Optional[Message]
-
- self._application.initializationFinished.connect(self._onAppInitialized)
- self._i18n_catalog = i18nCatalog("cura")
- self._sdk_version = ApplicationMetadata.CuraSDKVersion
- self._last_notified_packages = set() # type: Set[str]
- """Packages for which a notification has been shown. No need to bother the user twice for equal content"""
-
- # This is a plugin, so most of the components required are not ready when
- # this is initialized. Therefore, we wait until the application is ready.
- def _onAppInitialized(self) -> None:
- self._package_manager = self._application.getPackageManager()
- # initial check
- self._getPackagesIfLoggedIn()
-
- self._application.getCuraAPI().account.loginStateChanged.connect(self._onLoginStateChanged)
- self._application.getCuraAPI().account.syncRequested.connect(self._getPackagesIfLoggedIn)
-
- def _onLoginStateChanged(self) -> None:
- # reset session
- self._last_notified_packages = set()
- self._getPackagesIfLoggedIn()
-
- def _getPackagesIfLoggedIn(self) -> None:
- if self._application.getCuraAPI().account.isLoggedIn:
- self._getUserSubscribedPackages()
- else:
- self._hideSyncMessage()
-
- def _getUserSubscribedPackages(self) -> None:
- self._application.getCuraAPI().account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SYNCING)
- url = CloudApiModel.api_url_user_packages
- self._application.getHttpRequestManager().get(url,
- callback = self._onUserPackagesRequestFinished,
- error_callback = self._onUserPackagesRequestFinished,
- timeout = 10,
- scope = self._scope)
-
- def _onUserPackagesRequestFinished(self, reply: "QNetworkReply", error: Optional["QNetworkReply.NetworkError"] = None) -> None:
- if error is not None or reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
- Logger.log("w",
- "Requesting user packages failed, response code %s while trying to connect to %s",
- reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url())
- self._application.getCuraAPI().account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR)
- return
-
- try:
- json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
- # Check for errors:
- if "errors" in json_data:
- for error in json_data["errors"]:
- Logger.log("e", "%s", error["title"])
- self._application.getCuraAPI().account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR)
- return
- self._handleCompatibilityData(json_data["data"])
- except json.decoder.JSONDecodeError:
- Logger.log("w", "Received invalid JSON for user subscribed packages from the Web Marketplace")
-
- self._application.getCuraAPI().account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SUCCESS)
-
- def _handleCompatibilityData(self, subscribed_packages_payload: List[Dict[str, Any]]) -> None:
- user_subscribed_packages = {plugin["package_id"] for plugin in subscribed_packages_payload}
- user_installed_packages = self._package_manager.getAllInstalledPackageIDs()
-
- # We need to re-evaluate the dismissed packages
- # (i.e. some package might got updated to the correct SDK version in the meantime,
- # hence remove them from the Dismissed Incompatible list)
- self._package_manager.reEvaluateDismissedPackages(subscribed_packages_payload, self._sdk_version)
- user_dismissed_packages = self._package_manager.getDismissedPackages()
- if user_dismissed_packages:
- user_installed_packages.update(user_dismissed_packages)
-
- # We check if there are packages installed in Web Marketplace but not in Cura marketplace
- package_discrepancy = list(user_subscribed_packages.difference(user_installed_packages))
-
- if user_subscribed_packages != self._last_notified_packages:
- # scenario:
- # 1. user subscribes to a package
- # 2. dismisses the license/unsubscribes
- # 3. subscribes to the same package again
- # in this scenario we want to notify the user again. To capture that there was a change during
- # step 2, we clear the last_notified after step 2. This way, the user will be notified after
- # step 3 even though the list of packages for step 1 and 3 are equal
- self._last_notified_packages = set()
-
- if package_discrepancy:
- account = self._application.getCuraAPI().account
- account.setUpdatePackagesAction(lambda: self._onSyncButtonClicked(None, None))
-
- if user_subscribed_packages == self._last_notified_packages:
- # already notified user about these
- return
-
- Logger.log("d", "Discrepancy found between Cloud subscribed packages and Cura installed packages")
- self._model.addDiscrepancies(package_discrepancy)
- self._model.initialize(self._package_manager, subscribed_packages_payload)
- self._showSyncMessage()
- self._last_notified_packages = user_subscribed_packages
-
- def _showSyncMessage(self) -> None:
- """Show the message if it is not already shown"""
-
- if self._message is not None:
- self._message.show()
- return
-
- sync_message = Message(self._i18n_catalog.i18nc(
- "@info:generic",
- "Do you want to sync material and software packages with your account?"),
- title = self._i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", ))
- sync_message.addAction("sync",
- name = self._i18n_catalog.i18nc("@action:button", "Sync"),
- icon = "",
- description = "Sync your plugins and print profiles to Ultimaker Cura.",
- button_align = Message.ActionButtonAlignment.ALIGN_RIGHT)
- sync_message.actionTriggered.connect(self._onSyncButtonClicked)
- sync_message.show()
- self._message = sync_message
-
- def _hideSyncMessage(self) -> None:
- """Hide the message if it is showing"""
-
- if self._message is not None:
- self._message.hide()
- self._message = None
-
- def _onSyncButtonClicked(self, sync_message: Optional[Message], sync_message_action: Optional[str]) -> None:
- if sync_message is not None:
- sync_message.hide()
- self._hideSyncMessage() # Should be the same message, but also sets _message to None
- self.discrepancies.emit(self._model)
diff --git a/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py b/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py
deleted file mode 100644
index cee2f6318a..0000000000
--- a/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import os
-from typing import Optional
-
-from PyQt5.QtCore import QObject, pyqtSlot
-
-from UM.Qt.QtApplication import QtApplication
-from UM.Signal import Signal
-from .SubscribedPackagesModel import SubscribedPackagesModel
-
-
-class DiscrepanciesPresenter(QObject):
- """Shows a list of packages to be added or removed. The user can select which packages to (un)install. The user's
-
- choices are emitted on the `packageMutations` Signal.
- """
-
- def __init__(self, app: QtApplication) -> None:
- super().__init__(app)
-
- self.packageMutations = Signal() # Emits SubscribedPackagesModel
-
- self._app = app
- self._package_manager = app.getPackageManager()
- self._dialog = None # type: Optional[QObject]
- self._compatibility_dialog_path = "resources/qml/dialogs/CompatibilityDialog.qml"
-
- def present(self, plugin_path: str, model: SubscribedPackagesModel) -> None:
- path = os.path.join(plugin_path, self._compatibility_dialog_path)
- self._dialog = self._app.createQmlComponent(path, {"subscribedPackagesModel": model, "handler": self})
- assert self._dialog
- self._dialog.accepted.connect(lambda: self._onConfirmClicked(model))
-
- def _onConfirmClicked(self, model: SubscribedPackagesModel) -> None:
- # If there are incompatible packages - automatically dismiss them
- if model.getIncompatiblePackages():
- self._package_manager.dismissAllIncompatiblePackages(model.getIncompatiblePackages())
- # For now, all compatible packages presented to the user should be installed.
- # Later, we might remove items for which the user unselected the package
- if model.getCompatiblePackages():
- model.setItems(model.getCompatiblePackages())
- self.packageMutations.emit(model)
diff --git a/plugins/Toolbox/src/CloudSync/DownloadPresenter.py b/plugins/Toolbox/src/CloudSync/DownloadPresenter.py
deleted file mode 100644
index 8a5e763f3c..0000000000
--- a/plugins/Toolbox/src/CloudSync/DownloadPresenter.py
+++ /dev/null
@@ -1,153 +0,0 @@
-# Copyright (c) 2020 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-import tempfile
-from typing import Dict, List, Any
-
-from PyQt5.QtNetwork import QNetworkReply
-
-from UM.i18n import i18nCatalog
-from UM.Logger import Logger
-from UM.Message import Message
-from UM.Signal import Signal
-from UM.TaskManagement.HttpRequestManager import HttpRequestManager
-from cura.CuraApplication import CuraApplication
-from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
-from .SubscribedPackagesModel import SubscribedPackagesModel
-
-i18n_catalog = i18nCatalog("cura")
-
-
-class DownloadPresenter:
- """Downloads a set of packages from the Ultimaker Cloud Marketplace
-
- use download() exactly once: should not be used for multiple sets of downloads since this class contains state
- """
-
- DISK_WRITE_BUFFER_SIZE = 256 * 1024 # 256 KB
-
- def __init__(self, app: CuraApplication) -> None:
- # Emits (Dict[str, str], List[str]) # (success_items, error_items)
- # Dict{success_package_id, temp_file_path}
- # List[errored_package_id]
- self.done = Signal()
-
- self._app = app
- self._scope = UltimakerCloudScope(app)
-
- self._started = False
- self._progress_message = self._createProgressMessage()
- self._progress = {} # type: Dict[str, Dict[str, Any]] # package_id, Dict
- self._error = [] # type: List[str] # package_id
-
- def download(self, model: SubscribedPackagesModel) -> None:
- if self._started:
- Logger.error("Download already started. Create a new %s instead", self.__class__.__name__)
- return
-
- manager = HttpRequestManager.getInstance()
- for item in model.items:
- package_id = item["package_id"]
-
- def finishedCallback(reply: QNetworkReply, pid = package_id) -> None:
- self._onFinished(pid, reply)
-
- def progressCallback(rx: int, rt: int, pid = package_id) -> None:
- self._onProgress(pid, rx, rt)
-
- def errorCallback(reply: QNetworkReply, error: QNetworkReply.NetworkError, pid = package_id) -> None:
- self._onError(pid)
-
- request_data = manager.get(
- item["download_url"],
- callback = finishedCallback,
- download_progress_callback = progressCallback,
- error_callback = errorCallback,
- scope = self._scope)
-
- self._progress[package_id] = {
- "received": 0,
- "total": 1, # make sure this is not considered done yet. Also divByZero-safe
- "file_written": None,
- "request_data": request_data,
- "package_model": item
- }
-
- self._started = True
- self._progress_message.show()
-
- def abort(self) -> None:
- manager = HttpRequestManager.getInstance()
- for item in self._progress.values():
- manager.abortRequest(item["request_data"])
-
- # Aborts all current operations and returns a copy with the same settings such as app and scope
- def resetCopy(self) -> "DownloadPresenter":
- self.abort()
- self.done.disconnectAll()
- return DownloadPresenter(self._app)
-
- def _createProgressMessage(self) -> Message:
- return Message(i18n_catalog.i18nc("@info:generic", "Syncing..."),
- lifetime = 0,
- use_inactivity_timer = False,
- progress = 0.0,
- title = i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account"))
-
- def _onFinished(self, package_id: str, reply: QNetworkReply) -> None:
- self._progress[package_id]["received"] = self._progress[package_id]["total"]
-
- try:
- with tempfile.NamedTemporaryFile(mode = "wb+", suffix = ".curapackage", delete = False) as temp_file:
- bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE)
- while bytes_read:
- temp_file.write(bytes_read)
- bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE)
- self._app.processEvents()
- self._progress[package_id]["file_written"] = temp_file.name
- except IOError as e:
- Logger.logException("e", "Failed to write downloaded package to temp file", e)
- self._onError(package_id)
- temp_file.close()
-
- self._checkDone()
-
- def _onProgress(self, package_id: str, rx: int, rt: int) -> None:
- self._progress[package_id]["received"] = rx
- self._progress[package_id]["total"] = rt
-
- received = 0
- total = 0
- for item in self._progress.values():
- received += item["received"]
- total += item["total"]
-
- if total == 0: # Total download size is 0, or unknown, or there are no progress items at all.
- self._progress_message.setProgress(100.0)
- return
-
- self._progress_message.setProgress(100.0 * (received / total)) # [0 .. 100] %
-
- def _onError(self, package_id: str) -> None:
- self._progress.pop(package_id)
- self._error.append(package_id)
- self._checkDone()
-
- def _checkDone(self) -> bool:
- for item in self._progress.values():
- if not item["file_written"]:
- return False
-
- success_items = {
- package_id:
- {
- "package_path": value["file_written"],
- "icon_url": value["package_model"]["icon_url"]
- }
- for package_id, value in self._progress.items()
- }
- error_items = [package_id for package_id in self._error]
-
- self._progress_message.hide()
- self.done.emit(success_items, error_items)
- return True
diff --git a/plugins/Toolbox/src/CloudSync/LicenseModel.py b/plugins/Toolbox/src/CloudSync/LicenseModel.py
deleted file mode 100644
index 335a91ef84..0000000000
--- a/plugins/Toolbox/src/CloudSync/LicenseModel.py
+++ /dev/null
@@ -1,77 +0,0 @@
-from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
-from UM.i18n import i18nCatalog
-
-catalog = i18nCatalog("cura")
-
-
-# Model for the ToolboxLicenseDialog
-class LicenseModel(QObject):
- DEFAULT_DECLINE_BUTTON_TEXT = catalog.i18nc("@button", "Decline")
- ACCEPT_BUTTON_TEXT = catalog.i18nc("@button", "Agree")
-
- dialogTitleChanged = pyqtSignal()
- packageNameChanged = pyqtSignal()
- licenseTextChanged = pyqtSignal()
- iconChanged = pyqtSignal()
-
- def __init__(self, decline_button_text: str = DEFAULT_DECLINE_BUTTON_TEXT) -> None:
- super().__init__()
-
- self._current_page_idx = 0
- self._page_count = 1
- self._dialogTitle = ""
- self._license_text = ""
- self._package_name = ""
- self._icon_url = ""
- self._decline_button_text = decline_button_text
-
- @pyqtProperty(str, constant = True)
- def acceptButtonText(self):
- return self.ACCEPT_BUTTON_TEXT
-
- @pyqtProperty(str, constant = True)
- def declineButtonText(self):
- return self._decline_button_text
-
- @pyqtProperty(str, notify=dialogTitleChanged)
- def dialogTitle(self) -> str:
- return self._dialogTitle
-
- @pyqtProperty(str, notify=packageNameChanged)
- def packageName(self) -> str:
- return self._package_name
-
- def setPackageName(self, name: str) -> None:
- self._package_name = name
- self.packageNameChanged.emit()
-
- @pyqtProperty(str, notify=iconChanged)
- def iconUrl(self) -> str:
- return self._icon_url
-
- def setIconUrl(self, url: str):
- self._icon_url = url
- self.iconChanged.emit()
-
- @pyqtProperty(str, notify=licenseTextChanged)
- def licenseText(self) -> str:
- return self._license_text
-
- def setLicenseText(self, license_text: str) -> None:
- if self._license_text != license_text:
- self._license_text = license_text
- self.licenseTextChanged.emit()
-
- def setCurrentPageIdx(self, idx: int) -> None:
- self._current_page_idx = idx
- self._updateDialogTitle()
-
- def setPageCount(self, count: int) -> None:
- self._page_count = count
- self._updateDialogTitle()
-
- def _updateDialogTitle(self):
- self._dialogTitle = catalog.i18nc("@title:window", "Plugin License Agreement")
- if self._page_count > 1:
- self._dialogTitle = self._dialogTitle + " ({}/{})".format(self._current_page_idx + 1, self._page_count)
- self.dialogTitleChanged.emit()
diff --git a/plugins/Toolbox/src/CloudSync/LicensePresenter.py b/plugins/Toolbox/src/CloudSync/LicensePresenter.py
deleted file mode 100644
index 39ce11c8d3..0000000000
--- a/plugins/Toolbox/src/CloudSync/LicensePresenter.py
+++ /dev/null
@@ -1,142 +0,0 @@
-# Copyright (c) 2021 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-import os
-from collections import OrderedDict
-from typing import Dict, Optional, List, Any
-
-from PyQt5.QtCore import QObject, pyqtSlot
-
-from UM.Logger import Logger
-from UM.PackageManager import PackageManager
-from UM.Signal import Signal
-from cura.CuraApplication import CuraApplication
-from UM.i18n import i18nCatalog
-
-from .LicenseModel import LicenseModel
-
-
-class LicensePresenter(QObject):
- """Presents licenses for a set of packages for the user to accept or reject.
-
- Call present() exactly once to show a licenseDialog for a set of packages
- Before presenting another set of licenses, create a new instance using resetCopy().
-
- licenseAnswers emits a list of Dicts containing answers when the user has made a choice for all provided packages.
- """
-
- def __init__(self, app: CuraApplication) -> None:
- super().__init__()
- self._presented = False
- """Whether present() has been called and state is expected to be initialized"""
- self._catalog = i18nCatalog("cura")
- self._dialog = None # type: Optional[QObject]
- self._package_manager = app.getPackageManager() # type: PackageManager
- # Emits List[Dict[str, [Any]] containing for example
- # [{ "package_id": "BarbarianPlugin", "package_path" : "/tmp/dg345as", "accepted" : True }]
- self.licenseAnswers = Signal()
-
- self._current_package_idx = 0
- self._package_models = [] # type: List[Dict]
- decline_button_text = self._catalog.i18nc("@button", "Decline and remove from account")
- self._license_model = LicenseModel(decline_button_text=decline_button_text) # type: LicenseModel
- self._page_count = 0
-
- self._app = app
-
- self._compatibility_dialog_path = "resources/qml/dialogs/ToolboxLicenseDialog.qml"
-
- def present(self, plugin_path: str, packages: Dict[str, Dict[str, str]]) -> None:
- """Show a license dialog for multiple packages where users can read a license and accept or decline them
-
- :param plugin_path: Root directory of the Toolbox plugin
- :param packages: Dict[package id, file path]
- """
- if self._presented:
- Logger.error("{clazz} is single-use. Create a new {clazz} instead", clazz=self.__class__.__name__)
- return
-
- path = os.path.join(plugin_path, self._compatibility_dialog_path)
-
- self._initState(packages)
-
- if self._page_count == 0:
- self.licenseAnswers.emit(self._package_models)
- return
-
- if self._dialog is None:
-
- context_properties = {
- "catalog": self._catalog,
- "licenseModel": self._license_model,
- "handler": self
- }
- self._dialog = self._app.createQmlComponent(path, context_properties)
- self._presentCurrentPackage()
- self._presented = True
-
- def resetCopy(self) -> "LicensePresenter":
- """Clean up and return a new copy with the same settings such as app"""
- if self._dialog:
- self._dialog.close()
- self.licenseAnswers.disconnectAll()
- return LicensePresenter(self._app)
-
- @pyqtSlot()
- def onLicenseAccepted(self) -> None:
- self._package_models[self._current_package_idx]["accepted"] = True
- self._checkNextPage()
-
- @pyqtSlot()
- def onLicenseDeclined(self) -> None:
- self._package_models[self._current_package_idx]["accepted"] = False
- self._checkNextPage()
-
- def _initState(self, packages: Dict[str, Dict[str, Any]]) -> None:
-
- implicitly_accepted_count = 0
-
- for package_id, item in packages.items():
- item["package_id"] = package_id
- try:
- item["licence_content"] = self._package_manager.getPackageLicense(item["package_path"])
- except EnvironmentError as e:
- Logger.error(f"Could not open downloaded package {package_id} to read license file! {type(e)} - {e}")
- continue # Skip this package.
- if item["licence_content"] is None:
- # Implicitly accept when there is no license
- item["accepted"] = True
- implicitly_accepted_count = implicitly_accepted_count + 1
- self._package_models.append(item)
- else:
- item["accepted"] = None #: None: no answer yet
- # When presenting the packages, we want to show packages which have a license first.
- # In fact, we don't want to show the others at all because they are implicitly accepted
- self._package_models.insert(0, item)
- CuraApplication.getInstance().processEvents()
- self._page_count = len(self._package_models) - implicitly_accepted_count
- self._license_model.setPageCount(self._page_count)
-
-
- def _presentCurrentPackage(self) -> None:
- package_model = self._package_models[self._current_package_idx]
- package_info = self._package_manager.getPackageInfo(package_model["package_path"])
-
- self._license_model.setCurrentPageIdx(self._current_package_idx)
- self._license_model.setPackageName(package_info["display_name"])
- self._license_model.setIconUrl(package_model["icon_url"])
- self._license_model.setLicenseText(package_model["licence_content"])
- if self._dialog:
- self._dialog.open() # Does nothing if already open
-
- def _checkNextPage(self) -> None:
- if self._current_package_idx + 1 < self._page_count:
- self._current_package_idx += 1
- self._presentCurrentPackage()
- else:
- if self._dialog:
- self._dialog.close()
- self.licenseAnswers.emit(self._package_models)
-
-
-
diff --git a/plugins/Toolbox/src/CloudSync/RestartApplicationPresenter.py b/plugins/Toolbox/src/CloudSync/RestartApplicationPresenter.py
deleted file mode 100644
index 8776d1782a..0000000000
--- a/plugins/Toolbox/src/CloudSync/RestartApplicationPresenter.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from UM import i18nCatalog
-from UM.Message import Message
-from cura.CuraApplication import CuraApplication
-
-
-class RestartApplicationPresenter:
- """Presents a dialog telling the user that a restart is required to apply changes
-
- Since we cannot restart Cura, the app is closed instead when the button is clicked
- """
- def __init__(self, app: CuraApplication) -> None:
- self._app = app
- self._i18n_catalog = i18nCatalog("cura")
-
- def present(self) -> None:
- app_name = self._app.getApplicationDisplayName()
-
- message = Message(self._i18n_catalog.i18nc("@info:generic",
- "You need to quit and restart {} before changes have effect.",
- app_name))
-
- message.addAction("quit",
- name="Quit " + app_name,
- icon = "",
- description="Close the application",
- button_align=Message.ActionButtonAlignment.ALIGN_RIGHT)
-
- message.actionTriggered.connect(self._quitClicked)
- message.show()
-
- def _quitClicked(self, *_):
- self._app.windowClosed()
diff --git a/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py b/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py
deleted file mode 100644
index db16c5ea84..0000000000
--- a/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright (c) 2020 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-from PyQt5.QtCore import Qt, pyqtProperty, pyqtSlot
-
-from UM.PackageManager import PackageManager
-from UM.Qt.ListModel import ListModel
-from UM.Version import Version
-
-from cura import ApplicationMetadata
-from typing import List, Dict, Any
-
-
-class SubscribedPackagesModel(ListModel):
- def __init__(self, parent = None):
- super().__init__(parent)
-
- self._items = []
- self._metadata = None
- self._discrepancies = None
- self._sdk_version = ApplicationMetadata.CuraSDKVersion
-
- self.addRoleName(Qt.UserRole + 1, "package_id")
- self.addRoleName(Qt.UserRole + 2, "display_name")
- self.addRoleName(Qt.UserRole + 3, "icon_url")
- self.addRoleName(Qt.UserRole + 4, "is_compatible")
- self.addRoleName(Qt.UserRole + 5, "is_dismissed")
-
- @pyqtProperty(bool, constant=True)
- def hasCompatiblePackages(self) -> bool:
- for item in self._items:
- if item['is_compatible']:
- return True
- return False
-
- @pyqtProperty(bool, constant=True)
- def hasIncompatiblePackages(self) -> bool:
- for item in self._items:
- if not item['is_compatible']:
- return True
- return False
-
- def addDiscrepancies(self, discrepancy: List[str]) -> None:
- self._discrepancies = discrepancy
-
- def getCompatiblePackages(self) -> List[Dict[str, Any]]:
- return [package for package in self._items if package["is_compatible"]]
-
- def getIncompatiblePackages(self) -> List[str]:
- return [package["package_id"] for package in self._items if not package["is_compatible"]]
-
- def initialize(self, package_manager: PackageManager, subscribed_packages_payload: List[Dict[str, Any]]) -> None:
- self._items.clear()
- for item in subscribed_packages_payload:
- if item["package_id"] not in self._discrepancies:
- continue
- package = {
- "package_id": item["package_id"],
- "display_name": item["display_name"],
- "sdk_versions": item["sdk_versions"],
- "download_url": item["download_url"],
- "md5_hash": item["md5_hash"],
- "is_dismissed": False,
- }
-
- compatible = any(package_manager.isPackageCompatible(Version(version)) for version in item["sdk_versions"])
- package.update({"is_compatible": compatible})
-
- 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._items.append(package)
- self.setItems(self._items)
diff --git a/plugins/Toolbox/src/CloudSync/SyncOrchestrator.py b/plugins/Toolbox/src/CloudSync/SyncOrchestrator.py
deleted file mode 100644
index bb37c6d4a9..0000000000
--- a/plugins/Toolbox/src/CloudSync/SyncOrchestrator.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# Copyright (c) 2021 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-import os
-from typing import List, Dict, Any, cast
-
-from UM import i18n_catalog
-from UM.Extension import Extension
-from UM.Logger import Logger
-from UM.Message import Message
-from UM.PluginRegistry import PluginRegistry
-from cura.CuraApplication import CuraApplication
-from .CloudPackageChecker import CloudPackageChecker
-from .CloudApiClient import CloudApiClient
-from .DiscrepanciesPresenter import DiscrepanciesPresenter
-from .DownloadPresenter import DownloadPresenter
-from .LicensePresenter import LicensePresenter
-from .RestartApplicationPresenter import RestartApplicationPresenter
-from .SubscribedPackagesModel import SubscribedPackagesModel
-
-
-class SyncOrchestrator(Extension):
- """Orchestrates the synchronizing of packages from the user account to the installed packages
-
- Example flow:
-
- - CloudPackageChecker compares a list of packages the user `subscribed` to in their account
- If there are `discrepancies` between the account and locally installed packages, they are emitted
- - DiscrepanciesPresenter shows a list of packages to be added or removed to the user. It emits the `packageMutations`
- the user selected to be performed
- - The SyncOrchestrator uses PackageManager to remove local packages the users wants to see removed
- - The DownloadPresenter shows a download progress dialog. It emits A tuple of succeeded and failed downloads
- - The LicensePresenter extracts licenses from the downloaded packages and presents a license for each package to
- be installed. It emits the `licenseAnswers` signal for accept or declines
- - The CloudApiClient removes the declined packages from the account
- - The SyncOrchestrator uses PackageManager to install the downloaded packages and delete temp files.
- - The RestartApplicationPresenter notifies the user that a restart is required for changes to take effect
- """
-
- def __init__(self, app: CuraApplication) -> None:
- super().__init__()
- # Differentiate This PluginObject from the Toolbox. self.getId() includes _name.
- # getPluginId() will return the same value for The toolbox extension and this one
- self._name = "SyncOrchestrator"
-
- self._package_manager = app.getPackageManager()
- # Keep a reference to the CloudApiClient. it watches for installed packages and subscribes to them
- self._cloud_api = CloudApiClient.getInstance(app) # type: CloudApiClient
-
- self._checker = CloudPackageChecker(app) # type: CloudPackageChecker
- self._checker.discrepancies.connect(self._onDiscrepancies)
-
- self._discrepancies_presenter = DiscrepanciesPresenter(app) # type: DiscrepanciesPresenter
- self._discrepancies_presenter.packageMutations.connect(self._onPackageMutations)
-
- self._download_presenter = DownloadPresenter(app) # type: DownloadPresenter
-
- self._license_presenter = LicensePresenter(app) # type: LicensePresenter
- self._license_presenter.licenseAnswers.connect(self._onLicenseAnswers)
-
- self._restart_presenter = RestartApplicationPresenter(app)
-
- def _onDiscrepancies(self, model: SubscribedPackagesModel) -> None:
- plugin_path = cast(str, PluginRegistry.getInstance().getPluginPath(self.getPluginId()))
- self._discrepancies_presenter.present(plugin_path, model)
-
- def _onPackageMutations(self, mutations: SubscribedPackagesModel) -> None:
- self._download_presenter = self._download_presenter.resetCopy()
- self._download_presenter.done.connect(self._onDownloadFinished)
- self._download_presenter.download(mutations)
-
- def _onDownloadFinished(self, success_items: Dict[str, Dict[str, str]], error_items: List[str]) -> None:
- """Called when a set of packages have finished downloading
-
- :param success_items:: Dict[package_id, Dict[str, str]]
- :param error_items:: List[package_id]
- """
- if error_items:
- message = i18n_catalog.i18nc("@info:generic", "{} plugins failed to download".format(len(error_items)))
- self._showErrorMessage(message)
-
- plugin_path = cast(str, PluginRegistry.getInstance().getPluginPath(self.getPluginId()))
- self._license_presenter = self._license_presenter.resetCopy()
- self._license_presenter.licenseAnswers.connect(self._onLicenseAnswers)
- self._license_presenter.present(plugin_path, success_items)
-
- # Called when user has accepted / declined all licenses for the downloaded packages
- def _onLicenseAnswers(self, answers: List[Dict[str, Any]]) -> None:
- has_changes = False # True when at least one package is installed
-
- for item in answers:
- if item["accepted"]:
- # install and subscribe packages
- if not self._package_manager.installPackage(item["package_path"]):
- message = "Could not install {}".format(item["package_id"])
- self._showErrorMessage(message)
- continue
- has_changes = True
- else:
- self._cloud_api.unsubscribe(item["package_id"])
- # delete temp file
- try:
- os.remove(item["package_path"])
- except EnvironmentError as e: # File was already removed, no access rights, etc.
- Logger.error("Can't delete temporary package file: {err}".format(err = str(e)))
-
- if has_changes:
- self._restart_presenter.present()
-
- def _showErrorMessage(self, text: str):
- """Logs an error and shows it to the user"""
-
- Logger.error(text)
- Message(text, lifetime = 0, message_type = Message.MessageType.ERROR).show()
diff --git a/plugins/Toolbox/src/CloudSync/__init__.py b/plugins/Toolbox/src/CloudSync/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/plugins/Toolbox/src/ConfigsModel.py b/plugins/Toolbox/src/ConfigsModel.py
deleted file mode 100644
index a53817653f..0000000000
--- a/plugins/Toolbox/src/ConfigsModel.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (c) 2018 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
-
-
-class ConfigsModel(ListModel):
- """Model that holds supported configurations (for material/quality packages)."""
-
- def __init__(self, parent = None):
- super().__init__(parent)
-
- self._configs = None
-
- self.addRoleName(Qt.UserRole + 1, "machine")
- self.addRoleName(Qt.UserRole + 2, "print_core")
- self.addRoleName(Qt.UserRole + 3, "build_plate")
- self.addRoleName(Qt.UserRole + 4, "support_material")
- self.addRoleName(Qt.UserRole + 5, "quality")
-
- def setConfigs(self, configs):
- self._configs = configs
- self._update()
-
- def _update(self):
- items = []
- for item in self._configs:
- items.append({
- "machine": item["machine"],
- "print_core": item["print_core"],
- "build_plate": item["build_plate"],
- "support_material": item["support_material"],
- "quality": item["quality"]
- })
-
- self.setItems(items)
diff --git a/plugins/Toolbox/src/PackagesModel.py b/plugins/Toolbox/src/PackagesModel.py
deleted file mode 100644
index 97645ae466..0000000000
--- a/plugins/Toolbox/src/PackagesModel.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# Copyright (c) 2021 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
-
-
-class PackagesModel(ListModel):
- """Model that holds Cura packages.
-
- By setting the filter property the instances held by this model can be changed.
- """
-
- def __init__(self, parent = None):
- super().__init__(parent)
-
- self._metadata = None
-
- self.addRoleName(Qt.UserRole + 1, "id")
- self.addRoleName(Qt.UserRole + 2, "type")
- self.addRoleName(Qt.UserRole + 3, "name")
- self.addRoleName(Qt.UserRole + 4, "version")
- self.addRoleName(Qt.UserRole + 5, "author_id")
- self.addRoleName(Qt.UserRole + 6, "author_name")
- self.addRoleName(Qt.UserRole + 7, "author_email")
- self.addRoleName(Qt.UserRole + 8, "description")
- self.addRoleName(Qt.UserRole + 9, "icon_url")
- self.addRoleName(Qt.UserRole + 10, "image_urls")
- self.addRoleName(Qt.UserRole + 11, "download_url")
- self.addRoleName(Qt.UserRole + 12, "last_updated")
- self.addRoleName(Qt.UserRole + 13, "is_bundled")
- self.addRoleName(Qt.UserRole + 14, "is_active")
- self.addRoleName(Qt.UserRole + 15, "is_installed") # Scheduled pkgs are included in the model but should not be marked as actually installed
- self.addRoleName(Qt.UserRole + 16, "has_configs")
- self.addRoleName(Qt.UserRole + 17, "supported_configs")
- self.addRoleName(Qt.UserRole + 18, "download_count")
- self.addRoleName(Qt.UserRole + 19, "tags")
- self.addRoleName(Qt.UserRole + 20, "links")
- self.addRoleName(Qt.UserRole + 21, "website")
- self.addRoleName(Qt.UserRole + 22, "login_required")
-
- # 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):
- items = []
-
- if self._metadata is None:
- self.setItems(items)
- return
-
- for package in self._metadata:
- has_configs = False
- configs_model = None
-
- links_dict = {}
- if "data" in package:
- # 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}
-
- # This code never gets executed because the API response does not contain "supported_configs" in it
- # It is so because 2y ago when this was created - it did contain it. But it was a prototype only
- # and never got to production. As agreed with the team, it'll stay here for now, in case we decide to rework and use it
- # The response payload has been changed. Please see:
- # https://github.com/Ultimaker/Cura/compare/CURA-7072-temp?expand=1
- 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"])
-
- 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"].strip(),
- "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", []),
- })
-
- # 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)
-
- def setFilter(self, filter_dict: Dict[str, str]) -> None:
- """Set the filter of this model based on a string.
-
- :param filter_dict: Dictionary to do the filtering by.
- """
- 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
deleted file mode 100644
index e300d0ff34..0000000000
--- a/plugins/Toolbox/src/Toolbox.py
+++ /dev/null
@@ -1,878 +0,0 @@
-# Copyright (c) 2021 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-import json
-import os
-import tempfile
-from typing import cast, Any, Dict, List, Set, TYPE_CHECKING, Tuple, Optional, Union
-
-from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
-from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
-
-from UM.Extension import Extension
-from UM.Logger import Logger
-from UM.PluginRegistry import PluginRegistry
-from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
-from UM.Version import Version
-from UM.i18n import i18nCatalog
-from cura import ApplicationMetadata
-from cura.CuraApplication import CuraApplication
-from cura.Machines.ContainerTree import ContainerTree
-from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
-from .AuthorsModel import AuthorsModel
-from .CloudApiModel import CloudApiModel
-from .CloudSync.LicenseModel import LicenseModel
-from .PackagesModel import PackagesModel
-
-if TYPE_CHECKING:
- from UM.TaskManagement.HttpRequestData import HttpRequestData
- from cura.Settings.GlobalStack import GlobalStack
-
-i18n_catalog = i18nCatalog("cura")
-
-DEFAULT_MARKETPLACE_ROOT = "https://marketplace.ultimaker.com" # type: str
-
-try:
- from cura.CuraVersion import CuraMarketplaceRoot
-except ImportError:
- CuraMarketplaceRoot = DEFAULT_MARKETPLACE_ROOT
-
-
-class Toolbox(QObject, Extension):
- """Provides a marketplace for users to download plugins an materials"""
-
- def __init__(self, application: CuraApplication) -> None:
- super().__init__()
-
- self._application = application # type: CuraApplication
-
- # Network:
- self._download_request_data = None # type: Optional[HttpRequestData]
- self._download_progress = 0 # type: float
- self._is_downloading = False # type: bool
- self._cloud_scope = UltimakerCloudScope(application) # type: UltimakerCloudScope
- self._json_scope = JsonDecoratorScope(self._cloud_scope) # type: JsonDecoratorScope
-
- self._request_urls = {} # type: Dict[str, str]
- self._to_update = [] # type: List[str] # Package_ids that are waiting to be updated
- self._old_plugin_ids = set() # type: Set[str]
- self._old_plugin_metadata = dict() # type: Dict[str, Dict[str, Any]]
-
- # The responses as given by the server parsed to a list.
- self._server_response_data = {
- "authors": [],
- "packages": [],
- "updates": []
- } # type: Dict[str, List[Any]]
-
- # Models:
- self._models = {
- "authors": AuthorsModel(self),
- "packages": PackagesModel(self),
- "updates": PackagesModel(self)
- } # type: Dict[str, Union[AuthorsModel, PackagesModel]]
-
- self._plugins_showcase_model = PackagesModel(self)
- self._plugins_available_model = PackagesModel(self)
- self._plugins_installed_model = PackagesModel(self)
- self._plugins_installed_model.setFilter({"is_bundled": "False"})
- self._plugins_bundled_model = PackagesModel(self)
- self._plugins_bundled_model.setFilter({"is_bundled": "True"})
- self._materials_showcase_model = AuthorsModel(self)
- self._materials_available_model = AuthorsModel(self)
- self._materials_installed_model = PackagesModel(self)
- self._materials_installed_model.setFilter({"is_bundled": "False"})
- self._materials_bundled_model = PackagesModel(self)
- self._materials_bundled_model.setFilter({"is_bundled": "True"})
- self._materials_generic_model = PackagesModel(self)
-
- self._license_model = LicenseModel()
-
- # These properties are for keeping track of the UI state:
- # ----------------------------------------------------------------------
- # View category defines which filter to use, and therefore effectively
- # which category is currently being displayed. For example, possible
- # values include "plugin" or "material", but also "installed".
- self._view_category = "plugin" # type: str
-
- # View page defines which type of page layout to use. For example,
- # possible values include "overview", "detail" or "author".
- self._view_page = "welcome" # type: str
-
- # Active package refers to which package is currently being downloaded,
- # installed, or otherwise modified.
- self._active_package = None # type: Optional[Dict[str, Any]]
-
- self._dialog = None # type: Optional[QObject]
- self._confirm_reset_dialog = None # type: Optional[QObject]
- self._resetUninstallVariables()
-
- self._restart_required = False # type: bool
-
- # variables for the license agreement dialog
- self._license_dialog_plugin_file_location = "" # type: str
-
- self._application.initializationFinished.connect(self._onAppInitialized)
-
- # Signals:
- # --------------------------------------------------------------------------
- # Downloading changes
- activePackageChanged = pyqtSignal()
- onDownloadProgressChanged = pyqtSignal()
- onIsDownloadingChanged = pyqtSignal()
- restartRequiredChanged = pyqtSignal()
- installChanged = pyqtSignal()
- toolboxEnabledChanged = pyqtSignal()
-
- # UI changes
- viewChanged = pyqtSignal()
- detailViewChanged = pyqtSignal()
- filterChanged = pyqtSignal()
- metadataChanged = pyqtSignal()
- showLicenseDialog = pyqtSignal()
- closeLicenseDialog = pyqtSignal()
- uninstallVariablesChanged = pyqtSignal()
-
- def _restart(self):
- """Go back to the start state (welcome screen or loading if no login required)"""
-
- # For an Essentials build, login is mandatory
- if not self._application.getCuraAPI().account.isLoggedIn and ApplicationMetadata.IsEnterpriseVersion:
- self.setViewPage("welcome")
- else:
- self.setViewPage("loading")
- self._fetchPackageData()
-
- def _resetUninstallVariables(self) -> None:
- self._package_id_to_uninstall = None # type: Optional[str]
- self._package_name_to_uninstall = ""
- self._package_used_materials = [] # type: List[Tuple[GlobalStack, str, str]]
- self._package_used_qualities = [] # type: List[Tuple[GlobalStack, str, str]]
-
- def getLicenseDialogPluginFileLocation(self) -> str:
- return self._license_dialog_plugin_file_location
-
- def openLicenseDialog(self, plugin_name: str, license_content: str, plugin_file_location: str, icon_url: str) -> None:
- # Set page 1/1 when opening the dialog for a single package
- self._license_model.setCurrentPageIdx(0)
- self._license_model.setPageCount(1)
- self._license_model.setIconUrl(icon_url)
-
- self._license_model.setPackageName(plugin_name)
- self._license_model.setLicenseText(license_content)
- self._license_dialog_plugin_file_location = plugin_file_location
- self.showLicenseDialog.emit()
-
- # This is a plugin, so most of the components required are not ready when
- # this is initialized. Therefore, we wait until the application is ready.
- def _onAppInitialized(self) -> None:
- self._plugin_registry = self._application.getPluginRegistry()
- self._package_manager = self._application.getPackageManager()
-
- # 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()]
- installed_packages_query = "&installed_packages=".join(installed_package_ids_with_versions)
-
- self._request_urls = {
- "authors": "{base_url}/authors".format(base_url = CloudApiModel.api_url),
- "packages": "{base_url}/packages".format(base_url = CloudApiModel.api_url),
- "updates": "{base_url}/packages/package-updates?installed_packages={query}".format(
- base_url = CloudApiModel.api_url, query = installed_packages_query)
- }
-
- self._application.getCuraAPI().account.loginStateChanged.connect(self._restart)
-
- preferences = CuraApplication.getInstance().getPreferences()
-
- preferences.addPreference("info/automatic_plugin_update_check", True)
-
- # On boot we check which packages have updates.
- if preferences.getValue("info/automatic_plugin_update_check") and len(installed_package_ids_with_versions) > 0:
- # Request the latest and greatest!
- self._makeRequestByType("updates")
-
- def _fetchPackageData(self) -> None:
- self._makeRequestByType("packages")
- self._makeRequestByType("authors")
- self._updateInstalledModels()
-
- # Displays the toolbox
- @pyqtSlot()
- def launch(self) -> None:
- if not self._dialog:
- self._dialog = self._createDialog("Toolbox.qml")
-
- if not self._dialog:
- Logger.log("e", "Unexpected error trying to create the 'Marketplace' dialog.")
- return
-
- self._restart()
-
- self._dialog.show()
- # Apply enabled/disabled state to installed plugins
- self.toolboxEnabledChanged.emit()
-
- def _createDialog(self, qml_name: str) -> Optional[QObject]:
- Logger.log("d", "Marketplace: Creating dialog [%s].", qml_name)
- plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
- if not plugin_path:
- return None
- path = os.path.join(plugin_path, "resources", "qml", qml_name)
-
- dialog = self._application.createQmlComponent(path, {
- "toolbox": self,
- "handler": self,
- "licenseModel": self._license_model
- })
- if not dialog:
- return None
- return dialog
-
- def _convertPluginMetadata(self, plugin_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
- try:
- highest_sdk_version_supported = Version(0)
- for supported_version in plugin_data["plugin"]["supported_sdk_versions"]:
- if supported_version > highest_sdk_version_supported:
- highest_sdk_version_supported = supported_version
-
- formatted = {
- "package_id": plugin_data["id"],
- "package_type": "plugin",
- "display_name": plugin_data["plugin"]["name"],
- "package_version": plugin_data["plugin"]["version"],
- "sdk_version": highest_sdk_version_supported,
- "author": {
- "author_id": plugin_data["plugin"]["author"],
- "display_name": plugin_data["plugin"]["author"]
- },
- "is_installed": True,
- "description": plugin_data["plugin"]["description"]
- }
- return formatted
- except KeyError:
- Logger.log("w", "Unable to convert plugin meta data %s", str(plugin_data))
- return None
-
- @pyqtSlot()
- def _updateInstalledModels(self) -> None:
- # This is moved here to avoid code duplication and so that after installing plugins they get removed from the
- # list of old plugins
- old_plugin_ids = self._plugin_registry.getInstalledPlugins()
- installed_package_ids = self._package_manager.getAllInstalledPackageIDs()
- scheduled_to_remove_package_ids = self._package_manager.getToRemovePackageIDs()
-
- self._old_plugin_ids = set()
- self._old_plugin_metadata = dict()
-
- for plugin_id in old_plugin_ids:
- # Neither the installed packages nor the packages that are scheduled to remove are old plugins
- if plugin_id not in installed_package_ids and plugin_id not in scheduled_to_remove_package_ids:
- Logger.log("d", "Found a plugin that was installed with the old plugin browser: %s", plugin_id)
-
- old_metadata = self._plugin_registry.getMetaData(plugin_id)
- new_metadata = self._convertPluginMetadata(old_metadata)
- if new_metadata is None:
- # Something went wrong converting it.
- continue
- self._old_plugin_ids.add(plugin_id)
- self._old_plugin_metadata[new_metadata["package_id"]] = new_metadata
-
- all_packages = self._package_manager.getAllInstalledPackagesInfo()
- if "plugin" in all_packages:
- # For old plugins, we only want to include the old custom plugin that were installed via the old toolbox.
- # The bundled plugins will be included in JSON files in the "bundled_packages" folder, so the bundled
- # plugins should be excluded from the old plugins list/dict.
- all_plugin_package_ids = set(package["package_id"] for package in all_packages["plugin"])
- self._old_plugin_ids = set(plugin_id for plugin_id in self._old_plugin_ids
- if plugin_id not in all_plugin_package_ids)
- self._old_plugin_metadata = {k: v for k, v in self._old_plugin_metadata.items() if k in self._old_plugin_ids}
-
- self._plugins_installed_model.setMetadata(all_packages["plugin"] + list(self._old_plugin_metadata.values()))
- self._plugins_bundled_model.setMetadata(all_packages["plugin"] + list(self._old_plugin_metadata.values()))
- self.metadataChanged.emit()
- if "material" in all_packages:
- self._materials_installed_model.setMetadata(all_packages["material"])
- self._materials_bundled_model.setMetadata(all_packages["material"])
- self.metadataChanged.emit()
-
- @pyqtSlot(str)
- def install(self, file_path: str) -> Optional[str]:
- package_id = self._package_manager.installPackage(file_path)
- self.installChanged.emit()
- self._updateInstalledModels()
- self.metadataChanged.emit()
- self._restart_required = True
- self.restartRequiredChanged.emit()
- return package_id
-
- @pyqtSlot(str)
- def checkPackageUsageAndUninstall(self, package_id: str) -> None:
- """Check package usage and uninstall
-
- If the package is in use, you'll get a confirmation dialog to set everything to default
- """
-
- package_used_materials, package_used_qualities = self._package_manager.getMachinesUsingPackage(package_id)
- if package_used_materials or package_used_qualities:
- # Set up "uninstall variables" for resetMaterialsQualitiesAndUninstall
- self._package_id_to_uninstall = package_id
- package_info = self._package_manager.getInstalledPackageInfo(package_id)
- self._package_name_to_uninstall = package_info.get("display_name", package_info.get("package_id"))
- self._package_used_materials = package_used_materials
- self._package_used_qualities = package_used_qualities
- # Ask change to default material / profile
- if self._confirm_reset_dialog is None:
- self._confirm_reset_dialog = self._createDialog("dialogs/ToolboxConfirmUninstallResetDialog.qml")
- self.uninstallVariablesChanged.emit()
- if self._confirm_reset_dialog is None:
- Logger.log("e", "ToolboxConfirmUninstallResetDialog should have been initialized, but it is not. Not showing dialog and not uninstalling package.")
- else:
- self._confirm_reset_dialog.show()
- else:
- # Plain uninstall
- self.uninstall(package_id)
-
- @pyqtProperty(str, notify = uninstallVariablesChanged)
- def pluginToUninstall(self) -> str:
- return self._package_name_to_uninstall
-
- @pyqtProperty(str, notify = uninstallVariablesChanged)
- def uninstallUsedMaterials(self) -> str:
- return "\n".join(["%s (%s)" % (str(global_stack.getName()), material) for global_stack, extruder_nr, material in self._package_used_materials])
-
- @pyqtProperty(str, notify = uninstallVariablesChanged)
- def uninstallUsedQualities(self) -> str:
- return "\n".join(["%s (%s)" % (str(global_stack.getName()), quality) for global_stack, extruder_nr, quality in self._package_used_qualities])
-
- @pyqtSlot()
- def closeConfirmResetDialog(self) -> None:
- if self._confirm_reset_dialog is not None:
- self._confirm_reset_dialog.close()
-
- @pyqtSlot()
- def resetMaterialsQualitiesAndUninstall(self) -> None:
- """Uses "uninstall variables" to reset qualities and materials, then uninstall
-
- It's used as an action on Confirm reset on Uninstall
- """
-
- application = CuraApplication.getInstance()
- machine_manager = application.getMachineManager()
- container_tree = ContainerTree.getInstance()
-
- for global_stack, extruder_nr, container_id in self._package_used_materials:
- extruder = global_stack.extruderList[int(extruder_nr)]
- approximate_diameter = extruder.getApproximateMaterialDiameter()
- variant_node = container_tree.machines[global_stack.definition.getId()].variants[extruder.variant.getName()]
- default_material_node = variant_node.preferredMaterial(approximate_diameter)
- machine_manager.setMaterial(extruder_nr, default_material_node, global_stack = global_stack)
- for global_stack, extruder_nr, container_id in self._package_used_qualities:
- variant_names = [extruder.variant.getName() for extruder in global_stack.extruderList]
- material_bases = [extruder.material.getMetaDataEntry("base_file") for extruder in global_stack.extruderList]
- extruder_enabled = [extruder.isEnabled for extruder in global_stack.extruderList]
- definition_id = global_stack.definition.getId()
- machine_node = container_tree.machines[definition_id]
- default_quality_group = machine_node.getQualityGroups(variant_names, material_bases, extruder_enabled)[machine_node.preferred_quality_type]
- machine_manager.setQualityGroup(default_quality_group, global_stack = global_stack)
-
- if self._package_id_to_uninstall is not None:
- self._markPackageMaterialsAsToBeUninstalled(self._package_id_to_uninstall)
- self.uninstall(self._package_id_to_uninstall)
- self._resetUninstallVariables()
- self.closeConfirmResetDialog()
-
- @pyqtSlot()
- def onLicenseAccepted(self):
- self.closeLicenseDialog.emit()
- package_id = self.install(self.getLicenseDialogPluginFileLocation())
-
-
- @pyqtSlot()
- def onLicenseDeclined(self):
- self.closeLicenseDialog.emit()
-
- def _markPackageMaterialsAsToBeUninstalled(self, package_id: str) -> None:
- container_registry = self._application.getContainerRegistry()
-
- all_containers = self._package_manager.getPackageContainerIds(package_id)
- for container_id in all_containers:
- containers = container_registry.findInstanceContainers(id = container_id)
- if not containers:
- continue
- container = containers[0]
- if container.getMetaDataEntry("type") != "material":
- continue
- root_material_id = container.getMetaDataEntry("base_file")
- root_material_containers = container_registry.findInstanceContainers(id = root_material_id)
- if not root_material_containers:
- continue
- root_material_container = root_material_containers[0]
- root_material_container.setMetaDataEntry("removed", True)
-
- @pyqtSlot(str)
- def uninstall(self, package_id: str) -> None:
- self._package_manager.removePackage(package_id, force_add = True)
- self.installChanged.emit()
- self._updateInstalledModels()
- self.metadataChanged.emit()
- self._restart_required = True
- self.restartRequiredChanged.emit()
-
- def _update(self) -> None:
- """Actual update packages that are in self._to_update"""
-
- 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.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)
-
- @pyqtSlot(str)
- def update(self, plugin_id: str) -> None:
- """Update a plugin by plugin_id"""
-
- 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)
- self.toolboxEnabledChanged.emit()
- Logger.log("i", "%s was set as 'active'.", plugin_id)
- self._restart_required = True
- self.restartRequiredChanged.emit()
-
- @pyqtSlot(str)
- def disable(self, plugin_id: str) -> None:
- self._plugin_registry.disablePlugin(plugin_id)
- self.toolboxEnabledChanged.emit()
- Logger.log("i", "%s was set as 'deactive'.", plugin_id)
- self._restart_required = True
- self.restartRequiredChanged.emit()
-
- @pyqtProperty(bool, notify = metadataChanged)
- def dataReady(self) -> bool:
- return self._packages_model is not None
-
- @pyqtProperty(bool, notify = restartRequiredChanged)
- def restartRequired(self) -> bool:
- return self._restart_required
-
- @pyqtSlot()
- def restart(self) -> None:
- self._application.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._server_response_data["packages"]:
- if package["package_id"] == package_id:
- remote_package = package
- break
- return remote_package
-
- @pyqtSlot(str, result = bool)
- def canDowngrade(self, package_id: str) -> bool:
- # If the currently installed version is higher than the bundled version (if present), the we can downgrade
- # this package.
- local_package = self._package_manager.getInstalledPackageInfo(package_id)
- if local_package is None:
- return False
-
- bundled_package = self._package_manager.getBundledPackageInfo(package_id)
- if bundled_package is None:
- return False
-
- local_version = Version(local_package["package_version"])
- bundled_version = Version(bundled_package["package_version"])
- return bundled_version < local_version
-
- @pyqtSlot(str, result = bool)
- def isInstalled(self, package_id: str) -> bool:
- result = self._package_manager.isPackageInstalled(package_id)
- # Also check the old plugins list if it's not found in the package manager.
- if not result:
- result = self.isOldPlugin(package_id)
- return result
-
- @pyqtSlot(str, result = int)
- def getNumberOfInstalledPackagesByAuthor(self, author_id: str) -> int:
- count = 0
- for package in self._materials_installed_model.items:
- if package["author_id"] == author_id:
- count += 1
- return count
-
- # This slot is only used to get the number of material packages by author, not any other type of packages.
- @pyqtSlot(str, result = int)
- def getTotalNumberOfMaterialPackagesByAuthor(self, author_id: str) -> int:
- count = 0
- for package in self._server_response_data["packages"]:
- if package["package_type"] == "material":
- if package["author"]["author_id"] == author_id:
- count += 1
- return count
-
- @pyqtSlot(str, result = bool)
- def isEnabled(self, package_id: str) -> bool:
- 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:
- return plugin_id in self._old_plugin_ids
-
- def getOldPluginPackageMetadata(self, plugin_id: str) -> Optional[Dict[str, Any]]:
- return self._old_plugin_metadata.get(plugin_id)
-
- def isLoadingComplete(self) -> bool:
- populated = 0
- for metadata_list in self._server_response_data.items():
- if metadata_list:
- populated += 1
- return populated == len(self._server_response_data.items())
-
- # Make API Calls
- # --------------------------------------------------------------------------
- def _makeRequestByType(self, request_type: str) -> None:
- Logger.debug(f"Requesting {request_type} metadata from server.")
- url = self._request_urls[request_type]
-
- callback = lambda r, rt = request_type: self._onDataRequestFinished(rt, r)
- error_callback = lambda r, e, rt = request_type: self._onDataRequestError(rt, r, e)
- self._application.getHttpRequestManager().get(url,
- callback = callback,
- error_callback = error_callback,
- scope=self._json_scope)
-
- @pyqtSlot(str)
- def startDownload(self, url: str) -> None:
- Logger.info(f"Attempting to download & install package from {url}.")
-
- callback = lambda r: self._onDownloadFinished(r)
- error_callback = lambda r, e: self._onDownloadFailed(r, e)
- download_progress_callback = self._onDownloadProgress
- request_data = self._application.getHttpRequestManager().get(url,
- callback = callback,
- error_callback = error_callback,
- download_progress_callback = download_progress_callback,
- scope=self._cloud_scope
- )
-
- self._download_request_data = request_data
- self.setDownloadProgress(0)
- self.setIsDownloading(True)
-
- @pyqtSlot()
- def cancelDownload(self) -> None:
- Logger.info(f"User cancelled the download of a package. request {self._download_request_data}")
- if self._download_request_data is not None:
- self._application.getHttpRequestManager().abortRequest(self._download_request_data)
- self._download_request_data = None
- self.resetDownload()
-
- def resetDownload(self) -> None:
- self.setDownloadProgress(0)
- self.setIsDownloading(False)
-
- # Handlers for Network Events
- # --------------------------------------------------------------------------
- def _onDataRequestError(self, request_type: str, reply: "QNetworkReply", error: "QNetworkReply.NetworkError") -> None:
- Logger.error(f"Request {request_type} failed due to error {error}: {reply.errorString()}")
- self.setViewPage("errored")
-
- def _onDataRequestFinished(self, request_type: str, reply: "QNetworkReply") -> None:
- if reply.operation() != QNetworkAccessManager.GetOperation:
- Logger.log("e", "_onDataRequestFinished() only handles GET requests but got [%s] instead", reply.operation())
- return
-
- http_status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
- if http_status_code != 200:
- Logger.log("e", "Request type [%s] got non-200 HTTP response: [%s]", http_status_code)
- self.setViewPage("errored")
- return
-
- data = bytes(reply.readAll())
- try:
- json_data = json.loads(data.decode("utf-8"))
- except json.decoder.JSONDecodeError:
- Logger.log("e", "Failed to decode response data as JSON for request type [%s], response data [%s]",
- request_type, data)
- self.setViewPage("errored")
- return
-
- # Check for errors:
- if "errors" in json_data:
- for error in json_data["errors"]:
- Logger.log("e", "Request type [%s] got response showing error: %s", error.get("title", "No error title found"))
- self.setViewPage("errored")
- return
-
- # Create model and apply metadata:
- if not self._models[request_type]:
- Logger.log("e", "Could not find the model for request type [%s].", request_type)
- self.setViewPage("errored")
- return
-
- self._server_response_data[request_type] = json_data["data"]
- self._models[request_type].setMetadata(self._server_response_data[request_type])
-
- if request_type == "packages":
- self._models[request_type].setFilter({"type": "plugin"})
- self.reBuildMaterialsModels()
- self.reBuildPluginsModels()
- self._notifyPackageManager()
- elif request_type == "authors":
- self._models[request_type].setFilter({"package_types": "material"})
- self._models[request_type].setFilter({"tags": "generic"})
- elif request_type == "updates":
- # Tell the package manager that there's a new set of updates available.
- packages = self._server_response_data[request_type]
- self._package_manager.setPackagesWithUpdate({p['package_id'] for p in packages})
-
- self.metadataChanged.emit()
-
- if self.isLoadingComplete():
- self.setViewPage("overview")
-
- # 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"]:
- self._package_manager.addAvailablePackageVersion(package["package_id"], Version(package["package_version"]))
-
- def _onDownloadFinished(self, reply: "QNetworkReply") -> None:
- self.resetDownload()
-
- if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200:
- try:
- reply_error = json.loads(reply.readAll().data().decode("utf-8"))
- except Exception as e:
- reply_error = str(e)
- Logger.log("w", "Failed to download package. The following error was returned: %s", reply_error)
- return
- # Must not delete the temporary file on Windows
- self._temp_plugin_file = tempfile.NamedTemporaryFile(mode = "w+b", suffix = ".curapackage", delete = False)
- file_path = self._temp_plugin_file.name
- # Write first and close, otherwise on Windows, it cannot read the file
- self._temp_plugin_file.write(reply.readAll())
- self._temp_plugin_file.close()
- self._onDownloadComplete(file_path)
-
- def _onDownloadFailed(self, reply: "QNetworkReply", error: "QNetworkReply.NetworkError") -> None:
- Logger.log("w", "Failed to download package. The following error was returned: %s", error)
-
- self.resetDownload()
-
- def _onDownloadProgress(self, bytes_sent: int, bytes_total: int) -> None:
- if bytes_total > 0:
- new_progress = bytes_sent / bytes_total * 100
- self.setDownloadProgress(new_progress)
- Logger.log("d", "new download progress %s / %s : %s%%", bytes_sent, bytes_total, new_progress)
-
- def _onDownloadComplete(self, file_path: str) -> None:
- Logger.log("i", "Download complete.")
- package_info = self._package_manager.getPackageInfo(file_path)
- if not package_info:
- Logger.log("w", "Package file [%s] was not a valid CuraPackage.", file_path)
- return
- package_id = package_info["package_id"]
-
- try:
- license_content = self._package_manager.getPackageLicense(file_path)
- except EnvironmentError as e:
- Logger.error(f"Could not open downloaded package {package_id} to read license file! {type(e)} - {e}")
- return
- if license_content is not None:
- # get the icon url for package_id, make sure the result is a string, never None
- icon_url = next((x["icon_url"] for x in self.packagesModel.items if x["id"] == package_id), None) or ""
- self.openLicenseDialog(package_info["display_name"], license_content, file_path, icon_url)
- return
-
- installed_id = self.install(file_path)
- if installed_id != package_id:
- Logger.error("Installed package {} does not match {}".format(installed_id, package_id))
-
- # Getter & Setters for Properties:
- # --------------------------------------------------------------------------
- def setDownloadProgress(self, progress: float) -> None:
- if progress != self._download_progress:
- self._download_progress = progress
- self.onDownloadProgressChanged.emit()
-
- @pyqtProperty(int, fset = setDownloadProgress, notify = onDownloadProgressChanged)
- def downloadProgress(self) -> float:
- return self._download_progress
-
- def setIsDownloading(self, is_downloading: bool) -> None:
- if self._is_downloading != is_downloading:
- self._is_downloading = is_downloading
- self.onIsDownloadingChanged.emit()
-
- @pyqtProperty(bool, fset = setIsDownloading, notify = onIsDownloadingChanged)
- def isDownloading(self) -> bool:
- return self._is_downloading
-
- def setActivePackage(self, package: QObject) -> None:
- if self._active_package != package:
- self._active_package = package
- self.activePackageChanged.emit()
-
- @pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged)
- def activePackage(self) -> Optional[QObject]:
- """The active package is the package that is currently being downloaded"""
-
- return self._active_package
-
- def setViewCategory(self, category: str = "plugin") -> None:
- if self._view_category != category:
- self._view_category = category
- self.viewChanged.emit()
-
- # Function explicitly defined so that it can be called through the callExtensionsMethod
- # which cannot receive arguments.
- def setViewCategoryToMaterials(self) -> None:
- self.setViewCategory("material")
-
- @pyqtProperty(str, fset = setViewCategory, notify = viewChanged)
- def viewCategory(self) -> str:
- return self._view_category
-
- def setViewPage(self, page: str = "overview") -> None:
- if self._view_page != page:
- self._view_page = page
- self.viewChanged.emit()
-
- @pyqtProperty(str, fset = setViewPage, notify = viewChanged)
- def viewPage(self) -> str:
- return self._view_page
-
- # Exposed Models:
- # --------------------------------------------------------------------------
- @pyqtProperty(QObject, constant = True)
- def authorsModel(self) -> AuthorsModel:
- return cast(AuthorsModel, self._models["authors"])
-
- @pyqtProperty(QObject, constant = True)
- def packagesModel(self) -> PackagesModel:
- return cast(PackagesModel, self._models["packages"])
-
- @pyqtProperty(QObject, constant = True)
- def pluginsShowcaseModel(self) -> PackagesModel:
- return self._plugins_showcase_model
-
- @pyqtProperty(QObject, constant = True)
- def pluginsAvailableModel(self) -> PackagesModel:
- return self._plugins_available_model
-
- @pyqtProperty(QObject, constant = True)
- def pluginsInstalledModel(self) -> PackagesModel:
- return self._plugins_installed_model
-
- @pyqtProperty(QObject, constant = True)
- def pluginsBundledModel(self) -> PackagesModel:
- return self._plugins_bundled_model
-
- @pyqtProperty(QObject, constant = True)
- def materialsShowcaseModel(self) -> AuthorsModel:
- return self._materials_showcase_model
-
- @pyqtProperty(QObject, constant = True)
- def materialsAvailableModel(self) -> AuthorsModel:
- return self._materials_available_model
-
- @pyqtProperty(QObject, constant = True)
- def materialsInstalledModel(self) -> PackagesModel:
- return self._materials_installed_model
-
- @pyqtProperty(QObject, constant = True)
- def materialsBundledModel(self) -> PackagesModel:
- return self._materials_bundled_model
-
- @pyqtProperty(QObject, constant = True)
- def materialsGenericModel(self) -> PackagesModel:
- return self._materials_generic_model
-
- @pyqtSlot(str, result = str)
- def getWebMarketplaceUrl(self, page: str) -> str:
- root = CuraMarketplaceRoot
- if root == "":
- root = DEFAULT_MARKETPLACE_ROOT
- return root + "/app/cura/" + page
-
- # Filter Models:
- # --------------------------------------------------------------------------
- @pyqtSlot(str, str, str)
- def filterModelByProp(self, model_type: str, filter_type: str, parameter: str) -> None:
- if not self._models[model_type]:
- Logger.log("w", "Couldn't filter %s model because it doesn't exist.", model_type)
- return
- self._models[model_type].setFilter({filter_type: parameter})
- self.filterChanged.emit()
-
- @pyqtSlot(str, "QVariantMap")
- def setFilters(self, model_type: str, filter_dict: dict) -> None:
- if not self._models[model_type]:
- Logger.log("w", "Couldn't filter %s model because it doesn't exist.", model_type)
- return
- self._models[model_type].setFilter(filter_dict)
- self.filterChanged.emit()
-
- @pyqtSlot(str)
- def removeFilters(self, model_type: str) -> None:
- if not self._models[model_type]:
- Logger.log("w", "Couldn't remove filters on %s model because it doesn't exist.", model_type)
- return
- self._models[model_type].setFilter({})
- self.filterChanged.emit()
-
- # HACK(S):
- # --------------------------------------------------------------------------
- def reBuildMaterialsModels(self) -> None:
- materials_showcase_metadata = []
- materials_available_metadata = []
- materials_generic_metadata = []
-
- processed_authors = [] # type: List[str]
-
- for item in self._server_response_data["packages"]:
- if item["package_type"] == "material":
-
- author = item["author"]
- if author["author_id"] in processed_authors:
- continue
-
- # Generic materials to be in the same section
- if "generic" in item["tags"]:
- materials_generic_metadata.append(item)
- else:
- if "showcase" in item["tags"]:
- materials_showcase_metadata.append(author)
- else:
- materials_available_metadata.append(author)
-
- processed_authors.append(author["author_id"])
-
- self._materials_showcase_model.setMetadata(materials_showcase_metadata)
- self._materials_available_model.setMetadata(materials_available_metadata)
- self._materials_generic_model.setMetadata(materials_generic_metadata)
-
- def reBuildPluginsModels(self) -> None:
- plugins_showcase_metadata = []
- plugins_available_metadata = []
-
- for item in self._server_response_data["packages"]:
- if item["package_type"] == "plugin":
- if "showcase" in item["tags"]:
- plugins_showcase_metadata.append(item)
- else:
- plugins_available_metadata.append(item)
-
- self._plugins_showcase_model.setMetadata(plugins_showcase_metadata)
- self._plugins_available_model.setMetadata(plugins_available_metadata)
diff --git a/plugins/Toolbox/src/__init__.py b/plugins/Toolbox/src/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json
index d1914c68ce..501445f9d8 100644
--- a/resources/bundled_packages/cura.json
+++ b/resources/bundled_packages/cura.json
@@ -526,13 +526,13 @@
}
}
},
- "Toolbox": {
+ "Marketplace": {
"package_info": {
- "package_id": "Toolbox",
+ "package_id": "Marketplace",
"package_type": "plugin",
- "display_name": "Toolbox",
+ "display_name": "Marketplace",
"description": "Find, manage and install new Cura packages.",
- "package_version": "1.0.1",
+ "package_version": "1.0.0",
"sdk_version": "7.9.0",
"website": "https://ultimaker.com",
"author": {
diff --git a/resources/qml/MainWindow/ApplicationMenu.qml b/resources/qml/MainWindow/ApplicationMenu.qml
index 2924ae518f..497c5e1541 100644
--- a/resources/qml/MainWindow/ApplicationMenu.qml
+++ b/resources/qml/MainWindow/ApplicationMenu.qml
@@ -196,15 +196,7 @@ Item
}
}
- // show the Toolbox
- Connections
- {
- target: Cura.Actions.browsePackages
- function onTriggered()
- {
- curaExtensions.callExtensionMethod("Toolbox", "launch")
- }
- }
+ // show the Marketplace
Connections
{
target: Cura.Actions.openMarketplace
@@ -220,8 +212,8 @@ Item
target: Cura.Actions.marketplaceMaterials
function onTriggered()
{
- curaExtensions.callExtensionMethod("Toolbox", "launch")
- curaExtensions.callExtensionMethod("Toolbox", "setViewCategoryToMaterials")
+ curaExtensions.callExtensionMethod("Marketplace", "show")
+ curaExtensions.callExtensionMethod("Marketplace", "setVisibleTabToMaterials")
}
}
-}
\ No newline at end of file
+}
diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml
index cc400252e1..16d7d69062 100644
--- a/resources/qml/MainWindow/MainWindowHeader.qml
+++ b/resources/qml/MainWindow/MainWindowHeader.qml
@@ -83,53 +83,6 @@ Item
ExclusiveGroup { id: mainWindowHeaderMenuGroup }
}
- // Shortcut button to quick access the Toolbox
- Controls2.Button //TODO: Remove once new Marketplace is completed.
- {
- text: "Old Marketplace"
- height: Math.round(0.5 * UM.Theme.getSize("main_window_header").height)
- onClicked: Cura.Actions.browsePackages.trigger()
-
- hoverEnabled: true
-
- background: Rectangle
- {
- id: marketplaceButtonBorder
- radius: UM.Theme.getSize("action_button_radius").width
- color: UM.Theme.getColor("main_window_header_background")
- border.width: UM.Theme.getSize("default_lining").width
- border.color: UM.Theme.getColor("primary_text")
-
- Rectangle
- {
- id: marketplaceButtonFill
- anchors.fill: parent
- radius: parent.radius
- color: UM.Theme.getColor("primary_text")
- opacity: parent.hovered ? 0.2 : 0
- Behavior on opacity { NumberAnimation { duration: 100 } }
- }
- }
-
- contentItem: Label
- {
- id: label
- text: parent.text
- font: UM.Theme.getFont("default")
- color: UM.Theme.getColor("primary_text")
- width: contentWidth
- verticalAlignment: Text.AlignVCenter
- renderType: Text.NativeRendering
- }
-
- anchors
- {
- right: marketplaceButton.left
- rightMargin: UM.Theme.getSize("default_margin").width
- verticalCenter: parent.verticalCenter
- }
- }
-
Controls2.Button
{
id: marketplaceButton
diff --git a/resources/qml/Widgets/ScrollView.qml b/resources/qml/Widgets/ScrollView.qml
index 9e7531994c..deaecb5dfb 100644
--- a/resources/qml/Widgets/ScrollView.qml
+++ b/resources/qml/Widgets/ScrollView.qml
@@ -1,5 +1,5 @@
// Copyright (c) 2020 Ultimaker B.V.
-// Toolbox is released under the terms of the LGPLv3 or higher.
+// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.10
import QtQuick.Controls 2.3
diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json
index 433c9fff92..a81dcadb5c 100644
--- a/resources/themes/cura-dark/theme.json
+++ b/resources/themes/cura-dark/theme.json
@@ -176,10 +176,6 @@
"quality_slider_available": [255, 255, 255, 255],
- "toolbox_header_button_text_active": [255, 255, 255, 255],
- "toolbox_header_button_text_inactive": [128, 128, 128, 255],
- "toolbox_premium_packages_background": [57, 57, 57, 255],
-
"monitor_printer_family_tag": [86, 86, 106, 255],
"monitor_text_disabled": [102, 102, 102, 255],
"monitor_icon_primary": [229, 229, 229, 255],
diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml
index 1320b54f37..8376ecb44a 100755
--- a/resources/themes/cura-light/styles.qml
+++ b/resources/themes/cura-light/styles.qml
@@ -602,62 +602,6 @@ QtObject
}
}
- property Component toolbox_action_button: Component
- {
- ButtonStyle
- {
- background: Rectangle
- {
- implicitWidth: UM.Theme.getSize("toolbox_action_button").width
- implicitHeight: UM.Theme.getSize("toolbox_action_button").height
- color:
- {
- if (control.installed)
- {
- 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 (control.installed)
- {
- 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")
- }
- }
- }
-
property Component monitor_button_style: Component
{
ButtonStyle
diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json
index a57e16a26c..81986683f4 100644
--- a/resources/themes/cura-light/theme.json
+++ b/resources/themes/cura-light/theme.json
@@ -428,9 +428,6 @@
"printer_config_matched": [50, 130, 255, 255],
"printer_config_mismatch": [127, 127, 127, 255],
- "toolbox_header_button_text_inactive": [0, 0, 0, 255],
- "toolbox_premium_packages_background": [240, 240, 240, 255],
-
"favorites_header_bar": [245, 245, 245, 255],
"favorites_header_hover": [245, 245, 245, 255],
"favorites_header_text": [31, 36, 39, 255],
@@ -647,24 +644,6 @@
"build_plate_selection_size": [15, 5],
"objects_menu_button": [0.3, 2.7],
- "toolbox_thumbnail_small": [6.0, 6.0],
- "toolbox_thumbnail_medium": [8.0, 8.0],
- "toolbox_thumbnail_large": [12.0, 10.0],
- "toolbox_footer": [1.0, 4.5],
- "toolbox_footer_button": [8.0, 2.5],
- "toolbox_header_tab": [12.0, 4.0],
- "toolbox_detail_header": [1.0, 14.0],
- "toolbox_back_column": [6.0, 1.0],
- "toolbox_back_button": [6.0, 2.0],
- "toolbox_installed_tile": [1.0, 8.0],
- "toolbox_property_label": [1.0, 2.0],
- "toolbox_heading_label": [1.0, 3.8],
- "toolbox_header": [1.0, 4.0],
- "toolbox_header_highlight": [0.25, 0.25],
- "toolbox_chart_row": [1.0, 2.0],
- "toolbox_action_button": [8.0, 2.5],
- "toolbox_loader": [2.0, 2.0],
-
"notification_icon": [1.5, 1.5],
"avatar_image": [6.8, 6.8],
diff --git a/scripts/check_invalid_imports.py b/scripts/check_invalid_imports.py
index ba21b9f822..b77a82568d 100644
--- a/scripts/check_invalid_imports.py
+++ b/scripts/check_invalid_imports.py
@@ -8,7 +8,7 @@ Run this file with the Cura project root as the working directory
Checks for invalid imports. When importing from plugins, there will be no problems when running from source,
but for some build types the plugins dir is not on the path, so relative imports should be used instead. eg:
from ..UltimakerCloudScope import UltimakerCloudScope <-- OK
-import plugins.Toolbox.src ... <-- NOT OK
+import plugins.Marketplace.src ... <-- NOT OK
"""