【rust/egui】(三)看看template的app.rs:序列化、持久化存储

说在前面

  • rust新手,egui没啥找到啥教程,这里自己记录下学习过程
  • 环境:windows11 22H2
  • rust版本:rustc 1.71.1
  • egui版本:0.22.0
  • eframe版本:0.22.0
  • 上一篇:这里

serde

  • app.rs中首先定义了我们的TemplateApp结构体

    rust 复制代码
    /// 继承序列化以及反序列化 用于存储一些状态数据
    #[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::Serializeserde是rust中用于序列化和反序列化(ser ialize and de serialize)一个框架。详细见这里

  • eframe中,我们使用的是ron提供的序列化实现,与json类似,但并不一致,例如以下是一个ron序列化的结果:

    shell 复制代码
    Scene( // 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",
            ),
        ],
    )

    详细请参考https://github.com/ron-rs/ron

  • 看一个简单的ron序列化例子

    rust 复制代码
    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之后我们可以干什么呢?让我们继续看代码:

    rust 复制代码
    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的各种准备工作完成后,才进行的回调

    rust 复制代码
    eframe::run_native(
            "demo app",
            native_options,
            Box::new(|cc| Box::new(demo_app::TemplateApp::new(cc))),
        )
  • 在该函数中,我们完成了一些状态数据的还原,即读取应用上一次的工作状态,那么这些数据又是在什么时候存储下来的呢?以及存在了哪里呢?

  • 在接下来的代码中,我们实现了eframe::App特征,在save方法中,我们对状态数据进行了存储,我们可以看看eframe::set_value的具体实现:

    rust 复制代码
    #[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的日志输出打开,可以看到存储路径打印出来了

    shell 复制代码
    [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修改的数据丢失了

    因此在开发/使用的时候需要注意多窗口下的数据存储问题

参考

相关推荐
sunbyte2 分钟前
Three.js + React 实战系列 - 联系方式提交表单区域 Contact 组件✨(表单绑定 + 表单验证)
开发语言·javascript·react.js
(ღ星辰ღ)17 分钟前
js应用opencv
开发语言·javascript·opencv
HY小海29 分钟前
【数据结构】双链表
c语言·开发语言·数据结构·学习
天上掉下来个程小白32 分钟前
添加购物车-02.代码开发
java·服务器·前端·后端·spring·微信小程序·苍穹外卖
Go Dgg1 小时前
Go语言实现豆瓣电影Top250爬虫
开发语言·爬虫·golang
真的想上岸啊1 小时前
c语言第一个小游戏:贪吃蛇小游戏03
c语言·开发语言·算法
User_芊芊君子1 小时前
【Java继承】——面向对象编程的基石
java·开发语言
老理说的好1 小时前
无线定位之 三 SX1302 网关源码 thread_gps 线程详解
开发语言·信息与通信
kyle~2 小时前
C++匿名函数
开发语言·c++·人工智能
code bean2 小时前
【Qt/C++】深入理解 Lambda 表达式与 `mutable` 关键字的使用
开发语言·c++·qt