Kivy: compiling to a single executable

前端 未结 2 1784
感情败类
感情败类 2020-11-27 20:43

Didn\'t get a response in the kivy forum, so trying here.

When I compile the tutorial pong code as a one file executable, I must still include the pong.kv file in th

相关标签:
2条回答
  • 2020-11-27 20:57

    Based on the links provided by KeyWeeUsr (Bundling data files with PyInstaller and Using PyInstaller to make EXEs from Python scripts) and combining that with Kivy's resource path method, here's a workable solution. I feel it's a bit rough around the edges because it uses SYS._MEIPASS (I would prefer a public API) and requires adding a code snippet to your Python code. However, the solution works on both Windows and Mac so will share.

    Assume I have the following code hierarchy:

    MyCode/
        MyApp.py  (This is the main program)
        myapp.kv  (This is the associated kv file)
    
        MyData/      (This is where data is located that the app uses)
           myapp.icns (e.g. icon file for mac)
           myapp.ico  (e.g. icon file for windows)
    
    Build/
        mac/ 
            myapp.spec (spec file to build on mac platform)
        pc/ 
            myapp.spec (spec file to build on windows platform)
    
    MyHiddenImports/ (Folder containing python files for hidden imports)
    

    I added a MyHiddenImports folder to the example in case your code also appends another folder containing python code to sys.path during run time.

    In MyApp.py add the following:

    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()
    

    The resources_add_path() tells Kivy where to look for the data/.kv files. For example, on the Mac, when running the pyinstaller app, it pointed to /private/var/folders/80/y766cxq10fb_794019j7qgnh0000gn/T/_MEI25602 and in windows, it pointed to c:\users\raj\AppData\Local\Temp_MEI64zTut (these folders get deleted after exiting app and creates another name when launched again).

    I created the initial Mac template spec file with the following command:

    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

    Here's the modified Mac OS Spec file:

    # -*- 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)
    

    Things to note: I added the hidden imports path to pathex, and referenced the package in hiddenimports. I appended the myapp.kv file to a.datas so it will be copied into the app. In the EXE, I added the Data tree. I included the prefix argument, as I wanted the Data folder to be copied into the app (versus having the children sit at the root level).

    To compile the code to create the app and put it into a dmg file I have a make-myapp script that does the following:

    pyinstaller -y --clean --windowed myapp.spec
    pushd dist
    hdiutil create ./myapp.dmg -srcfolder myapp.app -ov
    popd
    cp ./dist/myapp.dmg .
    

    Similarly, here's the windows spec file:

    # -*- 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' )
    

    And to compile the windows app:

    python -m PyInstaller myapp.spec

    0 讨论(0)
  • 2020-11-27 21:16

    If you don't care about the code length, what about loading kv data inside a .py file using Builder.load_string? This way the whole code is kept inside your python script and that may help to compile it to .exe.

    0 讨论(0)
提交回复
热议问题