egui 关闭确认对话框实战:用 close_requested 与 CancelClose 拦截窗口退出

发布时间:2026/10/31 15:35:05

egui 关闭确认对话框实战:用 close_requested 与 CancelClose 拦截窗口退出 egui 关闭确认对话框实战用 close_requested 与 CancelClose 拦截窗口退出【免费下载链接】eguiegui: an easy-to-use immediate mode GUI in Rust that runs on both web and native项目地址: https://gitcode.com/GitHub_Trending/eg/egui导读在使用 Rust egui/eframe 开发桌面应用时常常需要在用户点击窗口关闭按钮后弹出确认框避免误关导致未保存数据丢失。本篇文章以仓库中 examples/confirm_exit 官方示例为主体完整讲解如何监听视口viewport的关闭请求、通过ViewportCommand::CancelClose取消关闭并弹窗询问、再通过ViewportCommand::Close真正退出这一完整流程并深入到 eframe 与 egui-winit 源码说明这套机制在底层是如何被处理的。读完本文你将能独立实现关闭前确认未保存工作提醒等常见交互。示例概览与运行方式该示例位于 examples/confirm_exit核心文件为 src/main.rs。它实现了一个 320×240 的小窗口标题为 Confirm exit窗口中只有一行文字 Try to close the window当你点击窗口右上角的关闭按钮时不会立刻退出而是弹出 Do you want to quit? 对话框提供 No 与 Yes 两个按钮。运行方式非常简单在仓库根目录执行cargo run -p confirm_exit示例的 Cargo.toml 显示它依赖工作区中的eframe启用默认特性并额外启用了__screenshot特性用于 CI 中通过EFRAME_SCREENSHOT_TO环境变量导出截图仓库中的 screenshot.png 即由此生成。核心机制一次关闭被拆成两半要理解这个示例关键是明白 egui 对关闭窗口的处理方式关闭请求close request与真正关闭close是分离的两件事。用户点击窗口关闭按钮时窗口系统发出关闭请求egui 将这一请求记录为视口信息中的close_requested标志应用可以在每一帧读取它应用可以选择取消这次关闭发送ViewportCommand::CancelClose也可以选择放行什么都不做或发送ViewportCommand::Close只有未被取消的关闭请求才会真正导致窗口销毁、程序退出。在 crates/egui/src/data/input/viewport_info.rs 中close_requested()的实现如下/// This viewport has been told to close. /// /// If this is the root viewport, the application will exit /// after this frame unless you send a /// [crate::ViewportCommand::CancelClose] command. /// /// If this is not the root viewport, /// it is up to the user to hide this viewport the next frame. pub fn close_requested(self) - bool { self.events.contains(ViewportEvent::Close) }注意注释中强调的两点对于根视口即主窗口应用会在该帧结束后退出除非发送了CancelClose对于子视口额外的原生窗口则需要用户自行决定是否隐藏它。对应地crates/egui/src/viewport.rs 中定义了两个方向相反的视口命令pub enum ViewportCommand { /// Request this viewport to be closed. /// /// For the root viewport, this usually results in the application shutting down. /// For other viewports, the [crate::ViewportInfo::close_requested] flag will be set. Close, /// Cancel the closing that was signaled by [crate::ViewportInfo::close_requested]. CancelClose, // ... }ViewportCommand是 egui 输出给后端eframe/winit的命令应用通过Context::send_viewport_cmd发送见 crates/egui/src/viewport.rs 的说明。逐步拆解示例代码1. 应用状态与入口#[derive(Default)] struct MyApp { show_confirmation_dialog: bool, allowed_to_close: bool, }示例用两个布尔值管理状态show_confirmation_dialog控制确认弹窗是否可见allowed_to_close记录用户是否已经明确同意关闭即点击过 Yes。main函数中通过eframe::NativeOptions设置初始窗口大小并调用eframe::run_native启动应用fn main() - eframe::Result { env_logger::init(); // Log to stderr (if you run with RUST_LOGdebug). let options eframe::NativeOptions { viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]), ..Default::default() }; eframe::run_native( Confirm exit, options, Box::new(|_cc| Ok(Box::MyApp::default())), ) }2. 在每一帧检查关闭请求ui回调中在绘制完主面板后立即检查用户是否请求关闭if ui.input(|i| i.viewport().close_requested()) { if self.allowed_to_close { // do nothing - we will close } else { ui.send_viewport_cmd(egui::ViewportCommand::CancelClose); self.show_confirmation_dialog true; } }这里的逻辑是一旦检测到close_requested()为真说明用户点了关闭按钮如果allowed_to_close为真说明此前已确认过什么都不做关闭请求会被放行程序正常退出否则通过send_viewport_cmd(egui::ViewportCommand::CancelClose)取消本次关闭并置show_confirmation_dialog true弹出确认对话框。ui.input(...)是 egui 中安全读取当前帧输入的方式其中i.viewport()返回 ViewportInfoclose_requested()即为上文所述的关闭标志。3. 弹出确认对话框if self.show_confirmation_dialog { egui::Window::new(Do you want to quit?) .collapsible(false) .resizable(false) .show(ui.ctx(), |ui| { ui.horizontal(|ui| { if ui.button(No).clicked() { self.show_confirmation_dialog false; self.allowed_to_close false; } if ui.button(Yes).clicked() { self.show_confirmation_dialog false; self.allowed_to_close true; ui.send_viewport_cmd(egui::ViewportCommand::Close); } }); }); }点击No关闭对话框show_confirmation_dialog false并重置allowed_to_close false让下次关闭仍然需要确认点击Yes关闭对话框、置allowed_to_close true然后主动发送ViewportCommand::Close再次发起关闭请求。由于此时allowed_to_close已经为真下一帧检查到关闭请求时会放行程序随之退出。之所以点击 Yes 后要重新发送ViewportCommand::Close是因为用户第一次点关闭按钮时发出的请求已经被CancelClose取消了必须再发一次才能让窗口真正关闭。这正是该示例最精妙也最容易被忽略的一点。源码级原理eframe 如何处理关闭请求理解了应用层代码我们再深入 eframe 与 egui-winit 看这套机制如何落地。eframe 的 update 流程在 crates/eframe/src/native/epi_integration.rs 的update方法中eframe 先读取本次输入的关闭标志再运行 egui UI最后检查输出中是否包含取消命令let close_requested raw_input.viewport().close_requested(); // ... let full_output self.egui_ctx.run_ui(raw_input, |ui| { /* app 的 logic 与 ui */ }); if is_root_viewport close_requested { let canceled full_output.viewport_output[ViewportId::ROOT] .commands .contains(egui::ViewportCommand::CancelClose); self.handle_close_request(canceled); }handle_close_request的实现在同一文件的 L403-L409fn handle_close_request(mut self, canceled: bool) { if canceled { log::debug!(Closing of root viewport canceled with ViewportCommand::CancelClose); } else { log::debug!(Closing root viewport (ViewportCommand::CancelClose was not sent)); self.close true; } }可以看到决定窗口是否关闭的唯一依据就是本帧输出到根视口的命令里是否包含ViewportCommand::CancelClose。如果包含关闭被取消否则设置内部标志close true驱动主循环退出。也就是说即使示例代码不发送CancelClose只要应用在收到关闭请求的那一帧什么都不做窗口照样会关闭——取消关闭是主动动作必须显式发送命令。egui-winit 侧的事件来源关闭请求最初来自窗口系统。在 crates/egui-winit/src/lib.rs 附近winit 的WindowEvent::CloseRequested被转换为 egui 的视口事件最终体现为ViewportInfo中的ViewportEvent::Close从而被close_requested()读取。而ViewportCommand::Close与CancelClose这两个命令在 crates/egui-winit/src/lib.rs 的处理也值得注意ViewportCommand::Close { info.events.push(egui::ViewportEvent::Close); } ViewportCommand::CancelClose { // Need to be handled elsewhere }Close会把关闭事件重新推入视口事件列表再次触发close_requested而CancelClose在此处无需处理——它由上一节介绍的 eframeupdate流程在更高层看到并决定是否放行。这正是示例中点击 Yes 后再次发送 Close能被正常响应、且allowed_to_close标志能在下一帧生效的原因。与旧 API 的对比在 crates/eframe/CHANGELOG.md 中记录了本机制的演进App::on_close_eventhas been replaced withctx.input(|i| i.viewport().close_requested())andctx.send_viewport_cmd(ViewportCommand::CancelClose).同时crates/eframe/src/epi.rs 中App::on_exit的文档也明确提示If you need to abort an exit checkctx.input(|i| i.viewport().close_requested())and respond withegui::ViewportCommand::CancelClose.也就是说on_exit是程序确定退出前的最后一个钩子通常用于保存状态、释放资源而关闭拦截要放在每一帧的 UI 回调中完成两者职责不同可以组合使用用close_requestedCancelClose拦截并询问确认退出后由on_exit做收尾保存。实战扩展从确认退出到未保存工作提醒示例展示的是最简形态你可以在此基础上演化出更实用的场景1. 未保存数据检测。把allowed_to_close换成是否有未保存修改的判断例如if ui.input(|i| i.viewport().close_requested()) { if self.document.is_saved() { // 放行 } else { ui.send_viewport_cmd(egui::ViewportCommand::CancelClose); self.show_save_dialog true; } }2. 多视口应用。对于通过Context::show_viewport创建的子视口关闭请求同样体现在其close_requested标志上由于子视口关闭不会自动退出整个程序处理方式可以是确认后隐藏子视口见 crates/egui/src/context.rs 中关于子视口关闭标志的说明。3. 键盘快捷键触发关闭。也可以主动向视口发送ViewportCommand::Close来模拟关闭请求如响应CtrlW走完全相同的确认流程实现方式与示例中 Yes 按钮的代码一致。注意事项与平台差异必须在发出关闭请求的同一帧内决定是否取消egui 是立即模式immediate modeCancelClose的判定以该帧输出为准因此不能异步延时后才决定如果弹出的是模态确认框通常要保证在用户做出选择前后续每一帧都检查并CancelClose直到用户点击按钮。allowed_to_close标志的生命周期示例在用户点 Yes 后先置标志再发Close命令依赖下一帧的检查来放行。若你的确认流程跨越多帧例如确认框异步等待需要确保每次收到close_requested且尚未确认时都发送CancelClose否则窗口会提前退出。Web 端行为本示例针对桌面原生窗口eframe native。Web 端浏览器标签页的关闭由浏览器接管egui 无法可靠拦截close_requested机制主要适用于原生视口场景。调试日志示例启用了env_logger用RUST_LOGdebug cargo run -p confirm_exit运行时可以在 stderr 中看到 eframe 输出的 Closing of root viewport canceled ... / Closing root viewport ... 日志crates/eframe/src/native/epi_integration.rs有助于排查关闭流程是否按预期工作。小结confirm_exit示例虽然只有几十行代码却完整演示了 egui 视口关闭机制的三个关键环节读取关闭请求close_requested()→ 取消关闭ViewportCommand::CancelClose→ 确认后重新发起ViewportCommand::Close。从源码看eframe 在每一帧结束后根据根视口命令中是否含CancelClose决定是否真正退出epi_integration.rs这既是该示例正确运行的基础也是你在自己应用中实现关闭拦截、未保存提醒等功能的底层依据。相关完整源码与运行说明可继续查看 examples/confirm_exit以及 crates/eframe/src/epi.rs 中Apptrait 的接口文档。【免费下载链接】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),仅供参考
延伸阅读

更多相关文章

2026/10/29 15:15:51

双目视觉测量全流程:从相机标定到工件变形量计算

简介:本资源是一套面向本科毕业设计、课程设计及工业视觉项目开发的双目视觉测量完整实现方案,聚焦工件三维形变量化分析这一典型机器视觉应用。项目融合OpenCV图像处理、MATLAB相机标定工具箱与C高性能计算,系统完成图像预处理、特征点提取、…

2026/10/25 10:04:01

CVAT安装教程:3步在Docker里跑起视觉标注平台

CVAT安装教程:3步在Docker里跑起视觉标注平台 【免费下载链接】cvat Computer Vision Annotation Tool (CVAT) is a leading platform for building high-quality visual datasets for vision AI. It offers open-source, cloud, and enterprise products, as well …

2026/10/31 15:34:26

DV-HOP节点定位精度优化:多通信半径与跳距加权

简介:面向无线传感器网络定位研究者的DV-Hop改进算法仿真材料,围绕多通信半径与跳距加权策略降低误差传播、提升节点定位精度,适合WSN课程设计、算法复现与参数对比等场景的高年级学生和开发人员参考。压缩包共5个文件,约66KB&…

2026/10/31 15:34:26

Calibre 3Dstack connect命令详解与IC验证实践

1. Calibre 3Dstack命令解析:connect功能深度剖析在IC验证领域,Calibre作为业界标准的物理验证工具,其3Dstack模块专门用于处理先进封装设计的复杂验证需求。今天我们要重点解析的是3Dstack模块中的connect命令——这个看似简单的指令在实际工…

2026/10/31 15:34:26

ponytail 轻量级任务编排与插件化扩展实战指南

1. 从“ponytail”这个标题说起:它到底是什么第一次看到“ponytail”这个词,很多人脑子里蹦出来的画面是扎在脑后的马尾辫。但在技术圈和工具链语境里,它早就不是发型那么简单了。我最早接触到这个词,是在一个前端工程化的讨论群里…

2026/10/31 15:34:26

ponytail 工具实战:轻量级信息聚合与快捷操作工作流搭建指南

1. 从“ponytail”这个标题说起:它到底是什么第一次看到“ponytail”这个词,很多人脑子里蹦出来的画面是扎起来的马尾辫。但在技术圈和效率工具圈里,ponytail 早就不是发型的意思了。它是一类轻量级信息聚合与快捷操作工具的代称,…

2026/10/31 15:34:26

Calibre 3Dstack工具链与connect命令在3D IC验证中的应用

1. Calibre 3Dstack工具链概述Calibre 3Dstack是Mentor Graphics(现为Siemens EDA)推出的针对3D IC设计的物理验证解决方案。作为Calibre工具家族的重要成员,它专门处理芯片堆叠设计中的复杂互连验证问题。在先进封装技术如HBM、Chiplet等架构…

2026/10/31 15:29:26

语音社交产品冷启动策略与内容增长引擎设计

1. 语音社交产品的冷启动困境与破局点2023年全球语音社交市场规模已达47.8亿美元,但新产品的90日留存率普遍低于15%。这个数据背后反映的是语音社交产品面临的典型增长困境:用户获取成本高、内容沉淀难、社交关系链脆弱。我在操盘多款语音社交产品时发现…

2026/10/30 20:55:10

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/30 20:55:10

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/30 20:55:09

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/31 0:02:41

猫狗目标检测数据集:VOC/COCO/YOLO三格式一体化实战包

简介:本资源是一套面向计算机视觉初学者与YOLO目标检测实践者的猫狗图像数据集及配套开发工具包,专为快速上手目标检测模型训练而设计。资源包含1000张真实场景高清猫狗图片,标注质量高,同时提供VOC(XML)、…

2026/10/31 0:02:41

HDU2017多校标程复现:算法竞赛工程化真题沙盒

简介:本资源为2017年HDU多校联合训练第一场官方配套资料,面向ACM程序设计竞赛备赛学生、算法爱好者及高校编程集训队成员,旨在提供权威解题参考与完整测试验证环境。压缩包共40个文件,含15份C标程(覆盖全部赛题&#x…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑