【问题标题】:Kivy: compiling to a single executableKivy:编译为单个可执行文件
【发布时间】:2016-06-27 10:17:22
【问题描述】:

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

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

GL:支持 EXT_framebuffer_object [信息] [GL] OpenGL 版本 [信息] [GL] OpenGL 供应商 [信息] [GL] OpenGL 渲染器 [INFO] [GL] OpenGL 解析版本:2、1 [INFO] [GL] 阴影版本 [INFO] [GL] 纹理最大尺寸 [INFO] [GL] 纹理最大单位 [INFO] [Window] 自动添加 sdl2 输入提供程序 [INFO] [Window] 虚拟键盘不允许, 单模,不对接 回溯(最近一次通话最后): 文件“”,第 81 行,在 文件“c:\python34\lib\site-packages\kivy\app.py”,第 802 行,在 跑步 根 = self.build() 文件“”,第 75 行,在构建中 文件“”,第 20 行,在 serveBall 中 AttributeError:“NoneType”对象没有属性“中心” 主要返回 -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 包含在数据列表中,但这没有帮助。

谢谢, -拉杰

【问题讨论】:

  • 我听说它可能的,并且有几个人做到了,但即使有这些我也没有运气:12。虽然这看起来有点过分(因为 .exe 大小),但也应该记录有效的 onefile 选项。
  • 对于其他数据文件,您建议的方法最终对我有用。但是,为了加载默认的 .kv 文件,我最终调用了 kivy.resource_add_path(resourcePath()) ,其中 resourcePath 在我的 main 部分返回了 sys._MEIPASS(或本地开发路径,如果未编译)。这似乎奏效了;也许你也会?
  • 我希望它会。如果 exe 适用于所有资源和 .kv,您能否将带有简单示例的 onefile 打包步骤附加到 docs/wiki?我认为将它放在那里以供将来参考会很好。
  • 我根据您提供的链接发布了答案。在修改 wiki 之前让我知道这是否有效。谢谢。
  • 这真的应该是一个评论,但我没有足够的代表,所以...... Raj 的回答很好而且效果很好,但要记住一件事:你还必须导入以下内容到你的 app.py 文件中(我之前的人可能只是碰巧已经在使用它,因此没有注意到需要导入它,或者认为它太明显而无法提及) import kivy, sys, os.path

标签: python kivy pyinstaller


【解决方案1】:

基于 KeyWeeUsr(Bundling data files with PyInstallerUsing PyInstaller to make EXEs from Python scripts)提供的链接,并结合 Kivy 的资源路径方法,这是一个可行的解决方案。我觉得它的边缘有点粗糙,因为它使用 SYS._MEIPASS(我更喜欢公共 API)并且需要在 Python 代码中添加代码 sn-p。但是,该解决方案适用于 Windows 和 Mac,因此将共享。

假设我有以下代码层次结构:

我的代码/ MyApp.py(这是主程序) myapp.kv(这是关联的 kv 文件) MyData/(这是应用程序使用的数据所在的位置) myapp.icns(例如 mac 的图标文件) myapp.ico(例如 Windows 的图标文件) 建造/ 苹果/ myapp.spec(在 mac 平台上构建的规范文件) 个人电脑/ myapp.spec(在 Windows 平台上构建的规范文件) 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()

resources_add_path() 告诉 Kivy 在哪里查找 data/.kv 文件。例如,在 Mac 上,运行 pyinstaller 应用程序时,它指向 /private/var/folders/80/y766cxq10fb_794019j7qgnh0000gn/T/_MEI25602,在 Windows 中,它指向 c:\users\raj\AppData\Local\Temp_MEI64zTut (这些文件夹在退出应用程序后被删除,并在再次启动时创建另一个名称)。

我使用以下命令创建了初始 Mac 模板规范文件:

pyinstaller --onefile -y --clean --windowed --name myapp --icon=../../Code/Data/myapp.icns --exclude-module _tkinter --exclude-module Tkinter -- exclude-module 附魔 --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 文件夹复制到应用程序中(而不是让孩子坐在根级别)。

要编译代码以创建应用程序并将其放入 dmg 文件,我有一个 make-myapp 脚本,它执行以下操作:

pyinstaller -y --clean --windowed myapp.spec 推送区 hdiutil create ./myapp.dmg -srcfolder myapp.app -ov 流行音乐 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

【讨论】:

  • 在我运行最终的 .exe 之前,它很好并且可以工作。包含来自 main.py 文件夹的文件,代码可以工作,但唯一没有的是 sdl2 或更好的说法是 bin 文件夹。当我在 Temp 中检查 MEI 文件夹时,除了 SDL... 之外什么都没有。没有LICENSE...,没有来自 sdl2 bin 文件夹的lib...,因此我收到了this 错误。我的 sdl2 安装正确,我在 pyinstaller(works) 中使用一个文件夹选项进行了尝试,所以从 share\sdl2\bin 文件夹中获取 lib...onefile 时出现问题
  • 在我的应用程序运行时,我不能使用 sdl2 中的任何东西。我查看了临时文件夹,也没有看到 sdl LICENSE 等文件。只是根文件夹中的基本 sdl dll。有没有我可以尝试编译的 sn-p 代码来看看我得到了什么?
  • 我为 mac 和 pc 编译了 touchtracer,它们似乎工作 - 我能够在两个平台上绘图。我可以将 .spec 文件作为我的答案的一部分发布,或者您是否希望我检查一些具体的内容?
  • 无需编辑答案,但我想知道您是否使用 pygame 作为窗口提供程序?我没有看到任何 lib* 文件,包括 default.png 的文件,所以我没有 sdl2 的窗口,这就是它不运行的原因。
  • 抱歉反馈不好,我发现我在 main.py 中使用了双 return os.path.join(os.path.abspath(".")) 而不是两个不同的返回,所以这就是 lib* 和许可证丢失的原因。这些说明可以正常工作:)
【解决方案2】:

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

【讨论】:

  • 这可能是一种方法,但不是一个好方法。想象一下,他会有照片和其他东西,而不仅仅是.kv
  • 正确,我有图标文件和一些正在使用的附加数据文件,它们现在位于 .exe 旁边。在 Mac 上,我可以在后处理步骤中将文件移动到不理想的 .app 文件夹中,我想知道我将来是否会遇到签名问题。
  • 例如使用 gettext 的多语言呢?很难找到本地化的字符串。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 2016-09-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多