python - Kivy: compilando a un solo ejecutable
pyinstaller (2)
Basado en los enlaces proporcionados por KeyWeeUsr (agrupando archivos de datos con PyInstaller y 1 ) y combinando eso con el método de ruta de recursos de Kivy, aquí hay una solución viable. Siento que es un poco difícil porque usa SYS._MEIPASS (preferiría una API pública) y requiere agregar un fragmento de código a su código de Python. Sin embargo, la solución funciona tanto en Windows como en Mac, por lo que la compartirá.
Supongamos que tengo la siguiente jerarquía de código:
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)
Agregué una carpeta MyHiddenImports al ejemplo en caso de que su código también agregue otra carpeta que contenga código python a sys.path durante el tiempo de ejecución.
En MyApp.py agregue lo siguiente:
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 () le dice a Kivy dónde buscar los archivos de datos / .kv.
Por ejemplo, en Mac, cuando ejecutaba la aplicación pyinstaller, apuntaba a / private / var / folder / 80 / y766cxq10fb_794019j7qgnh0000gn / T / _MEI25602 y en Windows apuntaba a c: / users / raj / AppData / Local / Temp_MEI64zTut ( estas carpetas se eliminan después de salir de la aplicación y crean otro nombre cuando se inician nuevamente).
Creé el archivo de especificaciones de la plantilla Mac inicial con el siguiente comando:
pyinstaller --onefile -y --clean --windowed --name myapp --icon = .. / .. / Code / Data / myapp.icns --exclude-module _tkinter --exclude-module Tkinter --exclude-module encantar --exclude-module twisted ../../Code/MyApp.py
Aquí está el archivo modificado de especificaciones de Mac OS:
# -*- 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)
Cosas a tener en cuenta: agregué la ruta de importación oculta a pathex y hice referencia al paquete en hiddenimports. Agregué el archivo myapp.kv a a.datas para que se copie en la aplicación. En el EXE, agregué el árbol de datos. Incluí el argumento prefijo, ya que quería que la carpeta de datos se copiara en la aplicación (en lugar de que los niños se sentaran en el nivel raíz).
Para compilar el código para crear la aplicación y ponerlo en un archivo dmg, tengo un script make-myapp que hace lo siguiente:
pyinstaller -y --clean --windowed myapp.spec pushd dist hdiutil create ./myapp.dmg -srcfolder myapp.app -ov popd cp ./dist/myapp.dmg .
Del mismo modo, aquí está el archivo de especificaciones de 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'' )
Y para compilar la aplicación de Windows:
python -m PyInstaller myapp.spec
No obtuve una respuesta en el foro kivy, así que intente aquí.
Cuando compilo el código pong de tutorial como un archivo ejecutable de un archivo, aún debo incluir el archivo pong.kv en la misma carpeta para que se ejecute. De lo contrario, aparece el siguiente error al iniciar el 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
¿Cómo puedo hacer que se ejecute como un ejecutable? Aquí está mi archivo 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'')
Tenga en cuenta que intenté incluir el pong.kv en la lista de datos, pero eso no ayudó.
Gracias -Raj
Si no le importa la longitud del código, ¿qué pasa con la carga de datos kv dentro de un archivo .py usando
Builder.load_string
?
De esta manera, todo el código se mantiene dentro de su script de Python y eso puede ayudar a compilarlo en .exe.