(该文写于去年8月份,目前iced更新到0.7已有些许API改动,本文内容已有部分错误,但仍有借鉴价值)
写在前面
从5月份起,我一直在寻找一个可用的,稳定的,开发者友好的Rust GUI框架,试图做到All in Rust,即一切的一切都可以靠Rust实现,其中GUI是相当重要的一部分,但找了许久,大部分给我答案是使用tauri
,可我不是一个前端程序员,也没有深入学习前端的想法,故寻找计划只好搁置,直到9月份我发现PopOS团队使用Iced框架制作了发行版的桌面,证明该框架已经得到了生产的认证,于是我开始尝试使用Iced进行GUI的制作。
劝退警告
Iced 作为一款正在高速发展的框架,其API有极大的不确定性,用来自开发团队的话就是
Iced is currently experimental software.Iced moves fast and the master branch can contain breaking changes!
指不定啥时候你平时用着的API就没了,或者一个lib突然出现了极大的变化,导致之前的代码完全推翻,如果你能承受此风险,可以继续往下学习。我在接触这个框架是碰到了如下问题
- 文档和crate实际使用时出现严重的不符合,当我试图在文档中搜索时,文档的结果和实际有较大出入
- 文档和crate和官方示例有极大的出入,体现在一些库成员已经deprecated或者move到了别的子库中,以及rust本身的不断更新导致代码也有一定的差异。
Iced框架介绍
Iced是一个简单的,易于理解的,高度模块化的GUI框架,其整个框架可以被简单的描述为4点
- 状态(State)
- 消息(Messages)
- 界面逻辑(View Logic)
- 更新逻辑(Update Logic)
根据本人自己的使用体验,这4者之间大概的关系如下
其中 view()
中的控件布局逻辑会绑定应用结构体里的控件状态,这样在交互时也会改变控件的状态。
可以发现其实是很好理解的,使用逻辑十分分明,接下来将用本人结合官方的例子来具体说明。
Iced-rs相关链接:
github
API文档
crate.io主页
成果展示
代码讲解
新建一个工程
进入命令行,输入
> cargo new counter
> cd counter
打开Cargo.toml
,向dependencies中添加
iced = "0.4"
然后执行
cargo run
完成对crate的获取以及看到输出Hello,World
建立一个Iced应用
首先引入Iced库
use iced::widget::{button, Column, Text};
use iced::{ Element, Length, Sandbox, Settings};
当我们需要实现一个计数器时,我们先想想,他大概由哪几部分组成,首先计数,那么他需要一个文本控件来显示数字,既然要实现数字的变化,那么就需要有两个按钮来使他增加或减少。于是我们建立结构体
struct Counter {
Value: i32,
increment_button: button::State,
decrement_button: button::State,
}
其中Value
为计数器的计数值,剩余两个为两个按钮的状态,这样就完成了我们的应用的“建模”。
给大伙发两条信息
根据上面的框图我们知道,你得让程序知道你点击了按钮,那么我们就需要定义Message
#[derive(Debug, Clone, Copy)]
pub enum Message {
IncementPressed,
DecrementPressed,
}
将结构体转变为真正的iced应用
根据官方文档知道,我们要为结构体实现(implement)一些 trait 让其成为应用
impl Sandbox for counter {
...
}
fn new
这个比较好理解,实例化一个counter结构体
fn new() -> Self {
Self {
Value: 0,
increment_button: button::State::new(),
decrement_button: button::State::new(),
}
}
fn update
首先你需要告诉iced你的消息枚举叫啥名字,所以
type Message = Message;
注意!第一个Message是Sandbox trait中定义的,而后一个是你自己在上面定义的,注意区分!
然后书写更新逻辑
fn update(&mut self, message: Self::Message) {
match message {
Message::IncementPressed => {
self.Value += 1;
}
Message::DecrementPressed => {
self.Value -= 1;
}
}
}
可以看到,当收到IncrementPressed消息时,结构体自身的Value就会 +1
fn view
这个便是书写界面布局和实例化控件的步骤了,直接上代码!
fn view(&mut self) -> Element<'_, Self::Message> {
Column::new()
.push(
button::Button::new(&mut self.increment_button, Text::new("+"))
.on_press(Message::IncementPressed),
)
.push(Text::new(self.Value.to_string()).size(50))
.push(
button::Button::new(&mut self.decrement_button, Text::new("-"))
.on_press(Message::DecrementPressed),
)
.into()
}
代码中,首先我实例化了一个Column控件,用来容纳按钮和文本控件,然后通过push将控件添加到Column中,注意控件的push顺序,会影响布局的顺序。最后的into()
相当重要,他负责把Column转换为Element类型,否则编译不通过。
大功告成
最后在main中对结构体执行run函数即可
fn main() -> {
Counter::run(Settings::default());
}
运行cargo run
则会出现一个简单的计数器界面。文章来源:https://www.toymoban.com/news/detail-592535.html
写在最后
这篇文章主要是为了加深我对Iced GUI框架的认识,将技术与思考细节记录下来,如果能够帮助到你,真是不胜荣幸(不过在跑通这个例子前,我真的碰到了一堆莫名其妙的问题,才有了这份结晶,大概率能帮助减少碰壁的概率)文章来源地址https://www.toymoban.com/news/detail-592535.html
到了这里,关于【Rust】Iced GUI库初使用及踩坑——写一个计数器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!