iced 入门一
简介
本教程的目标是创建一个简单的购物清单应用程序。我们希望允许添加和删除购物清单中的项目。
在编写代码之前,我们必须首先了解 Iced 构建的结构:Elm 架构。它是 GUI 库使用的架构,最初用于 Elm 编程语言。它的核心原则很简单。它围绕三个概念构建:模型、视图和更新。
- 模型,应用程序的状态。这是将根据用户交互等进行更新的所有数据。它将用于确定我们希望如何呈现 UI。在本教程中,我们将此称为模型或应用程序的状态。
- 视图,显示 UI 的函数。
- 更新,可以更新状态的函数。只有在此函数中,才能更新应用程序的状态。
创建新的 Iced 应用程序
首先,我们将用cargo开始一个新建项目。
cargo new iced-t
现在,到新创建的文件夹中的文件,并将 Iced 添加为依赖项。cargo.toml
[package]
name = "iced-t"
version = "0.1.0"
edition = "2021"
[dependencies]
iced = {version = "0.12.1"}
现在,在应用程序的文件夹中,我们会将这些导入添加到您的文件中。我们稍后会解释他们做什么。src/main.rs
use iced::{Element, Sandbox, Settings};
use iced::widget::text;
现在,我们将创建最基本的应用程序。首先,我们必须创建应用程序的主要结构。我们称之为“GroceryList”。现在,我们将其设为空。
struct GroceryList {}
我们还将创建一个枚举,该枚举也将为空。这将用于指导我们的应用程序如何更新我们的状态。我们稍后会详细了解这一点,但现在,让我们创建一个名为“Message”的枚举
#[derive(Debug, Clone)]
enum Message {}
现在我们将实现该特征。这是我们结构体的一个重要特征。我们还将 our 定义为处理应用程序更新的枚举。我们将其写为 .SandboxGroceryListMessagetype Message = Message
impl Sandbox for GroceryList {
type Message = Message;
/* Initialize your app */
fn new() -> GroceryList {
Self {}
}
/**
* The title of the window. It will show up on the top of your application window.
*/
fn title(&self) -> String {
String::from("Grocery List App")
}
fn update(&mut self, message: Self::Message) {
// Update the state of your app
}
fn view(&self) -> Element<Self::Message> {
text("This is where you will show the view of your app")
.into()
}
}
最后,我们将把我们的结构添加到函数中并运行它。GroceryListmain
fn main() -> iced::Result {
GroceryList::run(Settings::default())
}
为了启动我们的应用程序,我们正在实现一个名为“Sandbox”的特征。正如我之前所说,该特征对于运行我们的应用程序很重要。它允许我们使用我们的结构作为状态来启动我们的应用程序。
该特征是另一个称为“Application”的特征的简化版本。 主要用于制作演示或简单的应用程序,因此得名“Sandbox”。
Sandbox new GroceryList
该方法用于初始化我们的 .由于我们对此应用程序没有任何状态,因此此结构为空(目前)。
title
该方法应返回我们选择的字符串。此字符串将用作窗口的标题。对于我的操作系统,标题显示在窗口顶部。您可以将此字符串更改为要显示为标题的任何内容,它对我们的应用程序无关紧要。
update
该方法对我们的应用非常重要。如果你还记得我对 Elm 架构的解释,update 方法用于更新我们应用程序的状态。对应用程序使用的数据进行的所有更改都必须通过更新方法。现在,我们将它留空。
view into Element
方法。我们将在此处显示应用程序的 UI。在这里,我们使用的是之前导入的“文本”小部件。它将显示传入其中的字符串。我们运行该方法将我们的小部件转换为 Iced 。
运行我们的应用程序
为了运行我们的应用程序,我们将像任何其他 Rust 项目一样运行它。在文件夹的根目录中,运行以下命令:
cargo run --release
您应该看到类似于以下内容的内容:
hello word
我们正在显示文本,但它看起来非常简单。让我们的 UI 更具吸引力。
首先,让我们更新我们的导入。我们将添加一个新的小部件。
use iced::{alignment, Element, Length, Sandbox, Settings};
use iced::widget::{container, text};
接下来,让我们改变我们的方法。view
fn view(&self) -> Element<Self::Message> {
container(
text("Hello World")
)
.height(Length::Fill)
.width(Length::Fill)
.align_x(alignment::Horizontal::Center)
.align_y(alignment::Vertical::Center)
.into()
}
让我们分解一下我们刚刚添加的内容。在我们的方法中,我们添加了一个新的小部件,并将我们的小部件放在其中。容器小部件类似于 html 中的容器。它的目的是存储其他元素,在我们的例子中是小部件。与 HTML 不同,容器只能存储一个元素。我们将使用此小部件将文本放在应用程序的中心。
我们还链接了四种方法,这些方法正在设置和更改容器的大小。我们传递属性以将容器设置为尽可能大。接下来,我们设置并告诉我们的容器里面的小部件应该适合在哪里。我们指定元素应居中。
最后,我将更改应用的默认主题。我将通过在我们的结构中添加一个新方法来更改主题。
impl Sandbox for GroceryList {
// Other methods...
fn theme(&self) -> iced::Theme {
iced::Theme::Dark
}
}
如果再次运行应用程序,它将与此类似。
物品展示
现在我们的应用程序非常基础。我在本教程前面解释了状态,但目前,这个应用程序没有状态。我们即将改变这一点。我们将对当前应用程序进行两项修改。
- 将状态添加到我们的应用程序。
- 我们将使用一个外部函数将一堆小部件组合在一起。这有利于保持我们的应用程序模块化。
在main.rs导入以下内容
use iced::{alignment, Element, Length, Sandbox, Settings};
use iced::widget::{container, text, Column, column};
让我们也改变一下结构的定义。我们将添加一个字符串向量来表示购物清单中的物品。这将是我们的状态。.
struct GroceryList {
grocery_items: Vec<String>
}
为此,我们还将更改方法,以便正确初始化结构。
impl Sandbox for GroceryList {
fn new() -> GroceryList {
Self {
grocery_items: vec![
"Eggs".to_owned(),
"Milk".to_owned(),
"Flour".to_owned()
]
}
}
// ... Other methods
接下来,我们将创建一个新函数。一个将显示杂货清单的。老实说,没有必要为此功能创建新函数。我们可以将小部件传递到我们的方法中。但是,将我们的代码模块化是一种很好的做法。view
impl Sandbox for GroceryList {
// ...
}
fn items_list_view(items: &Vec<String>) -> Element<'static, Message> {
let mut column = Column::new()
.spacing(20)
.align_items(iced::Alignment::Center)
.width(Length::Fill);
for value in items {
column = column.push(text(value));
}
container(
column
)
.height(250.0)
.width(300)
.into()
}
最后,我们将在方法中使用我们的函数。view
fn view(&self) -> Element<Self::Message> {
container(
items_list_view(&self.grocery_items),
)
.height(Length::Fill)
.width(Length::Fill)
.align_x(alignment::Horizontal::Center)
.align_y(alignment::Vertical::Center)
.into()
}
我们添加了一个新的小部件 .我们在函数中使用这个小部件。
如果运行此应用,它将类似于以下内容:
添加用户输入
让我们为用户提供一种最终与我们的应用程序交互的方法。我们将添加两种用户输入方式,以及一种 .button,text_input
首先,让我们再次更新我们的导入。
use iced::{alignment, Element, Length, Padding, Sandbox, Settings};
use iced::widget::{button, column, container, row, text, text_input, Column};
现在,我们将再次更新我们的视图方法。
fn view(&self) -> Element<Self::Message> {
container(
column!(
items_list_view(&self.grocery_items),
row!(
text_input("Input grocery item", ""),
button("Submit")
)
.spacing(30)
.padding(Padding::from(30))
)
.align_items(iced::Alignment::Center)
)
.height(Length::Fill)
.width(Length::Fill)
.align_x(alignment::Horizontal::Center)
.align_y(alignment::Vertical::Center)
.into()
}
您会注意到我们正在使用一个新的小部件。它与列小部件几乎相同,但是,它不是将项目彼此叠加显示,而是水平显示它们。
我们初始化行的方式与之前创建的列不同。我们使用的是 Iced 库提供的宏。它允许我们初始化一个类似于微初始化向量中项目的方式。因此,我们可以指定将进入我们的每个元素,而无需调用该方法。为 提供了一个类似的宏,我们也在方法中调用它。
如果您运行代码,您将看到与此类似的内容。
更新
我们介绍了 Elm 架构的两个核心方面,视图和状态。现在终于到了介绍更新的时候了。Iced 将仅允许通过该方法更新状态。update
我们创建了一个名为 的枚举。 将用于让我们知道如何更新应用程序的状态。每个可以接收输入(文本输入、按钮等)的小部件都会发送消息。我们可以定义要发送的消息类型。从小部件发送消息后,我们将在方法中处理这些消息。
在开始之前,让我们更新我们的导入。
use iced::{alignment, widget::{button, column, container, row, scrollable, text, text_input, Column}, Element, Length, Padding, Sandbox, Settings};
接下来,让我们设置要发送和接收的消息。我们将更改枚举。Message
#[derive(Debug, Clone)]
enum Message {
InputValue(String),
Submitted,
}
我们还必须对我们的状态做出小小的改变。由于我们将从 接收值,我们必须将这些值存储在某个地方。因此,我们将在结构中添加另一个字段。
struct GroceryList {
grocery_items: Vec<String>,
input_value: String
}
而且,像往常一样,我们还必须更改初始化 .GroceryList
/* Initialize your app */
fn new() -> GroceryList {
Self {
grocery_items: vec![
"Eggs".to_owned(),
"Milk".to_owned(),
"Flour".to_owned()
],
input_value: String::default()
}
}
现在,让我们更改方法,以便在用户与我们的小部件交互时可以发送这些消息。
fn view(&self) -> Element<Self::Message> {
container(
column!(
items_list_view(&self.grocery_items),
row!(
text_input("Input grocery item", &self.input_value)
.on_input(|value| Message::InputValue(value))
.on_submit(Message::Submitted),
button("Submit")
.on_press(Message::Submitted)
)
.spacing(30)
.padding(Padding::from(30))
)
.align_items(iced::Alignment::Center)
)
.height(Length::Fill)
.width(Length::Fill)
.align_x(alignment::Horizontal::Center)
.align_y(alignment::Vertical::Center)
.into()
}
在这里,我们添加了一些方法,这些方法将在用户与小部件交互时创建消息。对于按钮,我们有发送消息的方法。
对于我们的文本输入,我们有两个交互。 当用户按回车键时调用。我们将发送与单击按钮时发送的消息相同的消息。我们也有方法。此方法在用户键入时触发。我们传递一个回调函数,该函数接受内部的字符串并返回 .此消息将存储字符串,以便我们可以使用该消息更新我们的应用程序。
我们还对输入进行了巧妙的更改,而不是像以前那样在其中传递一个空字符串,而是传递 .我们希望文本输入的值在用户键入时更新,否则,文本输入将卡为空字符串。
fn update(&mut self, message: Self::Message) {
match message {
Message::InputValue(value) => self.input_value = value,
Message::Submitted => {
self.grocery_items.push(self.input_value.clone());
self.input_value = String::default(); // Clear the input value
}
}
我们正在处理我们创建的两条消息。
- 每当用户将文本添加到我们创建的字段中时,我们都会将其存储为我们创建的字段中的状态。
- 每当用户提交文本时,我们都希望将该字符串推送到我们的 .我们还希望清除用户输入的先前值,以便小部件可以为空。
在运行项目之前,我们需要对 UI 进行一个小的更改。在我们之前创建的函数中。
fn items_list_view(items: &Vec<String>) -> Element<'static, Message> {
let mut column = Column::new()
.spacing(20)
.align_items(iced::Alignment::Center)
.width(Length::Fill);
for value in items {
column = column.push(text(value));
}
scrollable(
container(
column
)
)
.height(250.0)
.width(300)
.into()
}
我们只需要添加一个小部件来显示购物清单中的商品。此小部件将为用户提供在小部件的内容大于小部件本身时滚动的选项。现在,如果用户添加了大量购物清单项,则用户可以滚动到不可见的清单项。scrollablescrollable
如果你现在运行它,它应该看起来和我们上次运行应用程序时几乎一样,但这次我们实际上可以与它交互。
删除项目
就像将项目添加到购物清单一样,我们需要一条消息才能删除它们。
#[derive(Debug, Clone)]
enum Message {
InputValue(String),
Submitted,
DeleteItem(usize),
}
我们在变体将被传递一个数字,该数字表示我们要删除的项目的索引。
让我们将此更改添加到我们的方法中。
fn update(&mut self, message: Self::Message) {
match message {
Message::InputValue(value) => self.input_value = value,
Message::Submitted => {
self.grocery_items.push(self.input_value.clone());
self.input_value = String::default(); // Clear the input value
}
Message::DeleteItem(item) => {
self.grocery_items.remove(item);
},
}
}
此更改很简单。我们只需从向量中删除指定的项目。
现在让我们通过更改 UI 来完成此应用。每个购物清单项旁边都有一个按钮。此按钮将允许我们的用户删除杂货。让我们创建一个名为“grocery_item”的新函数。
fn grocery_item(index: usize, value: &str) -> Element<'static, Message> {
row!(
text(value),
button("Delete")
.on_press(Message::DeleteItem(index))
)
.align_items(iced::Alignment::Center)
.spacing(30)
.into()
}
我们将传递我们和字符串切片中每个杂货项的索引。
fn items_list_view(items: &Vec<String>) -> Element<'static, Message> {
let mut column = Column::new()
.spacing(20)
.align_items(iced::Alignment::Center)
.width(Length::Fill);
for (index, value) in items.into_iter().enumerate() {
column = column.push(grocery_item(index, value));
}
scrollable(
container(
column
)
)
.height(250.0)
.width(300)
.into()
}
如果你现在运行它,你应该看到这样的东西。
代码
use iced::{alignment, widget::{button, column, container, row, scrollable, text, text_input, Column}, Element, Length, Padding, Sandbox, Settings};
struct GroceryList {
grocery_items: Vec<String>,
input_value: String,
}
#[derive(Debug, Clone)]
enum Message {
InputValue(String),
Submitted,
DeleteItem(usize),
}
impl Sandbox for GroceryList {
type Message = Message;
/* 初始化应用 */
fn new() -> GroceryList {
Self {
grocery_items: vec![
"Eggs".to_owned(),
"Milk".to_owned(),
"Flour".to_owned()
],
input_value: String::default()
}
}
/**
* 窗口的标题。它将显示在应用程序窗口的顶部。
*/
fn title(&self) -> String {
String::from("Grocery List App")
}
fn update(&mut self, message: Self::Message) {
match message {
Message::InputValue(value) => self.input_value = value,
Message::Submitted => {
self.grocery_items.push(self.input_value.clone());
self.input_value = String::default(); // Clear the input value
}
Message::DeleteItem(item) => {
self.grocery_items.remove(item);
},
}
}
fn view(&self) -> Element<Self::Message> {
container(
column!(
items_list_view(&self.grocery_items),
row!(
text_input("Input grocery item", &self.input_value)
.on_input(|value| Message::InputValue(value))
.on_submit(Message::Submitted),
button("Submit")
.on_press(Message::Submitted)
)
.spacing(30)
.padding(Padding::from(30))
)
.align_items(iced::Alignment::Center)
)
.height(Length::Fill)
.width(Length::Fill)
.align_x(alignment::Horizontal::Center)
.align_y(alignment::Vertical::Center)
.into()
}
fn theme(&self) -> iced::Theme {
iced::Theme::Dark
}
}
// 设置数据排列方式
fn items_list_view(items: &Vec<String>) -> Element<'static, Message> {
let mut column = Column::new()
.spacing(20)
.align_items(iced::Alignment::Center)
.width(Length::Fill);
for (index, value) in items.into_iter().enumerate() {
column = column.push(grocery_item(index, value));
}
scrollable(
container(
column
)
)
.height(250.0)
.width(300)
.into()
}
fn grocery_item(index: usize, value: &str) -> Element<'static, Message> {
row!(
text(value),
button("Delete")
.on_press(Message::DeleteItem(index))
)
.align_items(iced::Alignment::Center)
.spacing(30)
.into()
}
fn main() -> iced::Result {
GroceryList::run(Settings::default())
}
- 点赞
- 收藏
- 关注作者
评论(0)