Search code examples
pythonwindowspyinstallerpyside2

Problem with Pyinstaller and loadUiType in PySide2


I want to convert my PySide2 app to exe file. It is important to use loadUiType function from PySide2.QtUiTools to load ui file. Minimal Reproducible Example contains:

Python file:

import os
from PySide2 import QtWidgets
from PySide2.QtUiTools import loadUiType
from PySide2 import QtXml

current_dir = os.environ.get(
    "_MEIPASS2",
    os.path.abspath(".")
)
Form, Base = loadUiType(os.path.join(current_dir, "ui\main.ui"))


class MainWidget(Base, Form):
    def __init__(self, parent=None):
        super(self.__class__, self).__init__(parent)
        self.setupUi(self)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    app.setStyle("fusion")
    main_widget_object = MainWidget()
    main_widget_object.show()
    sys.exit(app.exec_())

Ui file:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <widget class="QPushButton" name="button">
   <property name="geometry">
    <rect>
     <x>100</x>
     <y>110</y>
     <width>171</width>
     <height>23</height>
    </rect>
   </property>
   <property name="text">
    <string>Thank you for help :D</string>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

Using following command i convert main.py file to executable file with pyinstaller:

pyinstaller --noconfirm --onefile --console --add-data "D:/!python_projects/pyinstaller_example/ui/main.ui;."  "D:/!python_projects/pyinstaller_example/main.py"

Then i copy ui folder with main.ui to output folder created with exe file. When i run created exe file via cmd i get following message:

Cannot run 'pyside2-uic':  "Process failed to start: The system cannot find the file specified."  -  Exit status  QProcess::NormalExit  ( 0 )
 Check if 'pyside2-uic' is in PATH
Traceback (most recent call last):
  File "main.py", line 10, in <module>
TypeError: cannot unpack non-iterable NoneType object
[6392] Failed to execute script main

Am i missing something important in pyinstaller command or in py file?

EDITv1: I copied uic tool to folder with exe, rename it to "pyside2-uic" and following error apears:

Error while compiling the generated Python file
  File "<stdin>", line 1
    /********************************************************************************
    ^
SyntaxError: invalid syntax

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "main.py", line 10, in <module>
SystemError: <built-in function loadUiType> returned a result with an error set
[11612] Failed to execute script main

Solution

  • If the source code is checked:

    // Use the 'pyside2-uic' wrapper instead of 'uic'
    // This approach is better than rely on 'uic' since installing
    // the wheels cover this case.
    QString uicBin("pyside2-uic");
    QStringList uicArgs = {QString::fromUtf8(uiFileName)};
    

    it is observed that it uses the pyside2-uic tool to obtain the python code from the .ui but in recent versions of PySide2 that script was removed (perhaps a bug) since the uic tool was used.

    A possible solution is to copy the pyside2-uic script next to the executable:

    pyside2-uic

    # -*- coding: utf-8 -*-
    import re
    import sys
    from PySide2.scripts.pyside_tool import uic
    if __name__ == '__main__':
        sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
        sys.exit(uic())