Improve Python generator

The Python generator generates the .qrc resources file,
pyproject.toml, main.py and a settings.py files.
This way, pyside6-project can run the code without doing any change in
the QML code.

Change-Id: Iddcc0c7f7a48c581f656f6d156b17460d4106691
Reviewed-by: Knud Dollereder <knud.dollereder@qt.io>
This commit is contained in:
Jaime Resano
2025-01-20 11:16:11 +01:00
committed by Jaime Resano
parent 1278473e69
commit 4146d1bd01
7 changed files with 114 additions and 63 deletions

View File

@@ -11,5 +11,8 @@
<file alias="import_qml_components_h">templates/import_qml_components_h.tpl</file> <file alias="import_qml_components_h">templates/import_qml_components_h.tpl</file>
<file alias="qtquickcontrols_conf">templates/qtquickcontrols2_conf.tpl</file> <file alias="qtquickcontrols_conf">templates/qtquickcontrols2_conf.tpl</file>
<file alias="cmake_shared">templates/cmakelists_txt_shared.tpl</file> <file alias="cmake_shared">templates/cmakelists_txt_shared.tpl</file>
<file alias="python_generator_main">templates/python_generator_main.tpl</file>
<file alias="python_generator_settings">templates/python_generator_settings.tpl</file>
<file alias="python_pyproject_toml">templates/python_pyproject_toml.tpl</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -30,6 +30,7 @@ void Exporter::updateProject(QmlProject *project)
void Exporter::updateProjectItem(QmlProjectItem *item, bool updateEnabled) void Exporter::updateProjectItem(QmlProjectItem *item, bool updateEnabled)
{ {
connect(item, &QmlProjectItem::filesChanged, m_cmakeGen, &CMakeGenerator::update); connect(item, &QmlProjectItem::filesChanged, m_cmakeGen, &CMakeGenerator::update);
connect(item, &QmlProjectItem::filesChanged, m_pythonGen, &PythonGenerator::update);
connect(item, &QmlProjectItem::fileModified, m_cmakeGen, &CMakeGenerator::updateModifiedFile); connect(item, &QmlProjectItem::fileModified, m_cmakeGen, &CMakeGenerator::updateModifiedFile);
if (updateEnabled) { if (updateEnabled) {

View File

@@ -3,41 +3,14 @@
#include "pythongenerator.h" #include "pythongenerator.h"
#include "cmakewriter.h" #include "cmakewriter.h"
#include "resourcegenerator.h"
#include "projectexplorer/projectmanager.h" #include "projectexplorer/projectmanager.h"
#include "qmlprojectmanager/qmlproject.h" #include "qmlprojectmanager/qmlproject.h"
#include <QMenu> #include <QMenu>
namespace QmlProjectManager { namespace QmlProjectManager::QmlProjectExporter {
namespace QmlProjectExporter {
const char *PYTHON_MAIN_FILE_TEMPLATE = R"(
import os
import sys
from pathlib import Path
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from autogen.settings import url, import_paths
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
app_dir = Path(__file__).parent.parent
engine.addImportPath(os.fspath(app_dir))
for path in import_paths:
engine.addImportPath(os.fspath(app_dir / path))
engine.load(os.fspath(app_dir/url))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
)";
void PythonGenerator::createMenuAction(QObject *parent) void PythonGenerator::createMenuAction(QObject *parent)
{ {
@@ -66,8 +39,7 @@ PythonGenerator::PythonGenerator(QmlBuildSystem *bs)
void PythonGenerator::updateMenuAction() void PythonGenerator::updateMenuAction()
{ {
FileGenerator::updateMenuAction( FileGenerator::updateMenuAction("QmlProject.EnablePythonGenerator",
"QmlProject.EnablePythonGenerator",
[this]() { return buildSystem()->enablePythonGeneration(); }); [this]() { return buildSystem()->enablePythonGeneration(); });
} }
@@ -77,38 +49,47 @@ void PythonGenerator::updateProject(QmlProject *project)
return; return;
Utils::FilePath projectPath = project->rootProjectDirectory(); Utils::FilePath projectPath = project->rootProjectDirectory();
Utils::FilePath pythonPath = projectPath.pathAppended("Python"); Utils::FilePath pythonFolderPath = projectPath.pathAppended("Python");
if (!pythonPath.exists()) if (!pythonFolderPath.exists())
pythonPath.createDir(); pythonFolderPath.createDir();
Utils::FilePath mainFile = pythonPath.pathAppended("main.py"); Utils::FilePath mainFilePath = pythonFolderPath.pathAppended("main.py");
if (!mainFile.exists()) { if (!mainFilePath.exists()) {
const QString mainContent = QString::fromUtf8(PYTHON_MAIN_FILE_TEMPLATE); const QString mainFileTemplate = CMakeWriter::readTemplate(
CMakeWriter::writeFile(mainFile, mainContent); ":/templates/python_generator_main");
CMakeWriter::writeFile(mainFilePath, mainFileTemplate);
} }
Utils::FilePath autogenPath = pythonPath.pathAppended("autogen"); Utils::FilePath pyprojectFilePath = pythonFolderPath.pathAppended("pyproject.toml");
if (!autogenPath.exists()) if (!pyprojectFilePath.exists()) {
autogenPath.createDir(); const QString pyprojectFileTemplate = CMakeWriter::readTemplate(
":/templates/python_pyproject_toml");
Utils::FilePath settingsPath = autogenPath.pathAppended("settings.py"); const QString pyprojectFileContent = pyprojectFileTemplate.arg(project->displayName());
CMakeWriter::writeFile(settingsPath, settingsFileContent()); CMakeWriter::writeFile(pyprojectFilePath, pyprojectFileContent);
} }
QString PythonGenerator::settingsFileContent() const Utils::FilePath autogenFolderPath = pythonFolderPath.pathAppended("autogen");
{ if (!autogenFolderPath.exists())
QTC_ASSERT(buildSystem(), return {}); autogenFolderPath.createDir();
QString content("\n"); Utils::FilePath settingsFilePath = autogenFolderPath.pathAppended("settings.py");
content.append("url = \"" + buildSystem()->mainFile() + "\"\n"); const QString settingsFileTemplate = CMakeWriter::readTemplate(
":/templates/python_generator_settings");
const QString settingsFileContent = settingsFileTemplate.arg(buildSystem()->mainFile());
CMakeWriter::writeFile(settingsFilePath, settingsFileContent);
content.append("import_paths = [\n"); // Python code uses the Qt resources collection file (.qrc)
for (const QString &path : buildSystem()->importPaths()) ResourceGenerator::createQrc(project);
content.append("\t\"" + path + "\",\n");
content.append("]\n");
return content;
} }
} // namespace QmlProjectExporter. /*!
} // namespace QmlProjectManager. Regenerates the .qrc resources file
*/
void PythonGenerator::update(const QSet<QString> &added, const QSet<QString> &removed) {
Q_UNUSED(added);
Q_UNUSED(removed);
ResourceGenerator::createQrc(qmlProject());
// Generated Python code does not need to be updated
};
} // namespace QmlProjectExporter::QmlProjectManager.

View File

@@ -21,11 +21,10 @@ public:
static void createMenuAction(QObject *parent); static void createMenuAction(QObject *parent);
PythonGenerator(QmlBuildSystem *bs); PythonGenerator(QmlBuildSystem *bs);
void update(const QSet<QString> &added, const QSet<QString> &removed);
void updateMenuAction() override; void updateMenuAction() override;
void updateProject(QmlProject *project) override; void updateProject(QmlProject *project) override;
private:
QString settingsFileContent() const;
}; };
} // namespace QmlProjectExporter. } // namespace QmlProjectExporter.

View File

@@ -0,0 +1,26 @@
import sys
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from autogen.settings import setup_qt_environment
# Import here the Python files that define QML elements
def main():
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
setup_qt_environment(engine)
if not engine.rootObjects():
sys.exit(-1)
ex = app.exec()
del engine
return ex
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,36 @@
# This file is automatically generated by Qt Design Studio.
import os
import sys
from pathlib import Path
from PySide6.QtQml import QQmlApplicationEngine
project_root = Path(__file__).parent.parent.parent
def setup_qt_environment(qml_engine: QQmlApplicationEngine):
"""
Load the QML application. Import the compiled resources when the application is deployed.
"""
qml_app_url = "%1"
if "__compiled__" in globals():
# Application has been deployed using pyside6-deploy
try:
import autogen.resources # noqa: F401
except ImportError:
resource_file = Path(__file__).parent / "resources.py"
print(
f"Error: No compiled resources found in {resource_file.absolute()}\n"
f"Please compile the resources using pyside6-rcc or pyside6-project build",
file=sys.stderr,
)
sys.exit(1)
qml_engine.addImportPath(":/")
qml_engine.load(f":/{qml_app_url}")
return
qml_engine.addImportPath(str(project_root.absolute()))
os.environ["QT_QUICK_CONTROLS_CONF"] = str(project_root / "qtquickcontrols2.conf")
qml_engine.load(str(project_root / qml_app_url))

View File

@@ -0,0 +1,5 @@
[project]
name = "%1"
[tool.pyside6-project]
files = ["main.py", "autogen/settings.py"]