说在前面
- rust新手,egui没啥找到啥教程,这里自己记录下学习过程
- 环境:windows11 22H2
- rust版本:rustc 1.71.1
- egui版本:0.22.0
- eframe版本:0.22.0
- 上一篇:这里
serde
app.rs
中首先定义了我们的TemplateApp
结构体/// 继承序列化以及反序列化 用于存储一些状态数据 #[derive(serde::Deserialize, serde::Serialize)] #[serde(default)] // 在反序列化时,缺少的字段会使用Default特征对应的值进行初始化 pub struct TemplateApp { // Example stuff: label: String, // 声明该字段跳过序列化 #[serde(skip)] value: f32, } // 为TemplateApp实现Default特征 impl Default for TemplateApp { fn default() -> Self { Self { // Example stuff: label: "Hello World!".to_owned(), value: 2.7, } } }
- 在定义
TemplateApp
时,我们让其继承了serde::Deserialize, serde::Serialize
。serde
是rust中用于序列化和反序列化(serialize and deserialize)一个框架。详细见这里 - 在
eframe
中,我们使用的是ron
提供的序列化实现,与json
类似,但并不一致,例如以下是一个ron
序列化的结果:
详细请参考https://github.com/ron-rs/ronScene( // class name is optional materials: { // this is a map "metal": ( reflectivity: 1.0, ), "plastic": ( reflectivity: 0.5, ), }, entities: [ // this is an array ( name: "hero", material: "metal", ), ( name: "monster", material: "plastic", ), ], )
- 看一个简单的
ron
序列化例子use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize)] struct MyStruct { boolean: bool, float: f32, } impl MyStruct { fn new() -> Self { return ron::from_str("(boolean: true, float: 1.23)").unwrap(); } } fn main() { let x = MyStruct::new(); println!("RON: {}", ron::to_string(&x).unwrap()); } // output: // RON: (boolean:true,float:1.23)
- 关于更深层次的内容这里就不再展开了
(咱也展开不下去)。
持久化存储
- 有了
serde
之后我们可以干什么呢?让我们继续看代码:impl TemplateApp { /// 在第一帧之前调用 pub fn new(cc: &eframe::CreationContext<'_>) -> Self { // 我们也可以在这里定义我们的界面样式 使用`cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`. // 加载一些应用状态(比如上一次打开了那些文件之类的) 但是我们必须启用`persistence`特性 if let Some(storage) = cc.storage { // 这里我们使用ron取出存入的状态数据 并将其反序列化成TemplateApp return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default(); } Default::default() } } impl eframe::App for TemplateApp { /// 在应用关闭前调用 用于存储状态 fn save(&mut self, storage: &mut dyn eframe::Storage) { // 这里我们使用ron将TemplateApp序列化 并且存入对应的文件中 eframe::set_value(storage, eframe::APP_KEY, self); }
- 首先我们为
TemplateApp
实现了new方法 - 而在之前的
main.rs
中,我们可以看到该方法的调用,实际上,该函数是在eframe
的各种准备工作完成后,才进行的回调eframe::run_native( "demo app", native_options, Box::new(|cc| Box::new(demo_app::TemplateApp::new(cc))), )
- 在该函数中,我们完成了一些状态数据的还原,即读取应用上一次的工作状态,那么这些数据又是在什么时候存储下来的呢?以及存在了哪里呢?
- 在接下来的代码中,我们实现了
eframe::App
特征,在save
方法中,我们对状态数据进行了存储,我们可以看看eframe::set_value
的具体实现:#[cfg(feature = "ron")] pub fn set_value<T: serde::Serialize>(storage: &mut dyn Storage, key: &str, value: &T) { // 首先对TemplateApp进行序列化 match ron::ser::to_string(value) { // 如果序列化成功 那么进一步进行存储 这里并不会立即写文件 Ok(string) => storage.set_string(key, string), // 失败则打印日志 Err(err) => log::error!("eframe failed to encode data using ron: {}", err), } }
- 我们可以运行一下应用看看效果,先修改输入:
关闭应用后再打开:
可以看到字符串确实保持一致,而数值已经变回原样了。 - 那我们的数据到底存储在哪里呢?参照上一节的做法,将
eframe
的日志输出打开,可以看到存储路径打印出来了[2023-08-19T09:26:27Z DEBUG eframe] Using the glow renderer [2023-08-19T09:26:27Z DEBUG eframe::native::run] Entering the winit event loop (run_return)… [2023-08-19T09:26:27Z DEBUG eframe::native::file_storage] Loading app state from "C:\\Users\\xxxx\\AppData\\Roaming\\demo app\\data\\app.ron"…
- 打开文件,可以看到存储的内容确实在,其中还存储了一些其他数据
- 当我们直接修改对应的数据后再打开应用,对应的数据也发生了变化:
- 既然是单个文件存储,那么是否会有竞争问题呢?我们打开两个应用A,B
A想要改字符串,B同时改了字符串和数值,B先关闭,A后关闭
再次打开应用,B修改的数据丢失了
因此在开发/使用的时候需要注意多窗口下的数据存储问题
参考
- serde
- serde api
- ron