Skip to content
Snippets Groups Projects
Verified Commit 92bb8368 authored by Burak Hançerli's avatar Burak Hançerli :headphones:
Browse files

fix: better qmlproject parsing handling

parent 9a20e7cb
No related branches found
No related tags found
2 merge requests!22QDS-11332 Implement better qmlproject handling,!21Design studio connector
Pipeline #64504 passed
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include <QFileInfo> #include <QFileInfo>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject> #include <QJsonObject>
#include <QMessageBox>
#include <QSettings> #include <QSettings>
#include <QSslSocket> #include <QSslSocket>
...@@ -83,13 +83,6 @@ void Backend::initialize() ...@@ -83,13 +83,6 @@ void Backend::initialize()
qDebug("Initialization complete"); qDebug("Initialization complete");
} }
void Backend::showWarning(const QString &message)
{
QMessageBox msg(QMessageBox::Warning, "Warning", message, QMessageBox::Ok);
msg.exec();
qWarning() << message;
}
void Backend::updatePopup(const QString &text, bool indeterminate) void Backend::updatePopup(const QString &text, bool indeterminate)
{ {
emit popupTextChanged(text); emit popupTextChanged(text);
...@@ -128,8 +121,9 @@ bool Backend::connectDesignStudio() ...@@ -128,8 +121,9 @@ bool Backend::connectDesignStudio()
const QString projectPath = m_projectManager.unpackProject(projectData); const QString projectPath = m_projectManager.unpackProject(projectData);
if (projectPath.isEmpty()) { if (projectPath.isEmpty()) {
showWarning("Could not unpack project. Please check the logs for more " qCritical()
"information."); << "Could not unpack project. Please check the logs for more "
"information.";
emit popupClose(); emit popupClose();
return; return;
} }
...@@ -138,15 +132,16 @@ bool Backend::connectDesignStudio() ...@@ -138,15 +132,16 @@ bool Backend::connectDesignStudio()
updatePopup("Running project..."); updatePopup("Running project...");
if (!m_projectManager.runProject(projectPath, "UntitledProject35")) { if (!m_projectManager.runProject(projectPath, "UntitledProject35")) {
showWarning("Could not run project. Please check the logs for more " qCritical() << "Could not run project. Please check the logs for more "
"information."); "information.";
} else { } else {
m_projectManager.showAppWindow(); m_projectManager.showAppWindow();
} }
emit popupClose(); emit popupClose();
}); });
if (!m_designStudioConnector->initialize()) { if (!m_designStudioConnector->initialize()) {
showWarning("Could initialize server. Please check the logs for more information."); qCritical()
<< "Could initialize server. Please check the logs for more information.";
m_designStudioConnector.reset(); m_designStudioConnector.reset();
} }
}, },
...@@ -197,8 +192,8 @@ void Backend::runDemoProject(const QString &projectName) ...@@ -197,8 +192,8 @@ void Backend::runDemoProject(const QString &projectName)
updatePopup("Caching demo project..."); updatePopup("Caching demo project...");
if (!m_projectManager.cacheDemoProject(project, projectName)) { if (!m_projectManager.cacheDemoProject(project, projectName)) {
showWarning( qCritical()
"Could not cache demo project. Please check the logs for more information."); << "Could not cache demo project. Please check the logs for more information.";
emit popupClose(); emit popupClose();
return; return;
} }
...@@ -208,7 +203,7 @@ void Backend::runDemoProject(const QString &projectName) ...@@ -208,7 +203,7 @@ void Backend::runDemoProject(const QString &projectName)
updatePopup("Running demo project..."); updatePopup("Running demo project...");
if (!m_projectManager.runDemoProject(projectName)) if (!m_projectManager.runDemoProject(projectName))
showWarning("Could not run demo project. Please check the logs for more information."); qCritical() << "Could not run demo project. Please check the logs for more information.";
else else
m_projectManager.showAppWindow(); m_projectManager.showAppWindow();
...@@ -250,7 +245,7 @@ void Backend::runUserProject(const QString &url) ...@@ -250,7 +245,7 @@ void Backend::runUserProject(const QString &url)
updatePopup("Caching user project..."); updatePopup("Caching user project...");
if (!m_projectManager.cacheProject(projectData, projectInfo)) { if (!m_projectManager.cacheProject(projectData, projectInfo)) {
showWarning("Could not cache project. Please check the logs for more information."); qCritical() << "Could not cache project. Please check the logs for more information.";
emit popupClose(); emit popupClose();
return; return;
} }
...@@ -260,7 +255,7 @@ void Backend::runUserProject(const QString &url) ...@@ -260,7 +255,7 @@ void Backend::runUserProject(const QString &url)
updatePopup("Running cached project..."); updatePopup("Running cached project...");
if (!m_projectManager.runCachedProject(projectInfo)) if (!m_projectManager.runCachedProject(projectInfo))
showWarning("Could not run project. Please check the logs for more information."); qCritical() << "Could not run project. Please check the logs for more information.";
else else
m_projectManager.showAppWindow(); m_projectManager.showAppWindow();
......
...@@ -64,7 +64,6 @@ private: ...@@ -64,7 +64,6 @@ private:
QThread m_dsConnectorThread; QThread m_dsConnectorThread;
// member functions // member functions
void showWarning(const QString &message);
void updateUserProjectList(); void updateUserProjectList();
void updatePopup(const QString &text, bool indeterminate = true); void updatePopup(const QString &text, bool indeterminate = true);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <android/log.h> #include <android/log.h>
#include <QApplication> #include <QApplication>
#include <QMessageBox>
#include <QQmlContext> #include <QQmlContext>
#include "backend.h" #include "backend.h"
...@@ -38,7 +39,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt ...@@ -38,7 +39,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
const char *file = context.file ? context.file : ""; const char *file = context.file ? context.file : "";
const char *function = context.function ? context.function : ""; const char *function = context.function ? context.function : "";
QString logPrefix, logSuffix, newLog; QString logPrefix, logSuffix, newLog;
QMessageBox msgBox(QMessageBox::Critical, "Critical:", msg, QMessageBox::Ok);
switch (type) switch (type)
{ {
case QtDebugMsg: case QtDebugMsg:
...@@ -52,12 +53,14 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt ...@@ -52,12 +53,14 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
break; break;
case QtCriticalMsg: case QtCriticalMsg:
logPrefix = QStringLiteral("Critical: "); logPrefix = QStringLiteral("Critical: ");
msgBox.exec();
break; break;
case QtFatalMsg: case QtFatalMsg:
logPrefix = QStringLiteral("Fatal: "); logPrefix = QStringLiteral("Fatal: ");
logSuffix = QStringLiteral(" (%1:%2, %3)").arg(file).arg(context.line).arg(function); logSuffix = QStringLiteral(" (%1:%2, %3)").arg(file).arg(context.line).arg(function);
break; break;
} }
newLog += logPrefix + localMsg + logSuffix + "\n"; newLog += logPrefix + localMsg + logSuffix + "\n";
__android_log_print(ANDROID_LOG_DEBUG, "Qt_UI_Viewer", "%s", qPrintable(newLog)); __android_log_print(ANDROID_LOG_DEBUG, "Qt_UI_Viewer", "%s", qPrintable(newLog));
......
...@@ -120,187 +120,181 @@ QString ProjectManager::unpackProject(const QByteArray &project, bool extractZip ...@@ -120,187 +120,181 @@ QString ProjectManager::unpackProject(const QByteArray &project, bool extractZip
QString ProjectManager::findFile(const QString &dir, const QString &filter) QString ProjectManager::findFile(const QString &dir, const QString &filter)
{ {
QDirIterator it(dir, {filter}, QDir::Files, QDirIterator::Subdirectories); QDirIterator it(dir, {filter}, QDir::Files);
return it.next(); return it.next();
} }
void ProjectManager::parseQmlProjectFile(const QString &fileName, QString ProjectManager::readQmlProjectFile(const QString &qmlProjectFilePath)
QString *mainFile,
QStringList *importPaths)
{ {
/* if filename comes from a resource, then qml need qrc:/ at the mainfile and importPaths. QFile file(qmlProjectFilePath);
* But all other c++ call like QFileInfo::exists do not understand that, there we
* need to keep the only ":" character at the beginning of the string
*/
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Could not open Qml Project file! " << fileName + ": " << file.errorString(); qCritical() << "Could not open Qml Project file! " << file.fileName() + ": "
return; << file.errorString();
return {};
} }
const QString text = QString::fromUtf8(file.readAll()); return QString::fromUtf8(file.readAll());
}
QString ProjectManager::getMainQmlFile(const QString &projectPath,
const QString &qmlProjectFileContent)
{
const QRegularExpression mainFileRegExp("mainFile:\\s*\"(.*)\""); const QRegularExpression mainFileRegExp("mainFile:\\s*\"(.*)\"");
const QRegularExpressionMatch mainFileMatch = mainFileRegExp.match(text); const QRegularExpressionMatch mainFileMatch = mainFileRegExp.match(qmlProjectFileContent);
if (!mainFileMatch.hasMatch()) { if (!mainFileMatch.hasMatch()) {
qCritical() << "No main file found in " << fileName; qCritical() << "No main file found in " << qmlProjectFileContent;
return; return {};
} }
qDebug() << "Found main file: " << mainFileMatch.captured(1); qDebug() << "Found main file: " << mainFileMatch.captured(1);
QString basePath = QFileInfo(fileName).path() + "/"; return projectPath + "/" + mainFileMatch.captured(1);
}
*mainFile = basePath + mainFileMatch.captured(1); QStringList ProjectManager::getImportPaths(const QString &projectPath,
if (mainFile->startsWith(QLatin1String(":/"))) const QString &qmlProjectFileContent)
*mainFile = "qrc:" + mainFile->mid(1); {
const QRegularExpression importPathsRegExp("importPaths:\\s*\\[\\s*(.*)\\s*\\]");
const QRegularExpressionMatch importPathsMatch = importPathsRegExp.match(qmlProjectFileContent);
const QRegularExpression qt6ProjectRegExp("qt6Project:\\s*true"); if (!importPathsMatch.hasMatch()) {
const QRegularExpressionMatch qt6ProjectMatch = qt6ProjectRegExp.match(text); qWarning() << "No import paths found in " << qmlProjectFileContent;
if (!qt6ProjectMatch.hasMatch()) { return {};
qWarning("This is not a Qt6 project.\nQt5 projects might work, but they are not "
"officially supported.");
} }
const QRegularExpression importPathsRegExp("importPaths:\\s*\\[\\s*(.*)\\s*\\]"); QStringList importPaths;
const QRegularExpressionMatch importPathsMatch = importPathsRegExp.match(text); for (const QString &path : importPathsMatch.captured(1).split(",")) {
qDebug() << "verbose:: Found import path: " << path;
if (importPathsMatch.hasMatch()) { QString cleanedPath = path.trimmed();
for (const QString &path : importPathsMatch.captured(1).split(",")) { qDebug() << "verbose:: Cleaned import path: " << cleanedPath;
QString cleanedPath = path.trimmed(); cleanedPath = projectPath + "/" + cleanedPath.mid(1, cleanedPath.length() - 2);
cleanedPath = basePath + cleanedPath.mid(1, cleanedPath.length() - 2); qDebug() << "verbose:: Cleaned import path: " << cleanedPath;
if (QFileInfo::exists(cleanedPath)) { if (QFileInfo::exists(cleanedPath)) {
if (cleanedPath.startsWith(QLatin1String(":/"))) qDebug() << "verbose:: Adding import path: " << cleanedPath;
cleanedPath = "qrc:" + cleanedPath.mid(1); importPaths.append(cleanedPath);
importPaths->append(cleanedPath);
}
} }
} }
return importPaths;
}
bool ProjectManager::isQt6Project(const QString &qmlProjectFileContent)
{
const QRegularExpression qt6ProjectRegExp("qt6Project:\\s*true");
const QRegularExpressionMatch qt6ProjectMatch = qt6ProjectRegExp.match(qmlProjectFileContent);
return qt6ProjectMatch.hasMatch();
} }
bool ProjectManager::runProject(const QString &projectPath, const QString &projectName) bool ProjectManager::runProject(const QString &projectPath, const QString &projectName)
{ {
const QString newProjectName = QString(projectName).remove(".qmlrc").remove("#");
qDebug() << "Project location: " << projectPath; qDebug() << "Project location: " << projectPath;
qDebug() << "Project name: " << projectName;
qDebug() << "New project name: " << newProjectName;
QString mainQmlFilePath; const QString qmlProjectFile = findFile(projectPath, "*.qmlproject");
QStringList importPaths; if (qmlProjectFile.isEmpty()) {
qCritical() << "No \"*.qmlproject\" found in \"" << projectPath << "\".";
return false;
}
qDebug() << "Looking for qmlproject file in " << projectPath; const QString qmlProjectFileContent = readQmlProjectFile(qmlProjectFile);
const QString qmlProjectFile = findFile(projectPath, newProjectName + "*.qmlproject"); if (qmlProjectFileContent.isEmpty())
if (!qmlProjectFile.isEmpty()) { return false;
qDebug() << "Found qmlproject file: " << qmlProjectFile;
parseQmlProjectFile(qmlProjectFile, &mainQmlFilePath, &importPaths);
} else {
qWarning("Not found: \"*.qmlproject\". Looking for main.qml..");
mainQmlFilePath = findFile(projectPath, "main.qml");
if (mainQmlFilePath.isEmpty()) { const QString mainQmlFilePath = getMainQmlFile(projectPath, qmlProjectFileContent);
qWarning() << "Not found: \"main.qml\". Looking for \"" << newProjectName << ".qml\".."; if (mainQmlFilePath.isEmpty())
mainQmlFilePath = findFile(projectPath, newProjectName + ".qml"); return false;
}
const QString qtquickcontrols2File = findFile(projectPath, "qtquickcontrols2.conf");
if (!qtquickcontrols2File.isEmpty()) {
qputenv("QT_QUICK_CONTROLS_CONF", qtquickcontrols2File.toLatin1());
} }
if (mainQmlFilePath.isEmpty()) { const QStringList importPaths = getImportPaths(projectPath, qmlProjectFileContent);
qCritical() << "No \"*.qmlproject\", \"main.qml\" or \"" << newProjectName if (isQt6Project(qmlProjectFileContent)) {
<< ".qml\" found in \"" << projectPath << "\"."; qWarning() << "This is not a Qt6 project.\nQt5 projects might work, but they are not "
return false; "officially supported.";
} }
qDebug() << "Found mainQmlFile: " + mainQmlFilePath; qDebug() << "Project is parsed successfully.";
qDebug() << "Qml Project File: " << qmlProjectFile;
qDebug() << "Main Qml File: " << mainQmlFilePath;
qDebug() << "Import Paths: " << importPaths;
qDebug() << "Qt Quick Controls 2 File: " << qtquickcontrols2File;
QUrl mainQmlUrl = QUrl::fromUserInput(mainQmlFilePath); QUrl mainQmlUrl = QUrl::fromUserInput(mainQmlFilePath);
QFile file(mainQmlUrl.path()); QFile file(mainQmlUrl.path());
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Could not open mainQmlfile for reading! " << file.fileName() << ": " qCritical() << "Could not open mainQmlfile for reading! " << file.fileName() << ": "
<< file.errorString(); << file.errorString();
qWarning() << "Trying to open it as a resource file."; return false;
file.setFileName(mainQmlUrl.path().prepend(":"));
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Could not open mainQmlfile for reading! " << file.fileName() << ": "
<< file.errorString();
return false;
}
} }
qDebug() << "Looking for qtquickcontrols2File in " << projectPath; qDebug() << "Initializing the qmlEngine";
const QString qtquickcontrols2File = findFile(projectPath, "qtquickcontrols2.conf"); m_qmlEngine.reset(new QQmlEngine);
m_qmlEngine->clearComponentCache();
if (!qtquickcontrols2File.isEmpty()) { qDebug("Adding import paths");
qDebug() << "Found qtquickcontrols2File: " << qtquickcontrols2File; for (const QString &importPath : importPaths) {
qputenv("QT_QUICK_CONTROLS_CONF", qtquickcontrols2File.toLatin1()); qDebug() << "-- Import path: " << importPath;
} m_qmlEngine->addImportPath(importPath);
}
qDebug() << "Initializing the qmlEngine"; QObject::connect(m_qmlEngine.data(),
m_qmlEngine.reset(new QQmlEngine); &QQmlEngine::warnings,
m_qmlEngine->clearComponentCache(); this,
[&](const QList<QQmlError> &warnings) {
for (const auto &warning : warnings) {
qWarning() << warning.toString();
}
});
emit projectStateChanged("Loading project...");
QEventLoop().processEvents(QEventLoop::AllEvents, 1000);
qDebug() << "Loading mainQmlUrl: " << mainQmlUrl.toString();
m_qmlComponent.reset(new QQmlComponent(m_qmlEngine.data()));
m_qmlComponent->loadUrl(mainQmlUrl);
qDebug() << "Waiting for qmlComponent to load";
while (m_qmlComponent->isLoading())
QCoreApplication::processEvents();
qDebug() << "Checking if m_qmlComponent is ready";
if (!m_qmlComponent->isReady()) {
qCritical() << "m_qmlComponent is not ready. Reason:" << m_qmlComponent->errorString();
return false;
}
qDebug() << "Creating top level object";
QObject *topLevel = m_qmlComponent->create();
if (!topLevel && m_qmlComponent->isError()) {
qCritical() << "Error while creating Qml m_qmlComponent:" << m_qmlComponent->errorString();
return false;
}
qDebug("Adding import paths"); emit projectStateChanged("Setting up the quickWindow...");
for (const QString &importPath : importPaths) { QEventLoop().processEvents(QEventLoop::AllEvents, 1000);
qDebug() << "-- Import path: " << importPath;
m_qmlEngine->addImportPath(importPath);
}
QObject::connect(m_qmlEngine.data(), qDebug() << "Setting up the quickWindow";
&QQmlEngine::warnings, m_quickWindow.reset(qobject_cast<QQuickWindow *>(topLevel));
this, if (m_quickWindow) {
[&](const QList<QQmlError> &warnings) { qDebug() << "Running with incubator controller";
for (const auto &warning : warnings) { m_qmlEngine->setIncubationController(m_quickWindow->incubationController());
qWarning() << warning.toString(); } else {
} qWarning() << "Top level object is not a QQuickWindow. Trying QQuickView...";
});
QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
emit projectStateChanged("Loading project..."); if (!contentItem) {
QEventLoop().processEvents(QEventLoop::AllEvents, 1000); qCritical() << "Top level object cannot be casted to QQuickItem. Aborting.";
qDebug() << "Loading mainQmlUrl: " << mainQmlUrl.toString();
m_qmlComponent.reset(new QQmlComponent(m_qmlEngine.data()));
m_qmlComponent->loadUrl(mainQmlUrl);
qDebug() << "Waiting for qmlComponent to load";
while (m_qmlComponent->isLoading())
QCoreApplication::processEvents();
qDebug() << "Checking if m_qmlComponent is ready";
if (!m_qmlComponent->isReady()) {
qCritical() << "m_qmlComponent is not ready. Reason:" << m_qmlComponent->errorString();
return false;
}
qDebug() << "Creating top level object";
QObject *topLevel = m_qmlComponent->create();
if (!topLevel && m_qmlComponent->isError()) {
qCritical() << "Error while creating Qml m_qmlComponent:"
<< m_qmlComponent->errorString();
return false; return false;
} }
emit projectStateChanged("Setting up the quickWindow..."); qDebug() << "Initializing QQuickView";
QEventLoop().processEvents(QEventLoop::AllEvents, 1000); QQuickView *view = new QQuickView(m_qmlEngine.data(), nullptr);
m_quickWindow.reset(view);
qDebug() << "Setting up the quickWindow"; view->setContent(mainQmlUrl, m_qmlComponent.data(), contentItem);
m_quickWindow.reset(qobject_cast<QQuickWindow *>(topLevel)); view->setResizeMode(QQuickView::SizeViewToRootObject);
if (m_quickWindow) { m_quickWindow->setBaseSize(QSize(contentItem->width(), contentItem->height()));
qDebug() << "Running with incubator controller"; }
m_qmlEngine->setIncubationController(m_quickWindow->incubationController()); return true;
} else {
qWarning() << "Top level object is not a QQuickWindow. Trying QQuickView...";
QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
if (!contentItem) {
qCritical() << "Top level object cannot be casted to QQuickItem. Aborting.";
return false;
}
qDebug() << "Initializing QQuickView";
QQuickView *view = new QQuickView(m_qmlEngine.data(), nullptr);
m_quickWindow.reset(view);
view->setContent(mainQmlUrl, m_qmlComponent.data(), contentItem);
view->setResizeMode(QQuickView::SizeViewToRootObject);
m_quickWindow->setBaseSize(QSize(contentItem->width(), contentItem->height()));
}
return true;
} }
bool ProjectManager::cacheProject(const QByteArray &projectData, const QJsonObject &projectInfo) bool ProjectManager::cacheProject(const QByteArray &projectData, const QJsonObject &projectInfo)
......
...@@ -71,6 +71,10 @@ private: ...@@ -71,6 +71,10 @@ private:
// Member functions // Member functions
QString findFile(const QString &dir, const QString &filter); QString findFile(const QString &dir, const QString &filter);
QString readQmlProjectFile(const QString &qmlProjectFilePath);
QString getMainQmlFile(const QString &projectPath, const QString &qmlProjectFileContent);
QStringList getImportPaths(const QString &projectPath, const QString &qmlProjectFileContent);
bool isQt6Project(const QString &qmlProjectFileContent);
void parseQmlProjectFile(const QString &fileName, QString *mainFile, QStringList *importPaths); void parseQmlProjectFile(const QString &fileName, QString *mainFile, QStringList *importPaths);
void cacheProject(const QString &projectName, const QString &projectPath); void cacheProject(const QString &projectName, const QString &projectPath);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment