summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVikas Pachdha <vikas.pachdha@qt.io>2020-06-19 10:00:47 +0200
committerVikas Pachdha <vikas.pachdha@qt.io>2020-06-26 10:26:43 +0000
commit9daf5c130da6edb0f03b9f7f61c34c368d4cca56 (patch)
tree7db58ba08141334805f3bb415f3ebc1e599fcb2a
parent000281fed770b5af96d093724fb1be1347bf5eed (diff)
downloadqt-creator-9daf5c130da6edb0f03b9f7f61c34c368d4cca56.tar.gz
AssetExport: Export assets from renderable nodes
Task-number: QDS-1555 Change-Id: I3d5b60ee8214aeee054587f45045beea020d1f13 Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt1
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp162
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporter.h7
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp9
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri2
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs2
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h2
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp66
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.h42
9 files changed, 288 insertions, 5 deletions
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index 94a4f449a7..12b58ac14f 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -52,6 +52,7 @@ add_qtc_plugin(assetexporterplugin
assetexporterplugin/componentexporter.h assetexporterplugin/componentexporter.cpp
assetexporterplugin/exportnotification.h assetexporterplugin/exportnotification.cpp
assetexporterplugin/filepathmodel.h assetexporterplugin/filepathmodel.cpp
+ assetexporterplugin/parsers/assetnodeparser.h assetexporterplugin/parsers/assetnodeparser.cpp
assetexporterplugin/parsers/modelitemnodeparser.h assetexporterplugin/parsers/modelitemnodeparser.cpp
assetexporterplugin/parsers/modelnodeparser.h assetexporterplugin/parsers/modelnodeparser.cpp
assetexporterplugin/parsers/textnodeparser.h assetexporterplugin/parsers/textnodeparser.cpp
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
index e85ac11cab..49d763f6b4 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
@@ -26,15 +26,39 @@
#include "componentexporter.h"
#include "exportnotification.h"
+#include "qmlitemnode.h"
+#include "qmlobjectnode.h"
#include "utils/qtcassert.h"
+#include "utils/runextensions.h"
+#include "variantproperty.h"
+#include <QCryptographicHash>
+#include <QDir>
#include <QJsonArray>
#include <QJsonDocument>
#include <QLoggingCategory>
+#include <QWaitCondition>
-using namespace ProjectExplorer;
+#include <random>
+#include <queue>
+using namespace ProjectExplorer;
+using namespace std;
namespace {
+bool makeParentPath(const Utils::FilePath &path)
+{
+ QDir d;
+ return d.mkpath(path.toFileInfo().absolutePath());
+}
+
+QByteArray generateHash(const QString &token) {
+ static uint counter = 0;
+ std::mt19937 gen(std::random_device().operator()());
+ std::uniform_int_distribution<> distribution(1, 99999);
+ QByteArray data = QString("%1%2%3").arg(token).arg(++counter).arg(distribution(gen)).toLatin1();
+ return QCryptographicHash::hash(data, QCryptographicHash::Md5).toHex();
+}
+
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.assetExporter", QtInfoMsg)
Q_LOGGING_CATEGORY(loggerWarn, "qtc.designer.assetExportPlugin.assetExporter", QtWarningMsg)
Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.assetExporter", QtCriticalMsg)
@@ -42,6 +66,34 @@ Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.assetExporter",
namespace QmlDesigner {
+class AssetDumper
+{
+public:
+ AssetDumper();
+ ~AssetDumper();
+
+ void dumpAsset(const QPixmap &p, const Utils::FilePath &path);
+
+ /* Keeps on dumping until all assets are dumped, then quits */
+ void quitDumper();
+
+ /* Aborts dumping */
+ void abortDumper();
+
+private:
+ void addAsset(const QPixmap &p, const Utils::FilePath &path);
+ void doDumping(QFutureInterface<void> &fi);
+ void savePixmap(const QPixmap &p, Utils::FilePath &path) const;
+
+ QFuture<void> m_dumpFuture;
+ QMutex m_queueMutex;
+ QWaitCondition m_queueCondition;
+ std::queue<std::pair<QPixmap, Utils::FilePath>> m_assets;
+ std::atomic<bool> m_quitDumper;
+};
+
+
+
AssetExporter::AssetExporter(AssetExporterView *view, ProjectExplorer::Project *project, QObject *parent) :
QObject(parent),
m_currentState(*this),
@@ -71,11 +123,13 @@ void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::Fil
m_exportPath = exportPath;
m_currentState.change(ParsingState::Parsing);
triggerLoadNextFile();
+ m_assetDumper = make_unique<AssetDumper>();
}
void AssetExporter::cancel()
{
// TODO Cancel export
+ m_assetDumper.reset();
}
bool AssetExporter::isBusy() const
@@ -85,6 +139,21 @@ bool AssetExporter::isBusy() const
m_currentState == AssetExporter::ParsingState::WritingJson;
}
+Utils::FilePath AssetExporter::exportAsset(const QmlObjectNode &node)
+{
+ // TODO: Use this hash as UUID and add to the node.
+ QByteArray hash;
+ do {
+ hash = generateHash(node.id());
+ } while (m_usedHashes.contains(hash));
+ m_usedHashes.insert(hash);
+
+ Utils::FilePath assetPath = m_exportPath.pathAppended(QString("assets/%1.png")
+ .arg(QString::fromLatin1(hash)));
+ m_assetDumper->dumpAsset(node.toQmlItemNode().instanceRenderPixmap(), assetPath);
+ return assetPath;
+}
+
void AssetExporter::exportComponent(const ModelNode &rootNode)
{
qCDebug(loggerInfo) << "Exporting component" << rootNode.id();
@@ -149,6 +218,7 @@ void AssetExporter::writeMetadata() const
Utils::FilePath metadataPath = m_exportPath.pathAppended(m_exportPath.fileName() + ".metadata");
ExportNotification::addInfo(tr("Writing metadata to file %1.").
arg(metadataPath.toUserOutput()));
+ makeParentPath(metadataPath);
m_currentState.change(ParsingState::WritingJson);
QJsonObject jsonRoot; // TODO: Write plugin info to root
jsonRoot.insert("artboards", m_components);
@@ -165,6 +235,7 @@ void AssetExporter::writeMetadata() const
}
notifyProgress(1.0);
ExportNotification::addInfo(tr("Export finished."));
+ m_assetDumper->quitDumper();
m_currentState.change(ParsingState::ExportingDone);
}
@@ -189,4 +260,93 @@ QDebug operator<<(QDebug os, const AssetExporter::ParsingState &s)
return os;
}
+AssetDumper::AssetDumper():
+ m_quitDumper(false)
+{
+ m_dumpFuture = Utils::runAsync(&AssetDumper::doDumping, this);
+}
+
+AssetDumper::~AssetDumper()
+{
+ abortDumper();
+}
+
+void AssetDumper::dumpAsset(const QPixmap &p, const Utils::FilePath &path)
+{
+ addAsset(p, path);
+}
+
+void AssetDumper::quitDumper()
+{
+ m_quitDumper = true;
+ m_queueCondition.wakeAll();
+ if (!m_dumpFuture.isFinished())
+ m_dumpFuture.waitForFinished();
+}
+
+void AssetDumper::abortDumper()
+{
+ if (!m_dumpFuture.isFinished()) {
+ m_dumpFuture.cancel();
+ m_queueCondition.wakeAll();
+ m_dumpFuture.waitForFinished();
+ }
+}
+
+void AssetDumper::addAsset(const QPixmap &p, const Utils::FilePath &path)
+{
+ QMutexLocker locker(&m_queueMutex);
+ qDebug() << "Save Asset:" << path;
+ m_assets.push({p, path});
+}
+
+void AssetDumper::doDumping(QFutureInterface<void> &fi)
+{
+ auto haveAsset = [this] (std::pair<QPixmap, Utils::FilePath> *asset) {
+ QMutexLocker locker(&m_queueMutex);
+ if (m_assets.empty())
+ return false;
+ *asset = m_assets.front();
+ m_assets.pop();
+ return true;
+ };
+
+ forever {
+ std::pair<QPixmap, Utils::FilePath> asset;
+ if (haveAsset(&asset)) {
+ if (fi.isCanceled())
+ break;
+ savePixmap(asset.first, asset.second);
+ } else {
+ if (m_quitDumper)
+ break;
+ QMutexLocker locker(&m_queueMutex);
+ m_queueCondition.wait(&m_queueMutex);
+ }
+
+ if (fi.isCanceled())
+ break;
+ }
+ fi.reportFinished();
+}
+
+void AssetDumper::savePixmap(const QPixmap &p, Utils::FilePath &path) const
+{
+ if (p.isNull()) {
+ qCDebug(loggerWarn) << "Dumping null pixmap" << path;
+ return;
+ }
+
+ if (!makeParentPath(path)) {
+ ExportNotification::addError(AssetExporter::tr("Error creating asset directory. %1")
+ .arg(path.fileName()));
+ return;
+ }
+
+ if (!p.save(path.toString())) {
+ ExportNotification::addError(AssetExporter::tr("Error saving asset. %1")
+ .arg(path.fileName()));
+ }
+}
+
}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h
index 9c2212f5e2..a4d6df23c0 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h
@@ -29,6 +29,7 @@
#include <QJsonArray>
#include <QJsonObject>
+#include <QSet>
#include <memory>
@@ -41,6 +42,7 @@ class Project;
}
namespace QmlDesigner {
+class AssetDumper;
class AssetExporter : public QObject
{
@@ -68,6 +70,8 @@ public:
void cancel();
bool isBusy() const;
+ Utils::FilePath exportAsset(const QmlObjectNode& node);
+
signals:
void stateChanged(ParsingState);
void exportProgressChanged(double) const;
@@ -82,6 +86,7 @@ private:
void loadNextFile();
void onQmlFileLoaded();
+
private:
mutable class State {
public:
@@ -96,6 +101,8 @@ private:
Utils::FilePaths m_exportFiles;
Utils::FilePath m_exportPath;
QJsonArray m_components;
+ QSet<QByteArray> m_usedHashes;
+ std::unique_ptr<AssetDumper> m_assetDumper;
};
QDebug operator<< (QDebug os, const QmlDesigner::AssetExporter::ParsingState& s);
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp
index fcd492cb0e..e45e48ca0d 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp
@@ -34,6 +34,7 @@
#include "parsers/modelitemnodeparser.h"
#include "parsers/textnodeparser.h"
+#include "parsers/assetnodeparser.h"
#include "coreplugin/actionmanager/actionmanager.h"
#include "coreplugin/actionmanager/actioncontainer.h"
@@ -68,8 +69,9 @@ AssetExporterPlugin::AssetExporterPlugin() :
viewManager.registerViewTakingOwnership(m_view);
// Add parsers templates for factory instantiation.
- ComponentExporter::addNodeParser<ItemNodeParser>();
- ComponentExporter::addNodeParser<TextNodeParser>();
+ Component::addNodeParser<ItemNodeParser>();
+ Component::addNodeParser<TextNodeParser>();
+ Component::addNodeParser<AssetNodeParser>();
// Instantiate actions created by the plugin.
addActions();
@@ -93,7 +95,8 @@ void AssetExporterPlugin::onExport()
return;
FilePathModel model(startupProject);
- auto exportDir = startupProject->projectFilePath().parentDir();
+ QString exportDirName = startupProject->displayName() + "_export";
+ auto exportDir = startupProject->projectFilePath().parentDir().pathAppended(exportDirName);
AssetExporter assetExporter(m_view, startupProject);
AssetExportDialog assetExporterDialog(exportDir, assetExporter, model);
assetExporterDialog.exec();
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri
index b0e4f6392a..713ab1184f 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri
@@ -15,6 +15,7 @@ HEADERS += \
componentexporter.h \
exportnotification.h \
filepathmodel.h \
+ parsers/assetnodeparser.h \
parsers/modelitemnodeparser.h \
parsers/modelnodeparser.h \
parsers/textnodeparser.h
@@ -27,6 +28,7 @@ SOURCES += \
componentexporter.cpp \
exportnotification.cpp \
filepathmodel.cpp \
+ parsers/assetnodeparser.cpp \
parsers/modelitemnodeparser.cpp \
parsers/modelnodeparser.cpp \
parsers/textnodeparser.cpp
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs
index e8ce253736..e847525324 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs
@@ -47,6 +47,8 @@ QtcProduct {
"exportnotification.h",
"filepathmodel.cpp",
"filepathmodel.h",
+ "parsers/assetnodeparser.cpp",
+ "parsers/assetnodeparser.h",
"parsers/modelitemnodeparser.cpp",
"parsers/modelitemnodeparser.h",
"parsers/modelnodeparser.cpp",
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h
index 169d99ebfb..2e9ba7a56d 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h
@@ -57,7 +57,7 @@ const char ImportsTag[] = "extraImports";
const char UuidTag[] = "uuid";
const char ClipTag[] = "clip";
const char AssetDataTag[] = "assetData";
-const char AssetPath[] = "assetPath";
+const char AssetPathTag[] = "assetPath";
const char AssetBoundsTag[] = "assetBounds";
const char OpacityTag[] = "opacity";
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp
new file mode 100644
index 0000000000..159eccec46
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "assetnodeparser.h"
+#include "assetexportpluginconstants.h"
+#include "assetexporter.h"
+
+#include "qmlitemnode.h"
+#include "componentexporter.h"
+
+#include "utils/fileutils.h"
+
+#include <QPixmap>
+
+namespace QmlDesigner {
+using namespace Constants;
+AssetNodeParser::AssetNodeParser(const QByteArrayList &lineage, const ModelNode &node) :
+ ItemNodeParser(lineage, node)
+{
+
+}
+
+bool AssetNodeParser::isExportable() const
+{
+ auto hasType = [this](const QByteArray &type) {
+ return lineage().contains(type);
+ };
+ return hasType("QtQuick.Image") || hasType("QtQuick.Rectangle");
+}
+
+QJsonObject AssetNodeParser::json(Component &component) const
+{
+ QJsonObject jsonObject = ItemNodeParser::json(component);
+
+ QPixmap asset = objectNode().toQmlItemNode().instanceRenderPixmap();
+ Utils::FilePath assetPath = component.exporter().exportAsset(objectNode());
+
+ QJsonObject assetData;
+ assetData.insert(AssetPathTag, assetPath.toString());
+ jsonObject.insert(AssetDataTag, assetData);
+ return jsonObject;
+}
+}
+
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.h
new file mode 100644
index 0000000000..be764b17ec
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+#pragma once
+
+#include "modelitemnodeparser.h"
+
+namespace QmlDesigner {
+class Component;
+
+class AssetNodeParser : public ItemNodeParser
+{
+public:
+ AssetNodeParser(const QByteArrayList &lineage, const ModelNode &node);
+ ~AssetNodeParser() override = default;
+
+ bool isExportable() const override;
+ int priority() const override { return 200; }
+ QJsonObject json(Component &component) const override;
+};
+}