Kivy:编译为单个可执行文件

11

在kivy论坛没有得到回应,所以来这里试一试。

当我将教程中的pong代码编译为一个可执行文件时,仍然必须将pong.kv文件包含在同一个文件夹中才能运行。 否则,启动exe时会出现以下错误:

    GL:支持EXT_framebuffer_object
    [INFO] [GL] OpenGL版本 
    [INFO] [GL] OpenGL供应商 
    [INFO] [GL] OpenGL渲染器 
    [INFO] [GL] OpenGL解析版本:2.1
    [INFO] [GL] Shading版本 
    [INFO] [GL] 纹理最大大小 
    [INFO] [GL] 纹理最大单位 
    [INFO] [Window] 自动添加sdl2输入提供程序
    [INFO] [Window] 不允许虚拟键盘,
    单一模式,未停靠
     Traceback (most recent call last):
       File "", line 81, in 
       File "c:\python34\lib\site-packages\kivy\app.py", line 802, in
    run
         root = self.build()
       File "", line 75, in build
       File "", line 20, in serveBall
     AttributeError: 'NoneType' object has no attribute 'center'
    main returned -1

我该如何让它作为一个可执行文件运行?这是我的pong.spec文件:



    # -*- mode: python -*-

    from kivy.deps import sdl2, glew

    block_cipher = None


    a = Analysis(['Code\main.py'],
                 pathex=['E:\\Development\\Pong'],
                 binaries=None,
                 datas=None,
                 hiddenimports=[],
                 hookspath=[],
                 runtime_hooks=[],
                 excludes=[],
                 win_no_prefer_redirects=False,
                 win_private_assemblies=False,
                 cipher=block_cipher)
    pyz = PYZ(a.pure, a.zipped_data,
                 cipher=block_cipher)

    a.datas += [('Code\pong.kv', 'E:\\Development\\Pong\Code\pong.kv', 'DATA')] 

    exe = EXE(pyz,Tree('Code'),
              a.scripts,
              a.binaries,
              a.zipfiles,
              a.datas,
              *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
              name='pong',
              debug=False,
              strip=False,
              upx=True,
              console=True , icon='pong.ico')

请注意,我尝试将pong.kv包含在数据列表中,但这并没有帮助。

谢谢, -Raj


我听说过这是可能的,有几个人做到了,但即使使用了以下链接[1](http://irwinkwan.com/2013/04/29/python-executables-pyinstaller-and-a-48-hour-game-design-compo/),[2](https://dev59.com/EGsz5IYBdhLWcg3wxax3)也没有成功。尽管可能会因为.exe文件的大小而看起来有点浪费,但应该也要记录“onefile”选项的工作方式。 - Peter Badida
2
对于其他数据文件,您建议的方法最终对我起作用了。但是,为了加载默认的.kv文件,我在我的__main__部分中调用了kivy.resource_add_path(resourcePath()),其中resourcePath返回sys._MEIPASS(如果未编译,则为本地开发路径)。这似乎有效;也许对您也有用? - Raj
希望这样做。如果exe可以使用所有资源和.kv文件,您能否将您的“onefile”打包步骤附加到docs/wiki中,并提供简单的示例?我认为将来参考会很有用。 - Peter Badida
我根据你提供的链接发布了一个答案。在修改维基之前,请告诉我是否有效。谢谢。 - Raj
这其实应该是一条注释,但我没有足够的声望,所以...... Raj的回答非常好,并且运行良好,但需要记住一点:您还必须将以下内容导入到您的app.py文件中(在我之前的人可能已经在使用它,因此没有注意到需要导入它,或者认为它太显然了而没有提到)import kivy,sys,os.path。 - taigrr
2个回答

10
根据KeyWeeUsr提供的链接(使用PyInstaller捆绑数据文件使用PyInstaller从Python脚本创建EXE文件),并结合Kivy的资源路径方法,这里提供了一个可行的解决方案。由于它使用SYS._MEIPASS(我更喜欢公共API),并需要向Python代码中添加代码片段,因此感觉有些粗糙。但是,这个解决方案在Windows和Mac上都可以工作,因此分享一下。
假设我有以下代码层次结构:
MyCode/
    MyApp.py (这是主程序)
    myapp.kv (这是相关的kv文件)
MyData/ (这是应用程序使用的数据所在的位置) myapp.icns (例如用于mac的图标文件) myapp.ico (例如用于windows的图标文件)
Build/ mac/ myapp.spec (mac平台上的spec文件) pc/ myapp.spec (windows平台上的spec文件)
MyHiddenImports/ (包含隐藏导入的python文件的文件夹)
我在示例中添加了一个MyHiddenImports文件夹,以防您的代码在运行时还会附加另一个包含Python代码的文件夹到sys.path中。
在MyApp.py中添加以下内容:
def resourcePath():
    '''Returns path containing content - either locally or in pyinstaller tmp file'''
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS)

    return os.path.join(os.path.abspath("."))

if __name__ == '__main__':
    kivy.resources.resource_add_path(resourcePath()) # add this line
    my_app = MyApp()

资源文件_add_path()告诉Kivy在哪里查找数据/.kv文件。例如,在Mac上运行pyinstaller应用程序时,它指向/private/var/folders/80/y766cxq10fb_794019j7qgnh0000gn/T/_MEI25602;在Windows上,它指向c:\users\raj\AppData\Local\Temp_MEI64zTut(这些文件夹在退出应用程序后被删除,并在下次启动时创建另一个名称)。
我使用以下命令创建了初始的Mac模板spec文件: pyinstaller --onefile -y --clean --windowed --name myapp --icon=../../Code/Data/myapp.icns --exclude-module _tkinter --exclude-module Tkinter --exclude-module enchant --exclude-module twisted ../../Code/MyApp.py
以下是修改后的Mac OS Spec文件:
# -*- mode: python -*-

block_cipher = None


a = Analysis(['../../Code/MyApp.py'],
            pathex=['/Users/raj/Development/Build/mac', 
            '../../MyHiddenImports'],    
            binaries=None,
            datas=None,
            hiddenimports=['MyHiddenImports'],    
            hookspath=[],
            runtime_hooks=[],
            excludes=['_tkinter', 'Tkinter', 'enchant', 'twisted'],
            win_no_prefer_redirects=False,
            win_private_assemblies=False,
            cipher=block_cipher)

pyz = PYZ(a.pure, a.zipped_data,
            cipher=block_cipher)

a.datas += [('myapp.kv', '../../MyCode/my.kv', 'DATA')]

exe = EXE(pyz, Tree('../../Code/Data', 'Data'), 
            a.scripts,
            a.binaries,
            a.zipfiles,
            a.datas,
            name='myapp',
            debug=False,
            strip=False,
            upx=True,
            console=False , icon='../../Code/Data/myapp.icns')

app = BUNDLE(exe,
             name='myapp.app',
             icon='../../Code/Data/myapp.icns',
             bundle_identifier=None)

需要注意的事项:我将隐藏导入路径添加到pathex中,并在hiddenimports中引用了该包。我将myapp.kv文件附加到a.datas中,以便将其复制到应用程序中。在EXE中,我添加了Data树。我包括前缀参数,因为我希望将Data文件夹复制到应用程序中(而不是让子级位于根级别)。
要编译代码并创建应用程序并将其放入dmg文件中,我有一个make-myapp脚本,它执行以下操作: pyinstaller -y --clean --windowed myapp.spec pushd dist hdiutil create ./myapp.dmg -srcfolder myapp.app -ov popd cp ./dist/myapp.dmg .
类似地,这是Windows规范文件:
# -*- mode: python -*-

from kivy.deps import sdl2, glew

block_cipher = None


a = Analysis(['..\\..\\Code\\Cobbler.py'],
             pathex=['E:\\Development\\MyApp\\Build\\pc',
             '..\\..\\MyHiddenImports'],
             binaries=None,
             datas=None,
             hiddenimports=['MyHiddenImports'],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)

pyz = PYZ(a.pure, a.zipped_data,
         cipher=block_cipher)

a.datas += [('myapp.kv', '../../Code/myapp.kv', 'DATA')]

exe = EXE(pyz, Tree('..\\..\\Code\\Data','Data'),
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
          name='myapp',
          debug=False,
          strip=False,
          upx=True,
          console=False, icon='..\\..\\Code\\Data\\myapp.ico' )

编译 Windows 应用程序:

运行命令 "python -m PyInstaller myapp.spec"。


我的应用程序运行时似乎没有使用sdl2的任何内容。我查看了临时文件夹,也没有看到sdl许可证等文件。只有基本的sdl dll在根文件夹中。是否有一段代码片段可以尝试编译以查看结果? - Raj
我为Mac和PC编译了Touchtracer,它们似乎都可以工作 - 我能够在两个平台上绘图。我可以将.spec文件作为我的答案的一部分发布,或者您有特定的要求需要我检查吗? - Raj
抱歉反馈不好,我发现在我的main.py中使用了双重return os.path.join(os.path.abspath("."))而不是两个不同的返回值,这就是为什么lib*和许可证丢失的原因。指令可以正常运行 :) - Peter Badida
太好了,听到这个消息很高兴。我将回答标记为“正确”。另外,为了回答你之前的问题,我没有直接安装pygame。我通过https://kivy.org/docs/installation/installation-windows.html安装了sdl。例如:python -m pip install docutils pygments pypiwin32 kivy.deps.sdl2 kivy.deps.glew
kivy.deps.gstreamer --extra-index-url https://kivy.org/downloads/packages/simple/
- Raj
当我启动我的应用程序时,窗口打开但没有绘制任何内容,然后出现一个错误:“无法执行脚本my_app”(没有扩展名)。这里的问题是什么? - Nearoo
显示剩余2条评论

1
如果您不在意代码长度,那么使用Builder.load_string在.py文件中加载kv数据怎么样?这样整个代码都保留在您的Python脚本中,可能有助于将其编译为.exe文件。

这可能是一种方法,但不是一个好的方法。想象一下,他可能有图片和其他东西,而不仅仅是 .kv 文件。 - Peter Badida
没错,我有图标文件和一些额外的数据文件,它们现在与 .exe 文件放在一起使用。在 Mac 上,我能够在后处理步骤中将这些文件移动到 .app 文件夹中,但这并不理想,我想知道是否会在未来遇到签名问题。 - Raj
那么使用gettext进行多语言支持呢?将字符串提取出来以进行本地化会很困难吗? - Bill Bridge

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