即时模式 UI 基础
理解 egui 如何进行渲染。
即时模式 UI 基础 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 4 节课。
即时模式的含义
egui 是一个即时模式 GUI 库。与 GTK、Qt 这类保留模式工具包不同,后者会构建持久化的控件树,而 egui 会在每一帧从头开始重建整个 UI。
您的代码会在每一帧从上到下运行,并通过函数调用发出控件。这里没有存储的节点图——UI 只是运行闭包所产生的副作用。
更新循环
egui 应用会实现 eframe::App,其 update 方法每帧调用一次,通常每秒调用 60 次。
在 update 中,您要根据当前状态描述 UI 此刻应呈现的样子。egui 不会计算差异,而是直接重新发出全部内容。
impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _f: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.label("Hello, egui!");
});
}
}上下文和 Ui
有两种核心类型会在各处出现。egui::Context 是用于访问本帧整个 UI 状态的共享句柄,克隆它的成本很低。
Ui 是用于添加控件的区域。面板和窗口会在闭包中提供一个 &mut Ui;您可以在其上调用 ui.label 或 ui.button 等方法。
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Dashboard");
ui.separator();
ui.label("Widgets go here.");
});控件会返回响应
由于 UI 会在每帧重建,您无法像在保留模式工具包中那样注册回调。相反,每次控件调用都会立即返回一个 Response。
您可以在同一帧检查这个 Response:.clicked()、.hovered()、.changed()。这样,事件处理就能与布局代码保持在一起。
if ui.button("Save").clicked() {
self.save();
}
let resp = ui.text_edit_singleline(&mut self.name);
if resp.changed() {
self.dirty = true;
}状态由您维护
egui 几乎不存储任何应用数据。您的控件值保存在 self 中的 App 结构体里,您会将可变引用传入控件。
文本字段接收 &mut String;复选框接收 &mut bool。egui 会通过引用写回数据,因此您的结构体就是唯一的事实来源。
struct MyApp {
name: String,
enabled: bool,
}
// inside update:
ui.text_edit_singleline(&mut self.name);
ui.checkbox(&mut self.enabled, "Enabled");设置 eframe
eframe 是在桌面和 Web 上承载 egui 的官方框架。它负责原生窗口、GPU 表面和事件循环。
您可以通过 eframe::run_native 启动程序,并传入窗口标题、原生选项以及用于构建您的 App 的闭包。
fn main() -> eframe::Result<()> {
let opts = eframe::NativeOptions::default();
eframe::run_native(
"My App",
opts,
Box::new(|_cc| Ok(Box::new(MyApp::default()))),
)
}由输入驱动重绘
默认情况下,egui 只会在发生某些事情时重绘:鼠标移动、按键,或显式请求重绘。这样可以节省空闲应用的 CPU 资源。
对于动画或实时数据,请调用 ctx.request_repaint() 来安排下一帧,或者使用带时长参数的 request_repaint_after 进行定时更新。
ui.label(format!("Tick: {}", self.counter));
self.counter += 1;
ctx.request_repaint(); // keep animating基本无需保留控件 ID
egui 根据调用顺序和位置推导控件身份,并自动生成 Id。对于需要在帧之间保持稳定状态的可折叠标题等控件,这一点很重要。
当两个控件可能发生冲突时——例如循环中标签相同的按钮——请用 ui.push_id(i, |ui| ...) 包裹它们,以消除歧义。
for (i, item) in self.items.iter().enumerate() {
ui.push_id(i, |ui| {
if ui.button(&item.label).clicked() {
println!("clicked {}", i);
}
});
}即时模式的取舍
即时模式让界面代码天然由数据驱动:一个 if 就能显示或隐藏某个区域,一个 for 就能渲染列表。您无需手动添加或移除节点,也不会出现模型与视图不同步的问题。
代价是每一帧都会重新运行整个界面,因此请保持每帧的工作量较小,并避免在 update 中进行繁重计算。
if self.logged_in {
ui.label(format!("Welcome, {}", self.user));
} else {
ui.label("Please sign in.");
}最小完整应用
把这些内容组合起来:结构体保存状态,update 绘制界面,main 运行窗口。这就是任何 egui 桌面程序的完整骨架。
其他所有内容——布局、主题、打包——都建立在这个循环之上。
#[derive(Default)]
struct App { count: i32 }
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
if ui.button("+1").clicked() { self.count += 1; }
ui.label(format!("Count: {}", self.count));
});
}
}Rust 为何钟爱 egui
egui 的设计充分发挥了 Rust 的优势。在一次 update 调用中将 &mut self 借用出去,可以让借用检查器保证控件与您的模型之间不会发生数据竞争。
这里没有捕获共享可变状态的回调,也没有因为保留树而被迫使用 Rc<RefCell> 图结构——只有简单的所有权,以及每帧一次可变遍历。
快速检查
测试您对即时模式的理解。
总结
egui 采用即时模式:整个界面都会在每一帧的 eframe::App::update 中重新构建。控件是返回 Response 的函数调用,您可以立即检查其结果。
您的数据保存在 self 中,并通过可变引用传递,因此借用检查器会让模型与视图保持同步。eframe::run_native 负责承载窗口和事件循环。接下来我们将探索控件集合和布局系统。
常见问题解答
「即时模式 UI 基础」课时是免费的吗?
是的 — 「即时模式 UI 基础」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 4 节课。
「即时模式 UI 基础」这节课中我会学到什么?
理解 egui 如何进行渲染。 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Learn Rust Coding 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「即时模式 UI 基础」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Learn Rust Coding 课中编写并运行代码吗?
能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。