Pyinstaller 打包成一个文件时无法找到数据文件。

4
我也是第一次尝试构建一个包含数据文件的简单的独立可执行文件,但是在构建.exe时,Pyinstaller似乎找不到它们。一个--onedir版本看起来运行得很好。
此时我正在使用--debug开关。我能够运行onefile可执行文件,并且可以看到它似乎开始工作了。程序可以找到(sys._MEIPASS)临时目录(按要求打印所需的目录名称),但在从临时目录查找第一个数据文件时报告了“没有这样的文件或目录”的错误。我在.exe上使用了archiveviewer.py,并没有找到需要的数据文件,这似乎是问题所在,但我无法弄清楚原因。构建的数据文件在spec文件描述的目录中。我的完整spec文件如下:
# -*- mode: python -*-

a = Analysis(['develop6.py'],
         pathex=['C:\\PYINST20'],
         hiddenimports=[],
         hookspath=None)

a.datas += [ ('conlist.txt', 'C:\\pyinst20\\conlist.txt', 'DATA'), ('imageblank.gif', 'C:\\pyinst20\\imageblank.gif', 'DATA')]

pyz = PYZ(a.pure)

exe = EXE(pyz,
      a.scripts,
      a.binaries,
      a.zipfiles,
      a.datas,
      name=os.path.join('dist', 'develop6.exe'),
      debug=True,
      strip=None,
      upx=True,
      console=True )
5个回答

5

这个问题虽然旧,但是它是我发现的唯一解决相同问题的来源之一,我会在这里分享我的解决方案,以便能够帮助到其他人。

有两件主要的事情需要做,才能在“onefile”模式下添加数据文件到您的脚本中。

1. 调整路径

在您的脚本中,调整路径以查找捆绑的数据文件。根据PyInstaller文档(此处),可执行文件是从一个临时文件启动的,因此您的路径必须考虑到这个动态部分:

对于具有以下相对路径的文件: ./your/file/is/here.ext

代码将为:

import sys
wd = sys._MEIPASS
file_path = os.path.join(wd,<your>,<file>,<is>,<here>)

注意:为了使您的代码在其他上下文中也能正常工作,您可以执行以下操作:
import sys
import os
try:
   wd = sys._MEIPASS
except AttributeError:
   wd = os.getcwd()
file_path = os.path.join(wd,<your>,<file>,<is>,<here>)

2. 在PyInstaller规格中添加数据文件路径

根据PyInstaller的文档(此处),有两种将数据文件添加到捆绑包中的方法:

  1. 在脚本目录下运行pyinstaller <yourscript.py>命令时,通过传递选项--add-files和文件参数来实现

  2. 首先导航到您的脚本目录并运行 pyi-makespec <yourscript.py>生成一个规格文件,然后将您的文件添加到元组列表data=[]中。 元组包含文件的实际路径和其在捆绑包中的路径。如果您按照第一点执行了操作,这应该看起来像这样datas = [('/your/file/is/here.ext','/your/file/is/')]

然后运行PyInstall <yourscript.spec>基于您的规格文件构建捆绑包。


太好了!非常感谢你的帮助!它完美地运行了。 - phabi
1
很高兴能帮到你 :-) - Séraphin

1

经过反复查看混乱的stackoverflow主题Pyinstaller文档,我最终成功打包了我的应用程序。

步骤如下:

  1. In your app root directory and . venv/bin/activated:

    ➜  ~ pip install pyinstaller
    ➜  ~ pyi-makespec --windowed --onedir --i ./resources/img/icn.icns \
          --osx-bundle-identifier "com.myname.macOS.myappname" app.py
    
  2. Now, modify your app.spec just a tiny bit:

    added_files = [
     ('resources/img', 'resources/img'),
     ( 'README.md', '.' )
     ]
    

    initialize a dictionary added_files with the relative path to your resources and set datas = added_files. In my application I used images located at ./resources/img relative to my main.py file.

  3. And to finilize, this is perhaps the easiest to forget step and not-so obvious:

    ➜  ~ pyinstaller --onefile app.spec
    

请注意,这最后一步来自这里


我认为您将字典和元组列表混淆了。 - novski

1
使用类似 AutoPyToExe 的应用程序可以解决大部分 PyInstaller 的挑战。该界面允许您解决此处列出的大多数挑战。只需选择文件位置,而无需移动它们,即可了解有关隐藏文件、数据文件、清洁构建等等的更多信息。请参见附加文件。

enter image description here


0

我相信我找到了问题所在,它与规范文件的主体无关。看起来当我使用规范文件运行pyinstaller时,我的命令行语法是错误的。当正确运行时:

pyinstaller.py [options] <my_specfile.spec>

看起来能正常工作。


还有更多的细节吗?我卡在这里了。 回溯(最近的调用最后): 文件“<string>”,第19行,<module> IOError:[Errno 2]没有这样的文件或目录:“/tmp/_MEIQoipmC/data/en_stop.txt” - dranxo

0
我们可以按照文档设置不同的文件路径,当脚本作为包运行或未运行。然而,我的解决方法是设置一个稳定的顶级目录,其中所有非脚本文件都存放在其中,并与所有模块共享此顶级目录。然后在每个模块中,我可以基于顶级目录创建文件路径。
例如,在此布局中,所有模块代码都位于src文件夹中。这些模块需要访问conf文件夹、database文件夹和otherconfig.yaml。
├── conf
│   └── config.ini
├── database
├── entry_point.py
├── otherconfig.yaml
├── root_path.py
└── src
    ├── pkg1
    └── pkg2

root_path.py 的代码如下:

import os

path = os.getcwd()

在每个模块中,我可以这样创建配置文件的文件路径:
import root_path

config_path = f"{root_path.path}/conf/config.ini"
otherconfig_path = f"{root_path.path}/otherconfig.yaml"

然后,通过pyinstaller -F entry_point.py创建一个单文件可执行文件(不添加任何文件),我们只需要重新创建相同的布局,每个模块中的文件路径仍然有效。

├── bundled_executable
├── conf
│   └── config.ini
├── database
└── otherconfig.yaml

当然,这种解决方法无法隐藏配置文件。但对于我的用例,我需要给用户修改配置文件的自由,因此不隐藏文件实际上对我有帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接