[rustGUI][iced]基于rust的GUI库iced(0.13)的部件学习(04):实现窗口主 ...

守听  金牌会员 | 2025-1-17 12:46:18 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 874|帖子 874|积分 2622

前言
本文是关于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:
  1. iced_aw={version="0.11.0",features=["menu"]}
复制代码
然后在主程序里导入:
  1. use iced_aw::menu::{self,Item,Menu,MenuBar};
  2. use iced_aw::{pub struct Menu<'a, Message, Theme, Renderer>
  3. where
  4.     Theme: Catalog,
  5.     Renderer: renderer::Renderer,
  6. {
  7.     pub(super) items: Vec<Item<'a, Message, Theme, Renderer>>,
  8.     pub(super) spacing: Pixels,
  9.     pub(super) max_width: f32,
  10.     pub(super) width: Length,
  11.     pub(super) height: Length,
  12.     pub(super) axis: Axis,
  13.     pub(super) offset: f32,
  14. }};
复制代码
1、menu部件的先容

我们结合官方源码,先来先容一下怎样使用menu部件。
从上面的导入中,我们看到,iced_aw中,menu部件有Item、Menu、MenuBar这三个子项,你可以将它们的关系理解为层层递进,即Item是最根本的菜单项,而Menu包罗Item,MenuBar包罗Menu。
简单的理解就是,Item可以是一个个的单个菜单项,好比打开菜单,当然,也可以嵌套菜单,好比Item本身也是一个Menu。
而Menu就是一个个Item聚集在一起的一组菜单,好比文件菜单组。
以此类推,MenuBar就是一组组菜单栏的聚集,好比窗口顶部的菜单栏,包罗文件工具选项关于等等这样的菜单组。
还是通过源码来亲身看一下吧。
Item
官方源码
  1. /// Item inside a [`Menu`]
  2. #[must_use]
  3. pub struct Item<'a, Message, Theme, Renderer>
  4. where
  5.     Theme: Catalog,
  6.     Renderer: renderer::Renderer,
  7. {
  8.     pub(super) item: Element<'a, Message, Theme, Renderer>,
  9.     pub(super) menu: Option<Box<Menu<'a, Message, Theme, Renderer>>>,
  10. }
复制代码
可以看到MenuBar的参数roots,其类型与Menu的参数items的类型是一样的,这其实很好理解,MenuBar和Menu逻辑上就是一致的,二者都是之菜单的聚集,只是我们通常会将单组菜单与总菜单栏分开创建,所以源码中这个区分也是正常的。
除了上述三个函数外,iced_aw的menu部件还提供了两个宏:
  1. pub struct Menu<'a, Message, Theme, Renderer>
  2. where
  3.     Theme: Catalog,
  4.     Renderer: renderer::Renderer,
  5. {
  6.     pub(super) items: Vec<Item<'a, Message, Theme, Renderer>>,
  7.     pub(super) spacing: Pixels,
  8.     pub(super) max_width: f32,
  9.     pub(super) width: Length,
  10.     pub(super) height: Length,
  11.     pub(super) axis: Axis,
  12.     pub(super) offset: f32,
  13. }
复制代码
这应该很明确,是用于快捷创建MenuBar和Item的。
在iced_aw的官方示例中,提供了4种创建menu的例子,第一种是利用Item和MenuBar来规规矩矩的创建。
第二种方法是利用宏macro来创建。
第三种也是利用宏。
第四种是利用函数结合宏来创建。
我们来使用第一种,仿照官方示例,看一下现实效果,先编写view函数的代码:
  1. /// menu bar
  2. #[must_use]
  3. pub struct MenuBar<'a, Message, Theme, Renderer>
  4. where
  5.     Theme: Catalog,
  6.     Renderer: renderer::Renderer,
  7. {
  8.     roots: Vec<Item<'a, Message, Theme, Renderer>>,
  9.     spacing: Pixels,
  10.     padding: Padding,
  11.     width: Length,
  12.     height: Length,
  13.     check_bounds_width: f32,
  14.     draw_path: DrawPath,
  15.     scroll_speed: ScrollSpeed,
  16.     class: Theme::Class<'a>,
  17. }
复制代码
运行效果:

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

在iced_aw的源代码中,就提供了封装成函数来创建menu的方法,这样当然很好,所以我们也要这样做,由于,你应该也不想在view函数里编写大量的菜单创建条,显得臃肿而且未便于检查。
但我们不想用官方那种方法,所以我们本身封装一个menucreate函数,可以根据需要创建一个菜单组。
以下,我们的实例将这样来实现,我们的函数提供三种菜单样式,即button、纯文本以及slider,可以创建一组,也可以创建单个,且可以设置样式,本例中,我们只设置颜色来演示。
我们从最简单的开始,首先,我们先新建一个menuset.rs文件,在其中编写我们需要的函数。为了简单,我们对于button、text以及slider类型分别编写一个函数,以button为例,我们创建menu_button函数,这个函数将根据传入的content和msg来返回一个Item,其子元素是button。
menu_button
创建单个菜单项
  1. menu_bar,menu_items
复制代码
我们再来修改一下view函数:
  1. let sub1=Item::with_menu(
  2.             button("文件"),
  3.             Menu::new(
  4.                 [
  5.                     Item::new("打开"),
  6.                     Item::new("保存"),
  7.                     Item::new("关闭"),
  8.                 ].into()
  9.                 ).max_width(120.0).spacing(5)
  10.             );
  11.         
  12.         let sub2=Item::with_menu(
  13.             button("选项"),
  14.             Menu::new([
  15.                 Item::new("设置参数"),
  16.                 Item::new("状态监控"),
  17.                 Item::new("数据导入"),
  18.             ].into()
  19.             ).max_width(120.0).spacing(5)
  20.             );
  21.         
  22.         let menubar=MenuBar::new(vec![
  23.             sub1,
  24.             sub2
  25.         ]).width(iced::Length::Fill).spacing(5).padding(10);
  26.         column![
  27.             menubar,
  28.         ]
  29.         .into()
复制代码
相比之下,要简便不少,关键是我们可以直接传入菜单的名称和触发消息,函数会同一生成菜单组,而且,如果想要个别添加如slider、text,可以使用push直接添加。
3、主题切换

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

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

首先,我们新增一组Message,为了便于管理,我们新建一个enum:
  1. ///
  2. /// 创建单个菜单项-button
  3. ///
  4. pub fn menu_button<'a>(
  5.     msg:Message,
  6.     content:&'a str
  7. ) -> Item<'a,Message,Theme,Renderer>
  8. where
  9. {
  10.     let item=button(text(content).size(15)).on_press(msg);
  11.     Item::new(item)
  12. }
复制代码
创建完成后,在Message中调用以上摆列即可。
接着我们修改view函数,新增一组菜单:
  1. let item_open=menu_button(Message::BtnLoad,"打开");
  2.         let item_save=menu_button(Message::BtnLoad,"保存");
  3.         let item_close=menu_text("关闭");
  4.         let item_new=menu_button(Message::BtnLoad,"新建");
  5.         let item_sl=menu_slider(0.0..=1.0, 0.1,self.sldvalue,Message::Sld1);
  6.         let sub1=Item::with_menu(
  7.             button("文件"),
  8.             Menu::new(
  9.                 [
  10.                     item_open,
  11.                     item_save,
  12.                     item_close,
  13.                     item_new,
  14.                     item_sl,
  15.                 ].into()
  16.                 ).max_width(120.0).spacing(5)
  17.             );
  18.         
  19.         let item_setparam=menu_button(Message::BtnLoad, "设置参数");
  20.         let item_monitor=menu_button(Message::BtnLoad, "状态监控");
  21.         let item_dataimport=menu_button(Message::BtnLoad, "数据导入");
  22.         let sub2=Item::with_menu(
  23.             button("选项"),
  24.             Menu::new([
  25.                 item_setparam,
  26.                 item_monitor,
  27.                 item_dataimport
  28.             ].into()
  29.             ).max_width(120.0).spacing(5)
  30.             );
  31.    
  32.         
  33.         let menubar=MenuBar::new(vec![
  34.             sub1,
  35.             sub2,
  36.         ]).width(iced::Length::Fill).spacing(5).padding(10);
  37.         column![
  38.             menubar,
  39.         ]
  40.         .into()
复制代码
效果演示:

最后,我们需要为菜单实现功能,即当我们点击相应菜单时,整个窗口的主题颜色将会改变。
这需要在update函数中进行修改,同时,为了实现颜色改变,我们需要设置窗口的theme或者style。
需要先修改一下主函数:
  1. ///
  2. /// 创建一组菜单
  3. ///
  4. pub fn menu_group_add<'a>(
  5.     items:Vec<&'a str>,
  6.     msgs:Vec<Message>,
  7. ) -> Vec<Item<'a,Message,Theme,Renderer>>{
  8.     if items.len() != msgs.len() {
  9.         let res=MessageDialog::new()
  10.             .set_title("Error")
  11.             .set_description("items.len() != msgs.len()")
  12.             .show();
  13.         if res == rfd::MessageDialogResult::Yes {
  14.             panic!("items.len() != msgs.len()");
  15.         } else {
  16.             panic!("items.len() != msgs.len()");
  17.         }
  18.         
  19.     } else {
  20.         let mut vec1=Vec::new();
  21.         for (index,i) in items.iter().enumerate(){
  22.             let msg=msgs[index];
  23.             let btn=button(text(*i).size(15)).on_press(msg);
  24.             let item_temp=Item::new(btn);
  25.             vec1.push(item_temp);
  26.         }
  27.         vec1
  28.     }
  29.       
  30. }
复制代码
设置一下iced::applicationtheme参数,是一个闭包函数,函数返回的是Theme。
所以,我们需要为Counter新增一个函数:
  1. //菜单组1
  2.         let mut mga1=menu_group_add(
  3.             vec!["打开","关闭","保存"],
  4.         vec![Message::WJ(MenuWJ::Open),
  5.                     Message::WJ(MenuWJ::Close),
  6.                     Message::WJ(MenuWJ::Save)]);
  7.         mga1.push(menu_slider(0.0..=10.0, 0.1, self.sldvalue, Message::Sld1));
  8.         let sub1=menu_group_sub(
  9.             "文件",
  10.             Message::WJ(MenuWJ::Wj),
  11.             mga1);
  12.         
  13.         //菜单组2
  14.         let mut mga2=menu_group_add(
  15.             vec!["设置","调整","导入"],
  16.              vec![Message::XX(MenuXX::Xx),
  17.                     Message::XX(MenuXX::Modify),
  18.                     Message::XX(MenuXX::Import),
  19.              ]);
  20.         mga2.push(menu_text("导出"));
  21.         let sub2=menu_group_sub(
  22.             "选项",
  23.             Message::XX(MenuXX::Xx),
  24.             mga2);
  25.       
  26.         let menubar=MenuBar::new(vec![
  27.             sub1,
  28.             sub2,
  29.         ]).width(iced::Length::Fill).spacing(5).padding(10);
  30.         column![
  31.             menubar,
  32.         ]
  33.         .into()
复制代码
此处self.theme是需要我们新增的变量,用于设置当前窗口的Theme:
  1. #[derive(Debug, Clone,Copy)]
  2. enum MenuStyle{
  3.     Default,
  4.     Style1,
  5.     Style2,
  6.     Style3,
  7.     Ms,//head
  8. }
复制代码
theme的类型就是iced::Theme
这样一来,我们就将变量与窗口的主题绑定起来,如果我们要修改窗口主题,只需要修改theme变量的值即可。
所以,我们在updata函数里来更新:
  1. let mga3=menu_group_add(
  2.             vec!["默认","主题1","主题2","主题3"],
  3.             vec![
  4.                 Message::STYLE(MenuStyle::Default),
  5.                 Message::STYLE(MenuStyle::Style1),
  6.                 Message::STYLE(MenuStyle::Style2),
  7.                 Message::STYLE(MenuStyle::Style3),
  8.             ]);
  9.         let sub3=menu_group_sub(
  10.             "主题",
  11.             Message::STYLE(MenuStyle::Ms),
  12.             mga3);
复制代码
上面代码中使用的是系统预设的Theme。
效果演示:


也可以自定义,好比,我们创建一个函数,用于返回一个自定义颜色的Theme:
  1.    iced::application("count",Counter::update,Counter::view)
  2.             .default_font(iced::Font::with_name("微软雅黑"))
  3.             .window(Settings{
  4.                 size:iced::Size{
  5.                     width:400.0,
  6.                     height:300.0
  7.                 },
  8.                 position:Position::Specific(iced::Point{
  9.                     x:100.0,
  10.                     y:100.0
  11.                 }),
  12.                 icon:Some(myicon),
  13.                 ..Default::default()
  14.             })
  15.             .theme(Counter::theme)
  16.             .run()
复制代码
然后修改update函数:
  1. fn theme(&self) -> iced::Theme {
  2.         self.theme.clone()
  3.     }
复制代码
分别传入了四种自定义的颜色值。
最后看一下动态演示:

4、综述

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

守听

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表