mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-05-21 20:19:32 +08:00
CURA-5035 Re-implemented "installed" view
This commit is contained in:
parent
80c21acf79
commit
a947b768d3
@ -44,7 +44,7 @@ Window
|
||||
ToolboxLoadingPage
|
||||
{
|
||||
id: viewLoading
|
||||
visible: manager.viewCategory != "installed" && !dataReady
|
||||
visible: manager.viewCategory != "installed" && manager.viewPage == "loading"
|
||||
// TODO: Replace !dataReady with manager.viewPage == "loading"
|
||||
}
|
||||
ToolboxDownloadsPage
|
||||
@ -65,8 +65,7 @@ Window
|
||||
ToolboxInstalledPage
|
||||
{
|
||||
id: installedPluginList
|
||||
visible: manager.viewCategory == "installed" && dataReady
|
||||
// TODO: Replace !dataReady with manager.viewPage == "loading"
|
||||
visible: manager.viewCategory == "installed"
|
||||
}
|
||||
}
|
||||
ToolboxShadow
|
||||
|
@ -34,7 +34,7 @@ Column
|
||||
|
||||
Repeater
|
||||
{
|
||||
model: manager.materialShowcaseModel
|
||||
model: manager.pluginsShowcaseModel
|
||||
delegate: ToolboxDownloadsShowcaseTile {}
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,102 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// PluginBrowser is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Dialogs 1.1
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
ScrollView
|
||||
{
|
||||
anchors.fill: parent
|
||||
ListView
|
||||
id: base
|
||||
frameVisible: true
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
style: UM.Theme.styles.scrollview
|
||||
Column
|
||||
{
|
||||
id: pluginList
|
||||
property var activePlugin
|
||||
property var filter: "installed"
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
anchors
|
||||
{
|
||||
fill: parent
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
rightMargin: UM.Theme.getSize("default_margin").width
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
leftMargin: UM.Theme.getSize("double_margin").width
|
||||
topMargin: UM.Theme.getSize("double_margin").height
|
||||
bottomMargin: UM.Theme.getSize("double_margin").height
|
||||
top: parent.top
|
||||
}
|
||||
height: childrenRect.height + 4 * UM.Theme.getSize("default_margin").height
|
||||
Label
|
||||
{
|
||||
width: parent.width
|
||||
text: "Plugins"
|
||||
color: UM.Theme.getColor("text_medium")
|
||||
font: UM.Theme.getFont("medium")
|
||||
}
|
||||
Rectangle
|
||||
{
|
||||
color: "transparent"
|
||||
width: parent.width
|
||||
height: childrenRect.height + 1 * UM.Theme.getSize("default_lining").width
|
||||
border.color: UM.Theme.getColor("text_medium")
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
Column
|
||||
{
|
||||
height: childrenRect.height
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
rightMargin: UM.Theme.getSize("default_margin").width
|
||||
topMargin: UM.Theme.getSize("default_lining").width
|
||||
bottomMargin: UM.Theme.getSize("default_lining").width
|
||||
}
|
||||
Repeater
|
||||
{
|
||||
id: materialList
|
||||
model: manager.packagesModel
|
||||
delegate: ToolboxInstalledTile {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Label
|
||||
{
|
||||
width: base.width
|
||||
text: "Materials"
|
||||
color: UM.Theme.getColor("text_medium")
|
||||
font: UM.Theme.getFont("medium")
|
||||
}
|
||||
Rectangle
|
||||
{
|
||||
color: "transparent"
|
||||
width: parent.width
|
||||
height: childrenRect.height + 1 * UM.Theme.getSize("default_lining").width
|
||||
border.color: UM.Theme.getColor("text_medium")
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
Column
|
||||
{
|
||||
height: childrenRect.height
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
rightMargin: UM.Theme.getSize("default_margin").width
|
||||
topMargin: UM.Theme.getSize("default_lining").width
|
||||
bottomMargin: UM.Theme.getSize("default_lining").width
|
||||
}
|
||||
Repeater
|
||||
{
|
||||
id: pluginList
|
||||
model: manager.packagesModel
|
||||
delegate: ToolboxInstalledTile {}
|
||||
}
|
||||
}
|
||||
}
|
||||
model: manager.pluginsModel
|
||||
delegate: ToolboxInstalledTile {}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ Component {
|
||||
// Don't show required plugins as they can't be managed anyway:
|
||||
height: !model.required ? 84 : 0
|
||||
visible: !model.required ? true : false
|
||||
color: Qt.rgba(1.0, 0.0, 0.0, 0.1)
|
||||
// color: Qt.rgba(1.0, 0.0, 0.0, 0.1)
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
|
@ -14,5 +14,13 @@ Rectangle
|
||||
id: base
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: "red"
|
||||
color: "transparent"
|
||||
Label
|
||||
{
|
||||
text: "Fetching packages..."
|
||||
anchors
|
||||
{
|
||||
centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import platform
|
||||
import zipfile
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.CuraPackageManager import CuraPackageManager
|
||||
from .AuthorsModel import AuthorsModel
|
||||
from .PackagesModel import PackagesModel
|
||||
|
||||
@ -33,6 +34,7 @@ class Toolbox(QObject, Extension):
|
||||
super().__init__(parent)
|
||||
|
||||
self._plugin_registry = Application.getInstance().getPluginRegistry()
|
||||
self._package_manager = None
|
||||
self._packages_version = self._plugin_registry.APIVersion
|
||||
self._api_version = 1
|
||||
self._api_url = "https://api-staging.ultimaker.com/cura-packages/v{api_version}/cura/v{package_version}".format( api_version = self._api_version, package_version = self._packages_version)
|
||||
@ -48,11 +50,14 @@ class Toolbox(QObject, Extension):
|
||||
self._network_manager = None
|
||||
|
||||
|
||||
self._packages_metadata = [] # Stores the remote information of the packages
|
||||
self._packages_model = None # Model that list the remote available packages
|
||||
self._showcase_model = None
|
||||
self._packages_metadata = []
|
||||
self._packages_model = None
|
||||
self._plugins_showcase_model = None
|
||||
self._plugins_installed_model = None
|
||||
self._materials_showcase_model = None
|
||||
self._materials_installed_model = None
|
||||
self._authors_model = None
|
||||
self._installed_model = None
|
||||
|
||||
|
||||
# These properties are for keeping track of the UI state:
|
||||
# ----------------------------------------------------------------------
|
||||
@ -66,7 +71,7 @@ class Toolbox(QObject, Extension):
|
||||
# View page defines which type of page layout to use. For example,
|
||||
# possible values include "overview", "detail" or "author".
|
||||
# Formerly self._detail_view.
|
||||
self._view_page = "overview"
|
||||
self._view_page = "loading"
|
||||
|
||||
# View selection defines what is currently selected and should be
|
||||
# used in filtering. This could be an author name (if _view_page is set
|
||||
@ -155,9 +160,19 @@ class Toolbox(QObject, Extension):
|
||||
|
||||
@pyqtSlot()
|
||||
def browsePackages(self):
|
||||
self._createNetworkManager()
|
||||
self.requestShowcase()
|
||||
self.requestPackages()
|
||||
self._package_manager = Application.getInstance().getCuraPackageManager()
|
||||
# Create the network manager:
|
||||
# This was formerly its own function but really had no reason to be as
|
||||
# it was never called more than once ever.
|
||||
if self._network_manager:
|
||||
self._network_manager.finished.disconnect(self._onRequestFinished)
|
||||
self._network_manager.networkAccessibleChanged.disconnect(self._onNetworkAccesibleChanged)
|
||||
self._network_manager = QNetworkAccessManager()
|
||||
self._network_manager.finished.connect(self._onRequestFinished)
|
||||
self._network_manager.networkAccessibleChanged.connect(self._onNetworkAccesibleChanged)
|
||||
|
||||
self._requestShowcase()
|
||||
self._requestPackages()
|
||||
if not self._dialog:
|
||||
self._dialog = self._createDialog("Toolbox.qml")
|
||||
self._dialog.show()
|
||||
@ -224,8 +239,8 @@ class Toolbox(QObject, Extension):
|
||||
return self._plugins_model
|
||||
|
||||
@pyqtProperty(QObject, notify = showcaseMetadataChanged)
|
||||
def materialShowcaseModel(self):
|
||||
return self._showcase_model
|
||||
def pluginsShowcaseModel(self):
|
||||
return self._plugins_showcase_model
|
||||
|
||||
@pyqtProperty(QObject, notify = packagesMetadataChanged)
|
||||
def packagesModel(self):
|
||||
@ -239,6 +254,18 @@ class Toolbox(QObject, Extension):
|
||||
def dataReady(self):
|
||||
return self._packages_model is not None
|
||||
|
||||
@pyqtProperty(bool, notify = restartRequiredChanged)
|
||||
def restartRequired(self):
|
||||
return self._restart_required
|
||||
|
||||
@pyqtSlot()
|
||||
def restart(self):
|
||||
CuraApplication.getInstance().windowClosed()
|
||||
|
||||
|
||||
|
||||
# Checks
|
||||
# --------------------------------------------------------------------------
|
||||
def _checkCanUpgrade(self, id, version):
|
||||
# Scan plugin server data for plugin with the given id:
|
||||
for plugin in self._packages_metadata:
|
||||
@ -250,57 +277,32 @@ class Toolbox(QObject, Extension):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _checkAlreadyInstalled(self, id):
|
||||
metadata = self._plugin_registry.getMetaData(id)
|
||||
# We already installed this plugin, but the registry just doesn't know it yet.
|
||||
if id in self._newly_installed_plugin_ids:
|
||||
return True
|
||||
# We already uninstalled this plugin, but the registry just doesn't know it yet:
|
||||
elif id in self._newly_uninstalled_plugin_ids:
|
||||
def _checkInstalled(self, id):
|
||||
if id in self._will_uninstall:
|
||||
return False
|
||||
elif metadata != {}:
|
||||
if id in self._package_manager.getInstalledPackages():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _checkInstallStatus(self, plugin_id):
|
||||
if plugin_id in self._plugin_registry.getInstalledPlugins():
|
||||
return "installed"
|
||||
else:
|
||||
return "uninstalled"
|
||||
if id in self._will_install:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _checkEnabled(self, id):
|
||||
if id in self._plugin_registry.getActivePlugins():
|
||||
return True
|
||||
return False
|
||||
def _createNetworkManager(self):
|
||||
if self._network_manager:
|
||||
self._network_manager.finished.disconnect(self._onRequestFinished)
|
||||
self._network_manager.networkAccessibleChanged.disconnect(self._onNetworkAccesibleChanged)
|
||||
self._network_manager = QNetworkAccessManager()
|
||||
self._network_manager.finished.connect(self._onRequestFinished)
|
||||
self._network_manager.networkAccessibleChanged.connect(self._onNetworkAccesibleChanged)
|
||||
|
||||
@pyqtProperty(bool, notify = restartRequiredChanged)
|
||||
def restartRequired(self):
|
||||
return self._restart_required
|
||||
|
||||
@pyqtSlot()
|
||||
def restart(self):
|
||||
CuraApplication.getInstance().windowClosed()
|
||||
|
||||
|
||||
|
||||
# Make API Calls
|
||||
# --------------------------------------------------------------------------
|
||||
def requestPackages(self):
|
||||
def _requestPackages(self):
|
||||
Logger.log("i", "Toolbox: Requesting package list from server.")
|
||||
url = QUrl("{base_url}/packages".format(base_url = self._api_url))
|
||||
self._get_packages_request = QNetworkRequest(url)
|
||||
self._get_packages_request.setRawHeader(*self._request_header)
|
||||
self._network_manager.get(self._get_packages_request)
|
||||
|
||||
def requestShowcase(self):
|
||||
def _requestShowcase(self):
|
||||
Logger.log("i", "Toolbox: Requesting showcase list from server.")
|
||||
url = QUrl("{base_url}/showcase".format(base_url = self._api_url))
|
||||
self._get_showcase_request = QNetworkRequest(url)
|
||||
@ -381,6 +383,7 @@ class Toolbox(QObject, Extension):
|
||||
self._authors_metadata.append(package["author"])
|
||||
self._authors_model.setMetaData(self._authors_metadata)
|
||||
self.authorsMetadataChanged.emit()
|
||||
self.setViewPage("overview")
|
||||
except json.decoder.JSONDecodeError:
|
||||
Logger.log("w", "Toolbox: Received invalid JSON for package list.")
|
||||
return
|
||||
@ -390,12 +393,12 @@ class Toolbox(QObject, Extension):
|
||||
try:
|
||||
json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
|
||||
# Create packages model with all packages:
|
||||
if not self._showcase_model:
|
||||
self._showcase_model = PackagesModel()
|
||||
if not self._plugins_showcase_model:
|
||||
self._plugins_showcase_model = PackagesModel()
|
||||
self._showcase_metadata = json_data["data"]
|
||||
print(self._showcase_metadata)
|
||||
self._showcase_model.setPackagesMetaData(self._showcase_metadata)
|
||||
for package in self._showcase_model.items:
|
||||
self._plugins_showcase_model.setPackagesMetaData(self._showcase_metadata)
|
||||
for package in self._plugins_showcase_model.items:
|
||||
print(package)
|
||||
self.showcaseMetadataChanged.emit()
|
||||
except json.decoder.JSONDecodeError:
|
||||
@ -406,63 +409,66 @@ class Toolbox(QObject, Extension):
|
||||
pass
|
||||
|
||||
def _onDownloadProgress(self, bytes_sent, bytes_total):
|
||||
print("Downloading bytes:", bytes_total)
|
||||
if bytes_total > 0:
|
||||
new_progress = bytes_sent / bytes_total * 100
|
||||
self.setDownloadProgress(new_progress)
|
||||
if new_progress == 100.0:
|
||||
Logger.log("i", "Toolbox: Download complete.")
|
||||
self.setIsDownloading(False)
|
||||
self._download_reply.downloadProgress.disconnect(self._onDownloadProgress)
|
||||
|
||||
# must not delete the temporary file on Windows
|
||||
self._temp_plugin_file = tempfile.NamedTemporaryFile(mode = "w+b", suffix = ".curaplugin", delete = False)
|
||||
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(self._download_reply.readAll())
|
||||
self._temp_plugin_file.close()
|
||||
|
||||
self._onDownloadComplete(file_path)
|
||||
return
|
||||
|
||||
def _onDownloadComplete(self, file_path):
|
||||
with zipfile.ZipFile(file_path, "r") as zip_ref:
|
||||
plugin_id = None
|
||||
for file in zip_ref.infolist():
|
||||
if file.filename.endswith("/"):
|
||||
plugin_id = file.filename.strip("/")
|
||||
break
|
||||
Logger.log("i", "Toolbox: Download complete.")
|
||||
print(file_path)
|
||||
if self._package_manager.isPackageFile(file_path):
|
||||
self._package_manager.install(file_path)
|
||||
return
|
||||
else:
|
||||
Logger.log("w", "Toolbox: Package was not a valid CuraPackage.")
|
||||
|
||||
if plugin_id is None:
|
||||
msg = i18n_catalog.i18nc("@info:status", "Failed to get plugin ID from <filename>{0}</filename>", file_path)
|
||||
msg_title = i18n_catalog.i18nc("@info:tile", "Warning")
|
||||
self._progress_message = Message(msg, lifetime=0, dismissable=False, title = msg_title)
|
||||
return
|
||||
|
||||
# find a potential license file
|
||||
plugin_root_dir = plugin_id + "/"
|
||||
license_file = None
|
||||
for f in zip_ref.infolist():
|
||||
# skip directories (with file_size = 0) and files not in the plugin directory
|
||||
if f.file_size == 0 or not f.filename.startswith(plugin_root_dir):
|
||||
continue
|
||||
file_name = os.path.basename(f.filename).lower()
|
||||
file_base_name, file_ext = os.path.splitext(file_name)
|
||||
if file_base_name in ["license", "licence"]:
|
||||
license_file = f.filename
|
||||
break
|
||||
|
||||
# show a dialog for user to read and accept/decline the license
|
||||
if license_file is not None:
|
||||
Logger.log("i", "Found license file for plugin [%s], showing the license dialog to the user", plugin_id)
|
||||
license_content = zip_ref.read(license_file).decode('utf-8')
|
||||
self.openLicenseDialog(plugin_id, license_content, file_path)
|
||||
return
|
||||
|
||||
# there is no license file, directly install the plugin
|
||||
self.installPlugin(file_path)
|
||||
return
|
||||
# with zipfile.ZipFile(file_path, "r") as zip_ref:
|
||||
# plugin_id = None
|
||||
# for file in zip_ref.infolist():
|
||||
# if file.filename.endswith("/"):
|
||||
# plugin_id = file.filename.strip("/")
|
||||
# break
|
||||
#
|
||||
# if plugin_id is None:
|
||||
# msg = i18n_catalog.i18nc("@info:status", "Failed to get plugin ID from <filename>{0}</filename>", file_path)
|
||||
# msg_title = i18n_catalog.i18nc("@info:tile", "Warning")
|
||||
# self._progress_message = Message(msg, lifetime=0, dismissable=False, title = msg_title)
|
||||
# return
|
||||
#
|
||||
# # find a potential license file
|
||||
# plugin_root_dir = plugin_id + "/"
|
||||
# license_file = None
|
||||
# for f in zip_ref.infolist():
|
||||
# # skip directories (with file_size = 0) and files not in the plugin directory
|
||||
# if f.file_size == 0 or not f.filename.startswith(plugin_root_dir):
|
||||
# continue
|
||||
# file_name = os.path.basename(f.filename).lower()
|
||||
# file_base_name, file_ext = os.path.splitext(file_name)
|
||||
# if file_base_name in ["license", "licence"]:
|
||||
# license_file = f.filename
|
||||
# break
|
||||
#
|
||||
# # show a dialog for user to read and accept/decline the license
|
||||
# if license_file is not None:
|
||||
# Logger.log("i", "Found license file for plugin [%s], showing the license dialog to the user", plugin_id)
|
||||
# license_content = zip_ref.read(license_file).decode('utf-8')
|
||||
# self.openLicenseDialog(plugin_id, license_content, file_path)
|
||||
# return
|
||||
#
|
||||
# # there is no license file, directly install the plugin
|
||||
# self.installPlugin(file_path)
|
||||
# return
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user