Próbuję zbudować jednoplikowy plik EXE z PyInstaller, który ma zawierać obraz i ikonę. Za całe życie nie mogę tego zmusić --onefile
.
Jeśli to zrobię --onedir
, wszystko działa bardzo dobrze. Kiedy używam --onefile
, nie może znaleźć wymienionych plików dodatkowych (podczas uruchamiania skompilowanego EXE). Znajduje biblioteki DLL i wszystko inne w porządku, ale nie dwa obrazy.
Zajrzałem do katalogu tymczasowego wygenerowanego podczas uruchamiania \Temp\_MEI95642\
pliku EXE ( na przykład) i pliki rzeczywiście tam są. Kiedy upuszczam EXE w tym katalogu tymczasowym, znajduje je. Bardzo kłopotliwe.
Oto, co dodałem do .spec
pliku
a.datas += [('images/icon.ico', 'D:\\[workspace]\\App\\src\\images\\icon.ico', 'DATA'),
('images/loaderani.gif','D:\\[workspace]\\App\\src\\images\\loaderani.gif','DATA')]
Powinienem dodać, że próbowałem nie umieszczać ich również w podfolderach, ale nie miało to znaczenia.
Edycja: oznaczono nowszą odpowiedź jako poprawną z powodu aktualizacji PyInstaller.
źródło
a.datas += ...
) naprawdę mi pomogła. dokumentacja pyinstallera mówi o używaniu,COLLECT
ale nie udaje się umieścić plików w binarnym podczas używania--onefile
Odpowiedzi:
Nowsze wersje PyInstaller nie ustawiają
env
już zmiennej, więc doskonała odpowiedź Shisha nie będzie działać. Teraz ścieżka zostanie ustawiona jakosys._MEIPASS
:źródło
_MEIPASS2
w envs, alesys._MEIPASS
działa dobrze, więc +1. Proponuję:path = getattr(sys, '_MEIPASS', os.getcwd())
AttributeError
zamiast bardziej ogólnegoException
. Napotkałem problemy, w których brakowało mi importu sys i ścieżki po cichu domyślnieos.path.abspath
.pyinstaller rozpakowuje dane do folderu tymczasowego i przechowuje tę ścieżkę do katalogu w
_MEIPASS2
zmiennej środowiskowej. Aby pobrać_MEIPASS2
katalog w trybie spakowanym i użyć katalogu lokalnego w trybie rozpakowanym (programistycznym), używam tego:Wynik:
źródło
resource_path("file_to_be_accessed.mp3")
. Uważaj, aby w aktualnej wersji PyInstaller użyć max 'answer.--one-file
opcji?Wszystkie inne odpowiedzi używają bieżącego katalogu roboczego w przypadku, gdy aplikacja nie jest PyInstalled (tj.
sys._MEIPASS
Nie jest ustawiona). To nieprawda, ponieważ uniemożliwia uruchomienie aplikacji z katalogu innego niż ten, w którym znajduje się skrypt.Lepsze rozwiązanie:
źródło
os.env['_MEIPASS2']
(przy użyciu środowiska systemu operacyjnego) była starą metodą uzyskiwania katalogu. AFAIKsys._MEIPASS
to obecnie jedyna poprawna metoda.resource_path()
znajduje się w głównym skrypcie. Czy istnieje sposób, aby to działało, gdy jest napisane w module? Od teraz będzie próbował uzyskać zasoby w folderze, w którym znajduje się moduł, a nie w folderze głównym.__file__
. Jeśli chcesz, aby moduł miał dostęp do zasobów spoza jego własnego zakresu, będziesz musiał zaimplementować, że kod importujący zapewnia ścieżkę do nich dla twojego modułu, np. Przez moduł dostarczający wywołanie "set_resource_location ()", które wywołania importera - nie myśl, że to różni się od tego, jak powinieneś pracować, gdy nie używasz pyinstaller.Być może przegapiłem krok lub zrobiłem coś źle, ale metody, które są powyżej, nie połączyły plików danych z PyInstaller w jeden plik exe. Pozwól mi podzielić się krokami, co zrobiłem.
krok: Napisz jedną z powyższych metod do pliku py z importowaniem modułów sys i os. Próbowałem obu z nich. Ostatni to:
krok: Napisz, pyi-makespec file.py , do konsoli, aby utworzyć plik file.spec.
krok: Otwórz plik.spec w Notepad ++, aby dodać pliki danych, jak poniżej:
krok: wykonałem powyższe kroki, a następnie zapisałem plik specyfikacji. W końcu otworzyłem konsolę i napisz, pyinstaller file.spec (w moim przypadku file = Converter-GUI).
Wniosek: w folderze dist nadal jest więcej niż jeden plik.
Uwaga: używam Pythona 3.5.
EDYCJA: Wreszcie działa z metodą Jonathana Reinharta.
krok: Dodaj poniższe kody do pliku Python z importowaniem sys i os.
krok: Wywołaj powyższą funkcję, dodając ścieżkę do pliku:
krok: Napisz powyższą zmienną, która wywołuje funkcję do miejsca, w którym twoje kody wymagają ścieżki. W moim przypadku jest to:
krok: Otwórz konsolę w tym samym katalogu swojego pliku pythona, zapisz kody jak poniżej:
krok: Zapisz i zamknij plik ścieżki. Przejdź do folderu zawierającego specyfikację i plik py. Otwórz ponownie okno konsoli i wpisz poniższe polecenie:
Po kroku 6. jeden plik jest gotowy do użycia.
źródło
a.datas
tablica z kroku 5 pobiera krotki ciągów, zobacz pythonhosted.org/PyInstaller/spec-files.html#adding-data-files w celach informacyjnych.Zamiast przepisać cały mój kod ścieżki zgodnie z sugestią, zmieniłem katalog roboczy:
Po prostu dodaj te dwie linie na początku kodu, resztę możesz zostawić bez zmian.
źródło
Nieznaczna modyfikacja zaakceptowanej odpowiedzi.
źródło
Najczęstszą skargą / pytaniem, które widziałem w PyInstaller, jest „mój kod nie może znaleźć pliku danych, który na pewno zawarłem w paczce, gdzie to jest?” I nie jest łatwo zobaczyć, jaki / gdzie jest twój kod wyszukuje, ponieważ wyodrębniony kod znajduje się w lokalizacji tymczasowej i jest usuwany po zakończeniu. Dodaj ten fragment kodu, aby zobaczyć, co jest zawarte w pliku onefile i gdzie się znajduje, używając @Jonathon Reinhart's
resource_path()
źródło
Istniejące odpowiedzi wydawały mi się zagmatwane i zajęło mi dużo czasu ustalenie, gdzie jest problem. Oto kompilacja wszystkiego, co znalazłem.
Kiedy uruchamiam aplikację, pojawia się błąd
Failed to execute script foo
(jeślifoo.py
jest to plik główny). Aby rozwiązać ten problem, nie uruchamiaj PyInstaller z--noconsole
(lub edytuj,main.spec
aby zmienićconsole=False
=>console=True
). Dzięki temu uruchom plik wykonywalny z wiersza poleceń, a zobaczysz błąd.Pierwszą rzeczą do sprawdzenia jest prawidłowe pakowanie dodatkowych plików. Powinieneś dodać krotki, na przykład,
('x', 'x')
jeśli chcesz, aby folderx
został uwzględniony.Po awarii nie klikaj OK. Jeśli używasz systemu Windows, możesz użyć funkcji Wyszukaj wszystko . Poszukaj jednego ze swoich plików (np.
sword.png
). Powinieneś znaleźć tymczasową ścieżkę, w której rozpakował pliki (np.C:\Users\ashes999\AppData\Local\Temp\_MEI157682\images\sword.png
). Możesz przeglądać ten katalog i upewnić się, że zawiera wszystko. Jeśli nie możesz tego znaleźć w ten sposób, poszukaj czegoś takiego jakmain.exe.manifest
(Windows) lubpython35.dll
(jeśli używasz Pythona 3.5).Jeśli instalator zawiera wszystko, następnym prawdopodobnym problemem jest we / wy pliku: Twój kod Pythona szuka plików w katalogu pliku wykonywalnego zamiast w katalogu tymczasowym.
Aby to naprawić, każda odpowiedź na to pytanie działa. Osobiście znalazłem ich mieszankę do pracy: najpierw zmień katalog warunkowo w głównym pliku punktu wejścia, a wszystko inne działa tak, jak jest:
if hasattr(sys, '_MEIPASS'): os.chdir(sys._MEIPASS)
źródło
Jeśli nadal próbujesz umieścić pliki względem swojego pliku wykonywalnego zamiast w katalogu tymczasowym, musisz skopiować go samodzielnie. W ten sposób skończyłem to zrobić.
https://stackoverflow.com/a/59415662/999943
Dodajesz krok w pliku specyfikacji, który wykonuje kopię systemu plików do zmiennej DISTPATH.
Mam nadzieję, że to pomoże.
źródło
Zajmuję się tym problemem od dawna (no, bardzo długo). Przeszukałem prawie każde źródło, ale sprawy nie układały się w mojej głowie.
Wreszcie, myślę, że wymyśliłem dokładne kroki, które należy wykonać, którymi chciałem się podzielić.
Zwróć uwagę, że moja odpowiedź wykorzystuje informacje o odpowiedziach innych osób na to pytanie.
Jak utworzyć samodzielny plik wykonywalny projektu w języku Python.
Załóżmy, że mamy project_folder i drzewo plików wygląda następująco:
Po pierwsze, powiedzmy, że zdefiniowałeś swoje ścieżki
sound/
iimg/
foldery w zmiennychsound_dir
iimg_dir
w następujący sposób:Musisz je zmienić w następujący sposób:
Gdzie
resource_path()
jest zdefiniowane w górnej części skryptu jako:Aktywuj wirtualne środowisko, jeśli używasz Venv,
Zainstalować pyinstaller jeśli nie jeszcze przez:
pip3 install pyinstaller
.Uruchom:
pyi-makespec --onefile main.py
aby utworzyć plik specyfikacji dla procesu kompilacji i kompilacji.Spowoduje to zmianę hierarchii plików na:
Otwórz (z edior)
main.spec
:U góry wstaw:
Następnie zmień wiersz
datas=[],
nadatas=added_files,
Aby uzyskać szczegółowe informacje na temat operacji wykonanych na,
main.spec
zobacz tutaj.Biegać
pyinstaller --onefile main.spec
I to wszystko, można uruchomić
main
wproject_folder/dist
dowolnym miejscu, bez konieczności cokolwiek innego w swoim folderze. Możesz rozpowszechniać tylko tenmain
plik. To jest teraz prawdziwy samodzielny .źródło
Korzystając z doskonałej odpowiedzi od Maxa i tego postu na temat dodawania dodatkowych plików danych, takich jak obrazy lub dźwięk oraz moich własnych badań / testów, doszedłem do wniosku, że najłatwiej jest dodać takie pliki.
Jeśli chcesz zobaczyć przykład na żywo, moje repozytorium jest tutaj na GitHub.
Uwaga: dotyczy to kompilacji przy użyciu polecenia
--onefile
lub-F
w programie pyinstaller.Moje środowisko jest następujące.
Rozwiązanie problemu w 2 krokach
Aby rozwiązać ten problem, musimy wyraźnie powiedzieć Pyinstallerowi, że mamy dodatkowe pliki, które muszą być „dołączone” do aplikacji.
Musimy również używać ścieżki „względnej” , aby aplikacja działała poprawnie, gdy działa jako skrypt Pythona lub zamrożony EXE.
Mając to na uwadze, potrzebujemy funkcji, która pozwoli nam mieć ścieżki względne. Korzystając z funkcji, którą Max opublikował , możemy łatwo rozwiązać ścieżkę względną.
Użylibyśmy powyższej funkcji w ten sposób, aby ikona aplikacji była wyświetlana, gdy aplikacja jest uruchomiona jako skrypt LUB zamrożony plik EXE.
Następnym krokiem jest to, że musimy poinstruować Pyinstaller, gdzie znaleźć dodatkowe pliki podczas kompilacji, aby po uruchomieniu aplikacji zostały utworzone w katalogu tymczasowym.
Możemy rozwiązać ten problem na dwa sposoby, jak pokazano w dokumentacji , ale ja osobiście wolę zarządzać własnym plikiem .spec, więc tak właśnie zamierzamy to zrobić.
Po pierwsze, musisz już mieć plik .spec. W moim przypadku udało mi się stworzyć to, co potrzebne, uruchamiając
pyinstaller
z dodatkowymi argumentami można znaleźć dodatkowe argumenty tutaj . Z tego powodu mój plik specyfikacji może wyglądać trochę inaczej niż twój, ale zamieszczam go w całości w celach informacyjnych, po tym, jak wyjaśnię ważne elementy.added_files to zasadniczo lista zawierająca krotki, w moim przypadku chcę dodać tylko POJEDYNCZY obraz, ale możesz dodać wiele ikon ico, png lub jpg używając
('app/img/*.ico', 'app/img')
Możesz również utworzyć kolejną krotkę,added_files = [ (), (), ()]
aby mieć wiele importówPierwsza część krotki określa, jaki plik lub typ pliku chcesz dodać, a także gdzie je znaleźć. Pomyśl o tym jako CTRL + C
Druga część krotki mówi Pyinstallerowi, aby utworzył ścieżkę „app / img /” i umieścił pliki w tym katalogu Względem dowolnego katalogu tymczasowego, który zostanie utworzony po uruchomieniu pliku .exe. Pomyśl o tym jako CTRL + V
Poniżej
a = Analysis([main...
ustawiłemdatas=added_files
, pierwotnie tak było,datas=[]
ale chcemy, aby lista importów była, cóż, zaimportowana, więc przekazujemy nasze niestandardowe importy.Nie musisz tego robić, chyba że chcesz mieć określoną ikonę dla EXE, na dole pliku specyfikacji mówię Pyinstaller, aby ustawił ikonę mojej aplikacji na exe z opcją
icon='app\\img\\app_icon.ico'
.Kompilacja do EXE
Jestem bardzo leniwy; Nie lubię pisać rzeczy bardziej niż muszę. Utworzyłem plik .bat, który mogę po prostu kliknąć. Nie musisz tego robić, ten kod będzie działał w powłoce wiersza polecenia bez niego.
Ponieważ plik .spec zawiera wszystkie nasze ustawienia i argumenty kompilacji (czyli opcje), musimy tylko przekazać ten plik .spec do Pyinstaller.
źródło
Innym rozwiązaniem jest utworzenie punktu zaczepienia środowiska wykonawczego, które kopiuje (lub przenosi) dane (pliki / foldery) do katalogu, w którym przechowywany jest plik wykonywalny. Haczyk to prosty plik w Pythonie, który może zrobić prawie wszystko, tuż przed uruchomieniem aplikacji. Aby to ustawić należy skorzystać z
--runtime-hook=my_hook.py
opcji pyinstaller. Tak więc, jeśli twoje dane są folderem obrazów , powinieneś uruchomić polecenie:Plik cp_images_hook.py może wyglądać mniej więcej tak:
Przed każdym uruchomieniem folder obrazów jest przenoszony do bieżącego katalogu (z folderu _MEIPASS), więc plik wykonywalny zawsze będzie miał do niego dostęp. W ten sposób nie ma potrzeby modyfikowania kodu projektu.
Drugie rozwiązanie
Możesz skorzystać z mechanizmu runtime-hook i zmienić bieżący katalog, co według niektórych deweloperów nie jest dobrą praktyką, ale działa dobrze.
Kod haka można znaleźć poniżej:
źródło