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

news2024/11/13 9:33:52

前言
本专栏是关于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 example

use 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 example

use 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 equality
                if 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 example

use 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.coloradmin.cn/o/1931634.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

简约唯美的404HTML源码

源码介绍 简约唯美的404HTML源码,很适合做网站错误页,将下面的源码放到一个空白的html里面,然后上传到服务器里面即可使用 效果预览 完整源码 <!DOCTYPE html> <html><head><meta charset="utf-8"><title>404 Error Example<…

配置服务器

参考博客 1. https://blog.csdn.net/qq_31278903/article/details/83146031 2. https://blog.csdn.net/u014374826/article/details/134093409 3. https://blog.csdn.net/weixin_42728126/article/details/88887350 4. https://blog.csdn.net/Dreamhai/article/details/109…

Linux主机添加ipv6地址

一、添加网卡ipv6地址 通过命令行添加 ip add add 2001:db8:0:1::102/64 dev ens160 通过编辑/etc/sysconfig/network-scripts/目录下的ifcfg-配置文件添加 TYPEEthernet BOOTPROTOdhcp # 或者指定为 "static" 如果你想要静态配置 DEFROUTEyes IPV4_FAILURE_FAT…

多元统计分析概述

目录 1. 多元回归分析 2. 主成分分析&#xff08;PCA&#xff09; 3. 因子分析 4. 判别分析 5. 聚类分析 6. 多维尺度分析&#xff08;MDS&#xff09; 结论 多元统计分析是一组用于分析多个变量之间关系的统计方法。它广泛应用于各个领域&#xff0c;如市场研究、生物医…

C++中的语句详细介绍:简单语句、条件、循环迭代语句、跳转语句、异常处理语句、try语句等

文章目录 C中的语句(1)简单语句A.空语句B.复合语句 (2)条件语句(3)迭代语句A.常规for循环B.范围for循环C.while和do...while (4)跳转语句A.break语句B.continue语句C.goto语句 (5)异常处理语句A.标准异常B.throw抛出异常 (6)try语句 C中的语句 (1)简单语句 简单语句包括&#…

探索 Python 的宝藏:深入理解 NumPy库

探索 Python 的宝藏&#xff1a;深入理解 NumPy 库 引言&#xff1a;为何选择 NumPy&#xff1f; NumPy 是 Python 中一个基础而强大的库&#xff0c;它为 Python 语言提供了高性能的多维数组对象和相应的操作。在科学计算、数据分析、机器学习等领域&#xff0c;NumPy 以其高…

Android获取当前屏幕显示的是哪个activity

在 Android 中&#xff0c;要获取当前屏幕显示的 Activity&#xff0c;可以使用以下几种方法&#xff1a; 方法一&#xff1a;使用 ActivityManager 获取当前运行的任务信息 这是一个常见的方法&#xff0c;尽管从 Android 5.0 (API 21) 开始&#xff0c;有些方法变得不太可靠…

ctfshow~VIP限免题目20道(保姆级解析)

奈何自己没有实力&#xff0c;看到免费的东西就想占点便宜&#xff0c;想着做都做了就出个wp吧&#xff0c;本人小白&#xff0c;不喜勿喷&#xff01; 一、源码泄露 题目提示&#xff1a;开发注释未及时删除 题目给出了源码泄露&#xff0c;那咱直接看源码&#xff08;右键点…

2.I/O口

文章目录 I/O输出(点灯)分析电路代码编写 I/O输入(电平检测)代码编写 I/O内部电路分析51单片机STM32单片机输入输出 I/O输出(点灯) 分析电路 看电路图&#xff0c;元器件形成电压差&#xff0c;即可点亮LED灯 代码编写 使用不同操作进行LED控制 #include "reg52.h&quo…

AI助手在企业虚拟展厅中的应用有哪些?

在AI人工智能发展的浪潮下&#xff0c;视创云展也在元宇宙展厅中创新的融入了「AI智能助手」&#xff0c;当用户在虚拟展厅内自由探索时&#xff0c;AI智能助手始终如影随形&#xff0c;为用户提供即时、精准的信息解答与互动体验&#xff0c;使参观过程更加智能化、便捷化和个…

吴恩达机器学习笔记 三十八 二进制标签 均值归一化

标签 0 和 1 可以有很多种意义。从回归到二分分类&#xff1a;之前定义 ,而对于二进制标签&#xff0c;通过给出y^(i,j)为 1 的概率&#xff0c;其中 损失函数 均值归一化 计算每个电影的平均得分&#xff0c;例如第一部电影的平均分为2.5&#xff0c;第五部电影的平均分为1.2…

HP iLO5服务器硬件监控指标解读

在现代化数据中心中&#xff0c;服务器的稳定运行对于保障业务的连续性至关重要。HP的iLO&#xff08;Integrated Lights-Out&#xff09;技术&#xff0c;尤其是iLO5版本&#xff0c;为HP服务器提供了强大的远程管理功能。监控易作为一款专业的监控软件&#xff0c;通过支持HP…

Artix7系列FPGA实现SDI视频编解码,基于GTP高速接口,提供3套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案在Xilinx--Kintex系列FPGA上的应用本方案在Xilinx--Zynq系列FPGA上的应用 3、详细设计方案设计原理框图SDI 输入设备Gv8601a 均衡器GTP 高速接口-->解串与串化SMPTE SD/HD/3G SDI IP核BT1120转…

【爬虫】滑块缺口识别

滑块示例 分为背景图 和 滑块图 主要目的 识别背景图滑块缺口 下载识别库 pip install opencvcode import numpy as np import cv2def identify_gap(bg, tp):bg1 np.asarray(bytearray(bg), dtypenp.uint8)tp1 np.asarray(bytearray(tp), dtypenp.uint8)# 灰度bg_img cv2…

MySQL 数据库 - SQL

SQL通用语法 SQL通用语法 SQL语句可以单行或者多行书写&#xff0c;以分号结尾。SQL语句可以使用空格/缩进来增强语句的可读性。 注意&#xff1a;空格和缩进的个数是没有限制的&#xff0c;可以是 “一个” 也可以是 “多个”。MySQL数据库的SQL语句不区分大小写&#xff0c;…

魔幻Vscode,紫色爱好者的福音,真正的智能代码补全

&#x1f349;一、Dracula Official&#xff08;紫色爱好者的福音皮肤&#xff09; Dracula Official插件&#xff0c;打开扩展直接搜索Dracula Official下载即可&#xff0c;下载完成自动换上新皮&#xff01; Dracula Official &#x1f349;二、蓝色主题的福音 Aurora X 安…

[crypt]-密码学心声

通过音乐来传递情报&#xff0c;乐谱如下&#xff1a; 乐谱中有请转成艾塞克、十进制等等&#xff0c;可以将数字转为assic试试&#xff0c;1234567&#xff0c;猜测是8进制&#xff0c;三位一组&#xff0c;破解如下&#xff1a; oct8 [111, 114, 157, 166, 145, 123, 145, …

【2024】VsCode + Latex + Linux(Ubuntu) + wsl环境下配置教程 | 包含 中文配置,和 格式化处理

前言 本篇教程是针对WSL下的Ubuntu操作系统的配置教程&#xff0c;它和一般的Linux环境下的配置有所不同&#xff0c;并且和Windows环境下的也有所不同。 本篇博客编写参考了 官方文档&#xff08;Tex&#xff09; 和 插件官方&#xff08;Texlive Workshop&#xff09; 文档…

【中项】系统集成项目管理工程师-第一模块:IT技术和管理-1.1信息与信息化-1.1.3信息化基础

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&#xff0c;目前涵盖了计算机软件、计算机网络、计算机应…

NineData全面支持PostgreSQL可视化表结构设计

“PostgreSQL 是最像 Oracle 的开源关系型数据库“&#xff0c;也正因为如此&#xff0c;很多企业都青睐 PostgreSQL&#xff0c;拿它当成 Oracle 的替代品。所以毫无疑问&#xff0c;目前 PostgreSQL 在企业中非常常见。 对于直接接触 PostgreSQL 的开发人员而言&#xff0c;…