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="qtquickcontrols_conf">templates/qtquickcontrols2_conf.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>
</RCC>

View File

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

View File

@@ -3,41 +3,14 @@
#include "pythongenerator.h"
#include "cmakewriter.h"
#include "resourcegenerator.h"
#include "projectexplorer/projectmanager.h"
#include "qmlprojectmanager/qmlproject.h"
#include <QMenu>
namespace QmlProjectManager {
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())
)";
namespace QmlProjectManager::QmlProjectExporter {
void PythonGenerator::createMenuAction(QObject *parent)
{
@@ -66,9 +39,8 @@ PythonGenerator::PythonGenerator(QmlBuildSystem *bs)
void PythonGenerator::updateMenuAction()
{
FileGenerator::updateMenuAction(
"QmlProject.EnablePythonGenerator",
[this]() { return buildSystem()->enablePythonGeneration(); });
FileGenerator::updateMenuAction("QmlProject.EnablePythonGenerator",
[this]() { return buildSystem()->enablePythonGeneration(); });
}
void PythonGenerator::updateProject(QmlProject *project)
@@ -77,38 +49,47 @@ void PythonGenerator::updateProject(QmlProject *project)
return;
Utils::FilePath projectPath = project->rootProjectDirectory();
Utils::FilePath pythonPath = projectPath.pathAppended("Python");
if (!pythonPath.exists())
pythonPath.createDir();
Utils::FilePath pythonFolderPath = projectPath.pathAppended("Python");
if (!pythonFolderPath.exists())
pythonFolderPath.createDir();
Utils::FilePath mainFile = pythonPath.pathAppended("main.py");
if (!mainFile.exists()) {
const QString mainContent = QString::fromUtf8(PYTHON_MAIN_FILE_TEMPLATE);
CMakeWriter::writeFile(mainFile, mainContent);
Utils::FilePath mainFilePath = pythonFolderPath.pathAppended("main.py");
if (!mainFilePath.exists()) {
const QString mainFileTemplate = CMakeWriter::readTemplate(
":/templates/python_generator_main");
CMakeWriter::writeFile(mainFilePath, mainFileTemplate);
}
Utils::FilePath autogenPath = pythonPath.pathAppended("autogen");
if (!autogenPath.exists())
autogenPath.createDir();
Utils::FilePath pyprojectFilePath = pythonFolderPath.pathAppended("pyproject.toml");
if (!pyprojectFilePath.exists()) {
const QString pyprojectFileTemplate = CMakeWriter::readTemplate(
":/templates/python_pyproject_toml");
const QString pyprojectFileContent = pyprojectFileTemplate.arg(project->displayName());
CMakeWriter::writeFile(pyprojectFilePath, pyprojectFileContent);
}
Utils::FilePath settingsPath = autogenPath.pathAppended("settings.py");
CMakeWriter::writeFile(settingsPath, settingsFileContent());
Utils::FilePath autogenFolderPath = pythonFolderPath.pathAppended("autogen");
if (!autogenFolderPath.exists())
autogenFolderPath.createDir();
Utils::FilePath settingsFilePath = autogenFolderPath.pathAppended("settings.py");
const QString settingsFileTemplate = CMakeWriter::readTemplate(
":/templates/python_generator_settings");
const QString settingsFileContent = settingsFileTemplate.arg(buildSystem()->mainFile());
CMakeWriter::writeFile(settingsFilePath, settingsFileContent);
// Python code uses the Qt resources collection file (.qrc)
ResourceGenerator::createQrc(project);
}
QString PythonGenerator::settingsFileContent() const
{
QTC_ASSERT(buildSystem(), return {});
/*!
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
};
QString content("\n");
content.append("url = \"" + buildSystem()->mainFile() + "\"\n");
content.append("import_paths = [\n");
for (const QString &path : buildSystem()->importPaths())
content.append("\t\"" + path + "\",\n");
content.append("]\n");
return content;
}
} // namespace QmlProjectExporter.
} // namespace QmlProjectManager.
} // namespace QmlProjectExporter::QmlProjectManager.

View File

@@ -21,11 +21,10 @@ public:
static void createMenuAction(QObject *parent);
PythonGenerator(QmlBuildSystem *bs);
void update(const QSet<QString> &added, const QSet<QString> &removed);
void updateMenuAction() override;
void updateProject(QmlProject *project) override;
private:
QString settingsFileContent() const;
};
} // 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"]