<Rust>egui部件学习:如何在egui窗口中添加按钮button以及标签label部件?

前言

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

环境配置

系统:windows

平台:visual studio code

语言:rust

库:egui、eframe

概述

本文是本专栏的第二篇博文,主要讲述按钮button、标签label部件的使用。

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

系列博文链接:

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

基于第一篇文章,我们将设置自定义字体的函数写到单独的mod里:

名称可以自己起,如setfont,然后我们在main中调用函数即可。具体函数代码和之前是一样的,就不重复贴了,但是这里稍作了修改,将字体的字节数组作为参数传给函数:

rust 复制代码
pub fn setup_custom_fonts(ctx: &egui::Context,fontbyte:&'static [u8]) 

并且为了能在其他mod里调用,函数前面添加了pub关键词。

在第一篇中,我们只是介绍了如何显示中文字符,本文我们会说明窗口的显示,以及如何添加按钮、标签部件。

部件属性
窗口显示

先来看窗口的显示,不同于iced库。egui的部件显示和更新都放在了update函数里。而iced的部件显示放在view函数,响应则放在update中。

官方给出的典型的egui代码如下:

rust 复制代码
use eframe::egui;            

fn main() {
    let native_options = eframe::NativeOptions::default();
     eframe::run_native("My egui App", native_options, Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))));
}

 #[derive(Default)]
 struct MyEguiApp {}

 impl MyEguiApp {
     fn new(cc: &eframe::CreationContext<'_>) -> Self {
         // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_visuals.
         // Restore app state using cc.storage (requires the "persistence" feature).
         // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
         // for e.g. egui::PaintCallback.
         Self::default()
     }
 }

 impl eframe::App for MyEguiApp {
    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.heading("Hello World!");
        });
    }
 }

和iced是有些类似的,我们创建一个结构体,里面是我们要操作的数据,然后我们为其实现eframe的app特性,窗口建立后,结构体数据就可以通过app的update来更新了。

不同于iced,iced中application是包含了new函数的,但是egui的app中,并没有直接添加new,而是需要单独实现:

rust 复制代码
impl MyApp{
    fn new(cc: &eframe::CreationContext<'_>) -> Self { 
        setfont::setup_custom_fonts(&cc.egui_ctx,MY_FONTS_BYTES);
        Self {
            show_confirmation_dialog:false,
            allowed_to_close:false,
        }
    }
}

我们设置自定义字体,就是在new函数中初始化的。

按钮部件button

窗口可以显示了,但是运行后只是一个空的窗体,我们需要在其中添加按钮等部件,以实现和窗口的交互。

egui中,部件的添加,都是在show函数里:

rust 复制代码
 egui::CentralPanel::default().show(ctx, |ui| {  
            ui.heading("尝试关闭窗口");
            ui.button("按钮1")
            
        });

如上,show有两个参数,ctx可以设置UI的格式,而add_contents用于添加其他部件:

rust 复制代码
ui.button("按钮1") 

这种方式是快捷的调用,它返回的是一个Response,即交互数据,我们可以使用Response来进行逻辑处理。比如,按钮点击响应,可以这样设置:

rust 复制代码
 let btn_res=ui.button("按钮1");                  
 if btn_res.clicked(){
        println!("按钮1点击")
 }else {
                
}

根据egui的介绍,egui是即时模式,作者在github解释了这个问题:

所以,根据作者的说明,egui是很适合集成到游戏开发中的,如果你要了解更多,可以参考作者给出的说明:
https://docs.rs/egui/latest/egui/#understanding-immediate-mode

总的来说,egui并不需要存储部件以供使用,而是即时刷新,所以,egui的缺点是布局不是很方便,因为是即时刷新,所以需要提前知道布局。而且,有些场景下,可能还会有很大的问题,这是一个需要注意的点。

好了,我们现在回到本文的内容上,我们在update中添加了按钮后,我们来运行看一下:

当我们点击按钮后,会在控制台打印文本:

标签部件label

现在我们在窗口添加标签,点击按钮,将文本显示在标签上。

rust 复制代码
 let btn_res=ui.button("按钮1"); 
            if btn_res.clicked(){
                //println!("按钮1点击")
                self.lbltext="按钮1点击".to_string();
            }else {
                
            }
            ui.label(format!("{}",self.lbltext))

如上,我们添加了label,其文本内容是一个格式化的参数:

rust 复制代码
format!("{}",self.lbltext)

这样设置是为了文本可以动态变化,其中self.lbltext是我们在结构体中创建的元素:

rust 复制代码
#[derive(Default)]
struct MyApp {
    show_confirmation_dialog: bool,   
    allowed_to_close: bool,
    lbltext:String,
}

这样,运行后,我们点击按钮,就可以在标签上显示内容:

当然,我们也可以是文本框输入部件,来动态输入文本,来查看标签内容变化,不够,文本输入我们将在下一个章节里介绍,本文就不在赘述了。

完整代码

main.rs

rust 复制代码
#![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; 

mod setfont; 

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)))), 
    )
}



#[derive(Default)]
struct MyApp {
    show_confirmation_dialog: bool, 
    allowed_to_close: bool, 
    lbltext:String, 
}

impl MyApp{
    fn new(cc: &eframe::CreationContext<'_>) -> Self { 
        setfont::setup_custom_fonts(&cc.egui_ctx,MY_FONTS_BYTES); 
        Self { 
            show_confirmation_dialog:false, 
            allowed_to_close:false, 
            lbltext:"no text".to_string(), 
        }
    }
}

impl eframe::App for MyApp { 
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { 
        egui::CentralPanel::default().show(ctx, |ui| { 
            ui.heading("尝试关闭窗口");
            let btn_res=ui.button("按钮1"); 
            if btn_res.clicked(){ 
                //println!("按钮1点击")
                self.lbltext="按钮1点击".to_string();
            }else { 
                
            }
            ui.text_edit_singleline(&mut self.lbltext); 
            ui.label(format!("{}",self.lbltext))
            
        });

        // 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); 
                        }
                    });
                });
        }
    }
}
相关推荐
许野平2 分钟前
Rust 编译器使用的 C++ 编译器吗?
c++·rust
怪我冷i3 分钟前
Rust GUI框架Tauri V1 入门
rust
新知图书7 分钟前
Rust的常量
算法·机器学习·rust
难以触及的高度12 分钟前
source ~/.bash_profile有什么用
开发语言·bash
B.-12 分钟前
Remix 学习 - @remix-run/react 中主要的 hooks
前端·javascript·学习·react.js·web
骆晨学长23 分钟前
基于springboot学生健康管理系统的设计与实现
java·开发语言·spring boot·后端·spring
白总Server30 分钟前
php语言基本语法
开发语言·ide·后端·golang·rust·github·php
tuantuan_tech31 分钟前
开放式耳机哪个好用?开放式耳机好还是入耳式耳机好?
大数据·学习·生活·旅游·智能硬件
小林熬夜学编程33 分钟前
【Linux系统编程】第二十弹---进程优先级 && 命令行参数 && 环境变量
linux·运维·服务器·c语言·开发语言·算法
A懿轩A37 分钟前
MySQL SQL多表查询语句各种连接
java·开发语言·数据库·sql·mysql·mybatis