1

How can I include the QML file into my Python project as a single executable. When I run pyinstaller --onefile main.py, running the executable results in an error that the QML file is not found. Unless I use an absolute path or place view.qml in the same directory as my executable. I don't want to have a separate QML file, I want it combined into the executable.

main.py:

if __name__ == "__main__":
  app = QGuiApplication(sys.argv)
  engine = QQmlApplicationEngine()
  engine.load(QUrl("view.qml"))
  sys.exit(app.exec_())

view.qml:

import QtQuick 2.0

ApplicationWindow {
  id: window
  title: "Window"
  width: 900
  height: 600
  visible: true
}
β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
Aaron
  • 759
  • 1
  • 7
  • 15
  • 2
    Please add an example of the code loading the QML files as well as the directory structure of your project. – Herr von Wurst Sep 20 '19 at 22:07
  • 1
    Possible duplicate of [Determining application path in a Python EXE generated by pyInstaller](https://stackoverflow.com/questions/404744/determining-application-path-in-a-python-exe-generated-by-pyinstaller) read also [adding-data-files](https://pyinstaller.readthedocs.io/en/stable/spec-files.html#adding-data-files) – stovfl Sep 20 '19 at 22:10
  • 1
    I have added my example code, and updated the question. – Aaron Sep 21 '19 at 04:29

1 Answers1

6

My answer in addition to showing how to use the possible duplicate answer in this particular case, also shows an alternative using Qt's own tools.

1. Copy the .qml to the same executable folder

In this case you have to build the absolute path of the qml using the application path.

import os
import sys

from PySide2 import QtCore, QtGui, QtQml

# https://stackoverflow.com/a/404750/6622587
application_path = (
    os.path.dirname(sys.executable)
    if getattr(sys, "frozen", False)
    else os.path.dirname(os.path.abspath(__file__))
)


if __name__ == "__main__":
    import os
    import sys

    app = QtGui.QGuiApplication(sys.argv)
    engine = QtQml.QQmlApplicationEngine()
    file = os.path.join(application_path, "main.qml")
    engine.load(QtCore.QUrl.fromLocalFile(file))
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

Then copy the .qml to the same executable folder.

2. Add .qml to as data files

The data files are decompressed in the folder relative to sys._MEIPASS, if the --onefile option is not used then that path is the executable folder otherwise it will be decompressed in the temporary folder.

In your case it implements the following:

├── main.py
└── main.qml

main.py

import os
import sys

from PySide2 import QtCore, QtGui, QtQml

# https://stackoverflow.com/a/42615559/6622587
application_path = (
    sys._MEIPASS
    if getattr(sys, "frozen", False)
    else os.path.dirname(os.path.abspath(__file__))
)


if __name__ == "__main__":
    import os
    import sys

    app = QtGui.QGuiApplication(sys.argv)
    engine = QtQml.QQmlApplicationEngine()
    file = os.path.join(application_path, "main.qml")
    engine.load(QtCore.QUrl.fromLocalFile(file))
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

And run pyinstaller as follows:

pyinstaller --add-data "main.qml:." --onefile main.py

3. Use Qt Resource

You can create a .qrc that adds the qml, then convert them to .py and finally include it in the .py.

├── main.py
├── main.qml
└── qml.qrc

main.py

import sys

from PySide2 import QtCore, QtGui, QtQml

import qml_rc


if __name__ == "__main__":
    import os
    import sys

    app = QtGui.QGuiApplication(sys.argv)
    engine = QtQml.QQmlApplicationEngine()
    engine.load(":/main.qml")
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

qml.qrc

<RCC>
  <qresource prefix="/">
    <file>main.qml</file>
  </qresource>
</RCC>

To convert the qml.qrc to .py you must use the following command:

pyside2-rcc qml.qrc -o qml_rc.py 

and finally as it is already a .py we only run pyinstaller:

pyinstaller main.py --onefile
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Awesome! Very helpful explanations. You have helped me understand it a lot. I have tried the 2nd method in your answer and it works perfect. It's nice to be able to just maintain the .spec file to include all my extra "DATA" files. Thank you – Aaron Sep 21 '19 at 06:02
  • Hello, I'm trying to do option #3, It fails to load qml components in the same folder as the main qml file. What import do I need to add in the main qml file so it would see my components that are in the save folder. (Given that they all are registered in the qrc file.) – Curtwagner1984 Jul 11 '22 at 05:22