Slint 背景知识
Slint 是一个 GUI 工具包,可以高效地为任何显示器开发流畅的图形用户界面:嵌入式设备和桌面应用程序。支持多种编程语言,例如 Rust 、 C ++或 JavaScript 。 Slint 作为开源商业化产品,取得了很大的进展。
2022年 slint 发展回顾:
- 将SixFPS 改名为 slint ,定义了品牌。
- 在2022第一季度被公认为增长最快的 OSS 初创公司之一,年增长率为470%!
- slint 的免费专有许可证开始受到关注:已有15位大使正在使用此免费许可证构建他们的项目
- 参加嵌入式世界展览和会议,在那里展示了 Slint 在各种嵌入式设备上的强大功能,包括功率非常低的 Raspberry Pi Pico
- 与多家设计和服务公司建立了合作伙伴关系计划
- 获得了六位数的种子资金来扩大团队:用于产品开发、销售和业务发展.作为白银会员加入 Rust 基金会
- 参加了 EuroRust 2022会议, Tobias 在会上介绍了 Rust 和 C ++的互操作性
- Florian 在将 Slint 移植到 Redox OS 方面取得了惊人的进展,一些实用程序已经基于 Slint
- 与 PopOS 合作,使 Slint 成为开发人员的替代工具包
2023 年 Slint 1.0 正式版发布,标志着项目已顺利从开发阶段“毕业”,可正式用于生产环境。
Slint 创始团队都是来自于 Qt 社区(使用 Slint 有没有是曾相识的感觉)。
注:全球知名 Qt 咨询和 UI/UX 设计服务公司 tQCS 的合作伙伴有两家都加入了 Rust 基金会银牌会员(QT 的两个好逆子🐶🐱,既合作又竞争⛽️,目标指向蚕食 Qt 市场 🏴☠️,真是“父慈子孝”,“兄恭弟让”😂)。分别是:
- Slint: 极大地简化了取代 Qt 需求的嵌入式平台的 GUI 开发。支持 Rust/Cpp/Javascript ,有设计友好的 UI 标记语言。其创始人同样来自 Qt 项目主要贡献者,QtQml 引擎的主要开发者。
- KDAB :在嵌入式系统、3D 图形以及跨桌面、嵌入式和移动平台的工作方面拥有多年经验, KDAB( Slint 的竞争对手/合作伙伴) 是 Qt 项目的主要贡献者。他们正在研究 CXX-Qt 以使 Qt 和 Rust 更容易地一起使用。CXX-Qt 可用于使用 CMake 将 Rust 集成到 C ++应用程序中,或用于使用 Cargo 构建 Rust 应用程序,支持在 C ++、 QML 和 JavaScript 中使用。
目前 CXX - Qt 处于早期开发阶段, API 经常更改,可以参考 CXX-Qt book 来了解更多,而 Slint 发展迅猛, Slint 1.0 正式版已于2023年正式发布。
Slint 使用了声明式编程来简化 UI 的开发,优化应用程序开发和性能的方法是:
- 用声明式语言来描述 UI,使用的语法提供了一种广泛的方式来描述各种图形元素,同时易于阅读、编写和学习
- Slint 编译器对描述 UI 的代码进行优化并翻译成原生代码
- 采用任何语言编写的业务逻辑,可通过使用 Slint 提供的特定于语言的 API 与 UI 连接
Slint 整体架构
Slint 控件支持
Slint 的控件还是比较丰富的,基本够用了,官方提供的控件分为需要从"std-widgets.slint"文件导入(import)的小部件(Widgets)和不需要导入直接可用的内置部分(Builtin Elements And Enums),它们的属性、用途、以及使用方式可以查阅官方文档,非常简单(声明式编程都大同小异,通过属性控制显示行为,比如通用的位置xyz和布局相关的宽高、spacing、padding、alignment ,及背景,动画等),不用强行记忆,看几遍文档有个大概印象,大多数控件和属性都是"观其名,就知其大体用途",用到时候查阅具体如何使用即可:
- Positioning and Layout (位置和布局): slint-ui.com/releases/1.…
- Builtin Elements(内置组件): slint-ui.com/releases/1.…
- Builtin Enums(内置枚举): slint-ui.com/releases/1.…
- Widgets(需导入小部件): slint-ui.com/releases/1.…
Slint GUI 应用程序示例:一个简易计算器
一视频胜过千言万语(一定要看完视频,视频才是本文分享的重点),视频中,博主 Live coding 使用 Rust 和 Slint GUI 套件制作一个小型 GUI 应用程序:一个简易计算器,通过一步一步 Live coding 来示范开发一个简易计算器应用程序,学习如何使用 Slint 制作一些简单 GUI 程序的全过程,包括 Slint 的自定义组件,组件的属性和使用,全局单例和回调 ,Rust 代码和 Slint 交互,可以跟着博主思路和操作自己敲一遍代码,可能也许一定会有不一样的收获。
注:无法观看Youtube视频的同学,可以这里看完整视频mp.weixin.qq.com/s/ZmnG4Cu4R…,我给视频添加了中英文字幕,当然中文字幕是机译的,有些地方可能翻译不太准确,读者见谅,不过视频中操作和代码,即使不用字幕大体都能看懂(掘金不能上传视频😓)
完整代码:
创建一个新的 cargo 项目:
cargo new slint-calculator
cd slint-calculator
可以命令行添加 slint 的依赖项:
cargo add slint@1.0.0
最终 Cargo.toml 文件显示
[package]
name = "slint-calculator"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
slint = "1.0.0"
main.rs 中添加代码:
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
// 创建窗体应用程序
let app = App::new().unwrap();
let weak = app.as_weak();// as_weak避免内存泄露
let state: Rc<RefCell<CalcState>> = Rc::new(RefCell::new(CalcState::default()));
// 处理.slint中 CalcLogic 全局单例的回调
app.global::<CalcLogic>().on_button_pressed(move |value| {
let app = weak.unwrap();
let mut state = state.borrow_mut();
println!("pressed value: {}", value);
// 只处理输入的数字,保存到 state 中
if let Ok(val) = value.parse::<i32>() {
state.current_value *=10;
state.current_value += val;
app.set_value(state.current_value);
return;
}
// 处理等号"="逻辑
if value.as_str() == "=" {
let reslut = match state.operator.as_str() {
"+" => state.prev_value + state.current_value,
"-" => state.prev_value - state.current_value,
"*" => state.prev_value * state.current_value,
"/" => state.prev_value / state.current_value,
_=> state.current_value,
};
// 输出结果
app.set_value(reslut);
println!("{} {} {} = {}", state.prev_value, state.operator, state.current_value, reslut);
// 重置 state
state.current_value = 0;
state.prev_value = reslut;
state.operator = Default::default();
} else {
state.operator = value.clone();
state.prev_value = state.current_value;
state.current_value = 0;
}
});
// 运行窗体程序
app.run().unwrap();
println!("Hello, world! Hello, Slint!");
}
// 保存输入数据的结构体
#[derive(Default)]
struct CalcState {
prev_value: i32,
current_value: i32,
operator:slint::SharedString,
}
// Slint 宏构建 UI
slint::slint! {
import { VerticalBox } from "std-widgets.slint";
// 导出全局单例:Rust 代码可以操作
export global CalcLogic {
callback button-pressed(string);
}
// 自定义 Button 组件
component Button {
in property <string> text;
min-height:30px;
min-width: 30px;
in property <brush> background: @linear-gradient(-20deg, #a0a3e4, #3c58e3);
Rectangle {
background: ta.pressed ? red :ta.has-hover? background.darker(10%) : background;
animate background {duration: 100ms; }
border-radius: 4px;
border-width: 2px;
border-color: self.background.darker(20%);
ta := TouchArea {
// Button初始化
init => { debug("Button init"); }
// Button点击事件,回传给 Rust 处理
clicked => {
debug("Button clicked");
CalcLogic.button-pressed(root.text)
}
}
Text {text: root.text;}
}
}
export component App inherits Window {
title: "Slint Calculator";
in property <int> value: 0 ;
// Slint 内嵌的网格组件
GridLayout {
padding: 10px;
spacing: 5px;
Text {text: value; colspan: 3;}
Row {
Button {text: "1";}
Button {text: "2";}
Button {text: "3";}
Button {text: "+"; background: yellow;}
}
Row {
Button {text: "4";}
Button {text: "5";}
Button {text: "6";}
Button {text: "-"; background: yellow;}
}
Row {
Button {text: "7";}
Button {text: "8";}
Button {text: "9";}
Button {text: "*"; background: yellow;}
}
Row {
Button {text: "0";}
Button {text: "="; col: 2; background: green;}
Button {text: "/"; background: yellow;}
}
}
}
}
执行命令:cargo run,运行效果如下图(注:以上所有程序开发均在vs code下完成,依赖slint官方插件)
视频和代码只是学习 Slint + Rust 的一个简单示例,目的是通过开发一个应用程序的来学习 Slint 相关知识,当然这只是一个简易版的计算器,自己可以按照真实版计算器来继续完善功能,让它更像一个完整的计算器。