web-dev-qa-db-fra.com

Regroupement de fichiers de données avec PyInstaller (--onefile)

J'essaie de créer un fichier EXE avec PyInstaller, qui consiste à inclure une image et une icône. Je ne peux pas pour la vie de le faire fonctionner avec --onefile.

Si je fais --onedir tout fonctionnera très bien . Lorsque j'utilise --onefile, il ne trouvera pas les fichiers supplémentaires référencés (lors de l'exécution du fichier EXE compilé). Il trouve les DLL et tout le reste bien, mais pas les deux images.

J'ai regardé dans le répertoire temporaire généré lors de l'exécution de l'EXE (\Temp\_MEI95642\ par exemple) et les fichiers y sont effectivement. Lorsque je dépose le fichier EXE dans ce répertoire temporaire, il le trouve. Très déroutant.

C’est ce que j’ai ajouté au fichier .spec

a.datas += [('images/icon.ico', 'D:\\[workspace]\\App\\src\\images\\icon.ico',  'DATA'),
('images/loaderani.gif','D:\\[workspace]\\App\\src\\images\\loaderani.gif','DATA')]     

Je devrais ajouter que j’ai également essayé de ne pas les mettre dans des sous-dossiers, cela n’a fait aucune différence.

Edit: Répondez la réponse plus récente comme correcte en raison de la mise à jour de PyInstaller.

71
arboreal shark

Les versions les plus récentes de PyInstaller ne définissent plus la variable env. Par conséquent, l'excellent answer de Shish ne fonctionnera pas. Maintenant, le chemin est défini comme sys._MEIPASS:

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)
102
max

pyinstaller décompresse vos données dans un dossier temporaire et stocke ce chemin de répertoire dans la variable d'environnement _MEIPASS2. Pour obtenir le répertoire _MEIPASS2 en mode condensé et utiliser le répertoire local en mode décompressé (développement), j'utilise ceci:

def resource_path(relative):
    return os.path.join(
        os.environ.get(
            "_MEIPASS2",
            os.path.abspath(".")
        ),
        relative
    )

Sortie:

# in development
>>> resource_path("app_icon.ico")
"/home/shish/src/my_app/app_icon.ico"

# in production
>>> resource_path("app_icon.ico")
"/tmp/_MEI34121/app_icon.ico"
49
Shish

Toutes les autres réponses utilisent le répertoire de travail current dans le cas où l'application n'est pas PyInstalled (c'est-à-dire que sys._MEIPASS n'est pas défini). C'est faux, car cela vous empêche d'exécuter votre application à partir d'un répertoire autre que celui où se trouve votre script.

Une meilleure solution:

import sys
import os

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
    return os.path.join(base_path, relative_path)
14

Au lieu de réécrire tout mon code de chemin comme suggéré, j'ai changé le répertoire de travail:

if getattr(sys, 'frozen', False):
    os.chdir(sys._MEIPASS)

Ajoutez simplement ces deux lignes au début de votre code, vous pouvez laisser le reste tel quel.

6
Bobsleigh

J'ai peut-être manqué une étape ou fait quelque chose de mal, mais les méthodes décrites ci-dessus ne permettent pas de regrouper des fichiers de données avec PyInstaller dans un fichier exe. Permettez-moi de partager les étapes que j'ai faites.

  1. étape: écrivez l’une des méthodes ci-dessus dans votre fichier py en important des modules sys et os. J'ai essayé les deux. Le dernier est:

    def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
        base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
        return os.path.join(base_path, relative_path)
    
  2. étape: écrire, pyi-makespec fichier.py, à la console, pour créer un fichier file.spec.

  3. étape: Ouvrez, fichier.spec avec Notepad ++ pour ajouter les fichiers de données comme ci-dessous:

    a = Analysis(['C:\\Users\\TCK\\Desktop\\Projeler\\Converter-GUI.py'],
                 pathex=['C:\\Users\\TCK\\Desktop\\Projeler'],
                 binaries=[],
                 datas=[],
                 hiddenimports=[],
                 hookspath=[],
                 runtime_hooks=[],
                 excludes=[],
                 win_no_prefer_redirects=False,
                 win_private_assemblies=False,
                 cipher=block_cipher)
    #Add the file like the below example
    a.datas += [('Converter-GUI.ico', 'C:\\Users\\TCK\\Desktop\\Projeler\\Converter-GUI.ico', 'DATA')]
    pyz = PYZ(a.pure, a.zipped_data,
         cipher=block_cipher)
    exe = EXE(pyz,
              a.scripts,
              exclude_binaries=True,
              name='Converter-GUI',
              debug=False,
              strip=False,
              upx=True,
              #Turn the console option False if you don't want to see the console while executing the program.
              console=False,
              #Add an icon to the program.
              icon='C:\\Users\\TCK\\Desktop\\Projeler\\Converter-GUI.ico')
    
    coll = COLLECT(exe,
                   a.binaries,
                   a.zipfiles,
                   a.datas,
                   strip=False,
                   upx=True,
                   name='Converter-GUI')
    
  4. étape: J'ai suivi les étapes ci-dessus, puis enregistré le fichier de spécification. Enfin ouvert la console et écrire, pyinstaller fichier.spec (dans mon cas, file = Converter-GUI).

Conclusion: il y a toujours plus d'un fichier dans le dossier dist.

Note: J'utilise Python 3.5.

EDIT: Enfin, cela fonctionne avec la méthode de Jonathan Reinhart.

  1. étape: Ajoutez les codes ci-dessous à votre fichier python en important sys et os.

    def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
        base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
        return os.path.join(base_path, relative_path)
    
  2. step: Appelez la fonction ci-dessus en ajoutant le chemin de votre fichier:

    image_path = resource_path("Converter-GUI.ico")
    
  3. étape: écrivez la variable ci-dessus qui appelle la fonction là où vos codes ont besoin du chemin. Dans mon cas c'est:

        self.window.iconbitmap(image_path)
    
  4. étape: Ouvrez la console dans le même répertoire que votre fichier python, écrivez les codes comme ci-dessous:

        pyinstaller --onefile your_file.py
    
  5. step: Ouvrez le fichier .spec du fichier python, ajoutez le tableau a.datas et ajoutez l’icône à la classe exe, qui a été donnée ci-dessus avant l’édition au 3ème pas.
  6. étape: Enregistrez et quittez le fichier de chemin. Accédez à votre dossier contenant les fichiers spec et py. Ouvrez à nouveau la fenêtre de la console et tapez la commande ci-dessous:

        pyinstaller your_file.spec
    

Après la 6. étape, votre fichier est prêt à être utilisé.

6
dilde olupbiten

Légère modification de la réponse acceptée.

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)

    return os.path.join(os.path.abspath("."), relative_path)
2
Krishna Balan

Ajoutez ce morceau de code à voyez ce qui est inclus dans votre fichier unique, en utilisant la fonction resource_path() deJonathon Reinhart

for root, dirs, files in os.walk(resource_path("")):
    print(root)
    for file in files:
        print( "  ",file)
0
barny

J’ai trouvé les réponses existantes confuses et j’ai mis beaucoup de temps à cerner le problème. Voici une compilation de tout ce que j'ai trouvé.

Lorsque j'exécute mon application, le code d'erreur suivant est Failed to execute script foo (si foo.py est le fichier principal). Pour résoudre ce problème, n'exécutez pas PyInstaller avec --noconsole (ou éditez main.spec pour changer console=False => console=True). Avec cela, exécutez le fichier exécutable à partir d'une ligne de commande et vous verrez l'échec.

La première chose à vérifier est de bien empaqueter correctement vos fichiers supplémentaires. Vous devez ajouter des nuplets comme ('x', 'x') si vous souhaitez que le dossier x soit inclus.

Après cela, ne cliquez pas sur OK. Si vous utilisez Windows, vous pouvez utiliser Rechercher tout . Recherchez l'un de vos fichiers (par exemple, sword.png). Vous devriez trouver le chemin temporaire où les fichiers ont été décompressés (par exemple, C:\Users\ashes999\AppData\Local\Temp\_MEI157682\images\sword.png). Vous pouvez parcourir ce répertoire et vous assurer qu'il a tout inclus. Si vous ne le trouvez pas de cette façon, cherchez quelque chose comme main.exe.manifest (Windows) ou python35.dll (si vous utilisez Python 3.5).

Si le programme d'installation inclut tout, le prochain problème probable est le fichier I/O: votre code Python recherche les fichiers dans le répertoire de l'exécutable, au lieu du répertoire temporaire.

Pour résoudre ce problème, aucune des réponses à cette question ne fonctionne. Personnellement, j’ai trouvé un mélange de tous ces éléments: le changement de répertoire conditionnel est la première chose à faire dans votre fichier principal, et tout le reste fonctionne tel quel:

if hasattr(sys, '_MEIPASS'): os.chdir(sys._MEIPASS)

0
ashes999