简介

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 的简单应用的更多相关文章

  1. 8个免费实用的C++GUI库(转载)

      C++标准中并没有包含GUI,这也使得C++开发图形化界面需要依赖于第三方的库.实际上,图形界面恰恰是C++的强项,小到平常使用的各类桌面软件,大到魔兽世界这样的游戏,都是C++擅长的地方.C++ ...

  2. 8个免费实用的C++GUI库

    8个免费实用的C++GUI库 C++标准中并没有包含GUI,这也使得C++开发图形化界面需要依赖于第三方的库.实际上,图形界面恰恰是C++的强项,小到平常使用的各类桌面软件,大到魔兽世界这样的游戏,都 ...

  3. 使用Python3.6的标准GUI库tkinter快速创建GUI应用程序

    Python 提供了多个图形开发界面的库,几个常用 Python GUI 库如下: Tkinter: Tkinter 模块(Tk 接口)是 Python 的标准 Tk GUI 工具包的接口 .Tk 和 ...

  4. Qt和其它GUI库的对比

    http://c.biancheng.net/view/3876.html 世界上的 GUI 库多如牛毛,有的跨平台,有的专属于某个操作系统:有的只有 UI 功能,有的还融合了网络通信.多媒体处理.数 ...

  5. 8个必备的Python GUI库

    Python GUI 库有很多,下面给大家罗列常用的几种 GUI 库.下面介绍的这些GUI框架,能满足大部分开发人员的需要,你可以根据自己的需求,选择合适的GUI库. 很多人学习python,不知道从 ...

  6. Python:GUI库tkinter(三)

    这一章是对前两章的总结: Python:GUI库tkinter(一) Python:GUI库tkinter(二) 前两章是对控件的介绍,第一章可以知道各控件使用时的具体参数,第二章以具体的例子展示了每 ...

  7. [OpenCV实战]28 基于OpenCV的GUI库cvui

    目录 1 cvui的使用 1.1 如何在您的应用程序中添加cvui 1.2 基本的"hello world"应用程序 2 更高级的应用 3 代码 4 参考 有很多很棒的GUI库,例 ...

  8. Python 图形 GUI 库 pyqtgraph

    原文  Python 图形 GUI 库 pyqtgraph pyqtgraph 是纯 Python 图形 GUI 库,基于PyQT4 /pyside和NumPy.它主要目的用于在数学/科学/工程中.M ...

  9. Python GUI库

    PyQT不错的,只是要小心,这个东西是GPL的,如果你要写商业程序需要购买商业版授权.另外PyGTK.wxPython都是不错的GUI库.Python自带了一个基于TkInter的GUI库,如果你不想 ...

  10. MongDB .Net工具库MongoRepository的简单使用

    MongDB .Net工具库MongoRepository的简单使用 最近研究了一下MongoDB数据库,并使用了开源的在.net环境下的一个类库,Mongo仓库.对于数据的一些简单的操作非常好用,特 ...

随机推荐

  1. 21.10 Python 使用CRC32校验文件

    CRC文件校验是一种用于验证文件完整性的方法,通过计算文件的CRC值并与预先计算的CRC校验值进行比较,来判断文件是否发生变化,此类功能可以用于验证一个目录中是否有文件发生变化,如果发生变化则我们可以 ...

  2. Python 原生Socket实现端口扫描

    端口扫描,就是逐个对一段端口或指定的端口进行扫描.通过扫描结果可以知道一台计算机上都提供了哪些服务,Python中使用Socket即可实现对特定端口的探测,以及对C段的扫描. 扫描目标主机Banner ...

  3. centos7.9重启网卡提示Failed to start LSB: Bring up/down networking.

    前几天给一台机器状态centos7.9系统,设备有2个网口,今天重启网卡一直失败, 查看network状态,怀疑是eth0网卡有问题 查看eth0的网卡配置,发现是eth0网卡的BOOTPROTO=d ...

  4. GoodSync(最好的文件同步软件)

    GoodSync是一款使用创新的最好的文件同步软件,可以在你的台式机.笔记本.USB外置驱动器等设备直接进行数据文件同步软件. GoodSync将高度稳定的可靠性和极其简单的易用性完美结合起来,可以实 ...

  5. Asp.net平台常用的框架整理

    分布式缓存框架: Microsoft Velocity:微软自家分布式缓存服务框架. Memcahed:一套分布式的高速缓存系统,目前被许多网站使用以提升网站的访问速度. Redis:是一个高性能的K ...

  6. Git企业开发控制理论和实操-从入门到深入(四)|Git的远程操作|Gitee

    前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量博客汇总 然后就是博主最近最花时间的一 ...

  7. TF-VAEGAN:添加潜在嵌入(Latent Embedding)的VAEGAN处理零样本学习

    前面介绍了将VAE+GAN解决零样本学习的方法:f-VAEGAN-D2,这里继续讨论引入生成模型处理零样本学习(Zero-shot Learning, ZSL)问题.论文"Latent Em ...

  8. 面向对象之trait

    面向对象之trait 场景 一个web站点,它有很多不同的类:用户(User).页面(Page).联系表单(ContactFrom)等.我们可能需要在每个类中添加一个方法的定义,但是这样的话就会造成不 ...

  9. CF1921F Sum of Progression 题解

    题目链接:CF 或者 洛谷 一道经典的类型题,把这种类型的题拿出来单独说一下. 注意到问题中涉及到需要维护 \(a_{x+k\times step}\) 这样的信息,这样的信息很难用树型结构维护,比较 ...

  10. 小知识:TFA收集日志报错空间不足

    今天在某客户环境下分析某节点驱逐的故障,发现有安装TFA,所以使用一键收集包含故障时刻的日志 tfactl diagcollect -from "2020-08-14 03:00:00&qu ...