<Rust>egui部件学习:如何在窗口及部件显示中文字符?

前言
本专栏是关于Rust的GUI库egui的部件讲解及应用实例分析,主要讲解egui的源代码、部件属性、如何应用。

环境配置
系统:windows
平台:visual studio code
语言:rust
库:egui、eframe

概述
本文是本专栏的第一篇博文,主要讲述如何使用egui库来显示一个窗口以及如何在窗口显示中文字符。

egui是基于rust的一个GUI库,可以创建窗口并添加部件、布局,其github地址:
https://github.com/emilk/egui

事实上,类似于iced,egui都提供了示例程序,本专栏的博文都是建立在官方示例程序以及源代码的基础上,进行的实例讲解。
即,本专栏的文章并非只是简单的翻译egui的官方示例与文档,而是针对于官方代码进行的实际使用,会在官方的代码上进行修改,包括解决一些问题。

部件属性

在使用egui前,需要添加其依赖:

egui="0.28.1"
eframe="0.28.1"

将上面的库添加到你的项目的toml文件中,然后编译一下。

然后我们来看一个简单的示例,我们以官方提供的例子中的第一个confirm_exit例子来进行说明。这个例子很简单,就是生成一个窗口,并且在关闭窗口时弹出一个提示窗口,选择yes关闭,选择no,不关闭。

官方代码如下:

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release   
#![allow(rustdoc::missing_crate_level_docs)] // it's an exampleuse eframe::egui;fn main() -> eframe::Result {env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).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())),)
}#[derive(Default)]
struct MyApp {show_confirmation_dialog: bool,allowed_to_close: bool,
}impl eframe::App for MyApp {fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {egui::CentralPanel::default().show(ctx, |ui| {ui.heading("Try to close the window");});if ctx.input(|i| i.viewport().close_requested()) {if self.allowed_to_close {// do nothing - we will close} else {ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose);self.show_confirmation_dialog = true;}}if self.show_confirmation_dialog {egui::Window::new("Do you want to quit?").collapsible(false).resizable(false).show(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.ctx().send_viewport_cmd(egui::ViewportCommand::Close);}});});}}
}

运行之后显示如下:
在这里插入图片描述
点击关闭按钮,弹出提示窗口:
在这里插入图片描述
点击yes按钮,直接关闭,点击no按钮,提示窗口消失,窗口不关闭。

本文暂且不关注窗口如何显示以及如何添加部件,这将在以后的文章中说明。

现在,我们来修改上面的代码,将上面代码中涉及的text文本的内容都修改为中文,再来看看效果。

修改后的代码:

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release    
#![allow(rustdoc::missing_crate_level_docs)] // it's an exampleuse eframe::egui;fn main() -> eframe::Result {env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).let options = eframe::NativeOptions {viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),..Default::default()};eframe::run_native("egui测试窗口",options,Box::new(|_cc| Ok(Box::<MyApp>::default())),)
}#[derive(Default)]
struct MyApp {show_confirmation_dialog: bool,allowed_to_close: bool,
}impl eframe::App for MyApp {fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {egui::CentralPanel::default().show(ctx, |ui| {ui.heading("尝试关闭窗口");});if ctx.input(|i| i.viewport().close_requested()) {if self.allowed_to_close {// do nothing - we will close} else {ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose);self.show_confirmation_dialog = true;}}if self.show_confirmation_dialog {egui::Window::new("你想要关闭吗?").collapsible(false).resizable(false).show(ctx, |ui| {ui.horizontal(|ui| {if ui.button("否").clicked() {self.show_confirmation_dialog = false;self.allowed_to_close = false;}if ui.button("是").clicked() {self.show_confirmation_dialog = false;self.allowed_to_close = true;ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);}});});}}
}

再来运行看下:
在这里插入图片描述
点击关闭按钮:
在这里插入图片描述
可以看到,无论是窗口直接显示的文本还是按钮的文本,都显示乱码,这是因为egui自带的字体不支持中文导致的。

所以,和iced库一样,我们只要想办法将字体替换为自定义的字体(支持中文字符)即可。

支持中文字符的字体有很多,可以去网上自行下载,一般来说,最常用的应该是simsun.ttf,当然,也可以去egui作者提供的一个字体网站下载字体:
https://github.com/notofonts/noto-cjk/tree/main/Sans#downloading-noto-sans-cjk

下面我们来看下,如何替换字体,这里依然参考官方提供的示例custom_font。这里就不贴具体代码了,自定义字体的方法,是在efram的一个属性AppCreator中:

/// This is how your app is created.    
///
/// You can use the [`CreationContext`] to setup egui, restore state, setup OpenGL things, etc.
pub type AppCreator = Box<dyn FnOnce(&CreationContext<'_>) -> Result<Box<dyn App>, DynError>>;

其实就是设置CreationContext,其下的ctx:

 /// You can use this to customize the look of egui, e.g to call [`egui::Context::set_fonts`],/// [`egui::Context::set_visuals`] etc.pub egui_ctx: egui::Context,

context实现了set_fonts函数:

  /// Tell `egui` which fonts to use.       ////// The default `egui` fonts only support latin and cyrillic alphabets,/// but you can call this to install additional fonts that support e.g. korean characters.////// The new fonts will become active at the start of the next frame.pub fn set_fonts(&self, font_definitions: FontDefinitions) {crate::profile_function!();let pixels_per_point = self.pixels_per_point();let mut update_fonts = true;self.read(|ctx| {if let Some(current_fonts) = ctx.fonts.get(&pixels_per_point.into()) {// NOTE: this comparison is expensive since it checks TTF data for equalityif current_fonts.lock().fonts.definitions() == &font_definitions {update_fonts = false; // no need to update}}});if update_fonts {self.memory_mut(|mem| mem.new_font_definitions = Some(font_definitions));}}

而set_fonts的参数是FontDefinitions,我们设置其font_data即可。

pub struct FontDefinitions {       /// List of font names and their definitions.////// `epaint` has built-in-default for these, but you can override them if you like.pub font_data: BTreeMap<String, FontData>,/// Which fonts (names) to use for each [`FontFamily`].////// The list should be a list of keys into [`Self::font_data`]./// When looking for a character glyph `epaint` will start with/// the first font and then move to the second, and so on./// So the first font is the primary, and then comes a list of fallbacks in order of priority.pub families: BTreeMap<FontFamily, Vec<String>>,
}

font_data实现了from_static函数:

 pub fn from_static(font: &'static [u8]) -> Self {   Self {font: std::borrow::Cow::Borrowed(font),index: 0,tweak: Default::default(),}}

我们为from_static传入自定义字体的字节数组即可,可以适应include_byets来获取数组:

const ICON_BYTES:&[u8]=include_bytes!("../font/simsun.ttf");

如上,我们下载simsun.ttf字体文件,放到项目文件夹中,然后获取其静态字节数组。

 // Start with the default fonts (we will be adding to them rather than replacing them). let mut fonts = egui::FontDefinitions::default();// Install my own font (maybe supporting non-latin characters).// .ttf and .otf files supported.fonts.font_data.insert("my_font".to_owned(),egui::FontData::from_static(ICON_BYTES,),);

这是官方提供的示例代码,我们只是修改其中的字体的内容。
使用自定义字体后,我们再来运行一下程序看看:
在这里插入图片描述
点击关闭按钮后:
在这里插入图片描述

好了,以上就是使用egui显示窗口时,如何显示中文的解决办法,基本上是基于官方给的示例。
本文并没有修改太多,因为主要是说明如何显示中文的问题。对于一些初学者来说,可能即使看官方示例和说明,也不知道如何解决这个问题,如果我这边的解释能帮助你,那就不枉了。

后续的文章里,中文字符显示的函数将会被独立出来,单独作为一个mod,然后在main中调用,这样也方便管理。

完整代码:
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release     
#![allow(rustdoc::missing_crate_level_docs)] // it's an exampleuse eframe::egui;const MY_FONTS_BYTES:&[u8]=include_bytes!("../font/simsun.ttf");fn main() -> eframe::Result {env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).let options = eframe::NativeOptions {viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),..Default::default()};eframe::run_native("egui测试窗口",options,//Box::new(|_cc| Ok(Box::<MyApp>::default())),Box::new(|cc| Ok(Box::new(MyApp::new(cc)))),)
}///
/// 设置自定义字体
/// 
fn setup_custom_fonts(ctx: &egui::Context) {// Start with the default fonts (we will be adding to them rather than replacing them).let mut fonts = egui::FontDefinitions::default();// Install my own font (maybe supporting non-latin characters).// .ttf and .otf files supported.fonts.font_data.insert("my_font".to_owned(),egui::FontData::from_static(MY_FONTS_BYTES,),);// Put my font first (highest priority) for proportional text:fonts.families.entry(egui::FontFamily::Proportional).or_default().insert(0, "my_font".to_owned());// Put my font as last fallback for monospace:fonts.families.entry(egui::FontFamily::Monospace).or_default().push("my_font".to_owned());// Tell egui to use these fonts:ctx.set_fonts(fonts);
}#[derive(Default)]
struct MyApp {show_confirmation_dialog: bool,allowed_to_close: bool,
}impl MyApp{fn new(cc: &eframe::CreationContext<'_>) -> Self {setup_custom_fonts(&cc.egui_ctx);Self {show_confirmation_dialog:false,allowed_to_close:false,}}
}impl eframe::App for MyApp {fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {egui::CentralPanel::default().show(ctx, |ui| {ui.heading("尝试关闭窗口");});if ctx.input(|i| i.viewport().close_requested()) {if self.allowed_to_close {// do nothing - we will close} else {ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose);self.show_confirmation_dialog = true;}}if self.show_confirmation_dialog {egui::Window::new("你想要关闭吗?").collapsible(false).resizable(false).show(ctx, |ui| {ui.horizontal(|ui| {if ui.button("否").clicked() {self.show_confirmation_dialog = false;self.allowed_to_close = false;}if ui.button("是").clicked() {self.show_confirmation_dialog = false;self.allowed_to_close = true;ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);}});});}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/378339.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024-07-16 Unity插件 Odin Inspector5 —— Conditional Attributes

文章目录 1 说明2 条件特性2.1 DisableIf / EnableIf2.2 DisableIn / EnableIn / ShowIn / HideIn2.3 DisableInEditorMode / HideInEditorMode2.4 DisableInInlineEditors / ShowInInlineEditors2.5 DisableInPlayMode / HideInPlayMode2.6 ShowIf / HideIf2.7 ShowIfGroup / …

鸿蒙架构之AOP

零、主要内容 AOP 简介ArkTs AOP 实现原理 JS 原型链AOP实现原理 AOP的应用场景 统计类&#xff1a; 方法调用次数统计、方法时长统计防御式编程&#xff1a;参数校验代理模式实现 AOP的注意事项 一、AOP简介 对于Android、Java Web 开发者来说&#xff0c; AOP编程思想并不…

java智慧工地云平台源码,基于BIM+AI智能大数据中心和云平台的智慧工地监管平台

智慧工地云平台源码&#xff0c;智慧工地系统源码&#xff0c;工程管理系统APP源码&#xff0c; “智慧工地”基于BIM&#xff08;建筑信息模型&#xff09;AI&#xff08;人工智能&#xff09;智能大数据中心和云平台&#xff0c;围绕建筑工程项目全生命周期&#xff0c;集成安…

Linux下如何安装配置Graylog日志管理工具

Graylog是一个开源的日志管理工具&#xff0c;可以帮助我们收集、存储和分析大量的日志数据。它提供了强大的搜索、过滤和可视化功能&#xff0c;可以帮助我们轻松地监控系统和应用程序的运行情况。 在Linux系统下安装和配置Graylog主要包括以下几个步骤&#xff1a; 准备安装…

Scratch编程乐园:108课打造小小编程大师

《Scratch少儿趣味编程108例&#xff08;全视频微课版&#xff09;》以Scratch 3.6版本为基础&#xff0c;通过108个案例详细介绍了运用Scratch软件制作动画、游戏等趣味作品的方法&#xff0c;充分培养孩子的想象力和创造力。本书共分为9章&#xff0c;第1章概述Scratch下载、…

ArrayList.subList的踩坑

需求描述&#xff1a;跳过list中的第一个元素&#xff0c;获取list中的其他元素 原始代码如下&#xff1a; List<FddxxEnterpriseVerify> companyList fddxxEnterpriseVerifyMapper.selectList(companyQueryWrapper);log.info("获取多个法大大公司数据量为&#…

OMS 2.0至3.0升级项目成功案例:红袖女装

近日&#xff0c;巨益科技成功助力女装品牌红袖完成OMS 3.0升级&#xff0c;并顺利通过项目验收。此次升级通过优化系统架构、提高数据处理能力和实现多系统集成&#xff0c;红袖品牌显著提升了订单处理速度、库存管理精度和客户满意度&#xff0c;实现了运营效率和服务质量的全…

基于Python+Flask+SQLite的豆瓣电影可视化系统

FlaskMySQLEcharts 基于PythonFlaskSQLite的豆瓣电影可视化系统 Echarts 不支持登录注册&#xff0c;并且信息存储在数据库中 不含爬虫代码&#xff0c;或爬虫代码已失效 简介 基于PythonFlaskMySQL的豆瓣电影可视化系统&#xff0c;采用Echart构建图表&#xff0c;支持自定…

python 算法题之,统计不存在的值的累加和

s list(map(int, input().split())) k int(input()) s.sort() print(s)if s:m 0 # 统计找到的不存在的数的个数res 0 # 累值t 1 # 当前数i 0 # 列表中当前下标while True:if i < len(s) and s[i] t: # 如果当前数存在i 1else: # 当前数不存在res (res t) % …

第九课:服务器发布(静态nat配置)

一个要用到静态NAT的场景&#xff0c;当内网有一台服务器server1&#xff0c;假如一个用户在外网&#xff0c;从外网访问内网怎么访问呢&#xff0c;无法访问&#xff0c;这是因为外网没办法直接访问内网&#xff0c;这时候需要给服务器做一个静态NAT。 静态NAT指的是给服务器…

cpp 强制转换

一、static_cast static_cast 是 C 中的一个类型转换操作符&#xff0c;用于在类的层次结构中进行安全的向上转换&#xff08;从派生类到基类&#xff09;或进行不需要运行时类型检查的转换。它主要用于基本数据类型之间的转换、对象指针或引用的向上转换&#xff08;即从派生…

AI聊天可能涉黄?用户该如何对待AI聊天

AI伴侣是生成式大模型发展的产物&#xff0c;它是一个聊天机器人&#xff0c;能够随叫随到&#xff0c;提供情绪价值&#xff0c;还能发腿照和腹肌照。它可以是对现实或小说中某个人物的角色扮演&#xff0c;也可以是凭空创造出来的一个形象&#xff0c;总之不是真人。但因为接…

【学习】美国虚拟信用卡申请流程

WildCard 官方网址&#xff1a;https://bewildcard.com/i/PEACEFUL &#xff08;使用邀请码“PEACEFUL”可以享受开卡88 折优惠&#xff0c;注册时提示填写邀请码就可以填写&#xff09;

某服务商云服务器使用体验

雨云 雨云云服务商以其免费MC面板服务器、抗DDoS攻击服务和免费CDN服务三大核心优势&#xff0c;成为了众多企业和个人站长的首选云服务提供商。官网&#xff1a;https://app.rainyun.com 莫名的好感&#x1f603; 登录环节 在阿里&#xff0c;腾讯的官网二次进入时大多时…

深度学习驱动智能超材料设计与应用

在深度学习与超材料融合的背景下&#xff0c;不仅提高了设计的效率和质量&#xff0c;还为实现定制化和精准化的治疗提供了可能&#xff0c;展现了在材料科学领域的巨大潜力。深度学习可以帮助实现超材料结构参数的优化、电磁响应的预测、拓扑结构的自动设计、相位的预测及结构…

Hive 函数

分类 Hive 的函数分为两大类&#xff1a;内置函数&#xff08;Built-in-Functions&#xff09;、用户自定义函数&#xff08;User-Defined-Functions&#xff09;&#xff1b;内置函数可分为&#xff1a;数值类型函数、日期类型函数、字符串类型函数、集合函数等&#xff1b;用…

Redis-基础概念

目录 概念 Redis是什么 Redis 和 MySQL 的区别&#xff1f; Redis单线程有什么极端场景的瓶颈 Redis为什么快? 为什么Redis是单线程? Redis是单线程还是多线程 Redis为什么选择单线程做核心处理 Redis6.0之后引入了多线程&#xff0c;你知道为什么吗? 瓶颈是内存和I…

php相关

php相关 ​ 借鉴了小迪安全以及各位大佬的博客&#xff0c;如果一切顺利&#xff0c;会不定期更新。 如果感觉不妥&#xff0c;可以私信删除。 默认有php基础。 文章目录 php相关1. php 缺陷函数1. 与2. MD53. intval()4. preg_match() 2. php特性1. php字符串解析特性2. 杂…

用switch实现多分支选择结构

一 例子引入 #include<stdio.h> int main&#xff08;) {char grade&#xff1b;scanf("%c"&#xff0c;&grade);printf("Your score:");switch (grade){case A: printf("85~100\n"); break;case B: printf("70~84\n");br…

深度学习落地实战:识别火车票信息

前言 大家好&#xff0c;我是机长 本专栏将持续收集整理市场上深度学习的相关项目&#xff0c;旨在为准备从事深度学习工作或相关科研活动的伙伴&#xff0c;储备、提升更多的实际开发经验&#xff0c;每个项目实例都可作为实际开发项目写入简历&#xff0c;且都附带完整的代…