11

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 the same folder for it to run. Otherwise, I get the following error when launching the exe:


    GL: EXT_framebuffer_object is supported
    [INFO              ] [GL          ] OpenGL version 
    [INFO              ] [GL          ] OpenGL vendor 
    [INFO              ] [GL          ] OpenGL renderer 
    [INFO              ] [GL          ] OpenGL parsed version: 2, 1
    [INFO              ] [GL          ] Shading version 
    [INFO              ] [GL          ] Texture max size 
    [INFO              ] [GL          ] Texture max units 
    [INFO              ] [Window      ] auto add sdl2 input provider
    [INFO              ] [Window      ] virtual keyboard not allowed,
    single mode, not docked
     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

How can I get it to run as one executable. Here's my pong.spec file:



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

Note that I tried to include the pong.kv in the datas list but that didn't help.

Thanks, -Raj

Peter Badida
  • 11,310
  • 10
  • 44
  • 90
Raj
  • 221
  • 1
  • 2
  • 10
  • I heard it _is_ possible and a few people made it, but I had no luck even with these: [1](http://irwinkwan.com/2013/04/29/python-executables-pyinstaller-and-a-48-hour-game-design-compo/), [2](http://stackoverflow.com/questions/7674790/bundling-data-files-with-pyinstaller-onefile). Although it may seem like an overkill(because of .exe size), working `onefile` option should be documented too. – Peter Badida Mar 12 '16 at 09:36
  • 2
    For other data files, the approach you suggested ended up working for me. However, to load the default .kv file, I ended up calling kivy.resource_add_path(resourcePath()) where resourcePath returned sys._MEIPASS (or local development path if not compiled) in my __main__ section. This seemed to work; maybe will for you too? – Raj Mar 14 '16 at 20:01
  • I hope it'll. Could you append your `onefile` packaging steps with simple example to [docs](https://kivy.org/docs/guide/packaging-windows.html)/[wiki](https://github.com/kivy/kivy/wiki) if the exe works with all resources and `.kv`? I think it'd be nice to have it there for future reference. – Peter Badida Mar 14 '16 at 20:30
  • I posted an answer based on the links you provided. Let me know if that works before revising the wiki. Thanks. – Raj Mar 15 '16 at 20:20
  • This really should be a comment but I don't have enough rep, so... Raj's answer is great and works well, but one thing to keep in mind: you have to also import the following into your app.py file (the people here before me probably just happened to already be using it and therefore didn't notice the need to import it, or else thought it too obvious to mention) import kivy, sys, os.path – taigrr Apr 23 '17 at 13:40
  • Does this answer your question? [Include .kv/.json files while packaing kivy with PyInstaller --onefile?](https://stackoverflow.com/questions/48467917/include-kv-json-files-while-packaing-kivy-with-pyinstaller-onefile) – badams Jul 09 '20 at 18:06

2 Answers2

10

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

Community
  • 1
  • 1
Raj
  • 221
  • 1
  • 2
  • 10
  • It's nice and works until I run the final `.exe`. Files from main.py folder are included, code works, but the only thing that doesn't is sdl2 or better said `bin` folder. When I checked MEI folder in Temp, nothing except `SDL...` was in there. No `LICENSE...`, no `lib...` from sdl2 bin folder, therefore I got [this](https://github.com/kivy/kivy/issues/3957) error. My sdl2 is installed properly and I tried it with one folder option in pyinstaller(works), so there's something wrong with getting `lib...` from `share\sdl2\bin` folder with `onefile` – Peter Badida Mar 18 '16 at 11:07
  • I must not be using anything from sdl2 as my apps run. I looked in temp folder and don't see sdl LICENSE, etc. files either. Just basic sdl dlls in root folder. Is there a snippet of code that I can try to compile to see what I get? – Raj Mar 21 '16 at 16:35
  • I compiled touchtracer for both mac and pc and they seemed to work - I was able to draw on both platforms. I can post the .spec files as part of my answer or is there something specific that you'd like me to check? – Raj Mar 22 '16 at 00:24
  • No need to edit the answer, but I'd like to know do you use pygame as window provider? I don't see any of `lib*` files including that for default.png, so I get no Window with sdl2 and that's why it doesn't run. – Peter Badida Mar 22 '16 at 06:18
  • Sorry for bad feedback, I found out that I used double `return os.path.join(os.path.abspath("."))` instead of two different returns in my main.py, so that's the reason why `lib*` and licenses were missing. The instructions work without error :) – Peter Badida Mar 22 '16 at 16:48
  • Great to hear. I marked response as 'correct'. Also, to answer your previous question, I didn't install pygame directly. I installed sdl via https://kivy.org/docs/installation/installation-windows.html E.g: 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 Mar 22 '16 at 21:50
  • When I start my app, the window opens without drawing anything, and then an error pops up: "Failed to execute script my_app" (no extension). What's the problem here? – Nearoo Mar 06 '17 at 17:27
1

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.

illright
  • 3,991
  • 2
  • 29
  • 54
  • It may be a way, but not a good one. Imagine he'd have pictures and other stuff, not only `.kv`. – Peter Badida Mar 13 '16 at 14:36
  • Correct, I have the icon files and some additional data files that are used which right now sit along side the .exe. On the Mac, I am able in the post-processing step to move the files into the .app folder which isn't ideal and wonder if I'll run into signing issues in the future. – Raj Mar 14 '16 at 18:07
  • And what about multilanguage using gettext for example? Would be hard to get the strings out for localization. – Bill Bridge Aug 18 '17 at 07:15