关于 developing-gtk-apps
面向使用 Vala、Python 或 Rust 开发 GTK 4/libadwaita 应用的技能,侧重应用架构、生命周期、线程与资源管理,并提供语言专属参考资料。
主要功能
- 01Vala、Python 和 Rust 专属指南
- 02应用生命周期与线程指导
- 03GSettings 和操作模式
- 04打包、测试与调试参考
- 05Wayland 和 GNOME 集成指导
适用场景
- 01启动 GTK 4 应用开发
- 02排查启动崩溃或界面卡顿
- 03使用 GSettings 保存偏好设置
- 04为 Flatpak 打包准备 GTK 应用
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mhagrelius/ dotfiles --skill developing-gtk-apps安装后在 Claude Code 中输入/developing-gtk-apps确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
gnome-shell-companion-reference.mdgtk-dbus-reference.mdgtk-debugging-reference.mdgtk-gobject-reference.mdgtk-i18n-reference.mdgtk-packaging-reference.mdgtk-patterns-reference.mdgtk-testing-reference.mdpython-reference.mdrust-reference.mdvala-reference.md详细介绍
开发 GTK 应用
使用正确的架构、生命周期和模式,构建健壮的 GTK 4/libadwaita 应用。
核心原则: 先打好基础,再构建 UI。应用生命周期、线程模型和资源管理是大多数 GTK 应用出问题的根源。
与 UI 技能的关系: 本技能负责架构和基础设施。有关控件选择、布局和 HIG 合规性,请使用 designing-gnome-ui。
首先选择语言分支
每次运行都属于 Vala、Python 或 Rust 分支。根据项目检测语言——.vala 源文件或 project() 中包含 'vala' 表示 Vala;包含 gi 导入的 .py 源文件表示 Python;包含 gtk4 依赖的 Cargo.toml 或 .rs 源文件表示 Rust;对于新项目,使用用户指定的语言——然后在编写任何代码前打开对应的分支文件。所有代码级问题(语法、样板代码、应调用哪个函数)都应在对应文件中解答,而不是在此处:
| 语言 | 打开 | 涵盖内容 |
|---|---|---|
| Vala | vala-reference.md | 样板代码、所有权(owned/unowned/weak)、信号、属性、列表模型/ListView、异步/线程、模板、Meson、VAPI 验证 |
| Python | python-reference.md | 样板代码、GLib.idle_add、操作、GSettings,以及更深入的 Python 参考资料指引 |
| Rust | rust-reference.md | 样板代码、子类化(mod imp/wrapper)、clone!、属性、信号、列表模型/ListView、异步/线程、模板、构建、crate 源码验证、按错误文本索引的编译陷阱 |
如果项目完全通过代码构建控件树(仓库中没有 .ui 文件或 .blp 文件——这是常见的项目规范),请沿用这种方式:跳过模板/Blueprint/GResource 内容,手动构建控件。
子代理: 子代理不会继承此技能。将 GTK 工作委派给子代理时,提示词必须告知它在编写代码前调用 developing-gtk-apps(涉及 UI 决策时还要调用 designing-gnome-ui)。
本文件其余部分与语言无关:其中的规则适用于所有语言。
决策流程
| 任务 | 使用 |
|---|---|
| 设置界面该用哪个控件? | designing-gnome-ui |
| 如何组织偏好设置窗口? | designing-gnome-ui |
| 修复现有 GTK 应用的功能或行为 | 本技能(先选语言分支) |
| gtk-rs 调用编译失败 | 本技能(rust-reference.md 的 Compile Traps) |
| 应用启动时崩溃 | 本技能 |
| 操作期间 UI 冻结 | 本技能 |
| 如何保存用户偏好设置 | 本技能(GSettings) |
| 信号未触发/内存泄漏 | 本技能 |
| 配置新应用的样板代码 | 本技能 |
| 为 Flatpak 打包 | 本技能 |
| 应用无法定位/抬升自己的窗口 | gnome-shell-companion-reference.md |
| 需要全局快捷键,或要从程序坞隐藏 | gnome-shell-companion-reference.md |
当前情况(libadwaita 1.7+、GTK 4.18+)
替代项——使用当前 API:
AdwShortcutsDialog替代GtkShortcutsWindow(libadwaita 1.8+).dimmedCSS 类替代.dim-label- 在 libadwaita 应用中,
AdwSpinner替代GtkSpinner Widget::compute_bounds()替代allocation()(GTK 4.12 中已弃用;使用-D warnings的 clippy 会拒绝它)- 以 Wayland 为目标;X11/Broadway 后端在 GTK 4 中已弃用(计划在 GTK 5 中移除),GNOME 49+ 不再提供 X11 会话作为回退选项
Wayland 明确禁止的操作——设计时请先确认:客户端不能定位自己的窗口、读取窗口位置、将窗口提升到其他窗口之上、获取全局快捷键,或从程序坞隐藏。每项操作都需要配套的 GNOME Shell 扩展;请参阅 gnome-shell-companion-reference.md。
新增控件(libadwaita 1.6–1.8,均已包含在 1.9 API 中):
AdwToggleGroup- 用于一组互斥切换项的单个控件AdwBottomSheet- 持久显示的底部面板AdwWrapBox- 可将子项换行到新行的盒式容器AdwInlineViewSwitcher- 在卡片、侧边栏和盒式列表中切换视图
应用 ID 规则
| 规则 | 示例 |
|---|---|
| 反向域名表示法 | com.example.MyApp |
| 仅包含字母数字和点 | org.gnome.TextEditor |
| 至少 2 个段 | com.myapp(而非 myapp) |
| 与桌面文件匹配 | com.example.MyApp.desktop |
生命周期
| 信号 | 时机 | 用途 |
|---|---|---|
startup | 应用启动时执行一次 | 操作、CSS、GSettings |
activate | 每次启动或唤起时 | 创建/显示窗口 |
shutdown | 应用退出时 | 保存状态、清理 |
open | 向应用传入文件时 | 处理文件参数 |
重写 startup 时,先调用父类实现,再进行自己的设置——工具包会在父类处理程序中初始化自身;若未执行此步骤便创建窗口,窗口将无法正常工作。activate 会在已有窗口打开时复用该窗口,否则创建一个窗口,因此再次启动会唤起正在运行的实例。
线程
GTK 是单线程的:每次对控件的调用都必须在主循环所属的线程中执行。工作线程通过在主循环上调度空闲回调来传回结果(确切调用见你的分支文件)。优先使用 Gio 的异步 I/O,而非线程——它在主循环上运行,无需交接;仅将线程用于 CPU 密集型工作。
操作
操作将 UI 与行为连接起来;菜单和键盘快捷键通过详细名称调用操作。在 startup 中注册应用级操作(app.name),在构建窗口时注册窗口级操作(win.name),并将快捷键绑定到详细名称(app.quit → <Control>q)。在你的分支文件中编写代码;有状态/带参数的操作和菜单连接见 gtk-patterns-reference.md。
GSettings
用户偏好通过 GSettings 持久化,并由已编译的架构提供支持。将键直接绑定到对象属性,使二者无需处理程序代码即可同步;只有在需要对绑定属性之外的变化作出响应时,才订阅 changed::key。在你的分支文件中编写代码;架构 XML 和安装方式见 gtk-patterns-reference.md。
调试(快速参考)
GTK_DEBUG=interactive myapp # Open GTK Inspector (Ctrl+Shift+D)
G_MESSAGES_DEBUG=all myapp # Show all debug messages
G_DEBUG=fatal-criticals myapp # Abort on critical warnings
GSETTINGS_BACKEND=memory myapp # Test without persisting settings
完整的调试模式、性能分析和 GDB 内容见:gtk-debugging-reference.md。
完成标准
当你编写的代码满足以下所有检查项时,基础工作便已完成:
- 工作线程只能通过主循环上的空闲回调操作控件。
- 重写的
startup会先调用父类实现。 - 连接到生命周期更长对象(应用、设置、模型)的每个处理程序,都会在控件关闭时断开连接。
- 信号处理程序会迅速返回;耗时工作通过异步方式或工作线程运行。
- 用户文件存放在 XDG 目录下(GLib 的用户数据/配置/缓存辅助函数)。
- 应用 ID 使用反向域名格式,并与桌面文件匹配。
- 新代码使用“当前推荐”中的控件。
- 对 Wayland 禁止的功能,其控件会被禁用并附有说明,而不是在没有任何效果时仍处于锁定状态。
- (Rust)不会跨越可能重新进入对象的回调持有
RefCell借用。 journalctl --user _COMM=<app>会显示应用自身的警告,而不是-- No entries --。- (Vala)每个代码片段都通过了
vala-reference.md中的验证后编译检查。 - (Rust)每个代码片段都通过了
rust-reference.md中的验证后运行cargo check检查。
参考文件
| 需求 | 文件 |
|---|---|
| 所有 Vala 代码:样板代码、所有权、信号、列表模型、异步、模板、Meson、VAPI | vala-reference.md |
| 所有 Python 代码:样板代码、线程、操作、GSettings | python-reference.md |
所有 Rust 代码:样板代码、子类化、clone!、属性、信号、列表模型、异步、模板、构建、编译错误诊断 | rust-reference.md |
| 深入了解 GObject,仅限 Python:类、属性、信号、列表模型、工厂 | gtk-gobject-reference.md |
| 有状态操作、GSettings 架构、GResource、Blueprint、异步文件操作 | gtk-patterns-reference.md |
| 桌面文件、AppStream 元数据、Meson 安装、Flatpak、图标 | gtk-packaging-reference.md |
| 配套的 GNOME Shell 扩展:窗口放置、全局快捷键、Wayland 禁止的操作 | gnome-shell-companion-reference.md |
| 测试——pytest(Python)和 GLib.Test/meson test(Vala)、无头环境/CI | gtk-testing-reference.md |
| 国际化:gettext、复数形式、.po 文件、Blueprint i18n、RTL | gtk-i18n-reference.md |
| DBus 激活、接口导出、后台服务、Flatpak 门户 | gtk-dbus-reference.md |
| GTK Inspector、环境变量、性能分析、内存调试、让日志易于访问 | gtk-debugging-reference.md |
| UI 模式、组件、HIG | 使用 designing-gnome-ui 技能 |
外部参考
- GTK 4 API
- Libadwaita API
- Valadoc(GTK/GLib/Adw 的 Vala 绑定)
- gtk4-rs 文档(Rust 绑定;将 docs.rs 固定到
Cargo.toml中的 crate 版本) - Blueprint 文档