Pygame nie ładuje png po zrobieniu.exe z Pyinstaller

Próbowałem zrobić .exe z mojej gry. py i to było naprawdę frustrujące.

Używam Pythona 3.5.2, Pygame 1.9.2 i Pyinstaller 3.2.

Gra działa idealnie jako. py, ale po wejściu pyinstaller --debug game.py,exe jest zbudowany i uruchamiam go, dostaję następujące:

Ekran debugowania

Są to linie kodu z game.py to może być związane z błędem:

from os import path
img_dir = path.join(path.dirname(__file__), 'sprites')
title_screen = pygame.image.load(path.join(img_dir, 'title_screen.png'))

Myślę, że to musi mieć coś wspólnego z tym, że pyinstaler nie jest w stanie aby dostać mój folder sprites, ponieważ kiedy próbuję uruchomić pyinstaller --icon=/sprites/icon.ico game.py, dostaję to:

Icon error screen

Ale jeśli używam pyinstaller --icon=icon.ico game.py ładuje ikonę dobrze.

Oto mój plik spec:

# -*- mode: python -*-

block_cipher = None

added_files = [
         ( '/sprites', 'sprites' ),
         ( '/music', 'music' ),
         ( 'Heavitas.ttf', '.'),
         ( 'Roboto-Light.ttf', '.'),
         ( 'high scores.csv', '.')
         ]


a = Analysis(['spec_file.py'],
             pathex=['C:\\Users\\rodri\\Documents\\Code\\The Color That Fell From The Sky'],
             binaries=None,
             datas=added_files,
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          exclude_binaries=True,
          name='spec_file',
          debug=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='spec_file')
Author: Rodrigo Nascimento, 2016-12-18

2 answers

W końcu się udało!

Oto krótki przewodnik tego, co zrobiłem w przypadku, gdy czytasz to, ponieważ masz podobny problem. (Używałem Pythona 3.5.2, Pygame 1.9.2 i Pyinstaller 3.2)

Przygotuj swój kod

Jak C. _ wyjaśnia na drugiej odpowiedzi (dzięki), w przypadku, gdy ładujesz pliki w ten sposób:

import os
folder_path = os.path.join(path.dirname(__file__), 'folder')
some_image = pygame.image.load(os.path.join(folder_path, 'some_image.png'))

Zrób to zamiast:

import sys
import os
# If the code is frozen, use this path:
if getattr(sys, 'frozen', False):
    CurrentPath = sys._MEIPASS
# If it's not use the path we're on now
else:
    CurrentPath = os.path.dirname(__file__)
# Look for the 'sprites' folder on the path I just gave you:
spriteFolderPath = os.path.join(CurrentPath, 'sprites')
# From the folder you just opened, load the image file 'some_image.png'
some_image = pygame.image.load(path.join(spriteFolderPath, 'some_image.png'))

Jest to potrzebne, ponieważ po zamrożeniu kodu pliki zostaną przeniesione do innego folderu niż ten używałeś już wcześniej. Upewnij się, że robisz to ze wszystkimi plikami.

Oto kolejny przykład:

if hasattr(sys, '_MEIPASS'):  # the same logic used to set the image directory
    font = path.join(sys._MEIPASS, 'some_font.otf')  # specially useful to make a singlefile .exe
font = pygame.font.Font(font, size)
# Don't ask me the difference between hasattr and getattr because I don't know. But it works.

Icon (opcjonalnie)

Jeśli chcesz ikonę, która nie jest domyślna pyinstaller, możesz wybrać obraz png i przekonwertować go .ico za pomocą jakiegoś konwertera online (Google it). Po tym, umieścić .plik ico w tym samym folderze, w którym znajduje się Twój plik .py.

Stwórz plik spec

W tym momencie powinieneś wiedzieć, czy chcesz pojedynczy samowystarczalny .plik exe lub kilka oddzielne pliki, które będziesz zip i wysłać do ludzi. W każdym razie otwórz terminal w folderze, w którym znajduje się plik. py.

Jeśli chcesz mieć pojedynczy plik, użyj tego:

pyinstaller --onefile --icon=icon_file.ico game_file.py

Jeśli nie, Użyj tego:

pyinstaller --icon=icon_file.ico game_file.py

Jeśli nie chcesz teraz ustawiać ikony, po prostu nie używaj części --icon. Możesz to zmienić później. Nie można później zmienić opcji --onefile(przynajmniej z tego co wiem).

Plik o nazwie game_file.zostanie utworzona Specyfikacja. Automatycznie otrzyma Nazwa od game_file.py możesz to schrzanić, jeśli mają inne nazwy, więc nie bądź teraz kreatywny. Jeśli wybrałeś pojedynczy plik, powinien on wyglądać tak:

# -*- mode: python -*-

block_cipher = None

a = Analysis(['game_file.py'],
             pathex=['C:\\some\\path\\The path where your .py and .spec are'],
             binaries=None,
             datas=None,
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          exclude_binaries=True,
          name='game_file',
          debug=False,
          strip=False,
          upx=True,
          console=True , icon='icon_file.ico')

Jeśli zdecydujesz się mieć kilka plików, zobaczysz tę dodatkową część:

coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='game_file')

Po block_cipher = None dodasz pliki, które ładuje Twoja gra. Tak:

added_files = [
         ( 'a folder', 'b folder' ),  # Loads the 'a folder' folder (left) and creates
                                      # an equivalent folder called 'b folder' (right)
                                      # on the destination path
         ( 'level_1/level2', 'level_2' ),  # Loads the 'level_2' folder
                                           # that's inside the 'level_1' folder
                                           # and outputs it on the root folder
         ( 'comic_sans.ttf', '.'),  # Loads the 'comic_sans.ttf' file from
                                    # your root folder and outputs it with
                                    # the same name on the same place.
         ( 'folder/*.mp3', '.')  # Loads all the .mp3 files from 'folder'.
         ]

Teraz musisz dodać 'added_files' tutaj:

a = Analysis(['game_file.py'],
                 pathex=['C:\\some\\path\\The path where your .py and .spec are'],
                 binaries=None,
                 datas=added_files,  # Change 'None' to 'added_files' here
                                     # Leave everything else the way it is.
                 hiddenimports=[],
                 hookspath=[],
                 runtime_hooks=[],
                 excludes=[],
                 win_no_prefer_redirects=False,
                 win_private_assemblies=False,
                 cipher=block_cipher)

Możesz także zmienić niektóre ustawienia:

exe = EXE(pyz,
              a.scripts,
              exclude_binaries=True,
              name='game_file',  # Name of the output file. Equivalent to '--name'
                                 # Don't change it.
              debug=False,  # If True shows a debug screen on start. Equivalent to '--debug'.
              strip=False,
              upx=True,  # Compresses executable files and libraries
              # If console=True, a console screen will be shown on start up.
              # icon= is the location of the icon of the exe.
              console=True , icon='icon_file.ico')

If you didn ' t change the ścieżka lub nazwa wyjściowa exe, jak mówiłem, aby nie w komentarzach, po prostu musimy go uruchomić i .wcześniej utworzony plik exe zostanie zaktualizowany. Wpisz to w oknie poleceń:

pyinstaller game_file.spec

Pamiętaj, że {[13] } to plik, który właśnie edytowaliśmy, a 'game_file' to losowa nazwa, której użyłem jako przykładu. Zauważ również, że nie ma --some_option, ponieważ nie będą działać z plikiem spec. Dlatego musisz je zmienić bezpośrednio w skrypcie. --onefile również nie działa tutaj i nie może zrób to z głębi scenariusza, dlatego kazałem ci to zrobić wcześniej.

Zobaczysz, że dwa foldery zostały utworzone w tym samym folderze, w którym ty .plik spec jest. Ten o nazwie "Dist" zawiera plik exe i jeśli nie używałeś --onefile, powinien również zawierać kilka innych plików, które musisz spakować razem z exe, aby udostępnić aplikację innym osobom. Będzie też folder "Buid", ale nie wiem Do czego służy, bo nie trzeba go używać podanie.

Więc to jest to. Powinno zadziałać.

Moje błędy podczas zadawania pytania polegały na tym, że nie znałem części sys._MEIPASS (Jeszcze raz dziękuję C._), nazwa mojego pliku spec była inna od mojego pliku py, użyłem '/sprites' zamiast 'sprites' w added_files i nie wiedziałem, że powinienem uruchomić plik spec zamiast pliku py.

Aby uzyskać więcej informacji na temat Pyinstaller, spójrz na podręcznik , ale ponieważ jest to dalekie od dobrego i czasami mylące, jesteś lepiej z Google.

 12
Author: Rodrigo Nascimento,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-19 03:09:27

Podczas kompilacji z PyInstaller, wszystkie pliki są przenoszone do innego katalogu podczas uruchamiania exe. Tak więc, aby dostać się do tej lokalizacji, dodaj ją do początku kodu, zanim wygenerujesz ścieżki

import sys

if getattr(sys, 'frozen', False): # PyInstaller adds this attribute
    # Running in a bundle
    CurrentPath = sys._MEIPASS
else:
    # Running in normal Python environment
    CurrentPath = os.path.dirname(__file__)

Wszystkie ścieżki folderów mogą być następnie generowane z Twojej lokalizacji

spriteFolderPath = path.join(CurrentPath, 'sprites') # Do the same for all your other files

Następnie, gdy masz lokalizację, w której wykonujesz, możesz pobrać stamtąd wszystkie swoje pliki:

title_screen = pygame.image.load(path.join(spriteFolderPath, 'title_screen.png')) # Use spriteFolderPath instead of img_dir

Widzę również, że masz inne czcionki / rzeczy, możesz zrobić to samo, aby load them

fontRobotoLight = pygame.font.Font(path.join(CurrentPath, 'Roboto-Light.ttf'))

Dla Twojej ikony, po prostu wklej tymczasowy icon.ico w głównym folderze i wpisz pyinstaller -i "icon.ico" "spec_file.spec"

Wreszcie, ponieważ miałem ten sam problem wcześniej, polecam skompilować exe po prostu uruchamiając pyinstaller "spec_file.spec"

 1
Author: C._,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-18 04:43:54