From c2cd524cdb7844551f5d3254e73ebaea2c1d87a7 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 11 Feb 2022 10:29:13 +0100 Subject: [PATCH] QmlDesigner: Add functionality to welcome plugin * Add extraction progress and birth time * Add URL probing in downloader * Add last modified and available information * Overload openExample() function Task-number: QDS-6174 Change-Id: I68782629da3ec4da5aab95d00510b7bc7085aa66 Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- src/plugins/studiowelcome/examplecheckout.cpp | 115 +++++++++++++++--- src/plugins/studiowelcome/examplecheckout.h | 21 +++- .../studiowelcome/studiowelcomeplugin.cpp | 23 ++++ 3 files changed, 140 insertions(+), 19 deletions(-) diff --git a/src/plugins/studiowelcome/examplecheckout.cpp b/src/plugins/studiowelcome/examplecheckout.cpp index 39beb51c624..6bd1e2bded6 100644 --- a/src/plugins/studiowelcome/examplecheckout.cpp +++ b/src/plugins/studiowelcome/examplecheckout.cpp @@ -41,6 +41,8 @@ #include #include +#include + using namespace Utils; ExampleCheckout::ExampleCheckout(QObject *) {} @@ -137,11 +139,11 @@ FileDownloader::~FileDownloader() void FileDownloader::start() { m_tempFile.setFileName(QDir::tempPath() + "/" + name() + ".XXXXXX" + ".zip"); - m_tempFile.open(QIODevice::WriteOnly); auto request = QNetworkRequest(m_url); - request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, true); + request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, + QNetworkRequest::UserVerifiedRedirectPolicy); QNetworkReply *reply = Utils::NetworkAccessManager::instance()->get(request); QNetworkReply::connect(reply, &QNetworkReply::readyRead, [this, reply]() { @@ -159,20 +161,20 @@ void FileDownloader::start() emit progressChanged(); }); + QNetworkReply::connect(reply, &QNetworkReply::redirected, [reply](const QUrl &url) { + emit reply->redirectAllowed(); + }); + QNetworkReply::connect(reply, &QNetworkReply::finished, [this, reply]() { if (reply->error()) { m_tempFile.remove(); - if (m_url != reply->url()) { - m_url = reply->url(); - start(); - } else { - qDebug() << Q_FUNC_INFO << m_url << reply->errorString(); - emit downloadFailed(); - } + qDebug() << Q_FUNC_INFO << m_url << reply->errorString(); + emit downloadFailed(); } else { m_tempFile.flush(); m_tempFile.close(); m_finished = true; + emit tempFileChanged(); emit finishedChanged(); } }); @@ -181,8 +183,9 @@ void FileDownloader::start() void FileDownloader::setUrl(const QUrl &url) { m_url = url; - emit nameChanged(); + + probeUrl(); } QUrl FileDownloader::url() const @@ -222,16 +225,71 @@ QString FileDownloader::tempFile() const return QFileInfo(m_tempFile).canonicalFilePath(); } +QDateTime FileDownloader::lastModified() const +{ + return m_lastModified; +} + +bool FileDownloader::available() const +{ + return m_available; +} + +void FileDownloader::probeUrl() +{ + auto request = QNetworkRequest(m_url); + request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, + QNetworkRequest::UserVerifiedRedirectPolicy); + QNetworkReply *reply = Utils::NetworkAccessManager::instance()->head(request); + + QNetworkReply::connect(reply, &QNetworkReply::redirected, [reply](const QUrl &url) { + emit reply->redirectAllowed(); + }); + + QNetworkReply::connect(reply, &QNetworkReply::finished, [this, reply]() { + if (reply->error()) + return; + + m_lastModified = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime(); + emit lastModifiedChanged(); + + m_available = true; + emit availableChanged(); + }); + + QNetworkReply::connect(reply, + &QNetworkReply::errorOccurred, + [this, reply](QNetworkReply::NetworkError code) { + // QNetworkReply::HostNotFoundError + // QNetworkReply::ContentNotFoundError + m_available = false; + emit availableChanged(); + }); +} + + FileExtractor::FileExtractor(QObject *parent) : QObject(parent) { - if (Core::DocumentManager::instance()) - m_targetPath = Core::DocumentManager::projectsDirectory(); + m_targetPath = Utils::FilePath::fromString( + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); + + if (!m_targetPath.isEmpty()) + m_targetPath = m_targetPath.pathAppended("QtDesignStudio"); else m_targetPath = "/temp/"; - m_timer.setInterval(500); + m_timer.setInterval(100); m_timer.setSingleShot(false); + + QObject::connect(this, &FileExtractor::targetFolderExistsChanged, [this]() { + if (targetFolderExists()) { + m_birthTime = QFileInfo(m_targetPath.toString() + "/" + m_archiveName).birthTime(); + } else + m_birthTime = QDateTime(); + + emit birthTimeChanged(); + }); } FileExtractor::~FileExtractor() {} @@ -297,8 +355,17 @@ QString FileExtractor::count() const bool FileExtractor::targetFolderExists() const { - const QString targetFolder = m_targetPath.toString() + "/" + m_archiveName; - return QFileInfo(targetFolder).exists(); + return QFileInfo::exists(m_targetPath.toString() + "/" + m_archiveName); +} + +int FileExtractor::progress() const +{ + return m_progress; +} + +QDateTime FileExtractor::birthTime() const +{ + return m_birthTime; } QString FileExtractor::archiveName() const @@ -320,8 +387,9 @@ void FileExtractor::extract() m_timer.start(); const QString targetFolder = m_targetPath.toString() + "/" + m_archiveName; qint64 bytesBefore = QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable(); + qint64 compressedSize = QFileInfo(m_sourceFile.toString()).size(); - QTimer::connect(&m_timer, &QTimer::timeout, [this, bytesBefore, targetFolder]() { + QTimer::connect(&m_timer, &QTimer::timeout, [this, bytesBefore, targetFolder, compressedSize]() { static QHash hash; QDirIterator it(targetFolder, {"*.*"}, QDir::Files, QDirIterator::Subdirectories); @@ -336,9 +404,15 @@ void FileExtractor::extract() count++; } - m_size = QString::number(bytesBefore - - QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable()); + qint64 currentSize = bytesBefore + - QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable(); + // We can not get the uncompressed size of the archive yet, that is why we use an + // approximation. We assume a 50% compression rate. + m_progress = std::min(100ll, currentSize * 100 / compressedSize * 2); + emit progressChanged(); + + m_size = QString::number(currentSize); m_count = QString::number(count); emit sizeChanged(); }); @@ -351,6 +425,11 @@ void FileExtractor::extract() QObject::connect(archive, &Utils::Archive::finished, [this](bool ret) { m_finished = ret; m_timer.stop(); + + m_progress = 100; + emit progressChanged(); + + emit targetFolderExistsChanged(); emit finishedChanged(); QTC_ASSERT(ret, ;); }); diff --git a/src/plugins/studiowelcome/examplecheckout.h b/src/plugins/studiowelcome/examplecheckout.h index 1f8cf00d84c..4e256994377 100644 --- a/src/plugins/studiowelcome/examplecheckout.h +++ b/src/plugins/studiowelcome/examplecheckout.h @@ -78,6 +78,8 @@ class FileExtractor : public QObject Q_PROPERTY(QString sourceFile READ sourceFile WRITE setSourceFile) Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged) Q_PROPERTY(bool targetFolderExists READ targetFolderExists NOTIFY targetFolderExistsChanged) + Q_PROPERTY(int progress READ progress NOTIFY progressChanged) + Q_PROPERTY(QDateTime birthTime READ birthTime NOTIFY birthTimeChanged) public: explicit FileExtractor(QObject *parent = nullptr); @@ -94,6 +96,8 @@ public: QString size() const; QString count() const; bool targetFolderExists() const; + int progress() const; + QDateTime birthTime() const; QString sourceFile() const; QString archiveName() const; @@ -108,6 +112,8 @@ signals: void currentFileChanged(); void sizeChanged(); void targetFolderExistsChanged(); + void progressChanged(); + void birthTimeChanged(); private: Utils::FilePath m_targetPath; @@ -119,6 +125,8 @@ private: QString m_size; QString m_count; QString m_archiveName; + int m_progress = 0; + QDateTime m_birthTime; }; class FileDownloader : public QObject @@ -131,7 +139,9 @@ class FileDownloader : public QObject Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString completeBaseName READ completeBaseName NOTIFY nameChanged) Q_PROPERTY(int progress READ progress NOTIFY progressChanged) - Q_PROPERTY(QString tempFile READ tempFile NOTIFY finishedChanged) + Q_PROPERTY(QString tempFile READ tempFile NOTIFY tempFileChanged) + Q_PROPERTY(QDateTime lastModified READ lastModified NOTIFY lastModifiedChanged) + Q_PROPERTY(bool available READ available NOTIFY availableChanged) public: explicit FileDownloader(QObject *parent = nullptr); @@ -147,6 +157,8 @@ public: QString completeBaseName() const; int progress() const; QString tempFile() const; + QDateTime lastModified() const; + bool available() const; Q_INVOKABLE void start(); @@ -155,12 +167,19 @@ signals: void errorChanged(); void nameChanged(); void progressChanged(); + void tempFileChanged(); void downloadFailed(); + void lastModifiedChanged(); + void availableChanged(); private: + void probeUrl(); + QUrl m_url; bool m_finished = false; bool m_error = false; int m_progress = 0; QFile m_tempFile; + QDateTime m_lastModified; + bool m_available; }; diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index 0a387894bef..620f8f0c58f 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -232,6 +232,28 @@ public: QDesktopServices::openUrl(QUrl("qthelp://org.qt-project.qtcreator/doc/index.html")); } + Q_INVOKABLE void openExample(const QString &examplePath, + const QString &exampleName, + const QString &formFile, + const QString &explicitQmlproject) + { + const QString exampleFolder = examplePath + "/" + exampleName + "/"; + + QString projectFile = exampleFolder + exampleName + ".qmlproject"; + + if (!explicitQmlproject.isEmpty()) + projectFile = exampleFolder + explicitQmlproject; + + ProjectExplorer::ProjectExplorerPlugin::openProjectWelcomePage(projectFile); + + const QString qmlFile = QFileInfo(projectFile).dir().absolutePath() + "/" + formFile; + + // This timer should be replaced with a signal send from project loading + QTimer::singleShot(1000, [qmlFile](){ + Core::EditorManager::openEditor(Utils::FilePath::fromString(qmlFile)); + }); + } + Q_INVOKABLE void openExample(const QString &example, const QString &formFile, const QString &url, @@ -272,6 +294,7 @@ public: Core::EditorManager::openEditor(qmlFile); } + public slots: void resetProjects();