Rust中GUI库egui的简单应用指南

 更新时间:2024年03月13日 17:05:54   作者:二次元攻城狮  
egui(发音为“e-gooey”)是一个简单、快速且高度可移植的 Rust 即时模式 GUI 库,跨平台、Rust原生,适合一些小工具和游戏引擎GUI,下面就跟随小编一起来看看它的具体使用吧

简介

egui(发音为“e-gooey”)是一个简单、快速且高度可移植的 Rust 即时模式 GUI 库,跨平台、Rust原生,适合一些小工具和游戏引擎GUI:

文档:https://docs.rs/egui/latest/egui/

演示:https://www.egui.rs/#demo

github:https://github.com/emilk/egui

关于即时模式GUI,可以参考 使用C++界面框架ImGUI开发一个简单程序 里面的介绍,ImGUI是C++的一个即时模式GUI库。

简单示例

创建项目

首先使用cargo工具快速构建项目:

cargo new eguitest

然后添加依赖:

cargo add eframe

egui只是一个图形库,而不是图形界面开发框架,eframe是与egui配套使用的图形框架

为了静态插入图片,还需要增加egui_extras依赖:

cargo add egui_extras

然后在Cargo.toml文件中编辑features

egui_extras = { version = "0.26.2", features = ["all_loaders"] }

界面设计

打开src/main.rc,编写第一个eframe示例程序:

//隐藏Windows上的控制台窗口
#![windows_subsystem = "windows"]

use eframe::egui;

fn main() -> Result<(), eframe::Error> {
    // 创建视口选项,设置视口的内部大小为320x240像素
    let options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),
        ..Default::default()
    };

    // 运行egui应用程序
    eframe::run_native(
        "My egui App", // 应用程序的标题
        options, // 视口选项
        Box::new(|cc| {
            // 为我们提供图像支持
            egui_extras::install_image_loaders(&cc.egui_ctx);
            // 创建并返回一个实现了eframe::App trait的对象
            Box::new(MyApp::new(cc))
        }),
    )
}

//定义 MyApp 结构体
struct MyApp {
    name: String,
    age: u32,
}

//MyApp 结构体 new 函数
impl MyApp {
    fn new(cc: &eframe::CreationContext<'_>) -> Self {        
        // 结构体赋初值
        Self {
            name: "Arthur".to_owned(),
            age: 42,
        }
    }
}

//实现 eframe::App trait 
impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        // 在中央面板上显示egui界面
        egui::CentralPanel::default().show(ctx, |ui| {
            // 显示标题
            ui.heading("My egui Application"); 
            // 创建一个水平布局
            ui.horizontal(|ui| {
                // 显示姓名标签
                let name_label = ui.label("Your name: "); 
                // 显示姓名输入框(单行文本框)
                ui.text_edit_singleline(&mut self.name) 
                    .labelled_by(name_label.id); // 关联标签
            });

            // 显示年龄滑块
            ui.add(egui::Slider::new(&mut self.age, 0..=120).text("age")); 

            if ui.button("Increment").clicked() {
                // 点击按钮后将年龄加1
                self.age += 1;
            }

            // 显示问候语
            ui.label(format!("Hello '{}', age {}", self.name, self.age));            
            // 显示图片,图片放在main.rs的同级目录下(可以自定义到其它目录)
            ui.image(egui::include_image!("ferris.png")); 
        });
    }
}

运行结果如下:

切换主题

egui提供了明亮、暗黄两种主题,在APP结构体上添加 theme_switcher 方法:

impl MyApp {
    // 切换主题
    fn theme_switcher(&mut self, ui: &mut egui::Ui, ctx: &egui::Context) {
        ui.horizontal(|ui| {
            if ui.button("Dark").clicked() {
                ctx.set_visuals(egui::Visuals::dark());
            }
            if ui.button("Light").clicked() {
                ctx.set_visuals(egui::Visuals::light());
            }
        });
    }
}

然后在update函数中调用:

egui::CentralPanel::default().show(ctx, |ui| {
   //...
   // 切换主题
   self.theme_switcher(ui, ctx);
   // 显示图片
   ui.image(egui::include_image!("ferris.png")); 
});

egui的Style结构体可以自定义主题,不过一般默认主题就够用了。

自定义字体

egui默认不支持中文,实现一个 setup_custom_fonts 函数:

//自定义字体
fn setup_custom_fonts(ctx: &egui::Context) {
    // 创建一个默认的字体定义对象
    let mut fonts = egui::FontDefinitions::default();

    //安装的字体支持.ttf和.otf文件
    //文件放在main.rs的同级目录下(可以自定义到其它目录)
    fonts.font_data.insert(
        "my_font".to_owned(),
        egui::FontData::from_static(include_bytes!(
            "msyh.ttc"  
        )),
    );

    // 将字体添加到 Proportional 字体族的第一个位置
    fonts
        .families
        .entry(egui::FontFamily::Proportional)
        .or_default()
        .insert(0, "my_font".to_owned());

    // 将字体添加到 Monospace 字体族的末尾
    fonts
        .families
        .entry(egui::FontFamily::Monospace)
        .or_default()
        .push("my_font".to_owned());

    // 将加载的字体设置到 egui 的上下文中
    ctx.set_fonts(fonts);
}

然后再MyApp结构体的new方法中调用:

//...
impl MyApp {
    fn new(cc: &eframe::CreationContext<'_>) -> Self {
        //加载自定义字体
        setup_custom_fonts(&cc.egui_ctx);     
        //...
    }
}
//...

运行结果:

自定义图标

先导入image库,在终端中运行:

cargo add image

还需要导入std::sync::Arc、eframe::egui::IconData ,库引入区如下:

use eframe::egui;
use eframe::egui::IconData;
use std::sync::Arc;
use image;

在main()函数中将native_options的声明改为可变变量的声明,并加入改变图标代码:

fn main() -> Result<(), eframe::Error> {
    // 创建视口选项,设置视口的内部大小为320x240像素
    let mut options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),
        ..Default::default()
    };

    //导入图标,图片就用上面的
    let icon_data = include_bytes!("ferris.png");
    let img = image::load_from_memory_with_format(icon_data, image::ImageFormat::Png).unwrap();
    let rgba_data = img.into_rgba8();
    let (width, height) =(rgba_data.width(),rgba_data.height());
    let rgba: Vec<u8> = rgba_data.into_raw();
    options.viewport.icon=Some(Arc::<IconData>::new(IconData { rgba, width, height}));
    
    // ...    
}

经典布局

在上面示例的基础上,实现一个上中下或左中右的经典三栏布局,main函数不需要修改,只需要修改MyApp结构体的定义即可。

定义导航变量

先定义一个导航枚举,用来在标记当前要显示的界面:

//导航枚举
enum Page {
    Test,
    Settings,
}

为了方便理解示例,在 MyApp 中只定义一个 page 字段,并同步修改new函数:

//定义 MyApp 结构体
struct MyApp {
    page:Page,
}
//MyApp 结构体 new 函数
impl MyApp {
    fn new(cc: &eframe::CreationContext<'_>) -> Self {
        setup_custom_fonts(&cc.egui_ctx);     
        // 结构体赋初值
        Self {
            page:Page::Test,
        }
    }
}

实现导航界面

在 MyApp 中定义导航栏的界面,

impl MyApp {  

    //左侧导航按钮,egui没有内置树控件,有需要可以自己实现
    fn left_ui(&mut self, ui: &mut egui::Ui)  {   
        //一个垂直布局的ui,内部控件水平居中并对齐(填充全宽)
        ui.vertical_centered_justified(|ui| {          
            
            if ui.button("测试").clicked() {
                self.page=Page::Test;
            }

            if ui.button("设置").clicked() {
                self.page=Page::Settings;
            }
            //根据需要定义其它按钮
        });
    }

    //...其它方法
}

实现导航逻辑

在 MyApp 中定义一个 show_page 方法来进行界面调度,每个界面再单独实现自己的UI函数

impl MyApp {  
    //...其它方法

    //根据导航显示页面
    fn show_page(&mut self, ui: &mut egui::Ui)  {   

        match self.page {
            Page::Test => {
                self.test_ui(ui);
            }
            Page::Settings => {
                //...
            }
        }       
    }

    //为了方便理解示例这里只显示一张图片
    fn test_ui(&mut self, ui: &mut egui::Ui)  {         
        ui.image(egui::include_image!("ferris.png"));
    }

    //...其它方法
}

实现主框架布局

在 MyApp 中间实现 main_ui 方法,可以根据自己的需要调整各个栏的位置:

impl MyApp {  
    //...其它方法
    //主框架布局
    fn main_ui(&mut self, ui: &mut egui::Ui)  {        
        // 添加面板的顺序非常重要,影响最终的布局
        egui::TopBottomPanel::top("top_panel")
        .resizable(true)
        .min_height(32.0)
        .show_inside(ui, |ui| {
            egui::ScrollArea::vertical().show(ui, |ui| {
                ui.vertical_centered(|ui| {
                    ui.heading("标题栏");
                });
                ui.label("标题栏内容");
            });
        });

        egui::SidePanel::left("left_panel")
        .resizable(true)
        .default_width(150.0)
        .width_range(80.0..=200.0)
        .show_inside(ui, |ui| {
            ui.vertical_centered(|ui| {
                ui.heading("左导航栏");
            });
            egui::ScrollArea::vertical().show(ui, |ui| {
                self.left_ui(ui);
            });
        });

        egui::SidePanel::right("right_panel")
        .resizable(true)
        .default_width(150.0)
        .width_range(80.0..=200.0)
        .show_inside(ui, |ui| {
            ui.vertical_centered(|ui| {
                ui.heading("右导航栏");
            });
            egui::ScrollArea::vertical().show(ui, |ui| {
                ui.label("右导航栏内容");
            });
        });

        egui::TopBottomPanel::bottom("bottom_panel")
        .resizable(false)
        .min_height(0.0)
        .show_inside(ui, |ui| {
            ui.vertical_centered(|ui| {
                ui.heading("状态栏");
            });
            ui.vertical_centered(|ui| {
                ui.label("状态栏内容");
            });
        });

        egui::CentralPanel::default().show_inside(ui, |ui| {
            ui.vertical_centered(|ui| {
                ui.heading("主面板");
            });
            egui::ScrollArea::vertical().show(ui, |ui| {
                ui.label("主面板内容");

                self.show_page(ui);
            });
        });
    }       
}

调试运行

在 main 函数中稍微调整一下窗口大小:

// 创建视口选项
let mut options = eframe::NativeOptions {
    viewport: egui::ViewportBuilder::default().with_inner_size([1000.0, 500.0]),
    ..Default::default()
};

在 update 函数中调用 main_ui 函数:

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        //设置主题
        ctx.set_visuals(egui::Visuals::dark());
        // 在中央面板上显示egui界面
       egui::CentralPanel::default().show(ctx, |ui| {
        self.main_ui(ui); 
       });        
    }
}

运行结果如下:

以上就是Rust中GUI库egui的简单应用指南的详细内容,更多关于Rust egui的资料请关注脚本之家其它相关文章!

相关文章

  • Rust 高性能内存缓存 moka 完全指南

    Rust 高性能内存缓存 moka 完全指南

    本文介绍了Rust高性能内存缓存库moka,涵盖其核心特性、基础环境搭建、同步/异步实战、高级配置及生产环境最佳实践等内容,感兴趣的可以了解一下
    2026-05-05
  • Rust 连接 SQLite 数据库的过程解析

    Rust 连接 SQLite 数据库的过程解析

    本文通过一个例子给大家介绍了Rust 连接 SQLite 数据库的详细过程,我使用rusqlite这个crate,对Rust 连接 SQLite 数据库相关知识感兴趣的朋友跟随小编一起看看吧
    2022-01-01
  • Rust 中的文件操作示例详解

    Rust 中的文件操作示例详解

    Rust 中的路径操作是跨平台的,std::path 模块提供的了两个用于描述路径的类型,本文给大家介绍Rust 中的文件操作示例详解,感兴趣的朋友一起看看吧
    2021-11-11
  • Rust常用功能实例代码汇总

    Rust常用功能实例代码汇总

    通过一系列实用示例展示了Rust在文件操作、网络请求、并发编程、命令行工具以及数据库操作等方面的应用,这些示例不仅展示了 Rust 的强大功能,还提供了实际开发中的指导和参考,通过这些示例,您可以更好地理解 Rust 的特性,并将其应用于您的项目中
    2024-12-12
  • Rust中Protobuf使用详解

    Rust中Protobuf使用详解

    本文介绍了如何在Rust中使用Protobuf进行序列化和反序列化,并介绍了如何在项目中使用生成的Rust代码,通过include!宏和mod.rs文件的配置,可以方便地在Rust项目中使用Protobuf,感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • 关于Rust编译时报link.exe not found错误问题

    关于Rust编译时报link.exe not found错误问题

    这篇文章主要介绍了Rust编译的时候报出link.exe not found错误问题,解决方法是在命令行就是CMD执行相应的命令即可,本文给大家分解决方法,需要的朋友可以参考下
    2022-09-09
  • Rust在写库时实现缓存的操作方法

    Rust在写库时实现缓存的操作方法

    Moka是一个用于Rust的高性能缓存库,它提供了多种类型的缓存数据结构,包括哈希表、LRU(最近最少使用)缓存和 支持TTL(生存时间)缓存,这篇文章给大家介绍Rust在写库时实现缓存的相关知识,感兴趣的朋友一起看看吧
    2024-01-01
  • 前端基于Rust实现的Wasm进行图片压缩的技术文档(实现方案)

    前端基于Rust实现的Wasm进行图片压缩的技术文档(实现方案)

    在现代Web开发中,利用Rust编写的图片压缩代码可以编译成WebAssembly(Wasm)模块,Rust的内存安全特性和Wasm的跨平台能力,使得这种方案既高效又安全,对Rust Wasm图片压缩实现方案感兴趣的朋友一起看看吧
    2024-09-09
  • 使用vscode配置Rust运行环境全过程

    使用vscode配置Rust运行环境全过程

    VS Code对Rust有着较完备的支持,这篇文章主要给大家介绍了关于使用vscode配置Rust运行环境的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • 深入剖析Rust 中的 Move、Copy 和 Clone

    深入剖析Rust 中的 Move、Copy 和 Clone

    在 Rust 编程语言中,move、Copy 和 Clone 是所有权系统的核心概念,它们深刻影响着数据的传递和复制方式,这些机制不仅确保了内存安全,还提供了高效的性能优化手段,本文将深入探讨这些概念的内部机制、使用场景、性能影响以及高级用法,感兴趣的朋友一起看看吧
    2025-05-05

最新评论