[rustGUI][iced]基于rust的GUI库iced(0.13)的部件学习(03):图像的导入、显示、调整(暨image部件的使用介绍)

前言

本文是关于iced库的部件介绍,iced库是基于rust的GUI库,作者自述是受Elm启发。

iced目前的版本是0.13.1,相较于此前的0.12版本,有较大改动。

本合集是基于新版本的关于分部件(widget)的使用介绍,包括源代码介绍、实例使用等。

环境配置

系统:window10

平台:visual studio code

语言:rust

库:iced 0.13

图像导入及显示

iced中显示图像,可以使用image部件,但image部件不是默认启用的,需要启用feature。在toml文件添加:

toml 复制代码
iced={version="0.13.1", features=["image"]}

然后在代码中导入:

复制代码
use iced::widget::{button,column,text,row,container,slider,image};

之前我们在介绍为iced窗口设置图标时还提到过一个外部图像库image:

toml 复制代码
image="0.25.5"

导入image,为了不与iced的image部件名称冲突,需要为image改个名称:

复制代码
extern crate image as eximg;

1、图像导入

图像导入,通常需要使用文件对话框来导入文件,在iced中并没有提供对应的部件,因此,我们需要使用一个外部库,RFD,首先在toml中添加:

toml 复制代码
rfd="0.15.2"

下面是rfd库支持的功能:

包括了文件导入、保存、文件夹选择等,当然rfd也可以支持消息对话框。

rust 复制代码
use rfd::{FileDialog,MessageDialog};

本文的目的是实现图像文件的导入与显示,并可以调整一些图像属性,如大小、透明度等,因此,我们将添加一个按钮用于触发文件选中对话框,获取图像文件的路径并显示。

我们为结构体添加一个变量用于记录图像路径:

rust 复制代码
#[derive(Clone)]
struct Counter {
    slidervalue:f32,
    slidervalue2:f32,
    imgpath:String,
}

上述imgpath即图像路径变量,另外两个变量用于接受slider部件的实时值,因为我们需要调整图像的大小和透明度,正好可以适应slider,而关于slider的使用介绍,我们在上一篇博文里有单独介绍:
[rustGUI][iced]基于rust的GUI库iced(0.13)的部件学习(02):滑动条部件实现部件(文本或其他)缩放(slider)

然后我们添加按钮触发消息:

rust 复制代码
#[derive(Debug,Clone, Copy)]
enum Message {
    SliderChange(f32),
    SliderChange2(f32),
    BtnLoad,
}

上述的BtnLoad是按钮触发消息,另外两个是slider的滑动时触发,就不多说了。

我们希望的是,点击按钮时弹出文件对话框,因此需要在view函数里添加:

rust 复制代码
let btn_load=button(text("加载图片").size(15)).width(80).height(40)
                    .on_press(Message::BtnLoad);

然后在update函数里更新状态:

rust 复制代码
Message::BtnLoad =>{
                if let Some(file)=FileDialog::new()
                            .set_title("选择图片")
                            .add_filter("Image",&["png","jpg","jpeg","bmp"])
                            .add_filter("png",&["png"])
                            .set_directory("C://")
                            .pick_file(){
                                
                                println!("当前选择的文件:{:?}",file);
                                self.imgpath=file.display().to_string();

                            };
                
            }

上述代码中,FileDialog::new() 是rfd库中文件夹选择的用法,看rfd的官方示例:

我们在实例中,将获取的图像路径传递给我们设置的变量imgpath。

2、图像显示

当我们获取到了图像的本地路径之后,就可以使用iced的image部件来显示图像,我们首先来看下iced中image的定义:

官方源码
rust 复制代码
#[derive(Debug)]
pub struct Image<Handle = image::Handle> {
    handle: Handle,
    width: Length,
    height: Length,
    content_fit: ContentFit,
    filter_method: FilterMethod,
    rotation: Rotation,
    opacity: f32,
}

我们主要关注的就是handle这个参数,它的类型是Handle,可以理解为图像的原始数据来源,Handle在iced中的定义:

官方源码
rust 复制代码
pub enum Handle {
    /// A file handle. The image data will be read
    /// from the file path.
    ///
    /// Use [`from_path`] to create this variant.
    ///
    /// [`from_path`]: Self::from_path
    Path(Id, PathBuf),

    /// A handle pointing to some encoded image bytes in-memory.
    ///
    /// Use [`from_bytes`] to create this variant.
    ///
    /// [`from_bytes`]: Self::from_bytes
    Bytes(Id, Bytes),

    /// A handle pointing to decoded image pixels in RGBA format.
    ///
    /// Use [`from_rgba`] to create this variant.
    ///
    /// [`from_rgba`]: Self::from_rgba
    Rgba {
        /// The id of this handle.
        id: Id,
        /// The width of the image.
        width: u32,
        /// The height of the image.
        height: u32,
        /// The pixels.
        pixels: Bytes,
    },
}

可以看到,Handle定义了三种获取图像原始数据的方式,一种是直接从图像路径获取,一种是图像的字节数组获取,一种是从rgba数据获取。

我们在本文显然是使用第一种方式,即图像路径来获取handle,用于显示。iced官方给了典型的image使用代码:

rust 复制代码
image("ferris.png")

这是最简单的应用,不过我们在实例使用时,因为希望能调整图像的大小与透明度,所以可以这样写:

rust 复制代码
let img_handle=image::Handle::from_path(self.imgpath.clone());
let img1=image(img_handle).opacity(opacity).width(img_w).height(img_h);

其中,opacity用于调整透明度,width和height用于调整图像尺寸。

我们来看下实际显示效果:

3、图像调整

有了之前slider调整text文字大小的经验,此处就比较简单了,因为我们已经获取了图像路径,显然也就能获取图像尺寸了,这里,我们使用外部的image库来处理。image库的功能还是很强大的,看下它支持的图像处理格式:

为了方便管理,我们将获取图像尺寸封装为一个函数,与此前的img_to_icon函数放到一起,使用时直接调用即可:

rust 复制代码
///
/// 获取图片大小
/// 
/// 例:jpg or png -> (w,h)
pub fn get_img_size(path:&str) ->(f32,f32){

    if path != "" {
        let img_re=eximg::open(path);
        match img_re{
            Ok(img)=>{
                return (img.width() as f32,img.height() as f32)
            },
            Err(e)=>{
                let res=MessageDialog::new()
                        .set_title("错误")
                        .set_level(rfd::MessageLevel::Error)
                        .set_buttons(rfd::MessageButtons::Ok)
                        .set_description(&e.to_string())
                        .show();
                if res == rfd::MessageDialogResult::Yes{
                    return (0.0,0.0)
                }
                return (0.0,0.0)
            }
        }
        //return  (img.width() as f32,img.height() as f32)
    }
    return (0.0,0.0)

}
rust 复制代码
 let img_size=imgprocess::get_img_size(&self.imgpath);

看下完整的view函数:

rust 复制代码
let btn_load=button(text("加载图片").size(15)).width(80).height(40)
                   .on_press(Message::BtnLoad);
       let sl1=slider(
           0.0..=1.0, 
           self.slidervalue, 
           Message::SliderChange)
                               .height(40).width(200)
                               .step(0.01);
      
       let opacity= self.slidervalue;
       let tt1=text("透明度:").size(15);
       let tt2=text(format!("{:.2}",opacity)).size(15);
       let row1=row![tt1,sl1,tt2,].spacing(10)
                                               .align_y(iced::Center);


       let slider_scale=slider(0.1..=2.0,self.slidervalue2,Message::SliderChange2)
                                                       .height(40).width(200)
                                                       .step(0.01);
       let tt3=text("缩放:").size(15);
       let tt4=text(format!("{:.2}",self.slidervalue2)).size(15);
       let row2=row![tt3,slider_scale,tt4,].spacing(10);

       let tt_imgpath=text("当前图片路径:").size(15);
       let tt_imgpath2=text(format!("{}",self.imgpath)).size(15);
       let row3=row![tt_imgpath,tt_imgpath2,].spacing(10);

       let img_handle=image::Handle::from_path(self.imgpath.clone());
       let img_size=imgprocess::get_img_size(&self.imgpath);
       let img_w=img_size.0 * self.slidervalue2;
       let img_h=img_size.1 * self.slidervalue2;
       let tt_imgsize=text(format!("图片大小:{:?}",img_size)).size(15);
       //let row4=row![].spacing(10);

       let img1=image(img_handle).opacity(opacity).width(img_w).height(img_h);
       //let cont_color=Color::from_rgb(120.0, 120.0, 0.0);
       let cont_color2=iced::color!(0xE9E7E7,0.5);//#E9E7E7FF
       let cont_img=container(img1)
               .width(1000).height(1000)
               .align_x(iced::Center).align_y(iced::Center)
               .style(move |t|styles::mycontainerstyle(t, cont_color2));
       

       column![
           btn_load,
           row1,
           row2,
           row3,
           tt_imgsize,
           cont_img,
       ].align_x(iced::Center)
       .padding(20)
       .into()

它整体显示效果如下:

4、综述

先来看看动态演示:

结合前文slider的使用,本文介绍了如何在iced中导入图像、显示图像以及调整图像,当然也是很简单的示例。比如导入按钮,后续将会使用菜单替代。

相关推荐
DongLi0115 小时前
rustlings 学习笔记 -- exercises/05_vecs
rust
番茄灭世神1 天前
Rust学习笔记第2篇
rust·编程语言
shimly1234562 天前
(done) 速通 rustlings(20) 错误处理1 --- 不涉及Traits
rust
shimly1234562 天前
(done) 速通 rustlings(19) Option
rust
@atweiwei2 天前
rust所有权机制详解
开发语言·数据结构·后端·rust·内存·所有权
shimly1234562 天前
(done) 速通 rustlings(24) 错误处理2 --- 涉及Traits
rust
shimly1234562 天前
(done) 速通 rustlings(23) 特性 Traits
rust
shimly1234562 天前
(done) 速通 rustlings(17) 哈希表
rust
shimly1234562 天前
(done) 速通 rustlings(15) 字符串
rust
shimly1234562 天前
(done) 速通 rustlings(22) 泛型
rust