【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修改的数据丢失了

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

参考

相关推荐
程序员-珍17 分钟前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
弱冠少年24 分钟前
websockets库使用(基于Python)
开发语言·python·numpy
长天一色25 分钟前
C语言日志类库 zlog 使用指南(第五章 配置文件)
c语言·开发语言
liuxin3344556634 分钟前
教育技术革新:SpringBoot在线教育系统开发
数据库·spring boot·后端
一般清意味……37 分钟前
快速上手C语言【上】(非常详细!!!)
c语言·开发语言
卑微求AC37 分钟前
(C语言贪吃蛇)16.贪吃蛇食物位置随机(完结撒花)
linux·c语言·开发语言·嵌入式·c语言贪吃蛇
技术无疆1 小时前
【Python】Streamlit:为数据科学与机器学习打造的简易应用框架
开发语言·人工智能·python·深度学习·神经网络·机器学习·数据挖掘
金灰1 小时前
HTML5--裸体回顾
java·开发语言·前端·javascript·html·html5
爱上语文1 小时前
Java LeetCode每日一题
java·开发语言·leetcode
bug菌2 小时前
Java GUI编程进阶:多线程与并发处理的实战指南
java·后端·java ee