RustGUI学习(iced)之小部件(一):如何使用按钮和文本标签部件

前言
本专栏是学习Rust的GUI库iced的合集,将介绍iced涉及的各个小部件分别介绍,最后会汇总为一个总的程序。
iced是RustGUI中比较强大的一个,目前处于发展中(即版本可能会改变),本专栏基于版本0.12.1.

概述
这是本专栏的第一篇,主要讲述按钮(button)和文本标签(text)两个部件的使用,会结合实例来说明。

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

在这里插入图片描述
注:iced是一个受Elm启发而编写,适用于rust语言的跨平台的GUI库。

本篇内容:
1、button
2、text

一、按钮部件

按钮部件在iced中的定义如下:

/// Creates a new [`Button`] with the provided content.  
///
/// [`Button`]: crate::Button
pub fn button<'a, Message, Theme, Renderer>(
    content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Button<'a, Message, Theme, Renderer>
where
    Renderer: core::Renderer,
    Theme: button::StyleSheet,
{
    Button::new(content)
}

上面是官方的代码,可以知道,button其实是Button来创建的。而Button来自于iced_widget库:

#[allow(missing_debug_implementations)]
pub struct Button<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>      
where
    Theme: StyleSheet,
    Renderer: crate::core::Renderer,
{
    content: Element<'a, Message, Theme, Renderer>,
    on_press: Option<Message>,
    width: Length,
    height: Length,
    padding: Padding,
    clip: bool,
    style: Theme::Style,
}

我们可以看到,一个新建的button,拥有以下属性或者方法:
content、on_press、width、height、padding、clip、style
其中,content按照定义,是一个Element,即可以是任何元素,但一般情况下,我们会给content赋为纯文本(即按钮名称)或者文本部件(即text)。
width、height这两个属性好理解,即按钮的宽、高,宽、高属性既可以设置为预设值,也可以设置为自定义的大小。
padding是指的按钮的content与按钮整个框架的间距。
clip是指当按钮尺寸小于文字时,多出的文字是显示还是隐藏。
style是设置按钮的外观样式,可以使用库预设的样式,也可以使用自定义的样式,下文将会详述。
我们先来看一下button实际的代码:

button("按钮1").clip(false).width(80).height(40).style(style6).padding(4)    
            .on_press(Message::Clicked),

这是一个典型的button实例,上面的代码中,“按钮1”即content,其他属性则显而易见,其中style的参数style6如下:

let style6=theme::Button::Custom(Box::new(MyButtonStyle));

此处使用的是自定义外观,库预设的样式如下:

let style1=theme::Button::Primary;
        let style2=theme::Button::Secondary;
        let style3=theme::Button::Positive;
        let style4=theme::Button::Destructive;
        let style5=theme::Button::Text;

看一下这五种预设的按钮样式实际是什么样的:
在这里插入图片描述
如果预设样式足够使用,那么直接使用即可,如果预设样式不满足,那么还可以使用自定义样式,比如要设置按钮边框,添加圆角,改变颜色等,我们来看一下如何设置自定义按钮样式:
我们查看iced源码可知,按钮的样式是在StyleSheet这个特性下设置的:

pub trait StyleSheet {
    /// The supported style of the [`StyleSheet`].
    type Style: Default;

    /// Produces the active [`Appearance`] of a button.
    fn active(&self, style: &Self::Style) -> Appearance;

    /// Produces the hovered [`Appearance`] of a button.
    fn hovered(&self, style: &Self::Style) -> Appearance {
        let active = self.active(style);

        Appearance {
            shadow_offset: active.shadow_offset + Vector::new(0.0, 1.0),
            ..active
        }
    }

    /// Produces the pressed [`Appearance`] of a button.
    fn pressed(&self, style: &Self::Style) -> Appearance {
        Appearance {
            shadow_offset: Vector::default(),
            ..self.active(style)
        }
    }

    /// Produces the disabled [`Appearance`] of a button.
    fn disabled(&self, style: &Self::Style) -> Appearance {
        let active = self.active(style);

        Appearance {
            shadow_offset: Vector::default(),
            background: active.background.map(|background| match background {
                Background::Color(color) => Background::Color(Color {
                    a: color.a * 0.5,
                    ..color
                }),
                Background::Gradient(gradient) => {
                    Background::Gradient(gradient.mul_alpha(0.5))
                }
            }),
            text_color: Color {
                a: active.text_color.a * 0.5,
                ..active.text_color
            },
            ..active
        }
    }
}

如果我们要设置自定义的样式,那么新建一个样式结构体,然后将StyleSheet赋予我们新建的样式,在其中修改即可,如下:
1、新建样式:

struct MyButtonStyle;

2、实现StyleSheet特性

impl  StyleSheet for MyButtonStyle{      
    type Style = Theme;
    //激活时外观
    fn active(&self, style: &Self::Style) -> button::Appearance {
        Appearance{
            shadow_offset:Vector{x:0.0,y:0.0},
            background:Some(style.extended_palette().primary.base.color.into()),
            text_color:style.extended_palette().background.base.text,
            border:Border{
                color:Color::BLACK,
                width:1.0,
                radius:[3.0;4].into(),},
            shadow:Shadow { 
                color: Color::WHITE, 
                offset: Vector{x:0.0,y:0.0},
                blur_radius:0.0 },

        }
    }
    //悬停时外观
    fn hovered(&self, style: &Self::Style) -> Appearance {
        Appearance{
            shadow_offset:Vector{x:0.0,y:0.0},
            background:Some(style.extended_palette().primary.weak.color.into()),
            text_color:style.extended_palette().primary.weak.text,
            border:Border{
                color:Color::BLACK,
                width:1.0,
                radius:[3.0;4].into(),
            },
            shadow:Shadow {
                color: Color::WHITE,
                offset: Vector{x:0.0,y:0.0},
                blur_radius:0.0,
            }
        }
    }
    //按下时外观
    fn pressed(&self, style: &Self::Style) -> Appearance {
        Appearance{
            shadow_offset:Vector{x:0.0,y:0.0},
            background:Some(style.extended_palette().primary.strong.color.into()),
            text_color:style.extended_palette().primary.weak.text,
            border:Border{
                color:Color::BLACK,
                width:1.0,
                radius:[3.0;4].into(),
            },
            shadow:Shadow {
                color: Color::BLACK,
                offset: Vector{x:0.0,y:0.0},
                blur_radius:0.0,
            }
        }
    }
}

可以看到,我们在此处设置了按钮的激活、悬停、按压三种状态下的外观,具体来说,就是修改Appearance这个属性,其子属性如下:

pub struct Appearance {     
    /// The amount of offset to apply to the shadow of the button.
    pub shadow_offset: Vector,
    /// The [`Background`] of the button.
    pub background: Option<Background>,
    /// The text [`Color`] of the button.
    pub text_color: Color,
    /// The [`Border`] of the buton.
    pub border: Border,
    /// The [`Shadow`] of the butoon.
    pub shadow: Shadow,
}

这是官方定义的按钮的Appearance属性,我们只要一一修改,就可以得到我们自己的Appearance。如上面的示例代码中,我们修改了背景色、圆角半径等,其中shadow可以设置按钮的阴影,有兴趣的可以去尝试,可以做出不同的效果,当然,对于普通的UI界面来说,不需要那么复杂的效果,那么只要修改背景色、文字颜色、边框、圆角半径等即可。
当我们新建样式完成后,就可以在创建按钮时调用了:

let style6=theme::Button::Custom(Box::new(MyButtonStyle));

此处,Custom的参数形式,在官方定义中如下:

Custom(Box<dyn button::StyleSheet<Style = Theme>>), 

来看一下自定义样式和预设样式有什么不一样:
在这里插入图片描述
按钮的样式说的差不多了,下面说一下按钮最重要的一个属性,on_press,这是按钮能够进行交互的基础,当用户点击按钮时,就会触发on_press,然后将on_press的消息Message传递给后台,根据按钮传递的消息,运行相应的逻辑,然后再更新UI。
示例代码中如下:

 button("按钮1").clip(false).width(80).height(40).style(style1).padding(4)
            .on_press(Message::Clicked),

on_press的参数为Message::Clicked,此处的Message::Clicked需要自己创建,是enum枚举:

#[derive(Debug,Clone,Copy)]
enum Message{  
    Clicked,
}

当按钮被点击时,Message::Clicked就会被触发,然后在iced的update函数中,我们可以处理这个message:

 fn update(&mut self,message:Message){   
        match message{
            Message::Clicked => {
                self.value +=1;
            }    
        }


    }

即,当Message::Clicked触发一次,就会执行一次当前Message::Clicked下对应的程序,本例中即将value这个值加1.

那么,当我点击了按钮,value值也加1了,该如何将这个变化反应到UI界面,使其直观显示出来呢?

我们会用到iced的view函数:

fn view(&self) -> Element<Message>{           
         
    }

view函数实时显示当前UI,所以,我们要将部件都写在view函数中,比如,本例中我添加一个按钮、一个文本标签,然后当点击按钮时,文本的值加1.

fn view(&self) -> Element<Message>{     
     
        let style6=theme::Button::Custom(Box::new(MyButtonStyle));
        let  style7=theme::Text::Color(Color::BLACK);
        column![
    
            button("按钮6").clip(false).width(80).height(40).style(style6).padding(4)
            .on_press(Message::Clicked),
            text(format!("value:{}",self.value)).size(15)
            .width(40).height(40).style(style7),
        ].padding(20)
        .spacing(20)
        .into()
        
    }

如上,我们添加了按钮,这不用多说,又添加了一个text部件,其中,text部件的text属性部分,我们将其设置为与value值绑定,这样,每当value值变化,view函数都会刷新text部件的文本部分。
这其中,column是一种排列方式,即区域内的部件按照纵向排列,另一种方式是row,即横向排列。
看一下实际效果:
在这里插入图片描述

二、文本标签

再来说一下文本标签的使用,相比于按钮,文本标签要简单许多,在官方的定义中,text部件如下:

/// Creates a new [`Text`] widget with the provided content. 
///
/// [`Text`]: core::widget::Text
pub fn text<'a, Theme, Renderer>(
    text: impl ToString,
) -> Text<'a, Theme, Renderer>
where
    Theme: text::StyleSheet,
    Renderer: core::text::Renderer,
{
    Text::new(text.to_string())
}

可以看到,其来自于Text:

/// A paragraph of text.                  
#[allow(missing_debug_implementations)]
pub struct Text<'a, Theme, Renderer>
where
    Theme: StyleSheet,
    Renderer: text::Renderer,
{
    content: Cow<'a, str>,
    size: Option<Pixels>,
    line_height: LineHeight,
    width: Length,
    height: Length,
    horizontal_alignment: alignment::Horizontal,
    vertical_alignment: alignment::Vertical,
    font: Option<Renderer::Font>,
    shaping: Shaping,
    style: Theme::Style,
}

其中content是文本要显示的字符,size是文字的尺寸,width和height是宽、高,horizontal_alignment是设置文字的水平样式,有左、中、右可选,而vertical_alignment是设置文字的垂直样式,顶部、中间、底部可选,font是设置字体,如果不设置,则和主窗体保持一致,shaping应该指的是文字的显示质量。style是指文字样式,在text的style中,只有颜色可以修改,如下:

/// The style sheet of some text. 
pub trait StyleSheet {
    /// The supported style of the [`StyleSheet`].
    type Style: Default + Clone;

    /// Produces the [`Appearance`] of some text.
    fn appearance(&self, style: Self::Style) -> Appearance;
}
/// The apperance of some text.   
#[derive(Debug, Clone, Copy, Default)]
pub struct Appearance {
    /// The [`Color`] of the text.
    ///
    /// The default, `None`, means using the inherited color.
    pub color: Option<Color>,
}

可以看到,text的样式就是修改颜色。
我们在上面的示例中如下:

text(format!("value:{}",self.value)).size(15).shaping(Shaping::Advanced) 
            .width(40).height(40).style(style7),

如果要设置文字的排列样式,可以添加:

text(format!("value:{}",self.value)).size(15).shaping(Shaping::Advanced)     
            .horizontal_alignment(alignment::Horizontal::Center)
            .vertical_alignment(alignment::Vertical::Center)
            .width(40).height(40).style(style7),

如上代码,文字就会一直保持在中心位置。

下面再来简单说一下窗体的属性问题,iced建立窗体,有两种样式,一种是比较简单的呈现,是sandbox,另一种是application。
在前期,我们会使用sandbox来演示。

pub fn main() -> iced::Result {    
   Counter::run(Settings::default())
}

以上是官方的默认主程序,此处运行主窗体,其中的参数设置皆为默认,但默认的参数不一定适用,所以需要对其进行修改。
如果要修改默认参数,需要先导入:

use iced::font::Family; 
use iced::window::{self,icon,Position};

以及:

extern crate image;
use image::GenericImageView;

此处的image库是用来处理图像的。

接下来看一下如何修改窗口参数:

pub fn main() -> iced::Result {
   //Counter::run(Settings::default())
   let myfont="微软雅黑";
   let ico=image_to_icon("..\\gui-btn\\img\\ico1.png");
   Counter::run(Settings{
       window:window::Settings{
           size:Size{width:800.0,height:600.0},
           position:Position::Specific(Point{x:100.0,y:20.0}),
           icon:Some(ico),
           ..window::Settings::default()
       },
       default_font:Font { 
        family:Family::Name(myfont),
        ..Font::DEFAULT
     },
     ..Settings::default()
   })

}

如上代码,可以看到,我们设置了窗体尺寸、窗体显示位置、窗体图标、字体,事实上程序参数如下:

/// The settings of an application.    
#[derive(Debug, Clone)]
pub struct Settings<Flags> {
    /// The identifier of the application.
    ///
    /// If provided, this identifier may be used to identify the application or
    /// communicate with it through the windowing system.
    pub id: Option<String>,

    /// The window settings.
    ///
    /// They will be ignored on the Web.
    pub window: window::Settings,

    /// The data needed to initialize the [`Application`].
    ///
    /// [`Application`]: crate::Application
    pub flags: Flags,

    /// The fonts to load on boot.
    pub fonts: Vec<Cow<'static, [u8]>>,

    /// The default [`Font`] to be used.
    ///
    /// By default, it uses [`Family::SansSerif`](crate::font::Family::SansSerif).
    pub default_font: Font,

    /// The text size that will be used by default.
    ///
    /// The default value is `16.0`.
    pub default_text_size: Pixels,

    /// If set to true, the renderer will try to perform antialiasing for some
    /// primitives.
    ///
    /// Enabling it can produce a smoother result in some widgets, like the
    /// [`Canvas`], at a performance cost.
    ///
    /// By default, it is disabled.
    ///
    /// [`Canvas`]: crate::widget::Canvas
    pub antialiasing: bool,
}

其中,window参数又如下:

/// The window settings of an application.    
#[derive(Debug, Clone)]
pub struct Settings {
    /// The initial logical dimensions of the window.
    pub size: Size,

    /// The initial position of the window.
    pub position: Position,

    /// The minimum size of the window.
    pub min_size: Option<Size>,

    /// The maximum size of the window.
    pub max_size: Option<Size>,

    /// Whether the window should be visible or not.
    pub visible: bool,

    /// Whether the window should be resizable or not.
    pub resizable: bool,

    /// Whether the window should have a border, a title bar, etc. or not.
    pub decorations: bool,

    /// Whether the window should be transparent.
    pub transparent: bool,

    /// The window [`Level`].
    pub level: Level,

    /// The icon of the window.
    pub icon: Option<Icon>,

    /// Platform specific settings.
    pub platform_specific: PlatformSpecific,

    /// Whether the window will close when the user requests it, e.g. when a user presses the
    /// close button.
    ///
    /// This can be useful if you want to have some behavior that executes before the window is
    /// actually destroyed. If you disable this, you must manually close the window with the
    /// `window::close` command.
    ///
    /// By default this is enabled.
    pub exit_on_close_request: bool,
}

我们并不是每一个参数都需要修改,这里我们只是修改了尺寸、位置、图标以及字体这种常用的参数,其余的参数可以全部默认。
在我们上面的代码中,有一句处理图像的程序:

let ico=image_to_icon("..\\gui-btn\\img\\ico1.png");

其中的image_to_icon是我们自定义的函数,用于将图像转为icon,函数如下:

///
/// 将图片转为窗口图标
/// 
fn image_to_icon(img:&str)-> icon::Icon {           
    let img=image::open(img).unwrap();
    let img_de=img.dimensions();
    //println!("img_de is {:?}",img_de);
    let w1=img_de.0;
    let h1=img_de.1;
    let img_file=img.to_rgba8();
    let ico=icon::from_rgba(img_file.to_vec(),w1,h1);
    let ico_file=match ico{
        Ok(file)=>file,
        Err(e)=>panic!("error is {}",e),
    };
    ico_file
}

这里用到image库,需要在toml中添加:

image="0.25.1"

以上就是本篇的内容,其中涉及窗口的内容,在以后的篇章中将不再赘述。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

高效一键改写文章,智能伪原创工具轻松搞定

在信息爆炸的时代&#xff0c;想要高效率的一键改写文章却是很多创作者都想了解的方法。然而在人工智能技术发展的今天&#xff0c;智能伪原创工具的出现&#xff0c;也正是成了广大创作者用来一键改写文章的好方法&#xff0c;因为它的优势&#xff0c;可以为大家轻松完成改写…

光伏二次设备主要有哪些

光伏电站二次设备类型比较多&#xff0c;信息显示、数据安全、远动通信、电能质量、微机保护等都有不同设备相互配合完成&#xff0c;根据项目具体需求来选择&#xff0c;简单可以分为以下几种&#xff1a; 一、光伏二次设备保护屏&#xff1a; 1、光伏二次设备预制舱 二次设…

短视频矩阵系统源码====3年技术公司源头开发商交付

短视频矩阵系统#源头技术打磨 哈尔滨爆火带动了一波“北上热潮”&#xff0c;各地文旅坐不住了&#xff0c;兄弟们开“卷”&#xff01;这波互卷浪潮中&#xff0c;河南率先出圈。如今&#xff0c;河南文旅账号粉丝已经突破200w&#xff01; 01 矩阵打法&#xff0c;很难不火…

超越边界:如何ChatGPT 3.5、GPT-4、DALL·E 3和Midjourney共同重塑创意产业

KKAI&#xff08;kkai人工智能&#xff09;是一个整合了多种尖端人工智能技术的多功能助手平台&#xff0c;融合了OpenAI开发的ChatGPT3.5、GPT4.0以及DALLE 3&#xff0c;并包括了独立的图像生成AI—Midjourney。以下是这些技术的详细介绍&#xff1a; **ChatGPT3.5**&#xf…

Lab2: system calls

Using gdb Looking at the backtrace output, which function called syscall? 可以看到是trap.c中usertrap函数调用了syscall函数 What is the value of p->trapframe->a7 and what does that value represent? p->trapframe->a7的值为7&#xff0c;代表了函…

MATLAB 条件语句

MATLAB 条件语句 决策结构要求程序员应指定一个或多个要由程序评估或测试的条件&#xff0c;如果确定条件为真&#xff0c;则应指定要执行的一个或多个语句&#xff0c;如果条件为真&#xff0c;则可以选择要执行的其他语句。条件确定为假。 以下是大多数编程语言中常见的典型…

我们支持批量了!

当我们有很多文件要处理时&#xff0c;一个一个操作不仅费时费力&#xff0c;而且还容易漏掉某个文件。那么有没有更加简单的方法呢&#xff1f;可以试试批量功能&#xff01; 目前以下功能都支持批量操作 音频提取 视频格式转换 字幕生成 视频静音 图片格式转换 图片压缩 视频…

JavaScript —— APIs(四)

一、日期对象 1. 实例化 new 括号里面为空&#xff0c;得到当前时刻的时间 括号里面为指定日期&#xff0c;得到对应日期的时间 注意&#xff1a;若括号里面只有年月日&#xff0c;没有时间&#xff0c;则得到的结果就没有时间&#xff1b;括号里面指定时间是多少&#xff0c;…

身份证二要素核验介绍及使用方法

一、身份证二要素核验简介及重要性 身份证二要素核验是一种重要的身份验证技术&#xff0c;它在现代社会中发挥着至关重要的作用&#xff0c;特别是在涉及个人信息安全和隐私保护的领域。通过身份证二要素核验&#xff0c;我们可以有效地确认个人身份的真实性&#xff0c;从而…

爬虫学习笔记-数美验证

测试网址&#xff1a;智能验证码体验_图片验证码_数美科技数美科技智能验证码在线体验&#xff0c;智能识别风险用户级别&#xff0c;自行切换智能验证码难度及类型&#xff0c;提供滑动、拼图、点选、数字、动态等多种智能验证码服务&#xff0c;精准拦截机器行为。https://ww…

SOLIDWORKS Composer如何使用3D工具实现更真实的动画效果

当我们使用SOLIDWORKS composer创建动画时&#xff0c;往往会涉及到产品的安装与拆解&#xff0c;现实生活中我们在拆卸组装产品的时候&#xff0c;我们往往需要一些工具的协助&#xff0c;比如扳手、螺丝刀等等&#xff0c;那么我们如何在虚拟动画中也将这一过程以逼真的形式展…

新建云仓库

1.GitHub新建云仓库&#xff1a; LICENSE:开源许可证&#xff1b;README.md:仓库说明文件&#xff1b;开源项目&#xff1b;cocoaPodsName.podspec: CocoaPods项目的属性描述文件。 2.Coding新建云仓库&#xff1a; 备注&#xff1a; Coding新建项目&#xff1a;

STM32,复位和时钟控制

外部时钟 HSE 以后需要用到什么就这样直接拿去配就行了

左叶子之和(力扣404)

解题思路:用后序遍历找左孩子&#xff0c;需要注意的是左叶子需要通过其父节点来判断其是不是左叶子 具体代码&#xff1a; class Solution { public: int sumOfLeftLeaves(TreeNode * root) { if(rootNULL)return 0; if(root->leftNULL&&root->rightNULL)ret…

纳米尺度下的单粒子追踪,厦门大学方宁团队用 AI 奏响「细胞里的摇滚」

在微观世界里&#xff0c;每一个细胞都是一个繁忙的城市&#xff0c;而分子们则是这个城市中的居民。想象一下&#xff0c;如果我们能够追踪这些居民的每一个动作&#xff0c;或许便能够揭开生命奥秘的一角。这就是科学家们在活细胞中进行 3D 单粒子跟踪 (single particle trac…

lua整合redis

文章目录 lua基础只适合lua连接操作redis1.下载lua依赖2.导包,连接3.常用的命令1.set,get,push命令 2.自增管道命令命令集合4.使用redis操作lua1.实现秒杀功能synchronized关键字 分布式锁 lua 基础只适合 1.编译 -- 编译 luac a.lua -- 运行 lua a.lua2.命名规范 -- 多行注…

数字化转型成功的企业到底是什么样的?

数字化转型成功的企业通常具备以下特征&#xff1a; 1、以客户为中心的业务模式&#xff1a;成功的数字化转型企业将客户放在业务模式的核心位置&#xff0c;通过提供个性化的服务和产品来满足客户需求。这种以客户为中心的模式能够带来更好的客户体验和忠诚度。 2、强大的数…

Power BI 如何解决月份排序错误/乱序问题(自定义顺序/正确排序)

问题描述 在创建图标时&#xff0c;月份标签没有按照正确的顺序排列。 解决方案&#xff1a; Sort by Column 单击选中排序错误的列&#xff08;MMM&#xff09;根据列排序 (sort by Column)选择需要根据哪一列排序。 这里选择的是Month列&#xff0c;这一列有月份的序号1-…

Golang——GMP原理与调度

一. Golang调度器的由来 1.1 单进程时代不需要调度器 我们知道&#xff0c;一切软件都跑在操作系统上&#xff0c;真正用来干活(计算)的是CPU。早期的操作系统每一个程序就是一个进程&#xff0c;直到一个程序运行完&#xff0c;才能进行下一个进程&#xff0c;就是"单进程…

CSS盒子模型与常见问题

CSS盒子模型 显示模式转换显示模式 盒子模型边框线内边距padding 多值写法 尺寸计算与内减法模式外边距 清除默认样式元素溢出外边距问题合并现象塌陷现象 行内元素 – 内外边距问题 显示模式 显示模式&#xff1a;标签&#xff08;元素&#xff09;的显示方式作用&#xff1a;…