新闻详情

新闻详情

首页 / 资讯中心 / 详情

egui 外部事件循环集成实战:用 eframe::create_native 把 egui 应用嵌入你自己的 winit 事件循环

发布时间:2026/9/10 13:39:56来源:尧图网络
egui 外部事件循环集成实战:用 eframe::create_native 把 egui 应用嵌入你自己的 winit 事件循环
egui 外部事件循环集成实战用 eframe::create_native 把 egui 应用嵌入你自己的 winit 事件循环【免费下载链接】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 仓库中external_eventloop示例的核心技术如何不通过eframe::run_native启动程序而是借助eframe::create_native与 winit 的EventLoop手动接管事件循环让 egui 界面与其他窗口或第三方 GUI 工具包共存于同一个事件循环中。读完本文你将掌握外部事件循环的接入步骤、UserEvent的作用、底层渲染器分支原理并能结合external_eventloop_async示例理解异步场景下的进阶做法。一、示例背景为什么需要外部事件循环eframe默认的run_native会自行创建并独占 winit 事件循环这对大多数单窗口应用足够。但当你需要在同一进程内同时维护多个原生窗口、嵌入其他工具包如imgui-rs、iced或原生系统控件或者需要把 egui 界面作为某个更大程序的一部分时事件循环就必须由你来控制。仓库中的 external_eventloop/README.md 用一句话点明了这个示例的定位Example running an eframe application on an external eventloop. This allows you to run your eframe application alongside other windows and/or toolkits on the same event loop.也就是说这个示例演示的是把 eframe 应用挂到你自己创建的事件循环上从而与其他窗口/工具包共用同一条事件循环。对应的运行命令在 README 中给出cargo run -p external_eventloop该示例的完整源码位于 examples/external_eventloop/src/main.rs本文后续内容均围绕这份源码展开。二、整体结构从入口到界面external_eventloop示例的代码组织非常简洁全貌如下main()初始化日志、配置NativeOptions、创建外部事件循环、构建 eframe 应用并运行MyApp实现eframe::Apptrait定义界面的实际内容值累加、Spinner 开关、闪烁色块。其中MyApp通过RcCellu32在多个 UI 元素间共享一个可变状态初始值为 42并提供了三个交互控件struct MyApp { value: RcCellu32, // 共享的可变值 spin: bool, // 是否显示 spinner blinky: bool, // 是否显示闪烁色块 }点击 Increment Now 按钮会把value加 1ui.label实时显示当前值Toggle Spinner 控制ui.spinner()的显示Toggle Blinky 则利用ui.input(|i| i.time)读取全局时间按now % 1.0 0.5的规则在红/透明之间切换一个圆角Frame的颜色并通过ui.request_repaint_after_secs安排下一次重绘。这部分 UI 代码与普通 eframe 应用并无二致重点在于下方的事件循环接入方式。三、核心接入步骤create_native 外部 EventLoop3.1 创建带用户事件的事件循环外部事件循环接入的第一步是创建一个携带 eframe 自定义事件类型的事件循环let eventloop EventLoop::UserEvent::with_user_event().build().unwrap(); eventloop.set_control_flow(ControlFlow::Poll);这里的关键是EventLoop::UserEvent。UserEvent是 eframe 在 crates/eframe/src/native/winit_integration.rs 中定义的自定义事件类型目前包含两个变体RequestRepaint { viewport_id, when, cumulative_pass_nr }请求重绘携带目标视口、期望重绘时刻与累计 pass 序号AccessKitActionRequest(accesskit_winit::Event)启用accesskitfeature 时无障碍相关的动作请求。eframe 内部通过EventLoopProxyUserEvent向这条循环投递RequestRepaint事件参见 crates/eframe/src/native/glow_integration.rs 与 crates/eframe/src/native/wgpu_integration.rs因此你的事件循环必须用UserEvent作为泛型参数否则无法接收 eframe 的重绘请求。set_control_flow(ControlFlow::Poll)表示循环持续轮询事件。你也可以改用ControlFlow::Wait或WaitUntil(deadline)external_eventloop_async示例展示了根据ControlFlow动态调整等待策略的做法详见下文第五节。3.2 用 create_native 构建 eframe 应用拿到事件循环后调用eframe::create_native创建应用let mut winit_app eframe::create_native( External Eventloop Application, // 应用名 options, // NativeOptions Box::new(|_| Ok(Box::MyApp::default())), // App 创建回调 eventloop, // 外部事件循环的引用 );其中options与普通 eframe 程序相同示例里指定了初始窗口大小let options eframe::NativeOptions { viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]), ..Default::default() };从源码看create_native的完整签名位于 crates/eframe/src/lib.rspub fn create_nativea( app_name: str, mut native_options: NativeOptions, app_creator: AppCreatora, event_loop: winit::event_loop::EventLoopUserEvent, ) - EframeWinitApplicationa其内部先调用init_native完成初始化若viewport.title与viewport.app_id未设置会默认使用app_name填充然后根据native_options.renderer的分支分别调用create_glow或create_wgpu最终统一包装成EframeWinitApplication返回。也就是说创建过程中事件循环并未被运行它只是被借用注册进去这正是一切外部循环集成的关键。create_native的 doc 注释还给出了一个最小化的等价用法模板见 crates/eframe/src/lib.rs其骨架与示例完全一致并明确提示See theexternal_eventloopexample for a more complete example说明本示例就是官方推荐的外部事件循环参考实现。3.3 运行事件循环最后把winit_app交给事件循环运行eventloop.run_app(mut winit_app)?; Ok(())EframeWinitApplication实现了 winit 的ApplicationHandlerUserEventtrait参见 crates/eframe/src/native/run.rs并在user_event回调中处理UserEvent::RequestRepaint与UserEvent::AccessKitActionRequest。run_app是阻塞调用将一直运行到应用退出并返回Result因此main函数声明返回eframe::Result。四、源码级原理渲染器分支与依赖配置4.1 渲染器选择create_native的渲染器分支逻辑与run_native完全一致由NativeOptions::renderer决定启用glowfeature 时走Renderer::Glow调用create_glow启用wgpu_no_default_features时走Renderer::Wgpu调用create_wgpu。因此外部事件循环方案同时支持 OpenGLglow与 wgpu 两种后端不需要额外改动代码。示例的 Cargo.toml 中依赖声明为eframe { workspace true, features [default, __screenshot] } env_logger { workspace true, features [auto-color, humantime] } winit.workspace true其中__screenshotfeature 用于支持通过EFRAME_SCREENSHOT_TO环境变量输出截图CI 快照测试用日常本地运行并非必需。由于 eframe 默认 feature 已包含 winit示例中显式声明winit.workspace true是为了直接使用EventLoop、ControlFlow等 API。4.2 阻塞运行 vs 手动泵送eventloop.run_app(mut winit_app)?是阻塞式运行适合e frame 应用 少量外部窗口的简单场景。但如果你的外部事件循环还需要同时驱动其他工具例如异步运行时阻塞式run_app就会独占控制权。此时应改用pump_eframe_app手动泵送事件——它位于 crates/eframe/src/native/run.rs通过 winit 的pump_app_events每次处理一批事件并返回EframePumpStatusEframePumpStatus::Continue(ControlFlow)本轮事件处理完毕返回最终的ControlFlow调用方据此决定下一步动作EframePumpStatus::Exit(i32)应用请求退出携带退出码。五、进阶参考external_eventloop_async 中的异步循环仓库中还有一个配套示例 external_eventloop_async它把 tokio 异步运行时与外部事件循环结合展示了更完整的实战形态。其核心思路源码见 examples/external_eventloop_async/src/app.rs同样用EventLoop::UserEvent::with_user_event()创建事件循环并调用create_native用eventloop.as_raw_fd()把事件循环的文件描述符注册进 tokio 的AsyncFd这样 winit 事件可以被await等待依据ControlFlow决定等待方式Poll时直接轮询Wait时等待 fd 可读WaitUntil(deadline)时用tokio::time::timeout_at限时等待循环内调用winit_app.pump_eframe_app(mut eventloop, None)泵送 eframe 事件并根据返回的EframePumpStatus更新control_flow或退出循环。该示例还在 UI 中演示了异步任务的经典写法点击 Increment Later 后用Rc::clone共享valuetokio::task::spawn_local启动异步任务sleep1 秒后更新值并调用ctx.request_repaint()通知界面重绘。这里能安全使用spawn_local正是因为整个循环运行在单线程的LocalSet中——外部事件循环与异步运行时共享同一个线程这也是此类集成最常见的架构约束。六、注意事项与适用边界仅限原生平台create_native带有#[cfg(not(target_arch wasm32))]限制见 crates/eframe/src/lib.rsWeb 端并不适用本文方案面向桌面/原生场景。渲染器 feature使用前需确保eframe至少启用glow或wgpu_no_default_features二者之一create_native依赖这两条 feature 路径。UserEvent泛型不可省略事件循环必须声明为EventLoopUserEvent否则 eframe 内部通过EventLoopProxyUserEvent投递的重绘请求将无处安放界面将无法刷新。控制流选择ControlFlow::Poll会持续占用 CPU若想省电可参照 async 示例按需在Wait/WaitUntil之间切换。日志调试示例通过env_logger::init()初始化日志运行时可搭配RUST_LOGdebug cargo run -p external_eventloop观察 eframe 的渲染器选择与重绘调度日志。七、小结external_eventloop示例完整展示了 eframe 对外部事件循环的官方接入姿势用EventLoop::UserEvent承载 eframe 的自定义事件用create_native把应用挂载到既有循环上再用run_app阻塞或pump_eframe_app手动泵送驱动运行。结合 crates/eframe/src/lib.rs 的源码实现、crates/eframe/src/native/winit_integration.rs 的UserEvent定义以及 crates/eframe/src/native/run.rs 的泵送机制你可以把这条接入链路推广到自己的多窗口、多工具包或异步运行时场景中。【免费下载链接】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),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

频散曲线求解程序开发与工程应用实践 2026/9/10 14:22:06

频散曲线求解程序开发与工程应用实践

1. 频散曲线求解程序的核心价值与应用场景在地球物理勘探、材料科学和声学工程领域,频散曲线分析是一项基础而关键的技术手段。这种描述波传播速度随频率变化的曲线,能够揭示介质内部的物理特性与结构特征。传统的手工计算方法不仅效率低下,而…

阅读更多 →
Python开发实战:从基础到工程化的全面指南 2026/9/10 14:22:06

Python开发实战:从基础到工程化的全面指南

1. Python软件基础认知框架 作为从C转战Python的老程序员,我经常被新手问到一个核心问题:"到底什么是Python软件?"这绝不仅仅是一个安装包那么简单。让我们从二进制可执行文件的本质说起——当你在命令行输入 python script.py 时…

阅读更多 →
gRPC Wait-for-Ready 语义深度解析:通道状态机、配置开关与实战验证 2026/9/10 14:22:06

gRPC Wait-for-Ready 语义深度解析:通道状态机、配置开关与实战验证

gRPC Wait-for-Ready 语义深度解析:通道状态机、配置开关与实战验证 【免费下载链接】grpc C based gRPC (C, Python, Ruby, Objective-C, PHP, C#) 项目地址: https://gitcode.com/GitHub_Trending/gr/grpc 导读 本文以 gRPC 仓库中的官方设计文档 doc/wai…

阅读更多 →
Deepin 25系统监控工具v2.5开发与优化指南 2026/9/10 14:22:06

Deepin 25系统监控工具v2.5开发与优化指南

1. 项目概述:系统监控桌面小工具 v2.5 for Deepin 25 最近在Deepin社区看到不少用户讨论系统资源监控的需求,特别是升级到Deepin 25后,原有的系统监视器功能无法满足个性化需求。作为一个长期使用Deepin的开发者,我决定将自用的系…

阅读更多 →
Debian系统编译安装FreeSWITCH 1.10.12全指南 2026/9/10 14:22:06

Debian系统编译安装FreeSWITCH 1.10.12全指南

1. FreeSWITCH 1.10.12 编译环境准备 在Debian最新稳定版系统上编译FreeSWITCH 1.10.12之前,需要确保基础编译环境和依赖库完整。我通常使用Debian 12 "Bookworm"作为基准环境,这是目前最稳定的发行版本。 1.1 系统基础配置 首先更新系统并安…

阅读更多 →
Storybook Addon 开发:使用 `setQueryParams` 删除 URL 查询参数 2026/9/10 14:19:06

Storybook Addon 开发:使用 `setQueryParams` 删除 URL 查询参数

Storybook Addon 开发:使用 setQueryParams 删除 URL 查询参数 【免费下载链接】storybook Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation 项目地址: https://gitcode.com/GitHub_Trending/st…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞