Ich versuche mit PyInstaller eine EXE-Datei mit einer Datei zu erstellen, die ein Bild und ein Symbol enthält. Ich kann es nicht für mein Leben zum Arbeiten bringen --onefile
.
Wenn ich es tue --onedir
, funktioniert alles sehr gut. Wenn ich verwende --onefile
, kann es die referenzierten zusätzlichen Dateien nicht finden (wenn die kompilierte EXE ausgeführt wird). Es findet die DLLs und alles andere in Ordnung, nur nicht die beiden Bilder.
Ich habe in das temporäre Verzeichnis geschaut, das beim Ausführen der EXE- \Temp\_MEI95642\
Datei generiert wurde ( zum Beispiel), und die Dateien sind tatsächlich dort. Wenn ich die EXE in dieses temporäre Verzeichnis lege, findet sie sie. Sehr verwirrend.
Dies ist, was ich der .spec
Datei hinzugefügt habe
a.datas += [('images/icon.ico', 'D:\\[workspace]\\App\\src\\images\\icon.ico', 'DATA'),
('images/loaderani.gif','D:\\[workspace]\\App\\src\\images\\loaderani.gif','DATA')]
Ich sollte hinzufügen, dass ich versucht habe, sie nicht auch in Unterordnern abzulegen, was keinen Unterschied machte.
Bearbeiten: Neuere Antwort aufgrund des PyInstaller-Updates als korrekt markiert.
quelle
a.datas += ...
) hat mir gerade wirklich geholfen. In der Pyinstaller-Dokumentation wird über die Verwendung gesprochen,COLLECT
aber bei der Verwendung werden keine Dateien in die Binärdatei--onefile
Antworten:
Neuere Versionen von PyInstaller setzen die
env
Variable nicht mehr, sodass Shishs hervorragende Antwort nicht funktioniert. Jetzt wird der Pfad wie folgt festgelegtsys._MEIPASS
:quelle
_MEIPASS2
Envs, aber essys._MEIPASS
funktioniert gut, also +1. Ich schlage vor:path = getattr(sys, '_MEIPASS', os.getcwd())
AttributeError
statt der allgemeineren zu fangenException
. Ich bin auf Probleme gestoßen, bei denen mir der Systemimport gefehlt hat und der Pfad standardmäßig verwendet wurdeos.path.abspath
.pyinstaller entpackt Ihre Daten in einen temporären Ordner und speichert diesen Verzeichnispfad in der
_MEIPASS2
Umgebungsvariablen. Um das_MEIPASS2
Verzeichnis in den gepackten Modus zu bringen und das lokale Verzeichnis im entpackten (Entwicklungs-) Modus zu verwenden, verwende ich Folgendes:Ausgabe:
quelle
resource_path("file_to_be_accessed.mp3")
. Seien Sie vorsichtig, dass Sie max 'answer für die aktuelle Version von PyInstaller verwenden sollten.--one-file
Option?Alle anderen Antworten verwenden das aktuelle Arbeitsverzeichnis, wenn die Anwendung nicht PyInstalled ist (dh
sys._MEIPASS
nicht festgelegt ist). Dies ist falsch, da Sie dadurch nicht in der Lage sind, Ihre Anwendung in einem anderen Verzeichnis als dem auszuführen, in dem sich Ihr Skript befindet.Eine bessere Lösung:
quelle
os.env['_MEIPASS2']
(unter Verwendung der Betriebssystemumgebung) war eine alte Methode, um das Verzeichnis zu erhalten. AFAIKsys._MEIPASS
ist jetzt die einzig richtige Methode.resource_path()
sich im Hauptskript befindet. Gibt es eine Möglichkeit, es zum Laufen zu bringen, wenn es stattdessen in einem Modul geschrieben ist? Ab sofort wird versucht, Ressourcen in dem Ordner abzurufen, in dem sich das Modul befindet, nicht in dem Hauptmodul.__file__
. Wenn Sie möchten, dass ein Modul auf Ressourcen außerhalb seines eigenen Bereichs zugreifen kann, müssen Sie implementieren, dass der Importcode einen Pfad zu diesen bereitstellt, den Ihr Modul verwenden kann, z. B. indem das Modul einen Aufruf "set_resource_location ()" bereitstellt, den die Importer-Aufrufe - denken Sie nicht, dass dies anders ist, als wenn Sie Pyinstaller nicht verwenden.Vielleicht habe ich einen Schritt verpasst oder etwas falsch gemacht, aber die oben genannten Methoden haben Datendateien mit PyInstaller nicht in einer Exe-Datei gebündelt. Lassen Sie mich die Schritte teilen, die ich getan habe.
Schritt: Schreiben Sie eine der oben genannten Methoden in Ihre py-Datei, indem Sie sys- und os-Module importieren. Ich habe beide ausprobiert. Der letzte ist:
Schritt: Schreiben Sie pyi-makepec file.py in die Konsole, um eine file.spec-Datei zu erstellen.
Schritt: Öffnen Sie file.spec mit Notepad ++, um die folgenden Datendateien hinzuzufügen:
Schritt: Ich habe die obigen Schritte ausgeführt und dann die Spezifikationsdatei gespeichert. Zuletzt öffnete sich die Konsole und schrieb pyinstaller file.spec (in meinem Fall file = Converter-GUI).
Fazit: Der dist-Ordner enthält noch mehr als eine Datei.
Hinweis: Ich verwende Python 3.5.
EDIT: Schließlich funktioniert es mit Jonathan Reinharts Methode.
Schritt: Fügen Sie die folgenden Codes zu Ihrer Python-Datei hinzu, indem Sie sys und os importieren.
Schritt: Rufen Sie die obige Funktion auf, indem Sie den Pfad Ihrer Datei hinzufügen:
Schritt: Schreiben Sie die obige Variable, die die Funktion dort aufruft, wo Ihre Codes den Pfad benötigen. In meinem Fall ist es:
Schritt: Öffnen Sie die Konsole im selben Verzeichnis wie Ihre Python-Datei und schreiben Sie die folgenden Codes:
Schritt: Speichern und beenden Sie die Pfaddatei. Gehen Sie zu Ihrem Ordner, der die Spezifikations- und Py-Datei enthält. Öffnen Sie das Konsolenfenster erneut und geben Sie den folgenden Befehl ein:
Nach dem 6. Schritt ist Ihre eine Datei einsatzbereit.
quelle
a.datas
Array aus Schritt 5 Tupel von Zeichenfolgen akzeptiert , siehe pythonhosted.org/PyInstaller/spec-files.html#adding-data-files als Referenz.Anstatt meinen gesamten Pfadcode wie vorgeschlagen neu zu schreiben, habe ich das Arbeitsverzeichnis geändert:
Fügen Sie einfach diese beiden Zeilen am Anfang Ihres Codes hinzu, Sie können den Rest unverändert lassen.
quelle
Leichte Änderung an der akzeptierten Antwort.
quelle
Die häufigste Beschwerde / Frage, die ich bei PyInstaller gesehen habe, ist "Mein Code kann keine Datendatei finden, die ich definitiv in das Bundle aufgenommen habe, wo ist sie?", Und es ist nicht einfach zu sehen, was / wo Ihr Code ist sucht, weil sich der extrahierte Code an einem temporären Ort befindet und beim Beenden entfernt wird. Fügen Sie diesen Code hinzu , um mithilfe von @Jonathon Reinharts zu sehen, was in Ihrer OneFile enthalten ist und wo er sich befindet
resource_path()
quelle
Ich fand die vorhandenen Antworten verwirrend und brauchte lange, um herauszufinden, wo das Problem liegt. Hier ist eine Zusammenstellung von allem, was ich gefunden habe.
Wenn ich meine App starte, wird eine Fehlermeldung angezeigt
Failed to execute script foo
(wennfoo.py
es sich um die Hauptdatei handelt). Um dies zu beheben, führen Sie PyInstaller nicht mit aus--noconsole
(oder bearbeiten Sie esmain.spec
, um es zu ändernconsole=False
=>console=True
). Führen Sie damit die ausführbare Datei über eine Befehlszeile aus, und Sie werden den Fehler sehen.Als erstes müssen Sie überprüfen, ob Ihre zusätzlichen Dateien korrekt verpackt sind. Sie sollten Tupel hinzufügen, z. B.
('x', 'x')
wenn der Ordnerx
enthalten sein soll.Klicken Sie nach dem Absturz nicht auf OK. Wenn Sie unter Windows arbeiten, können Sie Search Everything verwenden . Suchen Sie nach einer Ihrer Dateien (z. B.
sword.png
). Sie sollten den temporären Pfad finden, in dem die Dateien entpackt wurden (z. B.C:\Users\ashes999\AppData\Local\Temp\_MEI157682\images\sword.png
). Sie können dieses Verzeichnis durchsuchen und sicherstellen, dass es alles enthält. Wenn Sie es auf diese Weise nicht finden können, suchen Sie nach etwas wiemain.exe.manifest
(Windows) oderpython35.dll
(wenn Sie Python 3.5 verwenden).Wenn das Installationsprogramm alles enthält, ist das nächste wahrscheinliche Problem die Datei-E / A: Ihr Python-Code sucht im Verzeichnis der ausführbaren Datei anstelle des temporären Verzeichnisses nach Dateien.
Um dies zu beheben, funktionieren alle Antworten auf diese Frage. Persönlich habe ich eine Mischung von allen gefunden, die funktioniert: Wechseln Sie das Verzeichnis als erstes in Ihrer Haupteinstiegspunktdatei, und alles andere funktioniert wie es ist:
if hasattr(sys, '_MEIPASS'): os.chdir(sys._MEIPASS)
quelle
Wenn Sie immer noch versuchen, Dateien relativ zu Ihrer ausführbaren Datei anstatt im temporären Verzeichnis abzulegen, müssen Sie sie selbst kopieren. So habe ich es geschafft.
https://stackoverflow.com/a/59415662/999943
Sie fügen der Spezifikationsdatei einen Schritt hinzu, der eine Dateisystemkopie in die Variable DISTPATH ausführt.
Hoffentlich hilft das.
quelle
Ich habe mich lange (na ja, sehr lange) mit diesem Thema beschäftigt. Ich habe fast jede Quelle durchsucht, aber die Dinge haben sich in meinem Kopf nicht verändert.
Schließlich denke ich, ich habe die genauen Schritte herausgefunden, die ich befolgen möchte.
Beachten Sie, dass meine Antwort Informationen zu den Antworten anderer auf diese Frage verwendet.
So erstellen Sie eine eigenständige ausführbare Datei eines Python-Projekts.
Angenommen, wir haben einen Projektordner und der Dateibaum lautet wie folgt:
Zunächst einmal, sagen wir , Sie haben Ihre Pfade definiert
sound/
undimg/
Ordner in Variablensound_dir
undimg_dir
wie folgt dar :Sie müssen sie wie folgt ändern:
Wobei
resource_path()
oben in Ihrem Skript Folgendes definiert ist:Aktivieren Sie die virtuelle Umgebung, wenn Sie ein Venv verwenden.
Installieren Sie pyinstaller, falls Sie dies noch nicht getan haben
pip3 install pyinstaller
.Ausführen:
pyi-makespec --onefile main.py
Zum Erstellen der Spezifikationsdatei für den Kompilierungs- und Erstellungsprozess.Dadurch wird die Dateihierarchie wie folgt geändert:
Offen (mit einem Edior)
main.spec
:Fügen Sie oben Folgendes ein:
Ändern Sie dann die Zeile von
datas=[],
bisdatas=added_files,
Einzelheiten zu den durchgeführten Operationen finden
main.spec
Sie hier.Lauf
pyinstaller --onefile main.spec
Und das alles ist, können Sie laufen
main
inproject_folder/dist
von überall, ohne irgendetwas anderes in ihrem Ordner mit. Sie können nur diesemain
Datei verteilen . Es ist jetzt ein wahrer Standalone .quelle
Mit der hervorragenden Antwort von Max und diesem Beitrag über das Hinzufügen zusätzlicher Datendateien wie Bilder oder Ton und meine eigenen Recherchen / Tests habe ich herausgefunden, was meiner Meinung nach der einfachste Weg ist, solche Dateien hinzuzufügen.
Wenn Sie ein Live-Beispiel sehen möchten, ist mein Repository hier auf GitHub.
Hinweis: Dies dient zum Kompilieren mit dem Befehl
--onefile
oder-F
mit pyinstaller.Meine Umgebung ist wie folgt.
Lösen des Problems in 2 Schritten
Um das Problem zu lösen, müssen wir Pyinstaller ausdrücklich mitteilen, dass wir zusätzliche Dateien haben, die mit der Anwendung "gebündelt" werden müssen.
Wir müssen auch einen "relativen" Pfad verwenden , damit die Anwendung ordnungsgemäß ausgeführt werden kann, wenn sie als Python-Skript oder als Frozen EXE ausgeführt wird.
Wenn dies gesagt ist, brauchen wir eine Funktion, die es uns ermöglicht, relative Pfade zu haben. Mit der Funktion Max Posted können wir die relative Pfadbildung leicht lösen.
Wir würden die obige Funktion wie folgt verwenden, damit das Anwendungssymbol angezeigt wird, wenn die App entweder als Skript oder als eingefrorene EXE ausgeführt wird.
Der nächste Schritt besteht darin, Pyinstaller anzuweisen, wo die zusätzlichen Dateien beim Kompilieren zu finden sind, damit sie beim Ausführen der Anwendung im temporären Verzeichnis erstellt werden.
Wir können dieses Problem auf zwei Arten lösen, wie in der Dokumentation gezeigt , aber ich persönlich bevorzuge es, meine eigene .spec-Datei zu verwalten, damit wir es so machen.
Zunächst müssen Sie bereits eine .spec-Datei haben. In meinem Fall konnte ich das erstellen, was ich brauchte, indem ich
pyinstaller
mit zusätzlichen Argumenten lief. Weitere Argumente finden Sie hier . Aus diesem Grund sieht meine Spezifikationsdatei möglicherweise etwas anders aus als Ihre, aber ich poste sie alle als Referenz, nachdem ich die wichtigen Teile erklärt habe.added_files ist im Wesentlichen eine Liste mit Tupeln. In meinem Fall möchte ich nur ein EINZELNES Bild hinzufügen, aber Sie können mehrere Icos, PNGs oder JPGs hinzufügen.
('app/img/*.ico', 'app/img')
Sie können auch ein anderes Tupel erstellenadded_files = [ (), (), ()]
, um mehrere ImportedurchzuführenDer erste Teil des Tupels definiert, welche Datei oder welchen Dateityp Sie hinzufügen möchten und wo Sie sie finden. Stellen Sie sich dies als STRG + C vor
Der zweite Teil des Tupels weist Pyinstaller an, den Pfad 'app / img /' zu erstellen und die Dateien in diesem Verzeichnis RELATIV zu dem temporären Verzeichnis abzulegen, das beim Ausführen der EXE-Datei erstellt wird. Stellen Sie sich dies als STRG + V vor
Unter
a = Analysis([main...
mir festgelegt habedatas=added_files
,ursprünglich verwendetes zu seindatas=[]
aber wir wollenum die Liste der Importe aus, na ja, importiertso dass wir in unseren eigenen Importen passieren.Sie müssen dies nur tun, wenn Sie ein bestimmtes Symbol für die EXE-Datei möchten. Am Ende der Spezifikationsdatei fordere ich Pyinstaller auf, mein Anwendungssymbol für die Exe mit der Option festzulegen
icon='app\\img\\app_icon.ico'
.Kompilieren zu EXE
Ich bin sehr faul; Ich mag es nicht mehr Dinge zu tippen als ich muss. Ich habe eine .bat-Datei erstellt, auf die ich einfach klicken kann. Sie müssen dies nicht tun, dieser Code wird ohne ihn in einer Eingabeaufforderungs-Shell ausgeführt.
Da die .spec-Datei alle unsere Kompilierungseinstellungen und -argumente (auch als Optionen bezeichnet) enthält, müssen wir diese .spec-Datei nur Pyinstaller übergeben.
quelle
Eine andere Lösung besteht darin, einen Laufzeit-Hook zu erstellen, der Ihre Daten (Dateien / Ordner) in das Verzeichnis kopiert (oder verschiebt), in dem die ausführbare Datei gespeichert ist. Der Hook ist eine einfache Python-Datei, die kurz vor der Ausführung Ihrer App fast alles kann. Um es einzustellen, sollten Sie die
--runtime-hook=my_hook.py
Option pyinstaller verwenden. Also, falls Sie Ihre Daten ein ist Bilder - Ordner, sollten Sie den Befehl ausführen:Die Datei cp_images_hook.py könnte ungefähr so aussehen:
Vor jeder Ausführung die Bilder - Ordner in dem aktuellen Verzeichnis (aus dem _MEIPASS Ordner) bewegt wird, so wird die ausführbare Datei immer Zugriff darauf haben. Auf diese Weise müssen Sie den Code Ihres Projekts nicht ändern.
Zweite Lösung
Sie können den Runtime-Hook-Mechanismus nutzen und das aktuelle Verzeichnis ändern. Dies ist nach Ansicht einiger Entwickler keine gute Vorgehensweise, funktioniert aber einwandfrei.
Den Hook-Code finden Sie unten:
quelle