PyInstaller + UI文件 - FileNotFoundError:[Errno 2]没有这样的文件或目录:

gir*_*ran 11 python pyinstaller pyqt4 qt-designer

我正在尝试使用PyInstaller将.py脚本导出到.exe,它依赖于使用Qt Designer创建的.ui文件.

我可以确认我的.py脚本在通过PyCharm运行时工作正常 - 我能够看到我用.ui文件创建的GUI.

但是,当我将.py脚本导出到.exe并启动它时,我在命令行中收到以下错误:

C:\Users\giranm>"C:\Users\giranm\PycharmProjects\PyQt Tutorial\dist\secSearch_demo.exe"
Traceback (most recent call last):
  File "secSearch_demo.py", line 13, in <module>
  File "site-packages\PyQt4\uic\__init__.py", line 208, in loadUiType
  File "site-packages\PyQt4\uic\Compiler\compiler.py", line 140, in compileUi
  File "site-packages\PyQt4\uic\uiparser.py", line 974, in parse
  File "xml\etree\ElementTree.py", line 1186, in parse
  File "xml\etree\ElementTree.py", line 587, in parse
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\giranm\\securitySearchForm.ui'
Failed to execute script secSearch_demo
Run Code Online (Sandbox Code Playgroud)

出于某种原因,.exe文件正在寻找路径中的.ui文件 - C:\ Users\giranm \

但是,已经做过一些研究,我被告知我需要使用os.getcwd()并确保我的脚本中有完整的路径.即使使用下面的代码,我仍然会尝试找到.ui文件时出错.

PyInstaller:IOError:[Errno 2]没有这样的文件或目录:

# import relevant modules etc...

cwd = os.getcwd()
securitySearchForm = os.path.join(cwd, "securitySearchForm.ui")
popboxForm = os.path.join(cwd, "popbox.ui")

Ui_MainWindow, QtBaseClass = uic.loadUiType(securitySearchForm)
Ui_PopBox, QtSubClass = uic.loadUiType(popboxForm)

# remainder of code below.  
Run Code Online (Sandbox Code Playgroud)

我知道可以将.ui文件转换为.py并使用pyuic4将它们导入主例程.但是,我将对.ui文件进行多次编辑,因此我不可能继续转换它们.

有没有解决这个问题,以便我可以创建一个独立的.exe?

我是使用PyQT4和PyInstaller的新手 - 任何帮助都将非常感谢!

gir*_*ran 11

在整个周末摸不着头脑并进一步研究SO之后,我设法使用UI文件按预期编译独立的.exe.

首先,我使用这个答案定义了以下函数

使用PyInstaller捆绑数据文件(--onefile)

# Define function to import external files when using PyInstaller.
def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)
Run Code Online (Sandbox Code Playgroud)

接下来,我使用此函数和所需类的变量导入.UI文件.

# Import .ui forms for the GUI using function resource_path()
securitySearchForm = resource_path("securitySearchForm.ui")
popboxForm = resource_path("popbox.ui")

Ui_MainWindow, QtBaseClass = uic.loadUiType(securitySearchForm)
Ui_PopBox, QtSubClass = uic.loadUiType(popboxForm)
Run Code Online (Sandbox Code Playgroud)

然后,我必须使用Qt Designer创建资源文件(.qrc)并使用此资源文件嵌入图像/图标.完成后,我使用pyrcc4将.qrc文件转换为.py文件,该文件将在主脚本中导入.

终奌站

C:\Users\giranm\PycharmProjects\PyQt Tutorial>pyrcc4 -py3 resources.qrc -o resources_rc.py
Run Code Online (Sandbox Code Playgroud)

蟒蛇

import resources_rc
Run Code Online (Sandbox Code Playgroud)

一旦我确认主.py脚本有效,我就会使用PyInstaller创建一个.spec文件.

终奌站

C:\Users\giranm\PycharmProjects\PyQt Tutorial>pyi-makespec --noconsole --onefile secSearch_demo.py
Run Code Online (Sandbox Code Playgroud)

根据PyInstaller的指南,我通过修改上面的.spec文件添加了数据文件.

https://pythonhosted.org/PyInstaller/spec-files.html#adding-data-files

最后,我使用上面的.spec文件编译.exe.


Mor*_*itz 5

在 Ubuntu 20.04 上测试的另一种方法是将.ui文件添加到dataspec 文件中的部分。首先生成一个 .spec 文件pyinstaller --onefile hello.py。然后更新规范文件并运行pyinstaller hello.spec

a = Analysis(['hello.py'],
             ...
             datas=[('mainwindow.ui', '.')],
             ...
Run Code Online (Sandbox Code Playgroud)

下一步是更新 Python 文件中的当前目录。为此,os.chdir(sys._MEIPASS)必须使用该命令。当未设置 _MEIPASS 时,将其包装在 try-catch 中以供开发使用。

import os
import sys

# Needed for Wayland applications
os.environ["QT_QPA_PLATFORM"] = "xcb"
# Change the current dir to the temporary one created by PyInstaller
try:
    os.chdir(sys._MEIPASS)
    print(sys._MEIPASS)
except:
    pass

from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QFile, QIODevice

if __name__ == "__main__":
    app = QApplication(sys.argv)

    ui_file_name = "mainwindow.ui"
    ui_file = QFile(ui_file_name)
    if not ui_file.open(QIODevice.ReadOnly):
        print(f"Cannot open {ui_file_name}: {ui_file.errorString()}")
        sys.exit(-1)
    loader = QUiLoader()
    window = loader.load(ui_file)
    ui_file.close()
    if not window:
        print(loader.errorString())
        sys.exit(-1)
    window.show()

    sys.exit(app.exec_())
Run Code Online (Sandbox Code Playgroud)