什么是egui?
在本篇尝试egui的文章中,我们将探索egui的定义、使用原因及方法,并通过构建一个西斯特西安钟来展示其实用性。西斯特西安钟采用了一种比现代阿拉伯数字更为古老的数字表示形式。egui 是一种即时模式图形用户界面(GUI)库,适合初学者快速上手。即时模式意味着交互逻辑直接嵌入UI代码之中,这与保留模式不同,在后者中,会维护一个独立的UI状态模型,并使用回调函数来更新界面。在egui中,用于显示部件的闭包同样包含了当用户与应用互动时更新应用状态的代码。
对于游戏开发者来说,egui是一个不错的选择,可以用于实时调整游戏参数,如物体大小或难度设置,而无需中断游戏进程。这种方式类似于即时模式编程,提供了快速构建应用的优势,尽管可能会增加CPU使用率;简化了按钮实现,无需回调函数;并且不需要检查应用程序是否与虚拟状态模型同步。受到C++世界中的即时模式库启发,egui为那些寻找Dear ImGui替代品以配合Bevy或Macroquad使用的Rust新手提供了帮助。
配置Rust环境
如果系统尚未安装Rust,请按照以下步骤操作:
首先,安装rustup,这是管理和更新本地Rust编译器和工具链的程序:
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
同时,还需安装C编译器(如GCC或Clang)。在macOS上,可以通过xcode-select --install命令获取Xcode的最小版本,除非已安装完整版Xcode。
确认安装成功:
rustc --version
启动egui模板
为了快速开始,有一个现成的模板可供使用。如果已安装GitHub CLI,可以通过终端创建新的仓库:
# 堆代码 duidaima.com
gh repo create cistercian-clock \
--template "emilk/eframe_template" --public
git clone \
https://github.com/YOUR-GITHUB-PROFILE/cistercian-clock.git
若未使用GitHub CLI,登录GitHub后点击页面右上角的“使用此模板”按钮来创建新仓库并克隆至本地。
进入新仓库文件夹并运行:
cargo run
首次执行可能较慢,因为需要下载依赖项并编译代码。后续编译将更快,仅重新编译更改过的部分。
定制模板
接下来,根据项目需求修改Cargo.toml文件中的信息,以及一些源代码文件。例如,将TemplateApp替换为CistercianClockApp,并添加必要的依赖项,如chrono用于时间处理,image用于解码自定义应用图标文件。最后,在src/main.rs和src/app.rs中做出相应更改,确保所有引用都指向新的应用名称。
通过这些步骤,您应该能够顺利地基于egui模板创建自己的西斯特西安钟应用。那么,现在就开始动手试试看吧?如果您对如何进一步优化这个应用有任何想法,不妨留言告诉我们! 为了使应用程序在WebAssembly (WASM) 上运行,一些细节被省略了。现在,保存 Cargo.toml、src/app.rs、src/lib.rs 和 src/main.rs 文件,并执行以下命令:
cargo run
应用程序应该像以前一样正常工作。不过,窗口标题等元素仍然显示为“eframe template”,稍后会对其进行修改。接下来,添加一个自定义的应用程序图标。
为egui应用添加自定义图标
需要准备一张256×256像素的PNG格式图标。如果已经有了SVG格式的logo,可以参考相关帖子将其转换成PNG格式。将 assets/icon-256.png 替换为自己的PNG图标,并更新 src/main.rs 文件以使用新的图标:
// src/main.rs
// 堆代码 duidaima.com
#![warn(clippy::all, rust_2018_idioms)]
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // 在Windows上发布时隐藏控制台窗口
use egui::IconData;
#[cfg(not(target_arch = "wasm32"))]
fn main() -> eframe::Result<()> {
env_logger::init(); // 如果用 `RUST_LOG=debug` 运行,则日志输出到标准错误流。
let icon_image = image::open("assets/icon-256.png").expect("应能打开图标PNG文件");
let width = icon_image.width();
let height = icon_image.height();
let icon_rgba8 = icon_image.into_rgba8().to_vec();
let icon_data = IconData {
rgba: icon_rgba8,
width,
height,
};
let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([750.0, 600.0]) // **调整了窗口尺寸**
.with_min_inner_size([750.0, 600.0]) // **最小窗口尺寸**
.with_icon(icon_data),
..Default::default()
};
eframe::run_native(
"Cistercian Clock", // **更改了窗口标题**
native_options,
Box::new(|cc| Ok(Box::new(cistercian_clock::CistercianClockApp::new(cc)))),
)
}
尝试重新运行应用程序,应该能看到新的图标已经生效!为了让开发过程更加高效,可以通过安装 cargo-watch 工具来实现自动编译和运行:
cargo install cargo-watch
然后使用如下命令进行自动化开发流程:
cargo watch -x check -x clippy -x run
这不仅会检查代码是否能够编译通过,还会使用clippy进行代码风格检查,最后运行程序。如果有进行测试驱动开发(TDD),还可以加入 -x test 来运行测试。对于egui中更有趣的部分,比如Cistercian钟表的代码实现,直接粘贴代码可能更为快捷。下面仅展示 src/app.rs 的一部分代码片段,完整代码请参照项目仓库或文档。
// src/app.rs
use chrono::{Local, Timelike};
use core::time::Duration;
use egui::{
epaint::Shadow,
scroll_area::ScrollBarVisibility,
style::{HandleShape, Selection, Widgets},
vec2, Color32,
FontFamily::Proportional,
FontId, Painter, Pos2, Rounding, ScrollArea, Sense, Stroke,
TextStyle::{self, Body, Button, Heading, Monospace, Name, Small},
Ui, Vec2, Visuals,
};
fn dark_mode_override() -> Visuals {
Visuals {
dark_mode: true,
override_text_color: Some(Color32::from_gray(252)),
widgets: Widgets::default(),
selection: Selection::default(),
hyperlink_color: Color32::from_rgb(90, 170, 255),
faint_bg_color: Color32::from_additive_luminance(5),
extreme_bg_color: Color32::from_gray(10),
code_bg_color: Color32::from_gray(64),
warn_fg_color: Color32::from_rgb(255, 143, 0),
error_fg_color: Color32::from_rgb(255, 0, 0),
window_rounding: Rounding::same(6.0),
window_shadow: Shadow {
offset: vec2(10.0, 20.0),
blur: 15.0,
spread: 0.0,
color: Color32::from_black_alpha(96),
},
window_fill: Color32::from_rgb(23, 18, 25),
window_stroke: Stroke::new(1.0, Color32::from_gray(60)),
window_highlight_topmost: true,
menu_rounding: Rounding::same(6.0),
panel_fill: Color32::from_rgb(23, 18, 25),
popup_shadow: Shadow {
offset: vec2(10.0, 20.0),
blur: 15.0,
spread: 0.0,
color: Color32::from_black_alpha(96),
},
resize_corner_size: 12.0,
text_cursor: Default::default(),
clip_rect_margin: 3.0,
button_frame: true,
collapsing_header_frame: false,
indent_has_left_vline: true,
striped: false,
slider_trailing_fill: false,
handle_shape: HandleShape::Rect { aspect_ratio: 1.0 },
interact_cursor: None,
image_loading_spinners: true,
numeric_color_space: NumericColorSpace::GammaByte,
}
}
pub fn light_mode_override() -> Visuals {
Visuals {
dark_mode: false,
override_text_color: Some(Color32::from_rgb(4, 3, 15)),
widgets: Widgets::light(),
selection: Selection::default(),
hyperlink_color: Color32::from_rgb(0, 155, 255),
faint_bg_color: Color32::from_additive_luminance(5),
extreme_bg_color: Color32::from_gray(255),
code_bg_color: Color32::from_gray(230),
warn_fg_color: Color32::from_rgb(255, 100, 0),
error_fg_color: Color32::from_rgb(255, 0, 0),
window_shadow: Shadow {
offset: vec2(10.0, 20.0),
blur: 15.0,
spread: 0.0,
color: Color32::from_black_alpha(25),
},
window_fill: Color32::from_gray(255),
window_stroke: Stroke::new(1.0, Color32::from_gray(190)),
panel_fill: Color32::from_gray(255),
popup_shadow: Shadow {
offset: vec2(6.0, 10.0),
blur: 8.0,
spread: 0.0,
color: Color32::from_black_alpha(25),
},
text_cursor: TextCursorStyle {
stroke: Stroke::new(2.0, Color32::from_rgb(0, 83, 125)),
..Default::default()
},
..Visuals::dark()
}
}
这段代码展示了如何定制深色模式和浅色模式的视觉效果。这里对颜色、阴影、边框等属性进行了精心设计,以确保界面既美观又实用。通过这种方式,开发者可以轻松地根据需求调整应用程序的外观,同时保持高效的开发流程。那么,下一步将会是怎样的挑战呢?你准备好迎接了吗? 这段代码是用Rust编写的,旨在构建一个名为CistercianClockApp的应用程序。该应用程序似乎是为了展示一种特殊的数字表示法,即西多会时钟风格的数字绘制。下面是对代码片段的中文解析,力求专业且易于理解。
结构定义与序列化设置
首先,定义了一个带有特定字段的结构体 CistercianClockApp,并使用了Serde库的属性来控制其序列化行为。通过添加#[serde(default)]属性,当反序列化旧状态时,任何新添加的字段都将被赋予默认值。此外,value字段标记了#[serde(skip)],这意味着在序列化过程中将跳过此字段。
应用初始化
接下来,实现了CistercianClockApp的构造函数new。此方法接收一个创建上下文CreationContext作为参数,并用于初始化应用程序的状态。如果存在先前保存的应用状态,那么就会从存储中加载它;否则,将使用默认值进行初始化。这里还提到了可以自定义egui(一个即时模式GUI库)的外观和字体设置,但具体实现未在此段代码中展示。
颜色配置
为了确保绘图时的视觉效果,定义了一个Colours结构体,用于存储多个颜色值。这些颜色将在绘制不同部分的数字时使用,从而提供丰富的视觉体验。
绘制个位数
定义了paint_unit_number函数,用于根据给定的数字绘制相应的线条。此函数接受一个画家对象、中心位置、缩放比例以及颜色配置等参数。通过一系列条件判断,确定如何基于提供的数字绘制正确的图案。
绘制十位数
同样的逻辑也适用于paint_tens_number函数,不过这次是针对十位数的绘制。这里的逻辑几乎与个位数相同,只是线条的位置有所调整,以适应十位数的布局。
绘制百位数
对于百位数,paint_hundreds_number函数遵循类似的模式,继续扩展这一概念。随着位数的增加,图形变得更加复杂,但核心思想保持一致:根据数值选择正确的位置和颜色来绘制线条。
绘制千位数
最后,paint_thousands_number 函数为千位数提供了相应的绘图逻辑。虽然代码片段没有完整显示这部分内容,但可以预见,它的实现方式与前述函数相似,只不过是在更高的数量级上操作。通过这种分层次的方式,代码有条不紊地实现了从个位到更高位数的数字绘制,不仅逻辑清晰,而且维护了良好的可读性和扩展性。每个函数都专注于自己的任务,使得整体设计简洁明了。
引人深思的问题
考虑到这段代码展示了如何使用编程手段实现艺术化的数字表达,不禁让人思考:我们是否可以通过编程创造更多独特的视觉语言? 这样的探索无疑为软件开发带来了更多的创意空间。