前言

本文是关于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>
where
Theme: 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>
where
Theme: 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>
where
Theme: 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()

运行效果:



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

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()

运行效果:



从代码上看,使用函数后并没有简单多少,只是看起来要整洁些。当然,上面只是测试一下,我们希望菜单创建函数是按来创建的,即函数根据输入返回的是一个菜单组。

所以,我们创建一个新的函数,根据传入信息,返回一组菜单。由于我们之前说过,我们要创建的菜单有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函数:

//菜单组1
let 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); //菜单组2
let 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);

效果演示:



最后,我们需要为菜单实现功能,即当我们点击相应菜单时,整个窗口的主题颜色将会改变。

这需要在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。

效果演示:





也可以自定义,比如,我们创建一个函数,用于返回一个自定义颜色的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);
}

分别传入了四种自定义的颜色值。

最后看一下动态演示:

4、综述

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

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

  1. [OpenCV实战]28 基于OpenCV的GUI库cvui

    目录 1 cvui的使用 1.1 如何在您的应用程序中添加cvui 1.2 基本的"hello world"应用程序 2 更高级的应用 3 代码 4 参考 有很多很棒的GUI库,例 ...

  2. 新做的一个基于OPENGL的gui库

    #include <BGE/All> ,text);     button->setName(name);     button->setSize(Vector2f(,)); ...

  3. Python GUI库

    PyQT不错的,只是要小心,这个东西是GPL的,如果你要写商业程序需要购买商业版授权.另外PyGTK.wxPython都是不错的GUI库.Python自带了一个基于TkInter的GUI库,如果你不想 ...

  4. 8个免费实用的C++GUI库(转载)

      C++标准中并没有包含GUI,这也使得C++开发图形化界面需要依赖于第三方的库.实际上,图形界面恰恰是C++的强项,小到平常使用的各类桌面软件,大到魔兽世界这样的游戏,都是C++擅长的地方.C++ ...

  5. Python 图形 GUI 库 pyqtgraph

    原文  Python 图形 GUI 库 pyqtgraph pyqtgraph 是纯 Python 图形 GUI 库,基于PyQT4 /pyside和NumPy.它主要目的用于在数学/科学/工程中.M ...

  6. 8个免费实用的C++GUI库

    8个免费实用的C++GUI库 C++标准中并没有包含GUI,这也使得C++开发图形化界面需要依赖于第三方的库.实际上,图形界面恰恰是C++的强项,小到平常使用的各类桌面软件,大到魔兽世界这样的游戏,都 ...

  7. C/C++编程GUI库比较

    转自:http://blog.csdn.net/lostown/article/details/658654 最强的GUI库当属Qt,毕竟是商业化的东西,功能最完整,什么都好,包括类似java代码风格 ...

  8. 用PYTHON首选的GUI库WXPYTHON做程序界面

    大家好,我是A8U神经网络,今天又要跟大家分享一下wxWidgets开发神经网络程序界面的一些经验,希望对开发有兴趣的朋友有所帮助.跨平台的GUI工具库以GTK +,Qt和wxWidgets闻名. G ...

  9. Qt和其它GUI库的对比

    http://c.biancheng.net/view/3876.html 世界上的 GUI 库多如牛毛,有的跨平台,有的专属于某个操作系统:有的只有 UI 功能,有的还融合了网络通信.多媒体处理.数 ...

  10. 8个必备的Python GUI库

    Python GUI 库有很多,下面给大家罗列常用的几种 GUI 库.下面介绍的这些GUI框架,能满足大部分开发人员的需要,你可以根据自己的需求,选择合适的GUI库. 很多人学习python,不知道从 ...

随机推荐

  1. 关于MNN的OPENCL和Vulkan支持

    关于MNN框架推理的时候,通过调用库当中结构体的内容,可以切换选择创建Session的具体配置.关于结构的描述见官方文档: 官方文档--创建Session CPU是编译的时候默认选择的配置方式,通过文 ...

  2. pycharm里的jinja2注释问题

    pycharm里html注释是{# #}而不是<!-- -->?   修改方式:如图修改成值None以后,command+/快捷键,html注释的符号就是<!-- 注释内容 --&g ...

  3. 动态配置生成echarts图表

    动态配置x轴和y轴的数据,并且可以选择柱状图.折线图.饼状图等图形 父组件代码: <template> <div class="reportPicture"> ...

  4. 安装cnpm时报错

    报错:npm WARN deprecated socks@1.1.10: If using 2.x branch, please upgrade to at least 2.1.6 to avoid ...

  5. Htq-基于Node.js的异步队列

    github: https://github.com/star7th/htq 部分介绍: 先介绍下基本概念. 我们在编写程序时,偶尔会遇到需要用到异步队列的情况.比如说,我发送一万封邮件,如果单纯使用 ...

  6. 13TB的StarRocks大数据库迁移过程

    公司有一套StarRocks的大数据库在大股东的腾讯云环境中,通过腾讯云的对等连接打通,通过dolphinscheduler调度datax离线抽取数据和SQL计算汇总,还有在大股东的特有的Flink集 ...

  7. CryptoHack做题记录

    一.GENERAL 1.ENCODING ASCII ascii = [99, 114, 121, 112, 116, 111, 123, 65, 83, 67, 73, 73, 95, 112, 1 ...

  8. 从Delphi到Lazarus——Lazarus编程时可以使用的组件(控件)

    0.前言 使用过可视化编程的人都知道在编程时组件的重要性.可以使用的组件越多,编程越方便快捷. 理论上,Delphi中的所有组件在Lazarus中都可以使用.当然,在Windows编程时多数是可以直接 ...

  9. GooseFS 在云端数据湖存储上的降本增效实践

    ​ | 导语 基于云端对象存储的大数据和数据湖存算分离场景已经被广泛铺开,计算节点的独立扩缩容极大地优化了系统的整体运行和维护成本,云端对象存储的无限容量与高吞吐也保证了计算任务的高效和稳定.然而,云 ...

  10. vue3笔记

    如何创建vue3项目 基于 vue 脚手架 npm i @vue/cli -g vue create <project-name> cd <project-name> npm ...