问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

PyInstaller打包实践:资源缺失报错解决

创作时间:
作者:
@小白创作中心

PyInstaller打包实践:资源缺失报错解决

引用
CSDN
1.
https://blog.csdn.net/2301_79442295/article/details/144324316

在使用PyInstaller打包Python项目时,经常会遇到资源文件缺失的问题。本文通过一个具体的Flappy Bird游戏项目为例,详细介绍了如何通过修改代码来解决打包后资源文件路径问题。

准备

首先需要安装PyInstaller:

pip install pyinstaller

打包命令如下:

pyinstaller --onefile --windowed --icon=flappy.ico --add-data "assets/sprites;assets/sprites" --add-data "assets/audio;assets/audio" main.py

参数说明:

  • --onefile:将所有文件打包成一个单独的 .exe 文件。
  • --windowed:防止显示控制台窗口。这对于图形界面程序(如使用 Pygame 的游戏)非常有用,可以避免控制台窗口弹出。
  • --icon=flappy.ico:指定自定义的软件图标(flappy.ico)用于 .exe 文件。
  • --add-data "assets/sprites;assets/sprites":将 assets/sprites 目录中的文件添加到打包文件中,并保留目录结构。
  • --add-data "assets/audio;assets/audio":将 assets/audio 目录中的文件添加到打包文件中,并保留目录结构。
  • main.py:指定你的主 Python 脚本路径。

修改代码

在原项目中,项目结构如下:

  • src:主要是代码文件
  • assets:主要是项目的资源文件(包括图片与声音文件)
  • main.py:程序入口

使用PyInstaller打包后找不到资源文件是因为当 PyInstaller 打包时,资源文件的路径会被修改。你需要使用 sys._MEIPASS 来确定运行时的资源路径,而不是直接使用固定路径。sys._MEIPASS 是 PyInstaller 在打包后提供的临时文件夹路径。

下面对 src 中的代码文件进行修改,由于源代码中对路径的引用都在 FlapPyBird\src\utils 这个文件夹下,所以只需要对其下代码修改。

images.py 为例,首先在首部加入:

import os
import sys

def get_resource_path(relative_path):
    if hasattr(sys, '_MEIPASS'):    # 当应用打包成exe时,使用 _MEIPASS 路径
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.abspath("."), relative_path)

然后在所有有路径的地方进行修改:

def __init__(self) -> None:
    self.numbers = list(
        (
            #pygame.image.load(f"assets/sprites/{num}.png").convert_alpha()
            pygame.image.load(get_resource_path(f"assets/sprites/{num}.png")).convert_alpha()
            for num in range(10)
        )
    )

可以看到原代码是:

pygame.image.load(f"assets/sprites/{num}.png").convert_alpha()

改后为:

pygame.image.load(get_resource_path(f"assets/sprites/{num}.png")).convert_alpha()

在路径前加了 get_resource_path(),动态获取路径。同理,将所有有路径的代码均在路径前加入 get_resource_path() 即可。

修改后的完整代码如下:

import random
from typing import List, Tuple
import os
import sys
import pygame
from .constants import BACKGROUNDS, PIPES, PLAYERS

def get_resource_path(relative_path):
    if hasattr(sys, '_MEIPASS'):    # 当应用打包成exe时,使用 _MEIPASS 路径
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.abspath("."), relative_path)

class Images:
    numbers: List[pygame.Surface]
    game_over: pygame.Surface
    welcome_message: pygame.Surface
    base: pygame.Surface
    background: pygame.Surface
    player: Tuple[pygame.Surface]
    pipe: Tuple[pygame.Surface]

    def __init__(self) -> None:
        self.numbers = list(
            (
                pygame.image.load(get_resource_path(f"assets/sprites/{num}.png")).convert_alpha()
                for num in range(10)
            )
        )
        # game over sprite
        self.game_over = pygame.image.load(
            get_resource_path("assets/sprites/gameover.png")
        ).convert_alpha()
        # welcome_message sprite for welcome screen
        self.welcome_message = pygame.image.load(
            get_resource_path("assets/sprites/message.png")
        ).convert_alpha()
        # base (ground) sprite
        self.base = pygame.image.load(get_resource_path("assets/sprites/base.png")).convert_alpha()
        self.randomize()

    def randomize(self):
        # select random background sprites
        rand_bg = random.randint(0, len(BACKGROUNDS) - 1)
        # select random player sprites
        rand_player = random.randint(0, len(PLAYERS) - 1)
        # select random pipe sprites
        rand_pipe = random.randint(0, len(PIPES) - 1)
        self.background = pygame.image.load(BACKGROUNDS[rand_bg]).convert()
        self.player = (
            pygame.image.load(PLAYERS[rand_player][0]).convert_alpha(),
            pygame.image.load(PLAYERS[rand_player][1]).convert_alpha(),
            pygame.image.load(PLAYERS[rand_player][2]).convert_alpha(),
        )
        self.pipe = (
            pygame.transform.flip(
                pygame.image.load(PIPES[rand_pipe]).convert_alpha(),
                False,
                True,
            ),
            pygame.image.load(PIPES[rand_pipe]).convert_alpha(),
        )

打包

最后使用以下命令进行打包:

pyinstaller --onefile --windowed --icon=flappy.ico --add-data "assets/sprites;assets/sprites" --add-data "assets/audio;assets/audio" main.py

如果你只需要exe文件,可以直接下载:https://download.csdn.net/download/2301_79442295/90096752?spm=1001.2014.3001.5503

运行效果如下:

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号