如何将所有资源打包成一个可执行文件?

6 投票
3 回答
16920 浏览
提问于 2025-04-16 21:29

我用Python写了一个GTK应用程序。

这个程序的所有图形界面都在一个glade文件里,还有一些图片用到。我想把我的应用程序打包成一个可执行文件(EXE)。为此,我使用了PyInstaller这个编译工具和UPX这个压缩工具。

我按照说明书的步骤做了:

python Configure.py
python Makespec.py --onefile --windowed --upx /path/to/yourscript.py
python Build.py /path/to/yourscript.spec

PyInstaller工作得很好,生成了一个exe文件。但是为了让我的应用程序正常运行,我必须把我的glade文件和图片文件复制到exe的文件夹里。

有没有办法把这些文件直接编译进可执行文件里呢?

我尝试了多种方式修改我的spec文件,但始终达不到我想要的效果。下面的spec文件只是把文件复制到目录里,并没有把它们编译进可执行文件中。

# -*- mode: python -*-
a = Analysis([os.path.join(HOMEPATH,'support\\_mountzlib.py'), os.path.join(HOMEPATH,'support\\useUnicode.py'), 'r:\\connection\\main.py'],
             pathex=['C:\\Documents and Settings\\Lixas\\Desktop\\pyinstaller-1.5-rc1'])

pyz = PYZ(a.pure)

exe = EXE( pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          name=os.path.join('dist', 'NetworkChecker.exe'),
          debug=False,
          strip=False,
          upx=True,
          console=False,
          icon='r:\\connection\\ikona.ico'        )

coll = COLLECT(
    exe,
    [('gui.glade', 'r:\\connection\\gui.glade', 'DATA')],
    [('question16.png', 'r:\\connection\\question16.png', 'DATA')],
#   a.binaries,
#   strip=False,
    upx=True,
    name='distFinal')

我希望能得到一个包含所有内容的单一可执行文件。

3 个回答

1

有没有办法把这些文件编译成可执行文件呢?

严格来说,是没有的,因为编译的对象是源代码,而glade文件是XML格式的,图片则是二进制数据。通常你需要做的是创建一个安装程序(也就是一个自解压的压缩包,运行时会把不同的文件放到正确的目录里)。

补充:如果你只是想要一个单文件的可执行程序(所以其实不是在“编译”,而是关心文件在系统上占用的数量),你可以试试这个脚本,它是基于py2exe的。它的做法是每次运行程序时创建临时文件,执行完成后再删除这些文件。

补充2:显然,你想要的在PyInstaller下也是可以实现的。根据文档

默认情况下,pyinstaller.py会创建一个包含主可执行文件和动态库的分发目录。选项--onefile表示你希望PyInstaller把所有内容打包成一个文件。

6

通过一些简单的修改,你可以把所有内容都放进你的源代码里,这样就能把它们打包进你的可执行文件里。

如果你在你的图片文件上运行 gdk-pixbuf-csource,你可以把这些图片转换成字符串,然后用 gtk.gdk.pixbuf_new_from_inline() 来加载它们。

你还可以把你的 Glade 文件作为字符串包含在程序里,然后用 gtk.Builder.add_from_string() 来加载它。

6

PyInstaller 确实可以让你把所有资源打包进 exe 文件里,而不需要把数据文件转换成 .py 文件,这样就简单多了。你的 COLLECT 对象看起来是正确的,真正麻烦的是在运行时如何访问这些文件。PyInstaller 会把这些文件解压到一个临时目录,并通过 _MEIPASS2 变量告诉你它们的位置。为了在开发模式和打包模式下都能获取文件路径,我使用了以下代码:

def resource_path(relative):
    return os.path.join(
        os.environ.get(
            "_MEIPASS2",
            os.path.abspath(".")
        ),
        relative
    )


# in development
>>> resource_path("gui.glade")
"/home/shish/src/my_app/gui.glade"

# in deployment
>>> resource_path("gui.glade")
"/tmp/_MEI34121/gui.glade"

撰写回答