注:此文适合于对rust有一些了解的朋友
iced是一个跨平台的GUI库,用于为rust语言程序构建UI界面。
这是一个系列博文,本文是第三篇,前两篇的链接:
1、Rust UI开发(一):使用iced构建UI时,如何在界面显示中文字符
2、Rust UI开发(二):iced中如何为窗口添加icon图标
本篇是系列第三篇,主要关注如何在窗口上显示图片,要在窗口显示一张图片,基本上需要解决两个问题,一是图片文件导入,二是图片文件显示。这两个功能对于其他成熟语言都不是问题,文件对话框和图片渲染都不是难事,但iced是缺少对话框部件的。
所以,就要借助于第三方库,下面我们将针对这两个方面做说明。
实际窗口效果预览:
一 文件对话框
至少目前为止(iced=0.10)iced中没有集成对话框功能,包括文件对话框、字体、颜色、消息等对话框都没有,但我看到其他支持rust的GUI库如egui、nwg(native–window–gui)等都是有对话框的,当然egui中是用rfd库来实现的。
所以,在本篇中,我们也是利用rfd来实现文件对话框功能。
rfd是Rusty File Dialogs的简写,是跨平台的rust库,提供打开/保存对话框的功能。
rfd的官方代码:
use rfd::FileDialog;
let files = FileDialog::new()
.add_filter("text", &["txt", "rs"])
.add_filter("rust", &["rs", "toml"])
.set_directory("/")
.pick_file();
使用起来也很简单,在你的项目的Cargo.toml中添加依赖:
rfd="0.12.1"
use rfd::FileDialog;
需要注意的是,FileDialog.pickfile()函数返回的是一个枚举类型Option,里面的数据就是文件的路径。
所以,我们可以使用Some来返回此路径。
if let Some(file)=FileDialog::new()
.set_directory("/")
.add_filter("all", &["*"]) //添加文件过滤,all是显示所有类型
.add_filter("文本文件(*txt)", &["txt", "rs"]) //只显示文本类型
.add_filter("图像文件(*png*jpg*bmp)", &["png","jpg","jpeg","bmp"]) //只显示图像类型
.set_title("打开图像")
.pick_file()
{
self.iamgepath=file.display().to_string();
};
这样我们打开的图像的路径,就赋给了self.imagepath。
二 将图片显示在窗口界面上
我们现在已经得到了图像的路径,那么我们如何将图像显示在窗口上呢?这里需要用到iced提供的image这个功能,它是被定义为iced_widget的一个特性,即Features。Features是Rust中的一个概念,或者是一种机制。以下是rust官方手册关于Features的概念,大家自己理解一下。
- Cargo “features” provide a mechanism to express conditional compilation and optional dependencies.
- A package defines a set of named features in the [features] table of Cargo.toml, and each feature can either be enabled or disabled. Features for the package being built can be enabled on the command–line with flags such as –features. Features for dependencies can be enabled in the dependency declaration in Cargo.toml.
本篇说明一下如何使用image这个Features,在你的项目的Cargo.toml文件中,添加了iced依赖后,添加以下语句:
iced.features=["image"]
use iced::widget::{text, button,slider,column,image,container};
另外,我们在本系列第二篇提到过一个第三方的图像库Image,实际上iced中处理图像也用到了这个库,所以我们将Image也添加到依赖中:
image="0.24.7"
为了不混乱iced的image和第三方image,我们在导入第三方image时,如下:
extern crate image as img_image;
当然,as后面的名字,你可以自己随便定义,只要你知道它是用来代替第三方image的“命名空间”即可。
image部件显示图像代码:
image(hd).content_fit(ContentFit::Fill),
此处,image函数的参数是一个Handle,官方关于image的源代码:
/// Creates a new [`Image`].
///
/// [`Image`]: widget::Image
#[cfg(feature = "image")]
pub fn image<Handle>(handle: impl Into<Handle>) -> crate::Image<Handle> {
crate::Image::new(handle.into())
}
let hd= if cfg!(target_arch = "wasm32") { //Wasm32是一种基于WebAssembly(Wasm)的32位虚拟机
image::Handle::from_path("iced_test/src/img1.png")
} else {
//image::Handle::from_path("../iced_test/src/img2.jpeg")
image::Handle::from_path(img_path)
};
如上,使用image-Handle-from_path函数,从图像路径获取image的Handle,然后将此Handle传给image部件即可。
完整代码:
use iced::widget::{text, button,slider,column,row,image,container};
use iced::{Alignment, Element, Length,Sandbox, Settings, ContentFit, alignment};
use iced::window;
use iced::window::icon;
use iced::window::Position;
use iced::Font;
use iced::font::Family;
extern crate image as img_image;
extern crate num_complex;
use rfd::FileDialog;
pub fn main() ->iced::Result{
//Counter::run(Settings::default())
let ff="微软雅黑";
//第二种获取rgba图片的方法,利用Image库
let img2=img_image::open("../iced_test/src/dota22.png");
let img2_path=match img2 {
Ok(path)=>path,
Err(error)=>panic!("error is {}",error),
};
let img2_file=img2_path.to_rgba8();
let ico2=icon::from_rgba(img2_file.to_vec(), 64, 64);
let ico2_file=match ico2{
Ok(file)=>file,
Err(error)=>panic!("error is {}",error),
};
Counter::run(Settings {
window:window::Settings{ //设置窗口尺寸和位置及图标
size:(800,600),
position:Position::Specific(100, 40),
icon:Some(ico2_file),
..window::Settings::default()
},
default_font:Font{ //设置UI界面的显示字体
family:Family::Name(ff),
..Font::DEFAULT},
..Settings::default()
})
}
pub struct Counter{
srcimgpath:String,
destimgpath:String,
slivalue:f32,
}
#[derive(Debug, Clone,Copy)]
pub enum Message {
OpenimgPressed,
SaveimgPressed,
SliderChanged(f32),
}
impl Sandbox for Counter {
type Message = Message;
fn new() -> Self {
let path=String::new();
Self { srcimgpath: path.to_string(), //to_string()类似于clone
destimgpath:path.to_string(),
slivalue:0.0}
}
fn title(&self) -> String {
String::from("iced_UI演示")
}
fn update(&mut self, message: Message) {
match message {
Message::OpenimgPressed => {
if let Some(file)=FileDialog::new()
.set_directory("D:\008 rustpro\iced_test\src")
.add_filter("all", &["*"]) //添加文件过滤,all是显示所有类型
.add_filter("文本文件(*txt)", &["txt", "rs"]) //只显示文本类型
.add_filter("图像文件(*png*jpg*bmp)", &["png","jpg","jpeg","bmp"]) //只显示图像类型
.set_title("打开图像")
.pick_file()
{
self.srcimgpath=file.display().to_string();
};
//println!("{:?}",file);
}
Message::SaveimgPressed=> {
self.destimgpath="".to_string();
}
Message::SliderChanged(vl)=>{
self.slivalue=vl;
}
}
}
fn view(&self) -> Element<Message> {
let img_path=&self.srcimgpath;
let hd= if cfg!(target_arch = "wasm32") { //Wasm32是一种基于WebAssembly(Wasm)的32位虚拟机
image::Handle::from_path("iced_test/src/img1.png")
} else {
//image::Handle::from_path("../iced_test/src/img2.jpeg")
image::Handle::from_path(img_path)
};
// let hd2= if cfg!(target_arch = "wasm32") { //Wasm32是一种基于WebAssembly(Wasm)的32位虚拟机
// image::Handle::from_path("iced_test/src/img1.png")
// } else {
// image::Handle::from_path(img_path)
// };
//println!("hd is :{:?}",hd);
container(
column![
row![
//btn1
button(text("打开图像")
.horizontal_alignment(alignment::Horizontal::Center)
.vertical_alignment(alignment::Vertical::Center)
.size(15)
).on_press(Message::OpenimgPressed)
.padding(4),
//btn2
button(text("保存图像")
.horizontal_alignment(alignment::Horizontal::Center)
.vertical_alignment(alignment::Vertical::Center)
.size(15)
).on_press(Message::SaveimgPressed)
.padding(4),
].spacing(10).padding(10)
.align_items(Alignment::Start),
//text:source image path
text(format!("原图像路径:{:?}",self.srcimgpath)).size(15)
.horizontal_alignment(alignment::Horizontal::Center)
.vertical_alignment(alignment::Vertical::Center),
row![
text("图像尺寸调整:").size(15),
//slider
slider(0.0..=100.0, self.slivalue, Message::SliderChanged).step(0.01).width(200),
].spacing(20),
//text:dest image path
text(&self.destimgpath).size(15),
row![
image(hd).content_fit(ContentFit::Fill),
//image(hd2).width(Length::Fixed(100.0)).height(Length::Fixed(100.0)).content_fit(ContentFit::Fill)
].spacing(10)
.padding(10)
]
.spacing(10)
.padding(30)
.align_items(Alignment::Start)
)
.into()
}
}
以上代码中,不仅包含本篇涉及的内容,也包含前2篇中涉及的内容。
动态演示图:
原文地址:https://blog.csdn.net/normer123456/article/details/134602643
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_15415.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!