First of all, I want to apologize for the very long question but this is driving me crazy. Saturday I have to exhibit this project to my school's open day and I have little to no time.
I've written a snake game in Python 3.9 with a friend (using Pygame) and I'm trying to create an executable file with PyInstaller from it (using multiple python modules and additional files such as images and music), but every time I execute the command pyinstaller Game.spec
and I try to run the executable I get this error:
This error is very unexpected and strange to me because I already made it executable about a week ago (but it was an older version and we change lots of things) in the same way and it worked flawlessly, but now it won't work in any way, we tried everything and read every post here on Stack, so the executable gets created but it won't start.
GIF Converter Module
We changed really a lot of things since we made the first executable, but the structure of the code remained pretty much the same with the only exception of a module that I wrote, basically it automatically splits animated GIFs into separated frames and loads them into Pygame (loading every single frame by hand was too time-wasting...). I suspect that this module might be one of the causes that ultimately lead to prompting that error every time I try to execute the game. We used this module to animate the buttons in the main menu:
How does it work?
from PIL import Image
import sys
import os
import pygame
class PyGimager:
def __init__(self, gif):
# Loading the desired gif image
self.gif = Image.open(gif)
# Variable where all the frames will be stored
self.frames = []
self.assetFrames_url = []
# This method divides all the frames of the gif in a .png file (in a desired folder)
def deconstructGif(self, mainName='decImage', savePath='LOCAL'):
path = savePath + "/" + mainName
self.assetFrames_url.append(resource_path(path))
local = True
if savePath != 'LOCAL':
try:
os.makedirs(savePath)
except OSError as error:
print("Path creation failed: {}".format(error))
local = False
for i in range(self.gif.n_frames):
self.gif.seek(i)
if not local:
self.gif.save(savePath + mainName + '{}.png'.format(i), 'PNG')
else:
self.gif.save(mainName + '{}.png'.format(i))
# loads all the frames in a bidimensional array
def gifLoader(self, loadPath="/"):
frame = ''
if loadPath == "/":
for image in os.listdir():
if image.endswith(".png"):
frame = pygame.image.load(image)
self.frames.append(frame)
else:
for image in os.listdir(loadPath):
if image.endswith(".png"):
frame = pygame.image.load(loadPath + image)
self.frames.append(frame)
def gifPlayer(self, screen, position=def_position, wait=500):
for frame in self.frames:
AnimatedSprite = frame
screen.blit(AnimatedSprite, position)
pygame.time.wait(wait)
pygame.display.update()
def getFirstFrame(self):
return self.frames[0]
Sorry for the messy code but I had to learn OOP in about 3 days to write all of this and I am still a bit confused. Anyway, the attributes of the class are pretty much self-explanatory, gif
contains the desired animated image given by the user, the frames
array will contain all the images extracted from the animated gif while assetFrames_url
is a copy of frames
containing the relative paths, we used it to bypass another bug in PyInstaller which didn't allow us to add our files to Game.spec
, so we had to pass through this function all the additional files, here's the source of this function.
We had to write by hand (I tried to automate it with a script but it was a huge failure) the path of every single additional element inside Game.spec
. With the method deconstructGif
the gif gets split (one image for each frame) in a specific folder, then all the split frames are loaded inside the frames
array with the gifLoader
method and finally they get animated with the method gifPlayer
.
For example, these are the folders of the animated buttons:
Game.spec
When we compiled the first executable we had to manually insert the path for each file inside an array of tuples. This is the last version:
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['Game.py'],
pathex=['C:\\Users\\gianm\\Documents\\GitKraken\\pyWormy'],
binaries=[],
datas=[
('Img/Buttons/Hard/HardButton0.png','Img/Buttons/Hard/'),('Img/Buttons/Hard/HardButton1.png','Img/Buttons/Hard/'),
('Img/Buttons/Easy/EasyButton0.png','Img/Buttons/Easy/'),('Img/Buttons/Easy/EasyButton1.png','Img/Buttons/Easy/'),
('Img/Buttons/Options/OptionsButton0.png','Img/Buttons/Options/'),('Img/Buttons/Options/OptionsButton1.png','Img/Buttons/Options/'),
('Img/Buttons/Play/PlayButton0.png','Img/Buttons/Play/'),('Img/Buttons/Play/PlayButton1.png','Img/Buttons/Play/'),
('Img/Buttons/Exit/ExitButton0.png','Img/Buttons/Exit/'),('Img/Buttons/Exit/ExitButton1.png','Img/Buttons/Exit/'),
('Img/Buttons/BackToMenu/BacktoMenuButton0.png','Img/Buttons/BackToMenu/'),('Img/Buttons/BackToMenu/BacktoMenuButton1.png','Img/Buttons/BackToMenu/'),
('SF_Pixelate.ttf','.'),
('Sounds/difficileSottofondo.wav','Sounds/'),('Sounds/facileSottofondo.wav','Sounds/'),('Sounds/lose.wav','Sounds/'),('Sounds/slurp.wav','Sounds/'),('Sounds/musica.wav','Sounds/'),
('Img/background.png','Img/'),('Img/GameOver.png','Img/'),('Img/startMenu.png','Img/'),
('Img/Sprites/Sprites3/CorpoSerpente.png','Img/Sprites/Sprites3/'),
('Img/Sprites/Sprites3/TestaDestra.png','Img/Sprites/Sprites3/'), ('Img/Sprites/Sprites3/TestaGiu.png','Img/Sprites/Sprites3/'),('Img/Sprites/Sprites3/TestaSinistra.png','Img/Sprites/Sprites3/'),('Img/Sprites/Sprites3/TestaSopra.png','Img/Sprites/Sprites3/'),
('Img/Sprites/Sprites3/Mela/C.png','Img/Sprites/Sprites3/Mela/'),('Img/Sprites/Sprites3/Mela/Ruby.png','Img/Sprites/Sprites3/Mela/'),('Img/Sprites/Sprites3/Mela/PHP.png','Img/Sprites/Sprites3/Mela/'),('Img/Sprites/Sprites3/Mela/JS.png','Img/Sprites/Sprites3/Mela/'),
('Img/Buttons/Hard.gif','Img/Buttons/'),('Img/Buttons/Easy.gif','Img/Buttons/'),('Img/Buttons/Options.gif','Img/Buttons/'),('Img/Buttons/Exit.gif','Img/Buttons/'),
('Img/Buttons/BackToMenu.gif','Img/Buttons/'),('Img/Buttons/Play.gif','Img/Buttons/'),
],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='pyWormy',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False , icon='Icon.ico')
Inside the datas
array, I inserted a tuple for each file, the first argument is the relative path of the image while the second is the relative path of the folder. The local paths are represented by a point ('.').
Things I've tried:
First of all, I tried to locate the "source" of the problem, so I opened the
.spec
file generated with the execution of the commandpyinstaller -i Icon.Ico --onefile Game.py
(with Ico.ico being the icon of the executable and Game.py the main module) and I activated the console debug, like this:console=True, icon='Icon.ico')
This is the error that i get in detail:
- I tried to pass in the data array the path for all the frames, it didn't work.
- I tried to pass in the paths of all the GIFs, it didn't work.
I apologize if this question may seem confusing and if the grammar is really bad but English is not my primary language and I need to fix this fast and I don't have time to correct all the mistakes. Thanks in advance!