- 跨平台
- 移动开发
- 桌面应用
- UI组件
【免费下载链接】kivy
Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS
本篇技术指南基于 Kivy 官方文档 doc/sources/guide/packaging-osx.rst,系统讲解在 macOS 上打包 Kivy 应用的三种主流方案:官方推荐的 Kivy SDK、Buildozer 一键打包,以及 PyInstaller(配合或不配合 Homebrew)的深度定制方案。读完本文,你将掌握从安装依赖、编写 spec 文件到最终生成.app与 DMG 安装镜像的完整流程,并能结合仓库源码理解 Kivy 为 PyInstaller 提供的 hook 机制,按需裁剪打包体积。
打包方式总览
Kivy 官方为 macOS 提供了多条打包路径,它们各有适用场景:
| 方案 | 适用场景 | 特点 |
|---|---|---|
| Kivy SDK(Kivy.app) | 常规应用、追求稳定 | 所有依赖打包在虚拟环境中,不引用系统二进制,官方推荐 |
| Buildozer | 快速出包、自动化 | 一条命令完成打包,底层仍调用 Kivy SDK |
| PyInstaller + Homebrew | 需要精细控制 | 依赖从源码编译,可在任意机器运行,需手动编辑 spec |
| PyInstaller(无 Homebrew) | 已自行安装依赖 | 完全手写 spec,灵活度最高,工作量也最大 |
下文逐一展开。其中示例应用使用仓库自带的 Touch Tracer 演示程序,其源码位于 examples/demo/touchtracer/main.py,配套的 touchtracer.kv 与 particle.png 会在打包时一并收集,是贯穿全文的实战对象。
方案一:使用 Kivy SDK(推荐)
Kivy 官方发布一个名为Kivy.app的 DMG 安装包,其中内置了一个虚拟环境:既包含完整的 Python 解释器,也包含 SDL、GStreamer 等全部二进制依赖,可直接作为打包 Kivy 应用的基础。
这是最安全的打包方式,原因在于:
- 打包出的应用只引用 DMG 内的 framework 或二进制,不包含任何对打包机器上系统二进制文件的引用;
- 与之相对,PyInstaller 会从本地 Python 安装中复制二进制,产物容易受打包机环境影响。
使用该方案时请注意两个版本前提:
- 该方式仅适用于 Kivy v2.0.0 及之后版本;
- Kivy.app 以
MACOSX_DEPLOYMENT_TARGET=10.9构建,即默认支持 macOS 10.9 及以上系统。值得注意的是,当前仓库的 tools/build_macos_dependencies.sh 中,依赖构建脚本已使用MACOSX_DEPLOYMENT_TARGET=10.15调用xcodebuild,同时 changelog.rst 也记录了 Xcode 14.3 在MACOSX_DEPLOYMENT_TARGET < 10.13时无法构建 SDL 的兼容性问题——若你自行构建依赖,建议按新脚本的部署目标执行。
具体操作分两条路线:一是直接基于已安装的 Kivy.app 打包,二是从零开始构建。两种方式的完整指令均记录在官方维护的kivy-sdk-packager 仓库(osx目录)的 README 中,文档只给出指引,不重复贴出全部命令。
方案二:使用 Buildozer 一键打包
Buildozer 是 Kivy 官方提供的跨平台打包工具,其 macOS 支持流程如下:
pip install git+http://github.com/kivy/buildozer cd /to/where/I/Want/to/package buildozer initbuildozer init会在当前目录生成buildozer.spec配置文件。仓库中的 examples/audio/buildozer.spec 是一份完整的参考示例,其中与 macOS 打包直接相关的关键项包括:
[app] # (str) Title of your application title = Audio Example # (str) Package name package.name = audio # (str) Source code where the main.py live source.dir = . # (list) Source files to include (let empty to include all the files) source.include_exts = py,png,jpg,kv,atlas,wav # (list) Application requirements # comma separated e.g. requirements = sqlite3,kivy requirements = kivy编辑 spec 文件时,请根据你的应用填写标题、包名、源码目录,并在requirements=一节追加额外依赖(例如requirements = kivy,pygame)。
关于requirements有两个值得注意的默认行为:
- 默认情况下,
requirements中指定的kivy 版本号会被忽略; - 打包时,Buildozer 优先使用位于
/Applications/Kivy.app的本地 Kivy.app;如果该目录不存在,则自动下载 kivy.org 上基于 Kivy master 分支的最新构建。
配置完成后,执行打包命令:
buildozer osx debug打包完成后,如果希望减小体积,可以手动移除应用运行所不需要的多余包,将产物精简到最小可用状态。
Buildozer 目前底层仍然调用 Kivy SDK 完成打包。如果你需要比 Buildozer 现有选项更精细的控制,可直接使用 SDK 方式(见方案一)。
方案三:PyInstaller + Homebrew 完整指南
当需要精细控制打包行为时,PyInstaller 是更灵活的选择。官方文档给出了一条经过验证的完整路线。
重要原则:请在你希望支持的最低 macOS 版本上进行打包,这样产物的兼容范围最广。
1. 安装 Homebrew 与 Python
首先安装 Homebrew(macOS 包管理器),然后安装 Python:
$ brew install python若要使用 Python 3,请执行
brew install python3,并将下文所有pip替换为pip3。
2. 从源码重装依赖
为了让产物可以在其他机器上运行,需要确保依赖二进制不以链接形式引用本机 Homebrew 的库,因此要用--build-from-source重新安装 SDL 系依赖:
$ brew reinstall --build-from-source sdl3 sdl3_image sdl3_ttf sdl3_mixer如果项目还依赖 GStreamer 或其他附加库,同样需要用
--build-from-source重装,详见下文"附加库"一节。
3. 安装 Cython 与 Kivy
$ pip install Cython $ pip install -U kivyCython是 Kivy 的编译依赖(Kivy 大量使用 Cython 编写扩展模块),必须先于 Kivy 安装;-U确保升级到最新版本。
4. 安装 PyInstaller
$ pip install -U pyinstaller5. 打包应用
使用pyinstaller直接指向应用的main.py:
$ pyinstaller -y --clean --windowed --name touchtracer \ --exclude-module _tkinter \ --exclude-module Tkinter \ --exclude-module enchant \ --exclude-module twisted \ /usr/local/share/kivy-examples/demo/touchtracer/main.py各参数含义如下:
| 参数 | 作用 |
|---|---|
-y | 覆盖输出目录中已有文件 |
--clean | 打包前清理缓存 |
--windowed | 不弹出终端窗口,适合 GUI 应用 |
--name touchtracer | 指定应用名 |
--exclude-module ... | 显式排除 Kivy 用不到的模块,减小体积 |
被排除的_tkinter/Tkinter(Tk GUI)、enchant(拼写检查)、twisted(异步框架)都是 Kivy 应用常见的冗余依赖。当前仓库的 kivy/tools/packaging/pyinstaller_hooks/init.py 中同样定义了excludedimports = ['tkinter', '_tkinter', 'twisted'],与这里的命令行排除逻辑相互印证。
注意:以上命令还不会复制图片、声音等附加资源文件,这一步需要在生成的
.spec文件中手动补充。
6. 编辑 spec 文件
执行上述命令后,当前目录会生成touchtracer.spec。需要修改其中的COLLECT()调用,把 Touch Tracer 的数据文件(touchtracer.kv、particle.png等)加入最终包。方法是在COLLECT中增加一个Tree()对象——它会递归搜索并打包指定目录下的所有文件:
coll = COLLECT(exe, Tree('/usr/local/share/kivy-examples/demo/touchtracer/'), a.binaries, a.zipfiles, a.datas, strip=None, upx=True, name='touchtracer')Tree()的路径应替换为实际示例目录;对你自己的应用而言,就是存放.kv、图片、音频等资源的目录。Tree会递归包含全部文件,因此要确认该目录下没有不想发布的敏感文件。
7. 构建 spec 并生成 DMG
$ pyinstaller -y --clean --windowed touchtracer.spec构建完成后进入dist目录,用 macOS 自带的hdiutil把.app封装成 DMG 镜像:
$ pushd dist $ hdiutil create ./Touchtracer.dmg -srcfolder touchtracer.app -ov $ popd-srcfolder touchtracer.app:指定被封装的应用;-ov:允许覆盖已存在的同名 DMG。
完成后,dist目录下就会出现Touchtracer.dmg,可直接分发安装。
附加库:GStreamer
如果项目依赖 GStreamer(如视频播放),需要以源码方式重装相关组件:
$ brew reinstall --build-from-source gstreamer gst-plugins-{base,good,bad,ugly}若项目需要 Ogg Vorbis 支持,请在上述命令中追加
--with-libvorbis选项。
此外,如果你使用的是 Homebrew 提供的 Python,在官方 Homebrew formula 合入相应改动之前,还需要手动安装带--with-python的gst-pythonformula。
GStreamer 与打包的关联在源码中也有体现:pyi_rth_kivy.py(见 kivy/tools/packaging/pyinstaller_hooks/pyi_rth_kivy.py)会在运行时把sys._MEIPASS与gst-plugins子目录写入GST_PLUGIN_PATH,并把GST_REGISTRY重定向到包内,确保解包后的应用能找到 GStreamer 插件;而 hook-kivy.py 对应的__init__.py中,_find_gst_binaries()会通过gst-inspect-1.0探测插件路径,收集libgst*插件及其依赖库作为binaries传入Analysis。
方案四:不使用 Homebrew 的 PyInstaller 手写 spec
如果你不希望依赖 Homebrew(例如已按 Kivy 官方"开发版"安装指南自行编译了 Kivy 及其依赖),可以完全手写 spec 文件。官方文档以testpackaging目录为例:
cd testpackaging git clone https://github.com/pyinstaller/pyinstaller在该目录创建touchtracer.spec,写入以下内容:
# -*- mode: python -*- block_cipher = None from kivy.tools.packaging.pyinstaller_hooks import get_deps_all, hookspath, runtime_hooks a = Analysis(['/path/to/yout/folder/containing/examples/demo/touchtracer/main.py'], pathex=['/path/to/yout/folder/containing/testpackaging'], binaries=None, win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, hookspath=hookspath(), runtime_hooks=runtime_hooks(), **get_deps_all()) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, exclude_binaries=True, name='touchtracer', debug=False, strip=False, upx=True, console=False ) coll = COLLECT(exe, Tree('../kivy/examples/demo/touchtracer/'), Tree('/Library/Frameworks/SDL3_ttf.framework/Versions/A/Frameworks/FreeType.framework'), a.binaries, a.zipfiles, a.datas, strip=False, upx=True, name='touchtracer') app = BUNDLE(coll, name='touchtracer.app', icon=None, bundle_identifier=None)使用前必须把以下路径替换为你的实际路径:
Analysis中的主脚本路径/path/to/yout/folder/containing/examples/demo/touchtracer/main.py;pathex中的工程目录/path/to/yout/folder/containing/testpackaging;COLLECT中Tree('../kivy/examples/demo/touchtracer/')的资源目录;Tree()中 FreeType framework 的路径(该路径随你的 SDL_ttf 安装位置而定)。
该 spec 的核心是利用 Kivy 官方提供的 PyInstaller 辅助模块(见 kivy/tools/packaging/pyinstaller_hooks/init.py):
hookspath():返回包含 Kivy 自定义 hook(hook-kivy.py)的目录,供Analysis使用;runtime_hooks():返回 Kivy 运行时 hook(pyi_rth_kivy.py)路径,负责在启动时设置KIVY_DATA_DIR、KIVY_MODULES_DIR、GST_PLUGIN_PATH等环境变量;get_deps_all():返回所有可能被间接导入的 Kivy 模块(含全部 core provider)、GStreamer 二进制与排除项,以字典形式展开为Analysis的hiddenimports/excludes/binaries参数。
随后执行:
pyinstaller/pyinstaller.py touchtracer.spec将touchtracer替换为你的应用名即可。完成后,dist/目录下会出现<yourapp>.app。注意BUNDLE()中的icon=None表示暂未设置图标,如需自定义应用图标可在此指定.icns文件。
源码级补充:理解 Kivy 的 PyInstaller hook 机制
方案四中使用的get_deps_all()只是冰山一角。Kivy 的 pyinstaller hooks 模块还提供了更精细的控制能力,理解它们有助于你进一步压缩打包体积、规避缺模块问题。
get_deps_minimal:按 core 模块裁剪 provider
与get_deps_all()打包"所有 provider"不同,get_deps_minimal(exclude_ignored=True, **kwargs)允许你按核心模块粒度控制打包内容。其关键字参数对应 Kivy 的 core 模块:audio、camera、clipboard、image、spelling、text、video、window,取值规则:
| 取值 | 行为 |
|---|---|
True(默认) | 包含本系统当前加载的 provider |
None | 完全排除该 core 模块(exclude_ignored=True时还会加入 excludes,防止被意外带入) |
| 字符串或字符串列表 | 只包含指定的 provider,如audio=['gstplayer', 'ffpyplayer']、spelling='enchant' |
官方示例:
a = Analysis(['..\\kivy\\examples\\demo\\touchtracer\\main.py'], ... hookspath=hookspath(), runtime_hooks=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, **get_deps_minimal(video=None, audio=None))为什么需要 hiddenimports
PyInstaller 通过静态分析 import 语句收集依赖,但 Kivy 的大量核心模块(如视频 provider)是通过__import__等方式间接导入的,PyInstaller 无法感知,必须通过hiddenimports显式声明。get_deps_all()/get_deps_minimal()返回的字典正是为此服务的。
覆盖默认 hook
PyInstaller 自带一个 Kivy hook,它会列出所有 provider 作为 hidden imports,导致体积偏大。你可以通过hookspath()指向仓库内置的替代 hook(kivy/tools/packaging/pyinstaller_hooks/hook-kivy.py),它只保留get_factory_modules()(所有注册进 Kivy Factory 的模块)与基础kivy_modules,把 provider 的取舍完全交给get_deps_minimal()/get_deps_all()决定。也可以在命令行加--additional-hooks-dir=HOOKSPATH覆盖默认 hook 中的hiddenimports、excludedimports全局变量。
生成自定义 hook 清单
如果想手动逐个勾选 provider,可以借助模块自带的生成器:
python -m kivy.tools.packaging.pyinstaller_hooks hook filename该命令(实现见 kivy/tools/packaging/pyinstaller_hooks/main.py)会把get_deps_all()得到的全部模块以列表形式写入filename指定的 hook 文件(不传filename则直接打印到终端),你只需注释掉不需要的 provider 即可。
打包注意事项汇总
- 在最低支持的 macOS 版本上打包:PyInstaller 产物通常向下兼容有限,选择过新的构建系统可能使旧系统用户无法运行;
- 源码编译依赖:使用 Homebrew 方案时务必对 SDL、GStreamer 等执行
--build-from-source重装,否则产物会携带对打包机 Homebrew 库的路径引用; - 资源文件不会自动收集:图片、音频、
.kv文件必须通过 spec 中的Tree()或datas显式加入; - GStreamer 需要额外配置:依赖视频播放时,除重装
gstreamer及 plugins 外,运行时还需GST_PLUGIN_PATH等环境变量配合(Kivy 的 runtime hook 已自动处理); - Kivy SDK 方案无需关心上述二进制细节:DMG 内虚拟环境已包含全部依赖,这也是官方推荐它的根本原因;
- 版本前提:Kivy SDK 打包方式仅适用于 Kivy v2.0.0 及以上,且 Kivy.app 基于
MACOSX_DEPLOYMENT_TARGET=10.9构建。
至此,你可以根据项目对"稳定性"与"可控性"的需求,在 Kivy SDK、Buildozer 与 PyInstaller 之间做出选择,并依据本文的完整命令与 spec 模板,在 macOS 上产出可分发的.app与 DMG。
- 跨平台
- 移动开发
- 桌面应用
- UI组件
【免费下载链接】kivy
Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS
相关推荐
Kivy Buildozer终极指南:一键打包Python移动应用
Kivy Buildozer终极指南:一键打包Python移动应用 Kivy Buildozer是Python开发者将应用部署到Android和iOS平台的终极
开发工具移动开发Kivy/Buildozer 跨平台应用打包工具安装指南
Kivy/Buildozer 跨平台应用打包工具安装指南 工具简介 Buildozer 是一个强大的自动化工具,专门用于将 Python 应用打包为移动平台的原
开发工具移动开发Kivy Buildozer终极指南:简单快速的跨平台应用打包方案
Kivy Buildozer终极指南:简单快速的跨平台应用打包方案 Kivy Buildozer是Python开发者构建跨平台应用的终极工具,能够将Python
开发工具移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考