[rustGUI][iced]基于rust的GUI库iced(0.13)的部件学习(04):实现窗口主题(颜色)变换(暨menu菜单的使用)

news/2025/1/17 13:02:12/文章来源:https://www.cnblogs.com/rongjv/p/18661975/rongjv

前言
本文是关于iced库的部件介绍,iced库是基于rust的GUI库,作者自述是受Elm启发。
iced目前的版本是0.13.1,相较于此前的0.12版本,有较大改动。
本合集是基于新版本的关于分部件(widget)的使用介绍,包括源代码介绍、实例使用等。

环境配置
系统:window10
平台:visual studio code
语言:rust
库:iced 0.13
扩展库:iced_aw

主题切换

本文来介绍一下如何为iced窗口进行主题切换,注意,本文以颜色变化为示例,附带一些部件的样式切换。
本文在切换时,将使用菜单来进行,所以需要用到菜单部件,但iced源码中并没有给出一个好用的menu部件,但iced的代码仓库包含一个iced_aw库,即iced addtional widgets,在iced_aw中,实现了menu部件,我们可以使用。
需要现在toml文件中添加依赖,并启用menu部件的features:

iced_aw={version="0.11.0",features=["menu"]}

然后在主程序里导入:

use iced_aw::menu::{self,Item,Menu,MenuBar};
use iced_aw::{menu_bar,menu_items};

1、menu部件的介绍

我们结合官方源码,先来介绍一下如何使用menu部件。
从上面的导入中,我们看到,iced_aw中,menu部件有Item、Menu、MenuBar这三个子项,你可以将它们的关系理解为层层递进,即Item是最基本的菜单项,而Menu包含Item,MenuBar包含Menu。
简单的理解就是,Item可以是一个个的单个菜单项,比如打开菜单,当然,也可以嵌套菜单,比如Item本身也是一个Menu。
而Menu就是一个个Item集合在一起的一组菜单,比如文件菜单组。
以此类推,MenuBar就是一组组菜单栏的集合,比如窗口顶部的菜单栏,包括文件工具选项关于等等这样的菜单组。
还是通过源码来亲自看一下吧。
Item

官方源码
/// Item inside a [`Menu`]
#[must_use]
pub struct Item<'a, Message, Theme, Renderer>
whereTheme: Catalog,Renderer: renderer::Renderer,
{pub(super) item: Element<'a, Message, Theme, Renderer>,pub(super) menu: Option<Box<Menu<'a, Message, Theme, Renderer>>>,
}

可以看到Item定义了两个参数,item和menu,其中item的类型是Element,这是iced中元素的原始类型,即菜单项可以是任意一种部件,通常我们定义的菜单项时button,因为点击时可以产生消息,但是不限于button,也可以是纯text或者是一个slider,任意元素都可以。
而menu的类型是一个枚举,里面的数据类型是Menu。
Menu

官方源码
pub struct Menu<'a, Message, Theme, Renderer>
whereTheme: Catalog,Renderer: renderer::Renderer,
{pub(super) items: Vec<Item<'a, Message, Theme, Renderer>>,pub(super) spacing: Pixels,pub(super) max_width: f32,pub(super) width: Length,pub(super) height: Length,pub(super) axis: Axis,pub(super) offset: f32,
}

Menu的参数中,items是一个Item的集合。

MenuBar

官方源码
/// menu bar
#[must_use]
pub struct MenuBar<'a, Message, Theme, Renderer>
whereTheme: Catalog,Renderer: renderer::Renderer,
{roots: Vec<Item<'a, Message, Theme, Renderer>>,spacing: Pixels,padding: Padding,width: Length,height: Length,check_bounds_width: f32,draw_path: DrawPath,scroll_speed: ScrollSpeed,class: Theme::Class<'a>,
}

可以看到MenuBar的参数roots,其类型与Menu的参数items的类型是一样的,这其实很好理解,MenuBar和Menu逻辑上就是一致的,二者都是之菜单的集合,只是我们通常会将单组菜单与总菜单栏分开创建,所以源码中这个区分也是正常的。
除了上述三个函数外,iced_aw的menu部件还提供了两个宏:

menu_bar,menu_items

这应该很明确,是用于快捷创建MenuBar和Item的。

在iced_aw的官方示例中,提供了4种创建menu的例子,第一种是利用Item和MenuBar来规规矩矩的创建。
第二种方法是利用宏macro来创建。
第三种也是利用宏。
第四种是利用函数结合宏来创建。
我们来使用第一种,仿照官方示例,看一下实际效果,先编写view函数的代码:

let sub1=Item::with_menu(button("文件"), Menu::new([Item::new("打开"),Item::new("保存"),Item::new("关闭"),].into()).max_width(120.0).spacing(5));let sub2=Item::with_menu(button("选项"), Menu::new([Item::new("设置参数"),Item::new("状态监控"),Item::new("数据导入"),].into()).max_width(120.0).spacing(5));let menubar=MenuBar::new(vec![sub1,sub2]).width(iced::Length::Fill).spacing(5).padding(10);column![menubar,].into()

运行效果:
image
看起来是可以的,但是上面我们只是创建了最简单的菜单,没有设置任何样式,也没有添加任何响应,若是想要创建好看的菜单栏,以及添加功能响应,那么还需要增加其他代码。

2、自己封装menu函数

在iced_aw的源代码中,就提供了封装成函数来创建menu的方法,这样当然很好,所以我们也要这样做,因为,你应该也不想在view函数里编写大量的菜单创建条,显得臃肿而且不便于检查。
但我们不想用官方那种方法,所以我们自己封装一个menucreate函数,可以根据需要创建一个菜单组。
以下,我们的实例将这样来实现,我们的函数提供三种菜单样式,即button、纯文本以及slider,可以创建一组,也可以创建单个,且可以设置样式,本例中,我们只设置颜色来演示。
我们从最简单的开始,首先,我们先新建一个menuset.rs文件,在其中编写我们需要的函数。为了简单,我们对于button、text以及slider类型分别编写一个函数,以button为例,我们创建menu_button函数,这个函数将根据传入的content和msg来返回一个Item,其子元素是button。

menu_button
创建单个菜单项

///
/// 创建单个菜单项-button
/// 
pub fn menu_button<'a>(msg:Message,content:&'a str
) -> Item<'a,Message,Theme,Renderer>
where {let item=button(text(content).size(15)).on_press(msg);Item::new(item) 
}

可以看到,函数非常简单,我们使用函数来修改之前的view函数:

let item_open=menu_button(Message::BtnLoad,"打开");let item_save=menu_button(Message::BtnLoad,"保存");let item_close=menu_text("关闭");let item_new=menu_button(Message::BtnLoad,"新建");let item_sl=menu_slider(0.0..=1.0, 0.1,self.sldvalue,Message::Sld1);let sub1=Item::with_menu(button("文件"), Menu::new([item_open,item_save,item_close,item_new,item_sl,].into()).max_width(120.0).spacing(5));let item_setparam=menu_button(Message::BtnLoad, "设置参数");let item_monitor=menu_button(Message::BtnLoad, "状态监控");let item_dataimport=menu_button(Message::BtnLoad, "数据导入");let sub2=Item::with_menu(button("选项"), Menu::new([item_setparam,item_monitor,item_dataimport].into()).max_width(120.0).spacing(5));let menubar=MenuBar::new(vec![sub1,sub2,]).width(iced::Length::Fill).spacing(5).padding(10);column![menubar,].into()

运行效果:
image
从代码上看,使用函数后并没有简单多少,只是看起来要整洁些。当然,上面只是测试一下,我们希望菜单创建函数是按来创建的,即函数根据输入返回的是一个菜单组。
所以,我们创建一个新的函数,根据传入信息,返回一组菜单。由于我们之前说过,我们要创建的菜单有button、text、slider三种可选,所以,组菜单创建函数,可以根据传入的模式mode和子项集合如按钮的msg和content,返回成组的Item。
menu_group_add
创建菜单组

///
/// 创建一组菜单
/// 
pub fn menu_group_add<'a>(items:Vec<&'a str>,msgs:Vec<Message>,
) -> Vec<Item<'a,Message,Theme,Renderer>>{if items.len() != msgs.len() {let res=MessageDialog::new().set_title("Error").set_description("items.len() != msgs.len()").show();if res == rfd::MessageDialogResult::Yes {panic!("items.len() != msgs.len()");} else {panic!("items.len() != msgs.len()");}} else {let mut vec1=Vec::new();for (index,i) in items.iter().enumerate(){let msg=msgs[index];let btn=button(text(*i).size(15)).on_press(msg);let item_temp=Item::new(btn);vec1.push(item_temp);}vec1}}

我们再来修改一下view函数:

//菜单组1let mut mga1=menu_group_add(vec!["打开","关闭","保存"],vec![Message::WJ(MenuWJ::Open),Message::WJ(MenuWJ::Close),Message::WJ(MenuWJ::Save)]);mga1.push(menu_slider(0.0..=10.0, 0.1, self.sldvalue, Message::Sld1));let sub1=menu_group_sub("文件", Message::WJ(MenuWJ::Wj), mga1);//菜单组2let mut mga2=menu_group_add(vec!["设置","调整","导入"],vec![Message::XX(MenuXX::Xx),Message::XX(MenuXX::Modify),Message::XX(MenuXX::Import),]);mga2.push(menu_text("导出"));let sub2=menu_group_sub("选项", Message::XX(MenuXX::Xx), mga2);let menubar=MenuBar::new(vec![sub1,sub2,]).width(iced::Length::Fill).spacing(5).padding(10);column![menubar,].into()

相比之下,要简洁不少,关键是我们可以直接传入菜单的名称和触发消息,函数会统一生成菜单组,而且,如果想要个别添加如slider、text,可以使用push直接添加。

3、主题切换

本文的目的是使用菜单实现主题(颜色)切换,上面我们实现了菜单的添加,并且自己封装了函数,现在我们能够快速添加菜单组了,我们添加一组新的菜单:

主题:

默认、主题1、主题2、主题3

首先,我们新增一组Message,为了便于管理,我们新建一个enum:

#[derive(Debug, Clone,Copy)]
enum MenuStyle{Default,Style1,Style2,Style3,Ms,//head
}

创建完成后,在Message中调用以上枚举即可。
接着我们修改view函数,新增一组菜单:

let mga3=menu_group_add(vec!["默认","主题1","主题2","主题3"], vec![Message::STYLE(MenuStyle::Default),Message::STYLE(MenuStyle::Style1),Message::STYLE(MenuStyle::Style2),Message::STYLE(MenuStyle::Style3),]);let sub3=menu_group_sub("主题", Message::STYLE(MenuStyle::Ms), mga3);

效果演示:
image
最后,我们需要为菜单实现功能,即当我们点击相应菜单时,整个窗口的主题颜色将会改变。
这需要在update函数中进行修改,同时,为了实现颜色改变,我们需要设置窗口的theme或者style。
需要先修改一下主函数:

   iced::application("count",Counter::update,Counter::view).default_font(iced::Font::with_name("微软雅黑")).window(Settings{size:iced::Size{width:400.0,height:300.0},position:Position::Specific(iced::Point{x:100.0,y:100.0}),icon:Some(myicon),..Default::default()}).theme(Counter::theme).run()

设置一下iced::applicationtheme参数,是一个闭包函数,函数返回的是Theme。
所以,我们需要为Counter新增一个函数:

fn theme(&self) -> iced::Theme {self.theme.clone()}

此处self.theme是需要我们新增的变量,用于设置当前窗口的Theme:

struct Counter {menustate:i32,sldvalue:f32,theme:iced::Theme,}

theme的类型就是iced::Theme
这样一来,我们就将变量与窗口的主题绑定起来,如果我们要修改窗口主题,只需要修改theme变量的值即可。
所以,我们在updata函数里来更新:

Message::STYLE(style)=>{match style {MenuStyle::Default=>{self.theme=iced::Theme::Dark;}MenuStyle::Style1=>{self.theme=iced::Theme::Light;}MenuStyle::Style2=>{self.theme=iced::Theme::TokyoNight;}MenuStyle::Style3=>{self.theme=iced::Theme::SolarizedDark;}MenuStyle::Ms=>{}}}

上面代码中使用的是系统预设的Theme。
效果演示:
image
image
也可以自定义,比如,我们创建一个函数,用于返回一个自定义颜色的Theme:

///
/// 根据传入颜色值返回主题
/// 
pub fn mystyle(c:Color) -> iced::Theme {let name= "mytheme".to_string();let palette=iced::theme::Palette{background:c,danger:Color::from_rgb(0.0, 0.0, 0.0),text:Color::WHITE,primary:Color::parse("#E7E6E6FF").unwrap(), success:Color::parse("#BCF7C8FF").unwrap(),};let custom=iced::theme::Custom::new(name, palette,);iced::Theme::Custom(Arc::new(custom))
}

然后修改update函数:

MenuStyle::Default=>{let color=Color::parse("#9EDFF0FF").unwrap();self.theme=styles::mystyle(color);}MenuStyle::Style1=>{let color=Color::parse("#B3F07AFF").unwrap();self.theme=styles::mystyle(color);}MenuStyle::Style2=>{let color=Color::parse("#FF9E9EFF").unwrap();self.theme=styles::mystyle(color);}MenuStyle::Style3=>{let color=Color::parse("#EEC964FF").unwrap();self.theme=styles::mystyle(color);}

分别传入了四种自定义的颜色值。
最后看一下动态演示:
image

4、综述

以上,我们在iced中实现了菜单的设置,以及利用菜单来切换主题颜色的功能。当然,功能依旧是比较简单的,但是本文的目的是介绍,我认为已经达到了目的,所以其他相关的,本文就不会赘述。

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

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

相关文章

网络认证

网络认证 网络认证概述 网络认证:Windows网络认证是指在Windows操作系统中进行网络通信和资源访问时,验证用户身份和授权权限的过程。它确保只有经过身份验证的用户能够访问网络资源,并根据其权限级别进行授权操作。 网络认证有哪些? 1.用户名和密码认证:这是最常见的认证…

ffmpeg简易播放器(1)--了解视频格式

视频帧 对于一份视频,实质上是多张图片高速播放形成的。每一张图片即为该视频的一帧。而每秒钟播放的图片张数便为所谓的帧率(Frame Rate/Frame Per Second)。常见的帧率有24fps(即一秒播放24张图片),60fps(一秒播放60张图片)等。也就是说,对于一个时长60秒的图片,如果帧率…

Issac Gym出现error: subprocess-exited-with-error报错

1. 前言 一方面便于日后自己的温故学习,另一方面也便于大家的学习和交流。 如有不对之处,欢迎评论区指出错误,你我共同进步学习! 2. 正文 我在安装humanoid gym pip install -e .的时候,出现下列问题:解决方法: pip install --upgrade setuptools没解决就先卸载setupt…

高版本Jenkins关闭跨站请求伪造保护(Debian系)

关闭方法: 修改配置文件:/etc/default/jenkins(Debian系默认位置) 找到函数JAVA_ARGS,加上-Dhudson.security.csrf.GlobalCrumbIssuerConfiguration.DISABLE_CSRF_PROTECTION=true这段,重启Jenkins。

【分享】 100+ 套开源大数据可视化大屏Html5模板,全网最炫!

************************************************* 博客园:http://www.cnblogs.com/kingboy2008/ 今日头条:https://www.toutiao.com/c/user/token/MS4wLjABAAAAlK12eJwVSGYna9n7enGhiqQA_r386diukZRcWQulCRc/? CSDN:http://hi.csdn.net/kingboy2008 *******************…

直接把浏览器中的url知道复制到postman中

直接把浏览器中的url知道复制到postman中 1.找到需要复制的接口 2.右击接口 3.点击copy 4.选择cURL(bash)遇见问题,这是你成长的机会,如果你能够解决,这就是收获。 作者:晚来南风晚相识 出处:https://www.cnblogs.com/IwishIcould/ 想问问题,打赏了卑微的博主,求求你备…

LSTM缓解梯度消失

LSTM缓解梯度消失 Bilibili【重温经典】大白话讲解LSTM长短期记忆网络 如何缓解梯度消失,手把手公式推导反向传播 前向传播结构及原理 以“期末考试周”为例理解回顾RNN的梯度消失原因反向传播:LSTM如何缓解梯度消失

云原生K8Sconfing泄露etcdproxy

一、Etcd未授权访问 攻击port:2379;默认通过证书认证,主要存放节点的数据,如一些token证书。第一种情况: 没有配置--client-cert-path参数打开证书验证(或者改为false),暴露外Etcd服务存在未授权访问风险; 暴露外部可以访问,直接未授权访问获取secrets和token利用; 第…

2024年微信公众号收到赞赏6487.50

等了好久的微信公众号2024年创作回顾终于推送了,这是一个针对微信公众号创作的年终总结,如果没有记错的话应该是微信公众号推送年终总结的第二年。2023年的完整总结数据在这里:「我的2023年公众号创作数据分享」,在这份总结里我发现了许多有趣的数据,例如2023年全年一共发…

rpm安装clickhouse(24.11.8.5单机)

环境:OS:Centos 7db:24.11.8.5 1.下载安装介质下载地址为:https://packagecloud.io/Altinity/clickhouse或是这里下载https://mirrors.tuna.tsinghua.edu.cn/clickhouse/rpm/stable/x86_64/ 我这里下载的是如下三个包clickhouse-client-24.8.11.5.noarch.rpmclickhouse-common-…

不知道怎么坏的,但是修好了。修一个吉他数字效果器!

本文介绍了ZOOM G1 Four数字效果器不开机故障的分析和排除。涉及到上电时序的分析。孤独摇滚看上头 吉他梦碎效果器 两年前看孤独摇滚上头入手了电吉他。家贫,配的是海鲜市场买的ZOOM G1 Four数字效果器。这里简单介绍一下他的供电方案,有4节1.5V干电池,9V内负外正的DC插座,…