【egui】官方示例 hello_world 完全解析

这是 egui 最简单的入门示例,展示了基本的 UI 控件:LabelTextEditSliderButton 和图片显示。

📁 文件结构

复制代码
hello_world/
├── Cargo.toml          # 项目依赖配置
├── src/
│   └── main.rs         # 主程序代码
└── screenshot.png      # 运行截图

📦 Cargo.toml 详解

toml 复制代码
[package]
name = "hello_world"                    # 项目名称
version = "0.1.0"                       # 版本号
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]  # 作者(egui 作者)
license = "MIT OR Apache-2.0"           # 双许可证
edition = "2024"                        # Rust 2024 版次
rust-version = "1.92"                   # 最低 Rust 版本要求
publish = false                          # 不发布到 crates.io

[lints]
workspace = true                         # 继承工作区的 lint 配置

[dependencies]
# eframe 主依赖,使用工作区配置,启用 __screenshot 功能用于截图
eframe = { workspace = true, features = [
  "default",
  "__screenshot",  # 可通过 EFRAME_SCREENSHOT_TO 环境变量输出截图
] }

# 图片支持扩展库
egui_extras = { workspace = true, features = ["default", "image"] }

# 日志库,支持彩色输出和时间戳
env_logger = { workspace = true, features = ["auto-color", "humantime"] }

关键点解析

依赖 作用
eframe 应用框架,提供窗口、事件循环和渲染
eframe/__screenshot 特殊功能,运行时设置 EFRAME_SCREENSHOT_TO=path.png 可保存截图
egui_extras 扩展功能,这里启用 image 功能加载图片
env_logger 日志输出,通过 RUST_LOG=debug 控制日志级别

🚀 main.rs 逐行解析

1. 文件头属性

rust 复制代码
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#![expect(rustdoc::missing_crate_level_docs)]
属性 说明
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 发布模式(--release)下在 Windows 隐藏控制台窗口,只显示 GUI 窗口
#![expect(rustdoc::missing_crate_level_docs)] 允许缺少 crate 级别文档(因为是示例)

2. 引入依赖

rust 复制代码
use eframe::egui;  // 通过 eframe 重新导出使用 egui

3. main 函数

rust 复制代码
fn main() -> eframe::Result {
    // 初始化日志系统,可以通过 RUST_LOG=debug 查看详细日志
    env_logger::init();
    
    // 配置窗口选项
    let options = eframe::NativeOptions {
        // viewport 字段:设置窗口大小 320x240
        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),
        ..Default::default()  // 其余使用默认值
    };
    
    // 运行应用
    eframe::run_native(
        "My egui App",                    // 窗口标题
        options,                           // 窗口配置
        Box::new(|cc| {                     // 应用创建闭包
            // 安装图片加载器,支持显示图片
            egui_extras::install_image_loaders(&cc.egui_ctx);
            
            // 返回默认的 MyApp 实例
            Ok(Box::<MyApp>::default())
        }),
    )
}

4. 应用状态结构体

rust 复制代码
struct MyApp {
    name: String,  // 姓名
    age: u32,      // 年龄
}

impl Default for MyApp {
    fn default() -> Self {
        Self {
            name: "Arthur".to_owned(),  // 默认姓名
            age: 42,                     // 默认年龄
        }
    }
}

5. 实现 eframe::App trait

rust 复制代码
impl eframe::App for MyApp {
    // ui 方法:每帧调用,绘制界面
    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
        // 创建一个中央面板(带背景和边距)
        egui::CentralPanel::default().show_inside(ui, |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);  // 关联标签,辅助功能
            });
            
            // 滑块控件,绑定 age,范围 0-120
            ui.add(egui::Slider::new(&mut self.age, 0..=120).text("age"));
            
            // 按钮,点击时 age+1
            if ui.button("Increment").clicked() {
                self.age += 1;
            }
            
            // 显示当前值
            ui.label(format!("Hello '{}', age {}", self.name, self.age));
            
            // 显示图片(使用 include_image! 宏在编译时嵌入图片)
            ui.image(egui::include_image!(
                "../../../crates/egui/assets/ferris.png"  // 路径相对于 hello_world 示例
            ));
        });
    }
}

🎨 界面控件详解

1. Label(标签)

rust 复制代码
ui.label("Your name: ");
ui.heading("My egui Application");  // 大标题样式

2. TextEdit(文本编辑框)

rust 复制代码
ui.text_edit_singleline(&mut self.name)  // 绑定到 String
    .labelled_by(name_label.id);          // 关联标签 ID

3. Slider(滑块)

rust 复制代码
egui::Slider::new(&mut self.age, 0..=120)  // 绑定到 u32,范围 0-120
    .text("age")                            // 显示文本

4. Button(按钮)

rust 复制代码
if ui.button("Increment").clicked() {  // 点击检测
    self.age += 1;
}

5. Image(图片)

rust 复制代码
ui.image(egui::include_image!("path/to/image.png"));
// include_image! 宏在编译时嵌入图片,无需运行时加载

🧠 关键概念理解

即时模式(Immediate Mode)

  • 每帧都重新构建 UI
  • 状态保存在 MyApp 结构体中
  • 控件通过返回值(如 .clicked())响应用户操作

布局系统

  • CentralPanel:占据剩余空间,提供默认背景
  • horizontal:水平排列子控件
  • 嵌套布局:CentralPanel 包含 horizontal

数据绑定

  • 控件直接修改应用状态:&mut self.name&mut self.age
  • 无需手动更新 UI,框架自动重绘

图片处理

  • egui_extras::install_image_loaders 初始化图片支持
  • include_image! 宏在编译时嵌入图片,适合小图片
  • 图片路径相对于当前文件

🏃 运行方式

bash 复制代码
# 普通运行
cargo run -p hello_world

# 带日志调试运行
RUST_LOG=debug cargo run -p hello_world

# 生成截图(需要 __screenshot 功能)
EFRAME_SCREENSHOT_TO=screenshot.png cargo run -p hello_world

📸 运行效果

界面包含:

  • 标题 "My egui Application"
  • "Your name:" 标签 + 文本输入框(默认 "Arthur")
  • 年龄滑块(0-120,默认 42)
  • "Increment" 按钮,点击年龄+1
  • 显示 "Hello '姓名', 年龄"
  • Ferris 螃蟹图片

💡 一句话总结

hello_world 示例展示了 egui 最核心的编程模式:状态存在结构体里,ui 方法每帧重新构建界面,控件直接读写状态,布局通过嵌套 Panel 和 horizontal 实现。

相关推荐
CS生5 小时前
Rust环境准备
开发语言·后端·rust
沛沛rh457 小时前
Rust 中的三个“写手“:print!、format!、write! 的详细区别
开发语言·后端·rust
henreash8 小时前
Rust引用借用测试
rust
devmoon9 小时前
从 0 到 1 实现两条独立区块链Parachain的跨链通信能力之实操指南
开发语言·rust·区块链·信息与通信·polkadot
devmoon9 小时前
区块链 Indexer 全解析:为什么 Web3 应用离不开数据索引器?(Polkadot / Ethereum / Solana 对比与未来展望)
rust·web3·区块链·以太坊·polkadot·solana·indexer
@PHARAOH9 小时前
WHAT - SWC Rust-based platform for the Web
开发语言·前端·rust
大鹏的NLP博客11 小时前
Rust + PyTorch 实现 BGE 向量检索系统
人工智能·pytorch·rust
无名之逆1 天前
你可能不需要WebSocket-服务器发送事件的简单力量
java·开发语言·前端·后端·计算机·rust·编程
Source.Liu1 天前
【egui】界面的坐标系统:f32 一统江湖
rust·egui