杰瑞科技汇

Python cxfreeze打包如何实现?

cx_Freeze 是一个非常流行和强大的打包工具,它可以将你的 Python 脚本及其所有依赖项(包括库、数据文件等)捆绑到一个或多个文件中,从而在没有安装 Python 环境的 Windows 机器上运行。

Python cxfreeze打包如何实现?-图1
(图片来源网络,侵删)

目录

  1. 准备工作
  2. 基本打包步骤
  3. 处理依赖项(关键步骤)
  4. 包含数据文件(如图片、配置等)
  5. 打包选项详解
  6. 完整示例:一个带 GUI 和数据文件的程序
  7. 常见问题与解决方案

准备工作

你需要确保你的 Python 环境已经安装了 cx_Freeze

安装 cx_Freeze

打开你的命令行工具(如 cmd 或 PowerShell),然后运行:

pip install cx_Freeze

准备一个简单的 Python 脚本

Python cxfreeze打包如何实现?-图2
(图片来源网络,侵删)

为了演示,我们创建一个名为 hello.py 的简单脚本。

# hello.py
import sys
import time
def main():
    print("Hello from a frozen executable!")
    print("Python version:", sys.version)
    for i in range(5):
        print(f"Counting: {i}")
        time.sleep(1)
    print("Program finished.")
if __name__ == "__main__":
    main()

基本打包步骤

cx_Freeze 的核心是一个名为 setup.py 的构建脚本,你需要在你的项目根目录下创建这个文件。

创建 setup.py

hello.py 同一个目录下,创建 setup.py 文件:

Python cxfreeze打包如何实现?-图3
(图片来源网络,侵删)
# setup.py
from cx_Freeze import setup, Executable
# 基本设置
setup(
    name="MyHelloApp",
    version="0.1",
    description="My first frozen application",
    executables=[Executable("hello.py")]
)

执行打包命令

打开命令行,切换到 setup.pyhello.py 所在的目录,然后运行:

python setup.py build

执行成功后,你会看到一个新的 build 目录,进入 build 目录,再进入 exe.win-amd64-3.10(或类似名称,取决于你的 Python 版本和系统架构)子目录,你就能找到 hello.exe 文件。

你可以直接运行它,或者将其复制到任何一台 Windows 电脑上运行,无需安装 Python。


处理依赖项(关键步骤)

对于简单的脚本,cx_Freeze 通常能自动找到所有依赖项,但对于复杂的项目,特别是使用了第三方库(如 requests, numpy, Pillow 等)时,自动检测可能会失败。

手动指定依赖项

为了确保所有库都被正确包含,最佳实践是在 setup() 函数中使用 options 参数来指定。

# setup.py
from cx_Freeze import setup, Executable
# 定义可执行文件
executables = [
    Executable("hello.py")
]
# 设置打包选项
build_exe_options = {
    # "packages": ["os", "sys", "time"], # 可以手动指定要包含的包
    "includes": [  # 更推荐使用 includes,显式包含可能被遗漏的模块
        "tkinter",  # 如果使用 Tkinter GUI,必须包含
        "PIL",      # 如果使用 Pillow (PIL),必须包含
        "requests", # 如果使用 requests,必须包含
        "numpy"     # 如果使用 numpy,必须包含
    ],
    "excludes": [  # 排除不需要的包,可以减小体积
        "matplotlib", # 如果你没用它,可以排除
        "unittest"
    ],
    "include_files": [  # 包含数据文件,见下一节
        # ("source_path", "dest_path_in_exe")
    ]
}
setup(
    name="MyHelloApp",
    version="0.1",
    description="My first frozen application",
    options={"build_exe": build_exe_options},
    executables=executables
)

如何找到需要包含的模块?

如果打包后的程序运行时提示 ModuleNotFoundError,你就需要找出是哪个模块没有被包含。

  1. 查看错误信息:错误信息会告诉你缺少哪个模块,No module named 'requests'
  2. 手动添加:将缺少的模块名添加到 build_exe_optionsincludes 列表中。
  3. 重复打包和测试:修改 setup.py 后,重新运行 python setup.py build 并测试。

包含数据文件(如图片、配置等)

如果你的程序需要额外的文件,比如图片、配置文件、数据库等,你需要告诉 cx_Freeze 将它们复制到最终生成的可执行文件旁边。

使用 include_files 选项。

假设你的项目结构如下:

my_project/
├── hello.py
├── setup.py
└── assets/
    ├── icon.png
    └── config.json

修改 setup.py

# setup.py
from cx_Freeze import setup, Executable
import os
# 定义可执行文件
executables = [
    Executable(
        "hello.py",
        base="Win32GUI" if sys.platform == "win32" else None,
        icon="assets/icon.ico"  # 可选:指定图标
    )
]
# 数据文件路径
# os.path.join 用于跨平台路径拼接
data_files = [
    (os.path.join("assets", "images"), ["assets/icon.png"]),  # 将 icon.png 复制到 exe 旁边的 assets/images 目录
    (os.path.join("assets"), ["assets/config.json"])          # 将 config.json 复制到 exe 旁边的 assets 目录
]
# 设置打包选项
build_exe_options = {
    "include_files": data_files,
    "includes": ["json"] # config.json 是 json 格式,确保包含 json 模块
}
setup(
    name="MyHelloApp",
    version="0.1",
    description="My first frozen application",
    options={"build_exe": build_exe_options},
    executables=executables
)

打包后,在 build/exe.win-amd64-3.10 目录下,你会看到 assets 文件夹,里面包含了 icon.pngconfig.json

在代码中访问数据文件

hello.py 中,你需要使用特殊变量 sys._MEIPASS 来获取可执行文件所在的临时目录,然后拼接路径。

# hello.py
import sys
import os
import json
def get_resource_path(relative_path):
    """获取资源的绝对路径,无论是开发环境还是打包后"""
    try:
        # PyInstaller 创建的临时文件夹
        base_path = sys._MEIPASS
    except Exception:
        # 开发环境
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)
def main():
    # 打印欢迎信息
    print("Hello from a frozen executable!")
    # 读取配置文件
    config_path = get_resource_path(os.path.join("assets", "config.json"))
    if os.path.exists(config_path):
        with open(config_path, 'r') as f:
            config = json.load(f)
            print("Config loaded:", config)
    else:
        print("Config file not found!")
    # 读取图片路径(仅作示例,不能直接在exe中用open读取图片)
    # icon_path = get_resource_path(os.path.join("assets", "icon.png"))
    # print(f"Icon path: {icon_path}")
if __name__ == "__main__":
    main()

打包选项详解

setup()Executable() 函数有很多有用的参数:

  • Executable(script, ...):

    • script: 要打包的主脚本路径。
    • base: 可执行文件的基类。
      • None: 控制台程序(黑窗口)。
      • Win32GUI: Windows GUI 程序(无黑窗口),打包 GUI 程序(如 Tkinter, PyQt)时必须使用。
      • Console: 显式指定为控制台程序。
    • icon: .ico 图标文件的路径。
    • targetName: 生成的 .exe 文件名。
    • compress: 是否压缩。True 可以减小体积,但可能略微增加启动时间。
  • setup(..., options={"build_exe": ...}):

    • packages: 自动包含指定包及其所有子包。
    • includes: 显式包含模块(推荐用于解决依赖问题)。
    • excludes: 排除模块,减小体积。
    • include_files: 包含数据文件,如上所述。
    • binaries: 包含非 Python 的二进制依赖(如某些 DLL 文件),很少需要手动设置。
    • path: 指定 Python 模块的搜索路径。
    • optimize: 优化级别,如 2

完整示例:一个带 GUI 和数据文件的程序

项目结构

my_app/
├── app.py          # 主程序
├── setup.py        # 打包脚本
├── assets/
│   ├── icon.ico    # 程序图标
│   └── style.css   # 样式文件
└── requirements.txt # 依赖列表

requirements.txt

tkinter
Pillow

app.py (使用 Tkinter)

# app.py
import tkinter as tk
from tkinter import messagebox
import sys
import os
def get_resource_path(relative_path):
    """获取资源的绝对路径"""
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)
def show_message():
    messagebox.showinfo("Hello", "This is a message from a frozen app!")
def main():
    root = tk.Tk()
    root.title("My Frozen App")
    # 设置图标
    icon_path = get_resource_path("assets/icon.ico")
    if os.path.exists(icon_path):
        root.iconbitmap(icon_path)
    # 读取并应用样式(仅作示例,Tkinter 内置样式)
    # style_path = get_resource_path("assets/style.css")
    # if os.path.exists(style_path):
    #     with open(style_path, 'r') as f:
    #         # 这里需要额外的库来应用CSS,ttkstyle
    #         pass
    label = tk.Label(root, text="Welcome to my frozen application!", font=("Arial", 14))
    label.pack(pady=20)
    button = tk.Button(root, text="Click Me!", command=show_message)
    button.pack(pady=10)
    root.mainloop()
if __name__ == "__main__":
    main()

setup.py

# setup.py
import sys
from cx_Freeze import setup, Executable
# 如果是控制台程序,base=None;如果是GUI程序,base="Win32GUI"
# Tkinter 是 GUI 框架,所以使用 Win32GUI
base = "Win32GUI" if sys.platform == "win32" else None
# 数据文件
data_files = [
    (os.path.join("assets"), ["assets/icon.ico"])
]
build_exe_options = {
    "include_files": data_files,
    "includes": ["tkinter"], # 显式包含 tkinter
}
setup(
    name="MyTkinterApp",
    version="1.0",
    description="A simple Tkinter application frozen with cx_Freeze",
    options={"build_exe": build_exe_options},
    executables=[
        Executable(
            "app.py",
            base=base,
            icon="assets/icon.ico",
            target_name="MyApp.exe"
        )
    ]
)

打包和运行

  1. 安装依赖:pip install -r requirements.txt
  2. 打包:python setup.py build
  3. 运行:进入 build/exe.win-amd64-3.10 目录,双击 MyApp.exe

常见问题与解决方案

  1. ModuleNotFoundError (缺少模块)

    • 原因: cx_Freeze 自动检测遗漏了某些依赖。
    • 解决: 在 build_exe_optionsincludes 列表中手动添加缺少的模块名。
  2. DLL not found 或找不到动态链接库

    • 原因: 某些 C 扩展库(如 numpy, Pillow)依赖外部的 DLL 文件,cx_Freeze 没有自动复制它们。
    • 解决:
      • 简单方法: 将这些 DLL 文件所在的 Python DLLs 目录(C:\Python311\Lib\site-packages\numpy.libs)下的所有 .dll 文件手动复制到你的 build/exe.win-amd64-3.10 目录。
      • 推荐方法: 使用 binaries 选项,你需要先找到这些 DLL 的位置,然后在 setup.py 中指定。
        build_exe_options = {
          "binaries": [
              ("path/to/your/dll/file.dll", ".") # 将 dll 复制到 exe 所在目录
          ]
        }
  3. 打包后程序启动慢或卡顿

    • 原因: cx_Freeze 默认将所有代码打包到一个 .exe 文件中,首次运行时需要解压,导致延迟。
    • 解决: 使用 zip_include_packages 选项,它会将指定的包打包成 .zip 文件,加快启动速度。
      build_exe_options = {
        "zip_include_packages": ["numpy", "PIL"], # 将这些包打包成 zip
        "zip_include_packages": ["*"] # 打包所有包为 zip (谨慎使用)
      }
  4. 控制台窗口一闪而过 (GUI 程序)

    • 原因: 你忘记在 Executable 中设置 base="Win32GUI"
    • 解决: 确保你的 Executable 定义中包含了 base="Win32GUI"

使用 cx_Freeze 打包 Python 程序的核心流程可以概括为:

  1. 安装: pip install cx_Freeze
  2. 创建 setup.py: 这是打包的配置文件。
  3. 定义 Executable: 指定主脚本和运行模式(控制台/GUI)。
  4. 配置 options: 使用 includes, excludes, include_files 等精确控制依赖和数据文件。
  5. 运行命令: python setup.py build
  6. 测试: 在目标机器上运行生成的 .exe 文件,并根据错误信息调试 setup.py

对于复杂项目,手动处理依赖项和数据文件是保证打包成功的关键,希望这份详细的指南能帮助你顺利完成打包!

分享:
扫描分享到社交APP
上一篇
下一篇