In base ai collegamenti forniti da KeyWeeUsr (Bundling data files with PyInstaller e Using PyInstaller to make EXEs from Python scripts) e combinandolo con il metodo del percorso delle risorse di Kivy, ecco una soluzione praticabile. Sento che è un po 'approssimativo, perché usa SYS._MEIPASS (preferirei una API pubblica) e richiede l'aggiunta di uno snippet di codice al codice Python. Tuttavia, la soluzione funziona sia su Windows che su Mac, quindi condividerà.
Si supponga che ho la seguente gerarchia di codice:
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)
ho aggiunto una cartella MyHiddenImports all'esempio nel caso in cui il codice aggiunge anche un'altra cartella che contiene il codice python a sys.path durante la fase di esecuzione.
Nel MyApp.py aggiungere la seguente:
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()
Il resources_add_path() dice a Kivy dove cercare i file di dati/.kv. Ad esempio, sul Mac, quando si esegue l'applicazione pyinstaller, punta a/private/var/folders/80/y766cxq10fb_794019j7qgnh0000gn/T/_MEI25602 e in Windows, indica c: \ users \ raj \ AppData \ Local \ Temp_MEI64zTut (queste cartelle vengono eliminate dopo l'uscita dall'app e crea un altro nome quando viene riavviato).
ho creato il file del modello spec iniziale Mac con il seguente comando:
pyinstaller --onefile -y --clean --windowed --name myapp --icon = .. /. ./Code/Data/myapp.icns --exclude modulo _tkinter --exclude-Tkinter --exclude modulo incantare --exclude modulo ritorto ../../Code/MyApp.py
Ecco il file delle specifiche per Mac OS modificato:
# -*- 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)
Cose da notare: ho aggiunto il percorso delle importazioni nascoste a pathex e ho fatto riferimento al pacchetto in hiddenimports. Ho aggiunto il file myapp.kv ad a.datas in modo che venga copiato nell'app. Nell'EXE, ho aggiunto la struttura dati. Ho incluso l'argomento prefisso, perché volevo che la cartella dei dati fosse copiata nell'app (invece di far sedere i bambini a livello di root).
Per compilare il codice per creare l'applicazione e metterlo in un file dmg Ho uno script make-myapp che fa il seguente:
pyinstaller -y --clean --windowed myapp.spec
pushd dist
hdiutil create ./myapp.dmg -srcfolder myapp.app -ov
popd
cp ./dist/myapp.dmg .
Allo stesso modo, ecco il file di Windows specifica:
# -*- 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')
E per compilare l'applicazione di Windows:
python -m PyInstaller myapp.spec
Ho sentito dire che è possibile e alcune persone ce l'hanno fatta, ma non ho avuto fortuna nemmeno con questi: [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). Sebbene possa sembrare un overkill (a causa della dimensione .exe), anche l'opzione 'onefile' dovrebbe essere documentata. – KeyWeeUsr
Per altri file di dati, l'approccio suggerito ha finito per funzionare per me. Tuttavia, per caricare il file .kv predefinito, ho finito per chiamare kivy.resource_add_path (resourcePath()) dove resourcePath ha restituito sys._MEIPASS (o percorso di sviluppo locale se non compilato) nella mia sezione __main__. Questo sembrava funzionare; forse lo sarà anche per te? – Raj
Spero che lo faccia. Potresti aggiungere le tue fasi di confezionamento 'onefile' con un semplice esempio a [docs] (https://kivy.org/docs/guide/packaging-windows.html)/[wiki] (https://github.com/kivy/ kivy/wiki) se l'exe funziona con tutte le risorse e '.kv'? Penso che sarebbe bello averlo lì per riferimento futuro. – KeyWeeUsr