I'm packaging a Kivy app using PyInstaller (3.4) for use on Windows machines. I've gotten it to compile and work on my machine but when moving the exe to another, it fails giving the error:
[WARNING] [Image ] Unable to load image
C:\Users\<OTHER_MACHINES_USER>\AppData\Local\Temp_MEI38442\kivy_install\data\glsl\default.png>
[CRITICAL] [Window ] Unable to find any valuable Window provider.
sdl2 - Exception: SDL2: Unable to load image
This works from the dist
folder when the whole thing is copied across (as in ./dist/client_iface.exe
) and so I figure its a dependency issue I've been unable to resolve, apparently with SDL2.
I believe that this problem is almost identical to this problem however that question is two years old and as yet has no successful solution.
I've followed a number of guides (including this one which is linked from the above post) but with no success.
The spec file, with adjustments as per the Kivy guide, is as follows:
# -*- mode: python -*-
from kivy.deps import sdl2, glew
import os
block_cipher = None
a = Analysis(
['client_iface.py'],
pathex=['D:\\Users\\<USER>\\Documents\\2_Projects\\py_dice_roller\\client'],
binaries=[('D:\\Users\\<USER>\\Documents\\2_Projects\\DnD\\py_dice_roller\\client\\SDL2.dll', '.')],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False
)
a.datas += [
('client_iface.kv', '.\\client_iface.kv', 'DATA'),
('active.png', '.\\images\\active.png', 'DATA'),
('back.png', '.\\images\\active.png', 'DATA'),
('normal.png', '.\\images\\active.png', 'DATA'),
('DroidSansMono.ttf', '.\\fonts\\DroidSansMono.ttf', 'DATA')
]
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher
)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='client_iface',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True,
icon='.\images\icon_dWU_icon.ico',
)
coll = COLLECT(
exe, Tree(os.getcwd()),
a.binaries,
a.zipfiles,
a.datas,
*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
strip=False,
upx=True,
name='client_iface'
)
And following the above linked I have included the following code in the script:
def resourcePath(path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS)
return os.path.join(os.path.abspath(path))
if __name__ == '__main__':
kivy.resources.resource_add_path(resourcePath('.'))
kivy.resources.resource_add_path(resourcePath('./images'))
main_app().run()
And my command in use is:
python -m PyInstaller -F --onefile .\client_iface.spec
The expected result would be that the single executable produced would run on any like-machine without error-ing or the need for the entire folder of additional files.
Any help would be appreciated.
The COLLECT
section of your .spec
file indicates that a onedir
executable will be created. In order to make a onefile
executable, you will need to remove the COLLECT
section. Also, the sdl2 and glew deps that are listed in the COLLECT
section can be added to the EXE
section as *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)]
in place of the default [],
that appears before the first keyword argument.
Also, note that most of the command line options of Pyinstaller
are ignored if you are using a .spec
file. Refer to the documentation for more details. Unfortunately, the Pyinstaller
documentation is rather vague in many places.