2016-03-12 11 views
7

Non ho ricevuto risposta nel forum di kivy, quindi prova qui.Kivy: compilazione di un singolo eseguibile

Quando compilo il codice pong tutorial come un file eseguibile, devo comunque includere il file pong.kv nella stessa cartella per l'esecuzione. In caso contrario, ricevo il seguente errore all'avvio del 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 

Come posso farlo funzionare come un unico eseguibile. Ecco il mio file di 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') 

Nota che ho cercato di includere il pong.kv nella lista dati, ma che non ha aiutato.

Grazie, -Raj

+0

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

+1

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

+0

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

risposta

4

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

+0

È bello e funziona finché non eseguo l'ultimo '.exe'. I file della cartella main.py sono inclusi, il codice funziona, ma l'unica cosa che non è sdl2 o meglio è detta cartella 'bin'. Quando ho controllato la cartella MEI in Temp, niente tranne "SDL ..." era lì dentro. No 'LICENZA ...', no 'lib ...' dalla cartella bin sdl2, quindi ho ottenuto [questo] errore (https://github.com/kivy/kivy/issues/3957). Il mio sdl2 è installato correttamente e l'ho provato con l'opzione di una cartella in pyinstaller (funziona), quindi c'è qualcosa di sbagliato nell'ottenere 'lib ...' dalla cartella 'share \ sdl2 \ bin' con' onefile' – KeyWeeUsr

+0

Non devo usare qualsiasi cosa da sdl2 mentre le mie app funzionano. Ho cercato nella cartella temporanea e non vedo i file sdl LICENZA, ecc. Solo le DLL sdl di base nella cartella principale. C'è uno snippet di codice che posso provare a compilare per vedere cosa ottengo? – Raj

+0

Beh, l'ho provato con la demo del touchtracer e le tue istruzioni e specifiche, quindi ... – KeyWeeUsr

0

Se non vi interessa circa la lunghezza del codice, per quanto riguarda il caricamento dei dati kv all'interno di un file .py utilizzando Builder.load_string? In questo modo l'intero codice è conservato all'interno del tuo script python e questo potrebbe aiutare a compilarlo in .exe.

+0

Potrebbe essere un modo, ma non è buono Immagina di avere immagini e altre cose, non solo '.kv'. – KeyWeeUsr

+0

Corretto, ho i file delle icone e alcuni file di dati aggiuntivi che vengono utilizzati proprio ora accanto al file .exe. Su Mac, sono in grado nella fase di post-elaborazione di spostare i file nella cartella .app che non è l'ideale e mi chiedo se mi troverò a dover affrontare problemi di firma in futuro. – Raj

+0

E che dire del multilinguaggio con gettext, ad esempio? Sarebbe difficile ottenere le stringhe per la localizzazione. –