#web-native #immediate #egui-integration #imgui #gamedev

egui

一个易于使用、即时模式的GUI,可在Web和本地环境中运行

46个版本 (27个破坏性版本)

0.28.1 2024年7月5日
0.27.2 2024年4月2日
0.27.1 2024年3月29日
0.24.1 2023年11月30日
0.1.2 2020年6月10日

GUI 中排名 1

Download history 34913/week @ 2024-05-02 31159/week @ 2024-05-09 33033/week @ 2024-05-16 34091/week @ 2024-05-23 35134/week @ 2024-05-30 34510/week @ 2024-06-06 35804/week @ 2024-06-13 31391/week @ 2024-06-20 31632/week @ 2024-06-27 38366/week @ 2024-07-04 39928/week @ 2024-07-11 41755/week @ 2024-07-18 41532/week @ 2024-07-25 41195/week @ 2024-08-01 47856/week @ 2024-08-08 42889/week @ 2024-08-15

每月下载量 180,216
用于 734 个crate(375个直接使用)

MIT/Apache

2.5MB
37K SLoC

🖌 egui:纯Rust中的易于使用GUI

github Latest version Documentation unsafe forbidden Build Status MIT Apache Discord

egui的开发由Rerun赞助,Rerun是一家构建
用于可视化多模态数据流的SDK的初创公司。


👉 点击运行Web演示 👈

egui(发音为“e-gooey”)是一个简单、快速且高度可移植的即时模式GUI库,适用于Rust。egui可以在Web、本地和您喜欢的游戏引擎中运行。

egui的目标是成为最容易使用的Rust GUI库,以及制作Rust中的Web应用的最简单方式。

egui可以在您绘制纹理三角形的地方使用,这意味着您可以轻松将其集成到您选择的任何游戏引擎中。

eframe是官方egui框架,它支持为Web、Linux、Mac、Windows和Android编写应用程序。

示例

ui.heading("My egui Application");
ui.horizontal(|ui| {
    ui.label("Your name: ");
    ui.text_edit_singleline(&mut name);
});
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}"));
ui.image(egui::include_image!("ferris.png"));

暗黑模式     亮色模式

部分

(egui的中文翻译文档 / 中文翻译)

快速开始

examples/文件夹中有一些简单的示例。如果您想编写一个Web应用程序,请访问https://github.com/emilk/eframe_template/并按照说明操作。官方文档在https://docs.rs/egui。要获得灵感和更多示例,请查看egui网页演示,并跟随其中的链接查看其源代码。

如果您想将egui集成到现有引擎中,请访问集成部分。

如果您有问题,请使用GitHub讨论。还有一个egui discord服务器。如果您想为egui做出贡献,请阅读贡献指南

演示

点击运行egui网页演示(在支持Wasm和WebGL的任何浏览器中运行)。使用eframe

要本地测试演示应用程序,请运行cargo run --release -p egui_demo_app

本地后端是egui_glow(使用glow),应该在Mac和Windows上直接运行,但在Linux上您需要先运行

sudoapt-get install -ylibclang-dev libgtk-3-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev

在Fedora Rawhide上,您需要运行

dnf install clang clang-devel clang-tools-extra libxkbcommon-devel pkg-config openssl-devel libxcb-devel gtk3-devel atk fontconfig-devel

注意:这只是为了演示应用程序 - egui本身是完全平台无关的!

目标

  • 最易于使用的GUI库
  • 响应式:调试构建中针对60 Hz
  • 友好:难以出错,且不会崩溃
  • 便携:相同的代码可以在Web和本地应用程序中使用
  • 易于集成到任何环境中
  • 一个简单的2D图形API,用于自定义绘制(epaint)。
  • 无回调
  • 纯立即模式
  • 可扩展性:易于为egui编写自己的小部件
  • 模块化:您应该能够使用egui的小部分并以新的方式组合它们
  • 安全性:egui中没有unsafe代码
  • 最小依赖

egui不是一个框架。egui是一个库,您可以直接调用它,而不是为一个编程环境。

注意:egui尚未实现所有这些目标!egui仍在开发中。

非目标

  • 成为最强大的GUI库
  • 原生外观的界面
  • 高级且灵活的布局(这与立即模式基本不兼容)

状态

egui正在积极开发中。它在其功能方面表现良好,但缺少许多功能,并且接口仍在变化。新版本将会有破坏性更改。

尽管如此,egui可以用来创建外观专业的应用程序,例如Rerun查看器

特性

  • 小部件:标签、文本按钮、超链接、复选框、单选按钮、滑块、可拖动值、文本编辑、颜色选择器、旋转器
  • 图片
  • 布局:水平、垂直、列、自动换行
  • 文本编辑:多行、复制/粘贴、撤销、支持表情符号
  • Windows:移动、调整大小、命名、最小化和关闭。自动调整大小和定位。
  • 区域:调整大小、垂直滚动、折叠标题(部分)、面板
  • 渲染:对线条、圆形、文本和凸多边形的抗锯齿渲染。
  • 悬停时显示工具提示
  • 通过AccessKit实现可访问性
  • 标签文本选择
  • 还有更多!

浅色主题

依赖项

egui具有最少的默认依赖项集

较重的依赖项被排除在egui之外,即使作为可选项。没有代码不是完全Wasm兼容的,是egui的一部分。

要加载图像到egui,可以使用官方的egui_extras存储库。

eframe另一方面有大量的依赖项,包括winitimage图形存储库、剪贴板存储库等。

egui适合谁?

egui旨在成为您想要以简单方式创建GUI或想要将GUI添加到游戏引擎时的最佳选择。

如果您不使用Rust,egui不是为您准备的。如果您想要看起来本地的GUI,egui不是为您准备的。如果您想要在升级时不会损坏的东西,egui不是为您准备的(目前还不是)。

但是,如果您正在用Rust编写需要简单GUI的交互式内容,egui可能适合您。

集成

egui旨在易于集成到您正在工作的任何现有游戏引擎或平台上。egui本身并不知道或关心它在哪个操作系统上运行或如何将内容渲染到屏幕上——这是egui集成的工作。

集成需要在每一帧执行以下操作

  • 输入:收集输入(鼠标、触摸、键盘、屏幕大小等)并将其提供给egui
  • 调用应用程序GUI代码
  • 输出:处理egui输出(光标更改、粘贴、纹理分配等)
  • 绘画:渲染egui生成的三角形网格(请参阅OpenGL示例

官方集成

以下为官方egui集成

  • eframe用于将相同的应用程序编译到Web/wasm和桌面/本地。使用egui-winitegui_glowegui-wgpu
  • egui_glow用于在本地和Web上使用glow渲染egui,并用于创建本地应用程序
  • egui-wgpu用于wgpu(WebGPU API)
  • egui-winit用于与winit集成

第三方集成

编写您自己的egui集成

缺少您正在工作的东西的集成?创建一个吧,很容易!请参阅 https://docs.rs/egui/latest/egui/#integrating-with-egui

为什么选择即时模式

egui是一个即时模式GUI库,与保留模式GUI库相反。保留模式与即时模式之间的区别可以用按钮的例子来最好地说明:在一个保留GUI中,你创建一个按钮,将其添加到某个UI中,并安装一些点击处理程序(回调)。按钮保留在UI中,要更改按钮上的文本,你需要存储某种对该按钮的引用。相比之下,在即时模式中,你立即显示按钮并与它交互,你每帧都这样做(例如,每秒60次)。这意味着不需要任何点击处理程序,也不需要存储对该按钮的任何引用。在egui中,它看起来像这样:if ui.button("保存文件").clicked() { save(file); }

有关即时模式的更详细描述,请参阅egui文档

这两种系统都有优点和缺点。

简而言之:即时模式GUI库更容易使用,但功能较少。

即时模式的优点

可用性

即时模式的主要优点是应用程序代码变得非常简单

  • 你永远不需要有任何点击处理程序和回调,这些回调会打断你的代码流程。
  • 你不必担心某个持久回调调用已经消失的东西。
  • 你的GUI代码可以很容易地生活在一个简单的函数中(不需要只为UI创建一个对象)。
  • 你不必担心应用程序状态和GUI状态不同步(即GUI显示过时的内容),因为GUI没有存储任何状态 - 它立即显示最新的状态。

换句话说,大量代码、复杂性和错误都消失了,你可以将时间集中在编写GUI代码之外更有趣的事情上。

即时模式的缺点

布局

即时模式的主要缺点是它使布局更加困难。比如说,你想要在屏幕中央显示一个小对话框窗口。为了正确定位窗口,GUI库必须首先知道窗口的大小。为了知道窗口的大小,GUI库必须首先布局窗口的内容。在保留模式中,这很容易:GUI库执行窗口布局,定位窗口,然后检查交互(“是否点击了OK按钮?”)。

在即时模式中,你会遇到一个悖论:为了知道窗口的大小,我们必须进行布局,但布局代码也会检查交互(“是否点击了OK按钮?”)因此它需要在显示窗口内容之前知道窗口的位置。这意味着我们必须在不知道窗口大小的前提下决定窗口的显示位置!

这是即时模式GUI的一个基本缺陷,任何试图解决它的尝试都会带来自己的缺点。

一种解决方案是存储大小并在下一帧中使用它。这会产生正确的布局延迟,导致第一次显示某些内容时的偶尔闪烁。egui为此做了一些事情,比如窗口和网格布局。

你还可以调用布局代码两次(一次获取大小,一次进行交互),但这不仅成本更高,而且实现起来也更复杂,在某些情况下两次还不够。egui永远不会这样做。

对于“原子”小部件(例如按钮),egui在显示之前就知道了其大小,因此在egui中实现按钮、标签等的居中无需任何特殊解决方案。

更多详情请见此问题

CPU使用率

由于即时模式GUI在每个帧都进行完整布局,布局代码需要快速。如果你的GUI非常复杂,这可能会对CPU造成压力。特别是,在滚动区域(带有非常长的滚动回退)中拥有非常大的UI可能会很慢,因为内容需要每帧都进行布局。

如果你考虑到这一点来设计GUI并避免使用巨大的滚动区域(或者只布局可见的部分),那么性能损失通常很小。对于大多数情况,你可以期望egui每帧占用1-2毫秒,但egui仍有很大的优化空间(这还不是我关注的重点)。egui只有在有交互(例如鼠标移动)或动画时才会重绘,所以如果你的应用程序处于空闲状态,就不会浪费CPU。

如果你的GUI交互性非常高,那么即时模式实际上可能比保留模式有更好的性能。打开任何网页并调整浏览器窗口大小,你会注意到浏览器在布局上非常慢,并且在这个过程中消耗了很多CPU。相比之下,调整egui中的窗口大小,你将获得无额外CPU成本下的平滑60 FPS。

标识符(IDs)

有一些GUI状态,你希望GUI库保留,即使在即时模式库egui中也是如此。这包括窗口的位置和大小以及用户在某些UI中的滚动距离。在这些情况下,你需要向egui提供一个唯一标识符(在父UI中唯一)的种子。例如:默认情况下,egui使用窗口标题作为唯一ID来存储窗口位置。如果你想有两个同名窗口(或一个具有动态名称的窗口),你必须向egui提供一些其他的ID来源(一些唯一的整数或字符串)。

egui还需要跟踪正在与之交互的窗口组件(例如正在拖动的滑块)。egui也使用唯一的ID来做这件事,但在这个情况下,ID是自动生成的,所以用户不需要担心这个问题。特别是,有两个具有相同名称的按钮是没有问题的(这与Dear ImGui形成对比)。

总的来说,ID处理是一个罕见的不便,并不是一个大的缺点。

常见问题解答

也请参阅GitHub 讨论区

我可以使用egui与非拉丁字符吗?

是的!但你需要使用Context::set_fonts安装自己的字体(.ttf.otf)。

我可以自定义egui的外观吗?

是的!你可以使用Context::set_style自定义颜色、间距、字体和大小。

这还不及CSS强大,但将会改进

以下是一个示例(来自https://github.com/a-liashenko/TinyPomodoro

如何在使用 async 的情况下使用 egui?

如果你在你的 GUI 代码中调用 .await,UI 将会冻结,这非常糟糕的用户体验。相反,保持 GUI 线程非阻塞,并通过类似以下方式与任何并发任务(async 任务或其他线程)进行通信:

如何创建一个文件对话框?

rfd 的异步版本支持原生和 Wasm。请参阅此处的示例应用程序 https://github.com/woelper/egui_pick_file,其中还包括通过 gitub pages 可用的演示。

关于可访问性,比如屏幕阅读器呢?

egui 包含对 AccessKit 的可选支持,目前该软件在 Windows 和 macOS 上实现了原生可访问性 API。此功能在 eframe 中默认启用。对于 AccessKit 尚未支持的平台(包括网络),有一个实验性的内置屏幕阅读器;在 网络演示 中,你可以在“后端”选项卡中启用它。

egui 中关于可访问性的原始讨论在 https://github.com/emilk/egui/issues/167。现在 AccessKit 支持已合并,为未来的可访问性工作提供了坚实的基础,请就特定的可访问性问题打开新的问题。

eguieframe 之间有什么区别?

egui 是一个用于布局和与按钮、滑块等交互的 2D 用户界面库。egui 不关心它是否在网络上或本地运行,也不知道如何收集输入或在屏幕上显示内容。这是 集成后端 的工作。

通常从游戏引擎中使用 egui(例如,使用 bevy_egui),但你也可以使用 eframe 独立使用 eguieframe 为网络和本地提供了集成,并处理输入和渲染。在 eframe 中,“frame”既指你的 egui 应用程序所在的框架,也指“框架”(eframe 是一个框架,egui 是一个库)。

如何在 egui 区域渲染 3D 内容?

有多种方法可以将 egui 与 3D 结合。最简单的方法是使用 3D 库,并在 3D 视图上放置 egui。例如,请参阅 bevy_eguithree-d

如果你想在 egui 视图中嵌入 3D 内容,有两个选项

Shape::Callback

示例

Shape::Callback 会在egui绘制时调用您的代码,以便使用背景渲染上下文显示任何内容。当使用 eframe 时,这将是 glow。其他集成可能会提供其他渲染上下文,如果它们支持 Shape::Callback 的话。

纹理渲染

您还可以将3D场景渲染到纹理中,并使用 ui.image() 显示。首先需要将本地纹理转换为 egui::TextureId,而如何做到这一点取决于您使用的集成。

示例

其他

约定和设计选择

所有坐标都在屏幕空间坐标中,(0, 0) 在左上角。

所有坐标都在逻辑“点”中,这些点可能由许多物理像素组成。

除非另有说明,所有颜色都有预乘的alpha值。

egui使用构建器模式来构建小部件。例如: ui.add(Label::new("Hello").text_color(RED)); 我并不是很欣赏构建器模式(在实现和使用中都很啰嗦),但在Rust有命名默认参数之前,这是我们能做的最好的。为了减轻一些冗余,有一些常用的辅助函数,例如 ui.label("Hello");

egui更倾向于使用传递给包装函数的 FnOnce 闭包来代替使用 begin/end 样式的函数调用(这可能导致错误),尽管lambda有点难看,所以我希望找到一个更好的解决方案。更多讨论请见 https://github.com/emilk/egui/issues/1004#issuecomment-1001650754

egui在每个对 Context 数据的访问时使用单个 RwLock 进行短时间锁定。这是为了使实现简单且事务性,并允许用户并行运行他们的UI逻辑。egui不是创建互斥量保护,而是使用传递给包装函数的闭包,例如 ctx.input(|i| i.key_down(Key::A))。这是为了减少用户意外双重锁定 Context 的可能性,这会导致死锁。

灵感

独一无二的 Dear ImGui 是一个出色的即时模式GUI库,适用于C++,支持多种后端。这个库彻底改变了我的GUI编程思维,让我从原本讨厌的编程任务转变为现在所享受的。

名称

该库和项目的名称是 "egui",发音为 "e-gooey"。请勿将其写作 "EGUI"。

该库最初被称为 "Emigui",但在2020年更名为 "egui"。

致谢

egui 作者和维护者:Emil Ernerfeldt (@emilk)。

以下为重要贡献者

egui 授权协议为 MITApache-2.0

  • 用于立方贝塞尔曲线和二次贝塞尔曲线的平铺算法来自 lyon_geom

默认字体


egui的开发由Rerun赞助,Rerun是一家构建
用于可视化多模态数据流的SDK的初创公司。

依赖项

~2-8.5MB
~67K SLoC