147 votes

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

J'essaie de construire un EXE d'un seul fichier avec PyInstaller qui doit inclure une image et une icône. Je ne parviens pas à le faire fonctionner avec --onefile .

Si je le fais --onedir tout fonctionne très bien. Lorsque j'utilise --onefile En revanche, il ne trouve pas les fichiers supplémentaires référencés (lorsqu'il exécute l'EXE compilé). Il trouve les DLL et tout le reste sans problème, 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 s'y trouvent effectivement. Lorsque je dépose l'EXE dans ce répertoire temporaire, il les trouve. Cela me laisse très perplexe.

C'est ce que j'ai ajouté à la .spec fichier

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 dois ajouter que j'ai essayé de ne pas les mettre dans des sous-dossiers également, cela n'a pas fait de différence.

Editar: Marqué une réponse plus récente comme correcte en raison de la mise à jour de PyInstaller.

11 votes

Merci beaucoup ! la ligne ici ( a.datas += ... ) m'a vraiment aidé à l'instant. La documentation de pyinstaller parle de l'utilisation de COLLECT mais cela ne permet pas de mettre les fichiers dans le binaire lorsque l'on utilise --onefile

0 votes

@IgorSerebryany : Je suis d'accord ! Je viens d'avoir exactement le même problème.

0 votes

Mon .exe se plante lorsque je clique sur la barre de menu si j'ai utilisé

0voto

ashes999 Points 3077

J'ai trouvé les réponses existantes confuses, et il m'a fallu beaucoup de temps pour comprendre où était le problème. Voici une compilation de tout ce que j'ai trouvé.

Lorsque je lance mon application, j'obtiens une erreur Failed to execute script foo (si foo.py est le fichier principal). Pour résoudre ce problème, ne lancez pas PyInstaller avec --noconsole (ou éditer main.spec pour changer console=False => console=True ). Avec cela, lancez l'exécutable depuis une ligne de commande, et vous verrez l'échec.

La première chose à vérifier est que l'emballage de vos fichiers supplémentaires est correct. Vous devriez ajouter des tuples comme ('x', 'x') si vous voulez que le dossier x à inclure.

Après qu'il se soit écrasé, ne cliquez pas sur OK. Si vous êtes sous Windows, vous pouvez utiliser Rechercher tout . Recherchez l'un de vos fichiers (ex. sword.png ). Vous devriez trouver le chemin temporaire où il a décompressé les fichiers (ex. C:\Users\ashes999\AppData\Local\Temp\_MEI157682\images\sword.png ). Vous pouvez parcourir ce répertoire et vous assurer qu'il contient tout. Si vous ne pouvez pas le trouver 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 problème suivant est probablement l'entrée/sortie de fichiers : votre code Python cherche des fichiers dans le répertoire de l'exécutable, et non dans le répertoire temporaire.

Pour y remédier, toutes les réponses à cette question fonctionnent. Personnellement, j'ai trouvé qu'un mélange de toutes ces réponses fonctionnait : changez le répertoire de manière conditionnelle en premier lieu dans votre fichier d'entrée principal, et tout le reste fonctionne tel quel :

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

2 votes

Désolé. Mais changer de répertoire n'est jamais la bonne réponse. Si un utilisateur fournit un chemin d'accès à l'application, il s'attend à ce qu'il soit relatif au répertoire actuel. Si vous changez cela, ce sera faux.

0voto

phyatt Points 6647

Si vous essayez toujours de mettre les fichiers relatifs à votre exécutable au lieu du répertoire temporaire, vous devez le copier vous-même. C'est ainsi que j'ai fini par y arriver.

https://stackoverflow.com/a/59415662/999943

Vous ajoutez une étape dans le fichier de spécification qui effectue une copie du système de fichiers dans la variable DISTPATH.

J'espère que cela vous aidera.

0voto

Luca Points 21

Je l'utilise en fonction de la solution max

def getPath(filename):
    import os
    import sys
    from os import chdir
    from os.path import join
    from os.path import dirname
    from os import environ

    if hasattr(sys, '_MEIPASS'):
        # PyInstaller >= 1.6
        chdir(sys._MEIPASS)
        filename = join(sys._MEIPASS, filename)
    elif '_MEIPASS2' in environ:
        # PyInstaller < 1.6 (tested on 1.5 only)
        chdir(environ['_MEIPASS2'])
        filename = join(environ['_MEIPASS2'], filename)
    else:
        chdir(dirname(sys.argv[0]))
        filename = join(dirname(sys.argv[0]), filename)

    return filename

0voto

Diggy. Points 5523

Pour ceux qui cherchent encore une réponse plus récente, voici la réponse :

Dans le documentation il y a un section sur l'accès aux fichiers de données ajoutés .
Voici l'essentiel de la situation.


Vous voudrez import pkgutil et localisez le dossier dans lequel vous avez ajouté le fichier de données ; c'est-à-dire la deuxième chaîne du tuple qui a été ajoutée au fichier spec :

datas = [("path/to/mypackage/data_file.txt", "path/to/mypackage")]

Le fait de savoir où vous avez ajouté le fichier de données peut ensuite être utilisé pour le lire en tant que données binaires et le décoder comme vous le souhaitez. Prenons cet exemple :

Structure du fichier :

mypackage
      __init__.py  # This is a MUST in order for the package to be registered
      data_file.txt  # The data file you've added

fichier_données.txt

Hello world!

main.py

import pkgutil

file = pkgutil.get_data("mypackage", "data_file.txt")
contents = file.decode("utf-8")
print(contents)  # Hello world!

Références :

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X