Based on the links provided by KeyWeeUsr ( Linking data files to PyInstaller and Using PyInstaller to make EXE from Python scripts ) and combining this with the Kivy resource path method, here is an executable solution. I feel this is a bit rough around the edges because it uses SYS._MEIPASS (I would prefer a public API) and requires adding a piece of code to your Python code. However, the solution works on both Windows and Mac, so it will share.
Suppose 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 (eg icon file for mac)
myapp.ico (eg 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 the MyHiddenImports folder to the example if your code also adds another folder containing python code to sys.path at runtime.
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())
Resource_add_path () tells Kivy where to look for /.vv data files. For example, on a Mac, when starting the pyinstaller application, it pointed to / private / var / folders / 80 / y766cxq10fb_794019j7qgnh0000gn / T / _MEI25602, and on Windows it pointed to c: \ users \ raj \ AppData \ Local \ Temp_MEI64zTut (these folders are deleted after exiting from the application and create a new name at startup again).
I created the source Mac template specification 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 specification 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)
Notes: I added a hidden import path to pathex and referenced the package in hiddenimports. I added the myapp.kv file to the a.datas file, so it will be copied to the application. In the exe, I added a data tree. I included the prefix argument, since I wanted the data folder to be copied to the application (compared to the children sitting at the root level).
To compile the code to create the application and put it in the 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 are the window specs:
# -*- 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 a Windows application:
python -m PyInstaller myapp.spec