【问题标题】:PyInstaller + UI Files - FileNotFoundError: [Errno 2] No such file or directory:PyInstaller + UI 文件 - FileNotFoundError: [Errno 2] 没有这样的文件或目录:
【发布时间】:2016-10-19 16:56:11
【问题描述】:

我正在尝试使用 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

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

但是,在进行了一些研究之后,我被告知我需要使用 os.getcwd() 并确保我的脚本中有完整路径。即使使用下面的代码,我在尝试定位 .ui 文件时仍然会出错。

PyInstaller: IOError: [Errno 2] No such file or directory:

# 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.  

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

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

我对使用 PyQT4 和 PyInstaller 还很陌生 - 任何帮助都将不胜感激!

【问题讨论】:

    标签: python pyqt4 pyinstaller qt-designer


    【解决方案1】:

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

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

    Bundling data files with 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)
    

    接下来,我使用此函数和所需类的变量导入 .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)
    

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

    终端

    C:\Users\giranm\PycharmProjects\PyQt Tutorial>pyrcc4 -py3 resources.qrc -o resources_rc.py
    

    Python

    import resources_rc
    

    确认主 .py 脚本有效后,我使用 PyInstaller 创建了一个 .spec 文件。

    终端

    C:\Users\giranm\PycharmProjects\PyQt Tutorial>pyi-makespec --noconsole --onefile secSearch_demo.py
    

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

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

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

    【讨论】:

    • 这仍然是每次你想open() 一个你捆绑在 witjh --add-data 属性中的文件。文件的根路径是sys._MEIPASS,后面是项目中的相对路径。
    • 这里是 Python 部署的新手:您能否编辑此答案以指定这些代码段应位于哪些文件中。
    【解决方案2】:

    你可以简单地使用:

    uic.loadUi(r'E:\Development\Python\your_ui.ui', self)
    

    使用完整路径,并使用带有标准参数的 pyinstaller,它工作正常。 r prefix 确保反斜杠按字面意思解释。

    【讨论】:

    • 看来pyinstaller 不理解相对导入!!
    【解决方案3】:

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

    a = Analysis(['hello.py'],
                 ...
                 datas=[('mainwindow.ui', '.')],
                 ...
    

    下一步是更新 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_())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-13
      • 2019-11-03
      • 2021-08-24
      相关资源
      最近更新 更多