python - PyInstaller 致命错误:无法执行脚本
问题描述
首先,我想为这个很长的问题道歉,但这让我发疯了。星期六我必须在学校的开放日展示这个项目,我几乎没有时间。
我和一个朋友(使用 Pygame)在 Python 3.9 中编写了一个蛇游戏,我正在尝试使用 PyInstaller 从它创建一个可执行文件(使用多个 python 模块和其他文件,如图像和音乐),但每次我执行命令pyinstaller Game.spec
,我尝试运行可执行文件我收到此错误:
这个错误对我来说是非常意外和奇怪的,因为我已经在大约一周前以相同的方式使其可执行(但它是一个旧版本,我们更改了很多东西)并且它完美地工作,但现在它无法工作无论如何,我们尝试了所有方法并阅读了 Stack 上的每一篇文章,因此创建了可执行文件,但它不会启动。
GIF 转换器模块
自从我们制作了第一个可执行文件以来,我们确实改变了很多东西,但代码的结构几乎保持不变,唯一的例外是我编写的一个模块,基本上它会自动将动画 GIF 分割成单独的帧并将它们加载到 Pygame (手动加载每一帧太浪费时间了......)。我怀疑这个模块可能是最终导致我每次尝试执行游戏时都会提示该错误的原因之一。我们使用此模块为主菜单中的按钮设置动画:
它是如何工作的?
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]
很抱歉代码乱七八糟,但我必须在大约 3 天内学习 OOP 才能编写所有这些,但我仍然有点困惑。无论如何,该类的属性几乎是不言自明的,gif
包含用户给出的所需动画图像,该frames
数组将包含从动画 gif 中提取的所有图像,同时assetFrames_url
是frames
包含相对路径的副本,我们用它来绕过 PyInstaller 中的另一个错误,它不允许我们将文件添加到Game.spec
.,所以我们必须通过这个函数传递所有附加文件,这里是这个函数的源代码。
我们不得不手写(我试图用脚本来自动化它,但它是一个巨大的失败)里面每个单独的附加元素的路径Game.spec
。使用该方法deconstructGif
,gif在特定文件夹中被分割(每帧一张图像),然后使用该方法将所有分割帧加载到frames
数组中gifLoader
,最后使用该方法进行动画处理gifPlayer
。
例如,这些是动画按钮的文件夹:
游戏规范
当我们编译第一个可执行文件时,我们必须手动将每个文件的路径插入到元组数组中。这是最后一个版本:
# -*- 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')
在datas
数组中,我为每个文件插入了一个元组,第一个参数是图像的相对路径,第二个参数是文件夹的相对路径。本地路径由点 ('.') 表示。
我尝试过的事情:
首先,我试图找到问题的“源头”,所以我打开了
.spec
执行命令生成的文件pyinstaller -i Icon.Ico --onefile Game.py
(Ico.ico 是可执行文件的图标,Game.py 是主模块)并激活控制台调试,像这样:console=True, icon='Icon.ico')
这是我详细得到的错误:
- 我试图在数据数组中传递所有帧的路径,但没有成功。
- 我试图传递所有 GIF 的路径,但没有成功。
如果这个问题可能看起来令人困惑,并且语法真的很糟糕,但英语不是我的主要语言,我需要快速解决这个问题,我没有时间纠正所有错误,我深表歉意。提前致谢!
解决方案
最后,我设法解决了这个问题。
基本上,Pygame 和 PIL (Python Imaging Library) 之间存在兼容性问题,我不得不重写整个 pyGimager 类,我无法制作单个可执行文件,但最终它可以工作。
推荐阅读
- windows - 如何使用 windows Mingw 编译器生成 .gcov 中间文本文件以及 lcov 如何使用这些 .gcov 文件?
- laravel - 我使用了 laravel 分页链接,但我得到了大 icos。< > 在顺风 css 代码中调用
- qt - 将 QML 位置插件缩放级别提高到 19 以上
- c# - C#用户输入不区分大小写使用switch
- wordpress - Apache 2 - Wordpress SSL Cloudflare 520
- cplex - LP 文件中使用 cplex 的幂符号的方程
- jsf - Primefaces datePicker 范围选择:只允许选择一个日期
- reactjs - Heroku react 应用程序构建失败(node-sass)
- r - 列表的变异逻辑
- git - 无法推送 github 大小限制