读项目NeteaseCloudMusicGtk4
netease-cloud-music-gtk4 是基于 GTK4 + Libadwaita 构造的网易云音乐播放器,专为 Linux 系统打造,已在 openSUSE Tumbleweed + GNOME 环境下测试。
如何将项目运行起来
我们要读一个程序的代码,不管它怎么样,先跑起来再说。这个程序使用的是 GTK4 + Libadwaita,是 Linux 桌面常用的 GUI 库。这个程序没有在 Window 上构建的说明,并且可能是只能在 Linux 上正常编译,因此我将在 Ubuntu 上尝试运行这个程序。
运行一个使用 GTK 的项目我尝试了两种方法:
- 使用 IDE(GNOME Builder)
- 使用文本编辑器(VS Code)
On GNOME Builder
通过 flatpak 安装 GNOME Builder.
flatpak install flathub org.gnome.Builder
启动后选择克隆仓库(Clone Repository)即可下载并打开项目。

这个项目中已经有了 flatpak 的描述文件,GNOME Builder 能够通过com.gitee.gmg137.NeteaseCloudMusicGtk4.json 文件自动安装需要的依赖。
要启用调试信息需要在 meson.build 中指定 buildtype=debug,这样 GNOME Builder 才会在编译的时候加上调试信息,打的断点才会有效。
GNOME Builder 用起来不是很习惯,很多使用 GTK 的人可能是使用 Vim 或者 VSCode.
On VSCode
尽管 VSCode 也有 flatpak 的插件,但是我尝试了没有成功。那不使用 GNOME Builder,也可以不使用 flatpak。不使用 flatpak 需要自己安装依赖:
sudo apt install -y libssl-dev libgtk-4-dev libdbus-glib-1-dev libadwaita-1-dev libgstreamer-plugins-bad1.0-dev gettext
这个项目使用 Rust 语言开发,对于 rustc 我推荐使用 Rust 官网提供的 rustup 安装。项目使用了 meson 作为构建工具,通过 pip 安装最新的 meson 和 ninja。
下载源码,编译,安装。
git clone https://github.com/gmg137/netease-cloud-music-gtk.git
cd netease-cloud-music-gtk
meson setup build
cd build
ninja install
现在通过 VSCode 打开 netease-cloud-music-gtk 就可以通过 VSCode 的 Rust 插件来运行和调试代码了。
为什么需要安装?因为 GTK 程序使用的资源和 UI 文件必须被编译被安装才能被程序访问到,换言之,修改 ui 文件后就要重新安装。
也可以不安装,但是需要修改现在的加载资源的方式,参考GUI development with Rust and GTK 4
程序的初始化
在 VSCode 中配置好后,就可以点击 main 函数上的 run 或者 debug 来启动程序了。一定要通过 meson 安装资源才能在不改动原来的代码的情况下正常启动程序。
整个程序都被封装在 NeteaseCloudMusicGtk4Application 内,main() 函数只做了各种初始化。程序激活后程序才会创建应用的主窗口,所有的其他的页面和组件都在主窗口内去创建和处理。整个应用程序的 activate() 只将窗口创建出来,然后设置一个循环等待消息。
fn activate(&self) {
let obj = self.obj();
let app = obj
.downcast_ref::<super::NeteaseCloudMusicGtk4Application>()
.unwrap();
if let Some(weak_window) = self.window.get() {
weak_window.upgrade().unwrap().present();
return;
}
let window = app.create_window();
let _ = self.window.set(window.downgrade());
// Setup action channel
let receiver = self.receiver.borrow_mut().take().unwrap();
MAINCONTEXT.spawn_local_with_priority(
Priority::HIGH,
clone!(@strong app => async move {
while let Ok(action) = receiver.recv().await {
app.process_action(action);
}
}),
);
// Ask the window manager/compositor to present the window
window.present();
}
在 Rust and GTK4 那本书中介绍了异步函数之间的通信使用的是 async_channel。要在普通的代码中使用异步函数就要有一个 runtime 来 spawn 异步块,MAINCONTEXT 也是 Glib 提供的用来 spawn 异步块的。
整个程序在创建窗口后就一直等待其他的 Action 的发起,这里的 Action 不是 GTK 库的 Action, 相当于传递消息。process_action() 就是收到传递来的消息,然后做对应的事情。
如何响应用户的操作
GNOME 有一个调试用的工具,提供 Ctrl+Shift+I 可以调出一个类似于 Chrome 里面的开发者工具的 Inspector。这样可以找到对应的组件的名字和id,顺着id搜索代码就能找到注册的回调函数。顺着名字就能找到对应的 ui 文件查看组件的结构。
以下一曲按钮(next_button)为例,因为下一曲按钮放置在一个名为 player_controls 的组件里面,在 palyer_controls.ui 中我们找到下面的代码:
<object class="GtkButton" id="next_button">
<property name="halign">fill</property>
<property name="valign">center</property>
<property name="icon-name">media-skip-forward-symbolic</property>
<signal name="clicked" handler="next_button_clicked_cb" swapped="true" />
<style>
<class name="circular" />
<class name="flat" />
</style>
</object>
这个按钮声明了一个信号处理函数,根据这个 handler 的名字就可以在Rust代码中找到回调函数的定义。
#[template_callback]
fn next_button_clicked_cb(&self) {
let sender = self.sender.get().unwrap().clone();
if let Ok(mut playlist) = self.playlist.lock() {
if let Some(song_info) = playlist.next_song() {
let song_info = song_info.to_owned();
sender
.send_blocking(Action::Play(song_info.to_owned()))
.unwrap();
sender
.send_blocking(Action::UpdateLyrics(song_info))
.unwrap();
sender
.send_blocking(
Action::UpdatePlayListStatus(playlist.get_position()))
.unwrap();
return;
}
}
sender
.send_blocking(Action::AddToast(gettext("No more songs!")))
.unwrap();
}
尽管这看起来有一点复杂,但是其实就是拿到下一首歌曲的信息然后发送3个消息:
- 播放歌曲
- 更新歌词
- 更新播放列表的状态
所有发送的消息最终被 process_action() 接收,process_action 函数内部相当于一个巨大的 switch-case.
按照这样的思路,去调试代码就能找到自己感兴趣的部分了。
总结
读一个项目大概就是这样,运行起来,可以调试。
此前我并没有写过什么 GUI 程序,即便是写过的通常也就是只有一个页面,所以对图形界面程序的页面路由和复杂的状态管理没有什么经验。这个程序当然也没有做的特别复杂。
GTK 这个图形库的学习资料比较少,也有类似 Qt 中的属性、信号的概念,只不过这些都是原生的 C 语言做的,因为 Rust 不支持继承,尽管实际上做同样的事情,但是代码的组织方式却不一样。
从项目的代码来看,能看到作者从 GNOME Builder 生成的 Hello World 模板逐步迭代开发的痕迹,代码当中有多处是代码重复的(很明显的拷贝粘贴),有时候作者采用的方法和 Rust GTK4 书上的例子的做法不一样,当然没有孰优孰劣之分,只是喜好不同。尽管代码在实现上还有一些缺点,但是作为一个可以正常使用且使用 GTK 开发的应用已经很棒了。
读项目NeteaseCloudMusicGtk4的更多相关文章
- my-Life项目开发流程
一:新建java web项目 (懂得使用gradle哦!) 1.http://www.cnblogs.com/xylle/p/5234380.html 2.新建项目后,然后新建module, 如果甲 ...
- [转] 学习React Native必看的几个开源项目
http://www.lcode.org/study-react-native-opensource-one/ http://gold.xitu.io/entry/575f498c128fe10057 ...
- 【转】react-native开发混合App-github开源项目
http://www.lcode.org/study-react-native-opensource-one/ http://gold.xitu.io/entry/575f498c128fe10057 ...
- GitHub 热点速览 Vol.13:近 40k star 计算机论文项目再霸 GitHub Trending 榜
作者:HelloGitHub-小鱼干 摘要:"潮流是个轮回",这句话用来形容上周的 GitHub Trending 最贴切不过.无论是已经获得近 40k 的高星项目 Papers ...
- 读 Linux 像读小说「GitHub 热点速览 v.22.03」
本周特推选取了一个画风有点意思的 Linux 代码带读项目 flash-linux0.11-talk,希望有趣的文风能带你读完 Linux 代码.当然画风可以增加阅读体验,彩色标记也是一种学习方法-- ...
- 如何阅读android framework源码
但如果想深入的了解Android系统, 那么可以看下我的一些简单的总结. 知识 Java Java是AOSP的主要语言之一. 没得说, 必需熟练掌握. 熟练的Android App开发 Linux A ...
- 刨根问底U3D---如何退出Play模式后保留数据更改
实际中遇到的需求 在做一款对抗类游戏,目前正在调整游戏的平衡性 所以就产生了一个需求 希望可以在Play模式时候对数据源做的更改可以在退出时候被保存下来. 举个Case, 比如 有一个炮塔 可以发射子 ...
- 转:微博"收藏/赞/转发"技术资料汇总
书籍 HTTP权威指南 <- @Fenng Introduction to Information Retrieval <- @陈利人 Lua 源码欣赏 <- @简悦云风 The A ...
- Angular v6 正式发布
Angular 6 正式发布 Angular 6 已经正式发布了!这个主要版本并不关注于底层的框架,更多地关注于工具链,以及使 Angular 在未来更容易快速推进. 作为发布的一部分,我们同步了主要 ...
- Angular6.0发布
Angular v6 新版本重点关注工具链以及工具链在 Angular 中的运行速度问题. Angular v6 是统一整体框架.Material 和 CLI 三大 Angular 组件的第一个版本, ...
随机推荐
- 多线程之lamda表达式
代码简化过程 public class TestLambda1 { //3.静态内部类 static class Like2 implements ILike{ ...
- Python类型提示完全指南:用类型安全重构你的代码,提升10倍开发效率
title: Python类型提示完全指南:用类型安全重构你的代码,提升10倍开发效率 date: 2025/2/23 updated: 2025/2/23 author: cmdragon exce ...
- 使用Windows任务计划程序实现每天更换一张Processing创意桌面壁纸
Windows任务计划程序(Windows Task Scheduler)是Windows操作系统中的一项系统工具,它允许用户安排自动执行的任务.通过任务计划程序,用户可以设定特定的时间或条件来运行各 ...
- MyCat分库分表-主从
一.MySQL数据同步 1.主节点配置,log-bin,指定文件名称 2.主节点配置server-id,默认为1 vim /etc/my.cof 在[mysqld]下添加如下配置 log-bin=im ...
- git码云安装及使用菜鸟教程
1.下载Windows本地码云 https://mirrors.huaweicloud.com/git-for-windows/(华为镜像下载),选择合适的版本下载,此处下载速度要快些 2.登录码云官 ...
- 三分钟构建高性能WebSocket服务 | 超优雅的Springboot整合Netty方案
前言 每当使用SpringBoot进行Weboscket开发时,最容易想到的就是spring-boot-starter-websocket(或spring-websocket).它可以让我们使用注解, ...
- [Qt 基础-02] QToolButton
QToolButton 文章目录 QToolButton 简介 1. arrowType 2. autoRaise 3. popupMode 4. toolButtonStyle 注意 信号的连接 样 ...
- 提示词工程——AI应用必不可少的技术
引言 在人工智能技术飞速发展的今天,大语言模型(LLM)已成为推动技术革新的核心引擎.然而,如何让这些"聪明"的模型真正落地业务场景.解决实际问题?答案往往不在于模型本身的参数规模 ...
- SSH登录:WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
错误信息: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICAT ...
- leetcode每日一题:判断一个括号字符串是否有效
题目 一个括号字符串是只由 '(' 和 ')' 组成的 非空 字符串.如果一个字符串满足下面 任意 一个条件,那么它就是有效的: 字符串为 (). 它可以表示为 AB(A 与 B 连接),其中A 和 ...