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 @@
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonObject>
#include <QMessageBox>
#include <QSettings>
#include <QSslSocket>
......@@ -83,13 +83,6 @@ void Backend::initialize()
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)
{
emit popupTextChanged(text);
......@@ -128,8 +121,9 @@ bool Backend::connectDesignStudio()
const QString projectPath = m_projectManager.unpackProject(projectData);
if (projectPath.isEmpty()) {
showWarning("Could not unpack project. Please check the logs for more "
"information.");
qCritical()
<< "Could not unpack project. Please check the logs for more "
"information.";
emit popupClose();
return;
}
......@@ -138,15 +132,16 @@ bool Backend::connectDesignStudio()
updatePopup("Running project...");
if (!m_projectManager.runProject(projectPath, "UntitledProject35")) {
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 {
m_projectManager.showAppWindow();
}
emit popupClose();
});
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();
}
},
......@@ -197,8 +192,8 @@ void Backend::runDemoProject(const QString &projectName)
updatePopup("Caching demo project...");
if (!m_projectManager.cacheDemoProject(project, projectName)) {
showWarning(
"Could not cache demo project. Please check the logs for more information.");
qCritical()
<< "Could not cache demo project. Please check the logs for more information.";
emit popupClose();
return;
}
......@@ -208,7 +203,7 @@ void Backend::runDemoProject(const QString &projectName)
updatePopup("Running demo project...");
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
m_projectManager.showAppWindow();
......@@ -250,7 +245,7 @@ void Backend::runUserProject(const QString &url)
updatePopup("Caching user project...");
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();
return;
}
......@@ -260,7 +255,7 @@ void Backend::runUserProject(const QString &url)
updatePopup("Running cached project...");
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
m_projectManager.showAppWindow();
......
......@@ -64,7 +64,6 @@ private:
QThread m_dsConnectorThread;
// member functions
void showWarning(const QString &message);
void updateUserProjectList();
void updatePopup(const QString &text, bool indeterminate = true);
......
......@@ -25,6 +25,7 @@
#include <android/log.h>
#include <QApplication>
#include <QMessageBox>
#include <QQmlContext>
#include "backend.h"
......@@ -38,7 +39,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
const char *file = context.file ? context.file : "";
const char *function = context.function ? context.function : "";
QString logPrefix, logSuffix, newLog;
QMessageBox msgBox(QMessageBox::Critical, "Critical:", msg, QMessageBox::Ok);
switch (type)
{
case QtDebugMsg:
......@@ -52,12 +53,14 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
break;
case QtCriticalMsg:
logPrefix = QStringLiteral("Critical: ");
msgBox.exec();
break;
case QtFatalMsg:
logPrefix = QStringLiteral("Fatal: ");
logSuffix = QStringLiteral(" (%1:%2, %3)").arg(file).arg(context.line).arg(function);
break;
}
newLog += logPrefix + localMsg + logSuffix + "\n";
__android_log_print(ANDROID_LOG_DEBUG, "Qt_UI_Viewer", "%s", qPrintable(newLog));
......
......@@ -120,187 +120,181 @@ QString ProjectManager::unpackProject(const QByteArray &project, bool extractZip
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();
}
void ProjectManager::parseQmlProjectFile(const QString &fileName,
QString *mainFile,
QStringList *importPaths)
QString ProjectManager::readQmlProjectFile(const QString &qmlProjectFilePath)
{
/* if filename comes from a resource, then qml need qrc:/ at the mainfile and importPaths.
* 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);
QFile file(qmlProjectFilePath);
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Could not open Qml Project file! " << fileName + ": " << file.errorString();
return;
qCritical() << "Could not open Qml Project file! " << file.fileName() + ": "
<< 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 QRegularExpressionMatch mainFileMatch = mainFileRegExp.match(text);
const QRegularExpressionMatch mainFileMatch = mainFileRegExp.match(qmlProjectFileContent);
if (!mainFileMatch.hasMatch()) {
qCritical() << "No main file found in " << fileName;
return;
qCritical() << "No main file found in " << qmlProjectFileContent;
return {};
}
qDebug() << "Found main file: " << mainFileMatch.captured(1);
QString basePath = QFileInfo(fileName).path() + "/";
return projectPath + "/" + mainFileMatch.captured(1);
}
*mainFile = basePath + mainFileMatch.captured(1);
if (mainFile->startsWith(QLatin1String(":/")))
*mainFile = "qrc:" + mainFile->mid(1);
QStringList ProjectManager::getImportPaths(const QString &projectPath,
const QString &qmlProjectFileContent)
{
const QRegularExpression importPathsRegExp("importPaths:\\s*\\[\\s*(.*)\\s*\\]");
const QRegularExpressionMatch importPathsMatch = importPathsRegExp.match(qmlProjectFileContent);
const QRegularExpression qt6ProjectRegExp("qt6Project:\\s*true");
const QRegularExpressionMatch qt6ProjectMatch = qt6ProjectRegExp.match(text);
if (!qt6ProjectMatch.hasMatch()) {
qWarning("This is not a Qt6 project.\nQt5 projects might work, but they are not "
"officially supported.");
if (!importPathsMatch.hasMatch()) {
qWarning() << "No import paths found in " << qmlProjectFileContent;
return {};
}
const QRegularExpression importPathsRegExp("importPaths:\\s*\\[\\s*(.*)\\s*\\]");
const QRegularExpressionMatch importPathsMatch = importPathsRegExp.match(text);
if (importPathsMatch.hasMatch()) {
for (const QString &path : importPathsMatch.captured(1).split(",")) {
QString cleanedPath = path.trimmed();
cleanedPath = basePath + cleanedPath.mid(1, cleanedPath.length() - 2);
if (QFileInfo::exists(cleanedPath)) {
if (cleanedPath.startsWith(QLatin1String(":/")))
cleanedPath = "qrc:" + cleanedPath.mid(1);
importPaths->append(cleanedPath);
}
QStringList importPaths;
for (const QString &path : importPathsMatch.captured(1).split(",")) {
qDebug() << "verbose:: Found import path: " << path;
QString cleanedPath = path.trimmed();
qDebug() << "verbose:: Cleaned import path: " << cleanedPath;
cleanedPath = projectPath + "/" + cleanedPath.mid(1, cleanedPath.length() - 2);
qDebug() << "verbose:: Cleaned import path: " << cleanedPath;
if (QFileInfo::exists(cleanedPath)) {
qDebug() << "verbose:: Adding import path: " << 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)
{
const QString newProjectName = QString(projectName).remove(".qmlrc").remove("#");
qDebug() << "Project location: " << projectPath;
qDebug() << "Project name: " << projectName;
qDebug() << "New project name: " << newProjectName;
QString mainQmlFilePath;
QStringList importPaths;
const QString qmlProjectFile = findFile(projectPath, "*.qmlproject");
if (qmlProjectFile.isEmpty()) {
qCritical() << "No \"*.qmlproject\" found in \"" << projectPath << "\".";
return false;
}
qDebug() << "Looking for qmlproject file in " << projectPath;
const QString qmlProjectFile = findFile(projectPath, newProjectName + "*.qmlproject");
if (!qmlProjectFile.isEmpty()) {
qDebug() << "Found qmlproject file: " << qmlProjectFile;
parseQmlProjectFile(qmlProjectFile, &mainQmlFilePath, &importPaths);
} else {
qWarning("Not found: \"*.qmlproject\". Looking for main.qml..");
mainQmlFilePath = findFile(projectPath, "main.qml");
const QString qmlProjectFileContent = readQmlProjectFile(qmlProjectFile);
if (qmlProjectFileContent.isEmpty())
return false;
if (mainQmlFilePath.isEmpty()) {
qWarning() << "Not found: \"main.qml\". Looking for \"" << newProjectName << ".qml\"..";
mainQmlFilePath = findFile(projectPath, newProjectName + ".qml");
}
const QString mainQmlFilePath = getMainQmlFile(projectPath, qmlProjectFileContent);
if (mainQmlFilePath.isEmpty())
return false;
const QString qtquickcontrols2File = findFile(projectPath, "qtquickcontrols2.conf");
if (!qtquickcontrols2File.isEmpty()) {
qputenv("QT_QUICK_CONTROLS_CONF", qtquickcontrols2File.toLatin1());
}
if (mainQmlFilePath.isEmpty()) {
qCritical() << "No \"*.qmlproject\", \"main.qml\" or \"" << newProjectName
<< ".qml\" found in \"" << projectPath << "\".";
return false;
const QStringList importPaths = getImportPaths(projectPath, qmlProjectFileContent);
if (isQt6Project(qmlProjectFileContent)) {
qWarning() << "This is not a Qt6 project.\nQt5 projects might work, but they are not "
"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);
QFile file(mainQmlUrl.path());
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Could not open mainQmlfile for reading! " << file.fileName() << ": "
<< file.errorString();
qWarning() << "Trying to open it as a resource file.";
file.setFileName(mainQmlUrl.path().prepend(":"));
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Could not open mainQmlfile for reading! " << file.fileName() << ": "
<< file.errorString();
return false;
}
qCritical() << "Could not open mainQmlfile for reading! " << file.fileName() << ": "
<< file.errorString();
return false;
}
qDebug() << "Looking for qtquickcontrols2File in " << projectPath;
const QString qtquickcontrols2File = findFile(projectPath, "qtquickcontrols2.conf");
qDebug() << "Initializing the qmlEngine";
m_qmlEngine.reset(new QQmlEngine);
m_qmlEngine->clearComponentCache();
if (!qtquickcontrols2File.isEmpty()) {
qDebug() << "Found qtquickcontrols2File: " << qtquickcontrols2File;
qputenv("QT_QUICK_CONTROLS_CONF", qtquickcontrols2File.toLatin1());
}
qDebug("Adding import paths");
for (const QString &importPath : importPaths) {
qDebug() << "-- Import path: " << importPath;
m_qmlEngine->addImportPath(importPath);
}
qDebug() << "Initializing the qmlEngine";
m_qmlEngine.reset(new QQmlEngine);
m_qmlEngine->clearComponentCache();
QObject::connect(m_qmlEngine.data(),
&QQmlEngine::warnings,
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");
for (const QString &importPath : importPaths) {
qDebug() << "-- Import path: " << importPath;
m_qmlEngine->addImportPath(importPath);
}
emit projectStateChanged("Setting up the quickWindow...");
QEventLoop().processEvents(QEventLoop::AllEvents, 1000);
QObject::connect(m_qmlEngine.data(),
&QQmlEngine::warnings,
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();
qDebug() << "Setting up the quickWindow";
m_quickWindow.reset(qobject_cast<QQuickWindow *>(topLevel));
if (m_quickWindow) {
qDebug() << "Running with incubator controller";
m_qmlEngine->setIncubationController(m_quickWindow->incubationController());
} 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;
}
emit projectStateChanged("Setting up the quickWindow...");
QEventLoop().processEvents(QEventLoop::AllEvents, 1000);
qDebug() << "Setting up the quickWindow";
m_quickWindow.reset(qobject_cast<QQuickWindow *>(topLevel));
if (m_quickWindow) {
qDebug() << "Running with incubator controller";
m_qmlEngine->setIncubationController(m_quickWindow->incubationController());
} 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;
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)
......
......@@ -71,6 +71,10 @@ private:
// Member functions
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 cacheProject(const QString &projectName, const QString &projectPath);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment