egui 与 eframe 官方示例仓库指南:从 Hello World 到自定义渲染的完整实践路线
【免费下载链接】eguiegui: an easy-to-use immediate mode GUI in Rust that runs on both web and native项目地址: https://gitcode.com/GitHub_Trending/eg/egui
本指南围绕 egui 仓库中的 examples 目录展开,它是学习和复用 egui 即时模式 GUI 最直接的一手素材库:每个示例都以eframe搭建原生窗口,覆盖 UI 控件、自定义字体、文件对话框、多窗口、3D 渲染与 Web 运行等常见场景。读完本文,你将掌握示例的运行方式、目录结构、每个示例的核心技术与适用场景,并能基于hello_world模板快速搭建自己的 egui 应用。
examples 目录是什么
examples/目录是 egui 官方维护的示例集合,其定位在 examples/README.md 中写得很清楚:
- 该目录下所有示例都使用
eframe来为一个egui应用创建窗口,完成"窗口托管 + 事件循环 + 渲染后端"这一整套引导工作; - 部分示例是
eframe特有的(例如自定义原生窗口装饰、多视口),但绝大多数 UI 代码对所有 egui 集成方式(如 egui-winit、egui_glow、egui-wgpu 或纯 Web 环境)同样适用; - 每个示例都是一个独立可
cargo run的 crate,带独立的Cargo.toml、README.md与运行截图。
从仓库结构看(Cargo.toml 工作区 + examples/ 目录),每个子目录即一个独立示例 crate,命名即主题,例如hello_world、custom_3d_glow、multiple_viewports。全部示例可通过 examples/run_all.sh 一键顺序运行:
for example_name in *; do if [ -d "$example_name" ]; then cargo run --quiet -p $example_name fi done该脚本用cargo run -p <crate名>的方式逐个启动目录下的示例,要求在当前工作区根目录(或 examples 目录)执行cargo,这也是各示例 README 中统一推荐的运行方式,例如:
cargo run -p hello_world cargo run -p custom_3d_glow版本与文档配套说明
原 README 特别强调了两点使用前提,本仓库现状与其完全吻合:
- main 分支的示例对应最新开发版 egui:仓库根目录的 rust-toolchain 指定了工具链,示例 crate 的
Cargo.toml(如 examples/hello_world/Cargo.toml)声明rust-version = "1.95"与edition = "2024",且依赖以workspace = true方式引用 Cargo.toml 中定义的eframe、egui_extras等 crate。因此这些示例依赖较新的 Rust 版本与最新 API,请使用较新的工具链编译。 - 若要针对特定版本查找示例:需切换到对应版本的 tag(如
latest)再查看该 tag 下的examples目录,而不是在 main 分支上直接套用旧版本代码。
官方还提供两类补充资料:egui.rs 官网的在线示例(每个都附源码链接),以及 egui 与 eframe 的 API 文档(docs.rs)。本仓库内的文档如 ARCHITECTURE.md、RELEASES.md 也可帮助你理解版本演进与架构背景。
最小可运行示例:hello_world 与 hello_world_simple
经典模板 hello_world
examples/hello_world/src/main.rs 是"控件演示"与"模板"双重定位的最小完整应用,完整代码如下:
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release #![expect(rustdoc::missing_crate_level_docs)] // it's an example use eframe::egui; fn main() -> eframe::Result { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). let options = eframe::NativeOptions { viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]), ..Default::default() }; eframe::run_native( "My egui App", options, Box::new(|cc| { // This gives us image support: egui_extras::install_image_loaders(&cc.egui_ctx); Ok(Box::<MyApp>::default()) }), ) } struct MyApp { name: String, age: u32, } impl Default for MyApp { fn default() -> Self { Self { name: "Arthur".to_owned(), age: 42, } } } impl eframe::App for MyApp { fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { egui::CentralPanel::default().show(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); }); ui.add(egui::Slider::new(&mut self.age, 0..=120).text("age")); if ui.button("Increment").clicked() { self.age += 1; } ui.label(format!("Hello '{}', age {}", self.name, self.age)); ui.image(egui::include_image!( "../../../crates/egui/assets/ferris.png" )); }); } }这个示例浓缩了 egui 应用的完整骨架,逐段拆解如下:
- 平台引导:
eframe::run_native(name, options, app_creator)是原生桌面入口。eframe::Result是统一的错误返回类型;NativeOptions通过viewport: egui::ViewportBuilder配置窗口属性(此处设with_inner_size([320.0, 240.0])指定初始窗口大小)。 - 应用状态即结构体:
MyApp { name, age }直接作为即时模式 UI 的状态载体,Default提供初始值。即时模式的核心特征就在这里——每次帧重绘时 UI 都会按当前状态重建,状态由你的结构体持有。 - UI 组装:
CentralPanel提供中央面板布局;heading、label、text_edit_singleline、Slider、button覆盖文本、输入、滑动条、按钮四类基础控件;Slider::new(&mut self.age, 0..=120)双向绑定状态与范围。 - 图片资源:
egui_extras::install_image_loaders(&cc.egui_ctx)安装图片加载器,随后用egui::include_image!宏把仓库内的 crates/egui/assets/ferris.png 编译进二进制并直接绘制。
其Cargo.toml(examples/hello_world/Cargo.toml)的依赖结构同样值得借鉴:
eframe = { workspace = true, features = [ "default", "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } # For image support: egui_extras = { workspace = true, features = ["default", "image"] } env_logger = { workspace = true, features = ["auto-color", "humantime"] }要点:eframe自带egui(代码里直接use eframe::egui);需要图片显示时启用egui_extras的imagefeature;__screenshotfeature 配合环境变量EFRAME_SCREENSHOT_TO可在 CI 中自动输出截图(这正是仓库 scripts/generate_example_screenshots.sh 批量生成示例截图所依赖的机制)。
更精简的 hello_world_simple
如果你只想看"最少的 egui 程序",examples/hello_world_simple/src/main.rs 只用了约 30 行:它省去自定义App结构体,改用eframe::run_ui_native直接以闭包形式书写 UI,局部变量name、age由闭包捕获,功能与hello_world完全一致:
eframe::run_ui_native("My egui App", options, move |ui, _frame| { egui::CentralPanel::default().show(ui, |ui| { ui.heading("My egui Application"); ui.horizontal(|ui| { let name_label = ui.label("Your name: "); ui.text_edit_singleline(&mut name) .labelled_by(name_label.id); }); ui.add(egui::Slider::new(&mut age, 0..=120).text("age")); if ui.button("Increment").clicked() { age += 1; } ui.label(format!("Hello '{name}', age {age}")); }); })对比可见:当应用逻辑简单、无需保留结构化状态时,run_ui_native是更轻量的选择;当应用复杂(多窗口、需要CreationContext做初始化、需要on_exit清理资源)时,run_native+impl eframe::App才是正确姿势。
进阶示例导读:按主题挑选你的参考实现
examples 目录覆盖了从"控件用法"到"渲染后端深度定制"的完整梯度,以下按主题梳理每个示例的核心价值(对应源码均在各自src/main.rs,运行命令见各目录 README):
窗口与视口管理
| 示例 | 主题 | 关键技术点 |
|---|---|---|
| confirm_exit | 退出确认 | 拦截关闭事件、弹出确认对话框 |
| custom_window_frame | 自定义窗口装饰 | ViewportBuilder::with_decorations(false)、with_transparent(true)隐藏系统边框,配合clear_color返回透明色,用ViewportCommand::StartDrag/Maximized/Close等命令自绘标题栏与窗口按钮 |
| multiple_viewports | 多窗口 | 一个进程中创建多个原生窗口视口 |
| serial_windows | 顺序窗口 | 依次弹出多个窗口的流程控制 |
| window_options(在 demo lib 中) | 窗口选项 | 窗口参数调节参考 |
custom_window_frame是eframe特有的高级用法,其实现思路值得细读(examples/custom_window_frame/src/main.rs):先用with_decorations(false)隐藏系统标题栏、with_transparent(true)启用透明以支持圆角,然后在App::clear_color中返回egui::Rgba::TRANSPARENT避免圆角外区域被底色覆盖;自绘标题栏时用ui.interact(rect, Id::new("title_bar"), Sense::click_and_drag())捕获拖拽,双击切换最大化(ViewportCommand::Maximized(!is_maximized)),拖拽时发送ViewportCommand::StartDrag让系统接管窗口移动。
输入与系统集成
| 示例 | 主题 | 关键技术点 |
|---|---|---|
| keyboard_events | 键盘事件 | 事件回调与按键状态查询 |
| file_dialog | 文件对话框与拖放 | rfd::FileDialog::new().pick_file()打开原生对话框;with_drag_and_drop(true)启用拖放,从ui.input(|i| i.raw.dropped_files / hovered_files)读取拖入与悬停文件,并用LayerId::new(Order::Foreground, ...)绘制拖放悬停遮罩预览 |
| custom_keypad | 自定义输入面板 | 组合自定义按键 UI 与文本输入 |
| user_attention | 窗口注意力 | 闪烁/提醒用户注意的窗口操作 |
file_dialog同时展示了原生与 Web 两种文件处理路径(examples/file_dialog/src/main.rs):原生端用file.path()拿路径;wasm32目标下改用file.web_file()获取文件名、MIME 类型与大小,#[cfg(...)]条件编译分平台实现。
字体与主题
| 示例 | 主题 | 关键技术点 |
|---|---|---|
| custom_font | 自定义字体 | 两种方式:ctx.add_font(FontInsert::new(...))增量添加字体并指定家族优先级;ctx.set_fonts(fonts)整体替换FontDefinitions,把自定义字体insert(0, ...)到Proportional首位、push到Monospace末尾作回退。include_bytes!内嵌仓库中的 crates/epaint_default_fonts/fonts/Hack-Regular.ttf,.ttf/.otf均支持 |
| custom_font_style | 字体样式 | 字号、字重、斜体等样式控制 |
| font_variations | 可变字体 | 使用仓库内的 examples/font_variations/data/Recursive-VariableFont.ttf 演示字重连续变化 |
| custom_style | 自定义样式 | 修改Style/Visuals全局换肤 |
| styling_engine | 样式引擎 | 类(class)与原子样式等新样式系统的应用 |
渲染与特效
| 示例 | 主题 | 关键技术点 |
|---|---|---|
| custom_3d_glow | 自定义 3D 渲染(OpenGL) | eframe::Renderer::Glow指定后端,通过egui::PaintCallback把glow的 GPU 绘制注入 egui 渲染流程;资源放在Arc<Mutex<...>>中以便在回调里使用,App::on_exit中销毁 GL 程序 |
| images | 图片显示 | 仓库内 examples/images/src/ferris.svg、examples/images/src/cat.webp、examples/images/src/ferris.gif 分别演示 SVG / WebP / GIF 三种格式的加载与动画 |
| screenshot | 截图功能 | ScreenshotCallback把当前帧保存为图片 |
| puffin_profiler | 性能分析 | 集成 puffin 对 egui 应用做 CPU 采样分析 |
| fractal_clock(在 demo lib 中) | 分形时钟动画 | 动画驱动的绘制参考 |
custom_3d_glow是"egui 中嵌入原生 GPU 渲染"的范本(examples/custom_3d_glow/src/main.rs):用ui.allocate_exact_size(egui::Vec2::splat(300.0), egui::Sense::drag())预留 300×300 的画布并接收拖拽输入,把旋转角写入egui_glow::CallbackFn闭包,构造egui::PaintCallback { rect, callback }交给ui.painter().add(...);渲染闭包内直接调用glowAPI 编译着色器、gl.draw_arrays绘制三角形。注意着色器版本按平台分支:wasm32 用#version 300 es,原生用#version 330。
事件循环与异步
| 示例 | 主题 | 关键技术点 |
|---|---|---|
| external_eventloop | 外部事件循环 | 不依赖run_native,自行驱动事件循环,将 egui 集成进既有主循环 |
| external_eventloop_async | 异步事件循环 | 在异步运行时(pollster/async)中驱动 eframe,代码拆分在 examples/external_eventloop_async/src/main.rs 与 examples/external_eventloop_async/src/app.rs |
| hello_android | Android 平台 | egui/eframe 在移动端(Android)的最小运行示例 |
| hello_world_par | 并行渲染 | 多线程(rayon 风格)渲染参考 |
弹层与交互
| 示例 | 主题 | 关键技术点 |
|---|---|---|
| popups | 弹出层 | Area/Popup相关弹层组合用法 |
| hello_world | 基础控件 | Label、TextEdit、Slider、Button入门(见上文完整代码) |
示例之外:把 demo 跑起来与进一步深入
更丰富的演示应用
除examples/外,仓库还提供了体量更大、覆盖更全的演示程序,适合系统性探索 egui 能力:
- egui_demo_app:完整的演示应用,含
egui_demo_lib中数十个 demo 页面(widget 画廊、窗口选项、文本布局、绘图、表格、颜色选择器等),源码在 egui_demo_lib/src/demo/ 按主题分文件组织;其 tests/snapshots/ 下的渲染快照既是回归测试也是视觉效果合集; - examples/run_all.sh 可一次跑遍全部 examples;
- 仓库 scripts/ 下的
build_demo_web.sh、setup_web.sh等脚本说明了如何把 demo 编译到 Web(wasm)运行,这也印证了 README 中"许多示例适用于任何 egui 集成"的说法——同一套 UI 代码可在原生与浏览器两种环境复用。
阅读顺序建议
- 先跑
hello_world(或更精简的hello_world_simple),对照上文代码理解run_native+App::ui骨架; - 按业务需求横向取用进阶示例:做工具软件看
custom_window_frame、file_dialog、confirm_exit;做展示/游戏类看custom_3d_glow、images;做跨平台/Web 应用看external_eventloop系列与hello_android; - 需要深挖某类控件或布局时,回到 egui_demo_lib/src/demo/ 查找对应 demo 页源码,并结合 crates/egui/src/ 的
widgets/、containers/源码理解实现细节。
小结
examples/目录是 egui 官方维护的"可运行文档":它用一个统一入口(eframe 窗口)+ 众多主题化示例,覆盖了从最小可运行程序到窗口定制、字体替换、文件拖放、GPU 3D 渲染、多视口、外部事件循环与 Android 部署的完整实践路径。阅读时记住 README 的两条准则——main 分支示例对应最新开发版 API、按需切换到对应版本 tag——再结合每个示例目录内的 README 与截图,即可高效定位并复用你需要的参考实现,快速搭建自己的 egui 应用。
【免费下载链接】eguiegui: an easy-to-use immediate mode GUI in Rust that runs on both web and native项目地址: https://gitcode.com/GitHub_Trending/eg/egui
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考