J'essaie de créer un fichier EXE unique avec PyInstaller qui doit inclure une image et une icône. Je ne peux pas pour la vie de moi faire fonctionner avec --onefile
.
Si je le fais --onedir
, tout fonctionne très bien. Lorsque j'utilise --onefile
, il ne trouve pas les fichiers supplémentaires référencés (lors de l'exécution de l'EXE compilé). Il trouve les DLL et tout le reste bien, mais pas les deux images.
J'ai regardé dans le temp-dir généré lors de l'exécution de EXE ( \Temp\_MEI95642\
par exemple) et les fichiers sont bien là. Quand je dépose le EXE dans ce répertoire temporaire, il les trouve. Très déroutant.
C'est ce que j'ai ajouté au .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 devrais ajouter que j'ai également essayé de ne pas les mettre dans des sous-dossiers, cela n'a fait aucune différence.
Modifier: la nouvelle réponse a été marquée comme correcte en raison de la mise à jour de PyInstaller.
la source
a.datas += ...
) m'a vraiment aidé à l'instant. la documentation pyinstaller parle de l'utilisationCOLLECT
mais cela ne parvient pas à mettre les fichiers dans le binaire lors de l'utilisation--onefile
Réponses:
Les nouvelles versions de PyInstaller ne définissent plus la
env
variable, donc l'excellente réponse de Shish ne fonctionnera pas. Maintenant, le chemin est défini commesys._MEIPASS
:la source
_MEIPASS2
envois, maissys._MEIPASS
fonctionne bien, donc +1. Je suggère:path = getattr(sys, '_MEIPASS', os.getcwd())
AttributeError
plutôt que le plus généralException
. J'ai rencontré des problèmes où il me manquait l'importation sys et le chemin par défaut était silencieuxos.path.abspath
.pyinstaller décompresse vos données dans un dossier temporaire et stocke ce chemin de répertoire dans la
_MEIPASS2
variable d'environnement. Pour obtenir le_MEIPASS2
répertoire en mode compressé et utiliser le répertoire local en mode décompressé (développement), j'utilise ceci:Production:
la source
resource_path("file_to_be_accessed.mp3")
. Méfiez-vous que vous devez utiliser max 'answer pour la version actuelle de PyInstaller.--one-file
option?Toutes les autres réponses utilisent le répertoire de travail courant dans le cas où l'application n'est pas PyInstalled (c'est
sys._MEIPASS
-à- dire n'est pas définie). 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:
la source
os.env['_MEIPASS2']
(utiliser l'environnement du système d'exploitation) était une ancienne méthode pour obtenir le répertoire. AFAIKsys._MEIPASS
est la seule méthode correcte maintenant.resource_path()
est dans le script principal. Existe-t-il un moyen de le faire fonctionner lorsqu'il est écrit dans un module à la place? À partir de maintenant, il essaiera d'obtenir des ressources dans le dossier où se trouve le module, pas dans le dossier principal.__file__
. Si vous voulez qu'un module puisse accéder à des ressources en dehors de sa propre portée, vous devrez implémenter que le code d'importation fournisse un chemin vers celles-ci pour que votre module puisse les utiliser, par exemple par le module fournissant un appel "set_resource_location ()" que le les appels d'importateurs - ne pensez pas que ce soit différent de la façon dont vous devriez travailler lorsque vous n'utilisez pas pyinstaller.Peut-être ai-je manqué une étape ou fait quelque chose de mal, mais les méthodes ci-dessus n'ont pas regroupé les fichiers de données avec PyInstaller dans un seul fichier exe. Permettez-moi de partager les étapes de ce que j'ai fait.
étape: écrivez l'une des méthodes ci-dessus dans votre fichier py avec l'importation des modules sys et os. J'ai essayé les deux. Le dernier est:
étape: Écrivez, pyi-makespec file.py , sur la console, pour créer un fichier file.spec.
étape: Ouvrez, file.spec avec Notepad ++ pour ajouter les fichiers de données comme ci-dessous:
étape: j'ai suivi les étapes ci-dessus, puis j'ai enregistré le fichier de spécifications. Enfin ouvert la console et écrire, pyinstaller file.spec (dans mon cas, file = Converter-GUI).
Conclusion: il y a encore plus d'un fichier dans le dossier dist.
Remarque: j'utilise Python 3.5.
EDIT: Enfin, cela fonctionne avec la méthode de Jonathan Reinhart.
étape: ajoutez les codes ci-dessous à votre fichier python avec l'importation de sys et os.
étape: Appelez la fonction ci-dessus en ajoutant le chemin de votre fichier:
étape: écrivez la variable ci-dessus qui appelle la fonction là où vos codes ont besoin du chemin. Dans mon cas c'est:
étape: Ouvrez la console dans le même répertoire de votre fichier python, écrivez les codes comme ci-dessous:
étape: enregistrez et quittez le fichier de chemin. Accédez à votre dossier qui comprend le fichier spec et py. Ouvrez à nouveau la fenêtre de la console et tapez la commande ci-dessous:
Après l'étape 6., votre fichier est prêt à être utilisé.
la source
a.datas
tableau de l'étape 5 prenne des tuples de chaînes, voir pythonhosted.org/PyInstaller/spec-files.html#adding-data-files pour référence.Au lieu de réécrire tout mon code de chemin comme suggéré, j'ai changé le répertoire de travail:
Ajoutez simplement ces deux lignes au début de votre code, vous pouvez laisser le reste tel quel.
la source
Légère modification de la réponse acceptée.
la source
La plainte / question la plus courante que j'ai vue avec PyInstaller est "mon code ne trouve pas un fichier de données que j'ai définitivement inclus dans le bundle, où est-il?", Et il n'est pas facile de voir quel / où votre code recherche parce que le code extrait se trouve dans un emplacement temporaire et est supprimé à sa sortie. Ajoutez ce morceau de code pour voir ce qui est inclus dans votre fichier unique et où il se trouve, en utilisant @Jonathon Reinhart's
resource_path()
la source
J'ai trouvé les réponses existantes déroutantes et j'ai mis beaucoup de temps à déterminer où se trouvait le problème. Voici une compilation de tout ce que j'ai trouvé.
Lorsque j'exécute mon application, j'obtiens une erreur
Failed to execute script foo
(sifoo.py
est le fichier principal). Pour résoudre ce problème, n'exécutez pas PyInstaller avec--noconsole
(ou modifiezmain.spec
pour changerconsole=False
=>console=True
). Avec cela, exécutez l'exécutable à partir d'une ligne de commande et vous verrez l'échec.La première chose à vérifier est que vous empaquetez correctement vos fichiers supplémentaires. Vous devez ajouter des tuples comme
('x', 'x')
si vous souhaitez que le dossierx
soit inclus.Après le crash, ne cliquez pas sur OK. Si vous êtes sous Windows, vous pouvez utiliser Rechercher tout . Recherchez l'un de vos fichiers (par exemple
sword.png
). Vous devriez trouver le chemin temporaire où il a décompressé les fichiers (par exempleC:\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, recherchez quelque chose commemain.exe.manifest
(Windows) oupython35.dll
(si vous utilisez Python 3.5).Si le programme d'installation inclut tout, le prochain problème probable est les E / S de fichiers: votre code Python recherche dans le répertoire de l'exécutable, au lieu du répertoire temporaire, des fichiers.
Pour résoudre ce problème, toutes les réponses à cette question fonctionnent. Personnellement, j'ai trouvé un mélange de tous pour fonctionner: changer de répertoire conditionnellement en premier lieu dans votre fichier de point d'entrée principal, et tout le reste fonctionne tel quel:
if hasattr(sys, '_MEIPASS'): os.chdir(sys._MEIPASS)
la source
Si vous essayez toujours de placer des fichiers relatifs à votre exécutable plutôt que dans le 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écifications qui effectue une copie du système de fichiers dans la variable DISTPATH.
J'espère que cela pourra aider.
la source
Je m'occupe de ce problème depuis longtemps (enfin, très longtemps). J'ai recherché presque toutes les sources mais les choses n'entraient pas dans un schéma dans ma tête.
Enfin, je pense avoir compris les étapes exactes à suivre, je voulais partager.
Notez que ma réponse utilise des informations sur les réponses des autres à cette question.
Comment créer un exécutable autonome d'un projet python.
Supposons que nous ayons un dossier_projet et que l'arborescence des fichiers se présente comme suit:
Tout d'abord, disons que vous avez défini vos chemins d'accès
sound/
et vosimg/
dossiers en variablessound_dir
etimg_dir
comme suit:Vous devez les modifier comme suit:
Où,
resource_path()
est défini en haut de votre script comme:Activez l'environnement virtuel si vous utilisez un venv,
Installez pyinstaller si vous ne l' avez pas encore, par:
pip3 install pyinstaller
.Exécuter:
pyi-makespec --onefile main.py
pour créer le fichier de spécification pour le processus de compilation et de construction.Cela changera la hiérarchie des fichiers en:
Ouvert (avec un édior)
main.spec
:En haut, insérez:
Ensuite, changez la ligne de
datas=[],
endatas=added_files,
Pour les détails des opérations effectuées sur
main.spec
voir ici.Courir
pyinstaller --onefile main.spec
Et qui est tout, vous pouvez exécuter
main
àproject_folder/dist
partir de n'importe où, sans avoir rien d' autre dans son dossier. Vous ne pouvez distribuer que cemain
fichier. C'est maintenant, un véritable autonome .la source
En utilisant l'excellente réponse de Max et de cet article sur l'ajout de fichiers de données supplémentaires comme des images ou du son et mes propres recherches / tests, j'ai compris ce que je pense être le moyen le plus simple d'ajouter de tels fichiers.
Si vous souhaitez voir un exemple en direct, mon référentiel est ici sur GitHub.
Remarque: ceci est pour la compilation à l'aide de la commande
--onefile
ou-F
avec pyinstaller.Mon environnement est le suivant.
Résoudre le problème en 2 étapes
Pour résoudre le problème, nous devons spécifiquement indiquer à Pyinstaller que nous avons des fichiers supplémentaires qui doivent être «regroupés» avec l'application.
Nous devons également utiliser un chemin `` relatif '' pour que l'application puisse fonctionner correctement lorsqu'elle s'exécute en tant que script Python ou EXE gelé.
Cela étant dit, nous avons besoin d'une fonction qui nous permet d'avoir des chemins relatifs. En utilisant la fonction que Max Posté, nous pouvons facilement résoudre le chemin relatif.
Nous utiliserions la fonction ci-dessus comme celle-ci afin que l'icône de l'application s'affiche lorsque l'application s'exécute en tant que script OU EXE gelé.
L'étape suivante est que nous devons indiquer à Pyinstaller où trouver les fichiers supplémentaires lors de la compilation afin que lorsque l'application est exécutée, ils soient créés dans le répertoire temporaire.
Nous pouvons résoudre ce problème de deux manières, comme indiqué dans la documentation , mais je préfère personnellement gérer mon propre fichier .spec, c'est ainsi que nous allons le faire.
Tout d'abord, vous devez déjà disposer d'un fichier .spec. Dans mon cas, j'ai pu créer ce dont j'avais besoin en exécutant
pyinstaller
des arguments supplémentaires, vous pouvez trouver des arguments supplémentaires ici . Pour cette raison, mon fichier de spécifications peut sembler un peu différent du vôtre, mais je le poste tout pour référence après avoir expliqué les éléments importants.added_files est essentiellement une liste contenant des tuple, dans mon cas, je ne veux ajouter qu'une seule image, mais vous pouvez ajouter plusieurs ico, png ou jpg en utilisant
('app/img/*.ico', 'app/img')
Vous pouvez également créer un autre tuple comme ceciadded_files = [ (), (), ()]
pour avoir plusieurs importationsLa première partie du tuple définit quel fichier ou quel type de fichier vous souhaitez ajouter ainsi que où les trouver. Considérez cela comme CTRL + C
La deuxième partie du tuple indique à Pyinstaller de créer le chemin «app / img /» et de placer les fichiers dans ce répertoire RELATIF au répertoire temporaire créé lorsque vous exécutez le .exe. Considérez cela comme CTRL + V
Sous
a = Analysis([main...
, j'ai définidatas=added_files
, à l'origine c'était le cas,datas=[]
mais nous voulons que la liste des importations soit, eh bien, importée, donc nous passons dans nos importations personnalisées.Vous n'avez pas besoin de le faire à moins que vous ne vouliez une icône spécifique pour l'EXE, au bas du fichier de spécifications, je dis à Pyinstaller de définir l'icône de mon application pour l'exe avec l'option
icon='app\\img\\app_icon.ico'
.Compilation en EXE
Je suis très paresseux; Je n'aime pas taper plus que nécessaire. J'ai créé un fichier .bat sur lequel je peux simplement cliquer. Vous n'êtes pas obligé de le faire, ce code fonctionnera très bien dans un shell d'invite de commande sans cela.
Puisque le fichier .spec contient tous nos paramètres de compilation et arguments (aka options), nous devons simplement donner ce fichier .spec à Pyinstaller.
la source
Une autre solution consiste à créer un hook d'exécution, qui copiera (ou déplacera) vos données (fichiers / dossiers) vers le répertoire dans lequel l'exécutable est stocké. Le hook est un simple fichier python qui peut presque tout faire, juste avant l'exécution de votre application. Pour le définir, vous devez utiliser l'
--runtime-hook=my_hook.py
option pyinstaller. Donc, dans le cas où vos données sont un dossier d' images , vous devez exécuter la commande:Le cp_images_hook.py pourrait être quelque chose comme ceci:
Avant chaque exécution, le dossier images est déplacé vers le répertoire courant (à partir du dossier _MEIPASS), de sorte que l'exécutable y aura toujours accès. De cette façon, il n'est pas nécessaire de modifier le code de votre projet.
Deuxième solution
Vous pouvez profiter du mécanisme de hook d'exécution et changer le répertoire courant, ce qui n'est pas une bonne pratique selon certains développeurs, mais cela fonctionne bien.
Le code de crochet se trouve ci-dessous:
la source