当前位置: 首页 > news >正文

Rust-GUI图形库:多窗口显示multi_window(4)

一、前置环境准备

  • 如前面两章环境一致

二、创建项目

  • Windows 打开 PowerShell / Linux/macOS 终端执行

1. 创建二进制项目

# 创建名为 multi_window 的项目 cargo new multi_window # 进入项目目录 cd multi_window

项目目录:

multi_window/ ├── Cargo.toml # 项目配置、依赖管理 ├── Cargo.lock # 依赖版本锁定文件(自动生成,无需修改) ├── src/ │ └── main.rs # 全部业务代码(窗口、逻辑、UI) └── target/ # 编译产物目录(自动生成) ├── debug/ # cargo run 开发编译文件 └── release/ # --release 发布成品程序

2. 添加 Iced 依赖(修改 Cargo.toml)

[package] name = "multi_window" version = "0.1.0" edition = "2021" # 依赖 Iced GUI 框架,使用最新稳定版 [dependencies] iced = "0.14"

说明:iced = "0.14"会自动引入窗口、组件、渲染全套功能,无需额外细分依赖。

三、完整 src/main.rs(整理带注释代码)

直接覆盖自动生成的src/main.rs

#![cfg_attr(windows, windows_subsystem = "windows")] // Windows平台编译后隐藏CMD黑色控制台窗口 // Iced 基础UI组件 use iced::widget::{ button, center, center_x, column, container, operation, scrollable, space, text, text_input, }; // 多窗口操作相关API use iced::window; // 布局、元素、填充、消息映射、订阅、异步任务、主题、坐标向量 use iced::{Center, Element, Fill, Function, Subscription, Task, Theme, Vector}; // 有序哈希表,用来按窗口ID存储多个窗口数据 use std::collections::BTreeMap; /// 程序入口,使用 daemon 模式(支持多窗口常驻程序) fn main() -> iced::Result { iced::daemon(Example::new, Example::update, Example::view) // 全局事件订阅(监听窗口关闭) .subscription(Example::subscription) // 根据窗口ID动态获取窗口标题 .title(Example::title) // 根据窗口ID动态切换窗口主题 .theme(Example::theme) // 根据窗口ID动态设置窗口缩放比例 .scale_factor(Example::scale_factor) .run() } /// 程序根状态:统一管理所有打开的子窗口 struct Example { // key: 窗口唯一ID value: 单个窗口的独立配置数据 windows: BTreeMap<window::Id, Window>, } /// 单个窗口独立配置数据 #[derive(Debug)] struct Window { // 窗口标题文本 title: String, // 缩放输入框临时文本(未提交) scale_input: String, // 当前生效的窗口缩放系数 current_scale: f32, // 窗口独立主题(每个窗口可以明暗/配色不同) theme: Theme, } /// 全部交互消息枚举,涵盖窗口创建、关闭、输入框修改、缩放提交 #[derive(Debug, Clone)] enum Message { // 点击新建窗口按钮 OpenWindow, // 新窗口创建完成,携带窗口ID WindowOpened(window::Id), // 窗口被关闭,携带窗口ID WindowClosed(window::Id), // 缩放输入框实时输入变更 ScaleInputChanged(window::Id, String), // 缩放输入框回车提交,应用缩放 ScaleChanged(window::Id, String), // 窗口标题输入框实时修改 TitleChanged(window::Id, String), } impl Example { /// 程序初始化:创建初始第一个窗口 fn new() -> (Self, Task<Message>) { // 发起打开窗口异步任务 let (_, open_task) = window::open(window::Settings::default()); ( Self { windows: BTreeMap::new(), }, // 窗口创建完成后发送 WindowOpened 消息 open_task.map(Message::WindowOpened), ) } /// 根据窗口ID读取对应窗口标题,供窗口标题渲染回调使用 fn title(&self, window: window::Id) -> String { self.windows .get(&window) .map(|win| win.title.clone()) .unwrap_or_default() } /// 消息更新核心逻辑,处理所有窗口增删、参数修改 fn update(&mut self, message: Message) -> Task<Message> { match message { // 点击新建窗口按钮 Message::OpenWindow => { // 没有已存在窗口则直接返回,不创建 let Some(&last_win_id) = self.windows.keys().last() else { return Task::none(); }; // 获取上一个窗口坐标,新窗口偏移20px,避免完全重叠 window::position(last_win_id) .then(|last_pos_opt| { let new_pos = last_pos_opt.map_or(window::Position::Default, |last_pos| { window::Position::Specific(last_pos + Vector::new(20.0, 20.0)) }); // 打开新窗口,携带偏移坐标 let (_, open_task) = window::open(window::Settings { position: new_pos, ..window::Settings::default() }); open_task }) .map(Message::WindowOpened) } // 新窗口创建完成,初始化窗口配置并自动聚焦标题输入框 Message::WindowOpened(win_id) => { // 根据当前窗口总数生成新窗口默认配置 let new_win = Window::new(self.windows.len() + 1); // 存入窗口映射表 self.windows.insert(win_id, new_win); // 自动聚焦当前窗口的标题输入框 operation::focus(format!("input-{win_id}")) } // 窗口关闭:删除窗口数据,无窗口时退出整个程序 Message::WindowClosed(win_id) => { self.windows.remove(&win_id); if self.windows.is_empty() { iced::exit() } else { Task::none() } } // 缩放输入框实时输入,仅更新临时文本,不生效 Message::ScaleInputChanged(win_id, text) => { if let Some(win) = self.windows.get_mut(&win_id) { win.scale_input = text; } Task::none() } // 缩放输入回车提交:解析数值并限制范围0.5~5.0,更新生效缩放 Message::ScaleChanged(win_id, input_text) => { if let Some(win) = self.windows.get_mut(&win_id) { win.current_scale = input_text .parse::<f32>() // 解析失败保留原有缩放值 .unwrap_or(win.current_scale) .clamp(0.5, 5.0); } Task::none() } // 标题输入框实时修改,同步更新窗口标题 Message::TitleChanged(win_id, new_title) => { if let Some(win) = self.windows.get_mut(&win_id) { win.title = new_title; } Task::none() } } } /// 单窗口渲染函数,每个独立窗口都会单独调用此view fn view(&self, window_id: window::Id) -> Element<'_, Message> { // 根据窗口ID取出对应窗口数据,绘制内容;无数据返回空白占位 if let Some(win) = self.windows.get(&window_id) { center(win.view(window_id)).into() } else { space().into() } } /// 根据窗口ID获取对应窗口专属主题 fn theme(&self, window_id: window::Id) -> Option<Theme> { Some(self.windows.get(&window_id)?.theme.clone()) } /// 根据窗口ID获取当前窗口缩放系数 fn scale_factor(&self, window_id: window::Id) -> f32 { self.windows .get(&window_id) .map(|win| win.current_scale) .unwrap_or(1.0) } /// 全局订阅:监听所有窗口关闭事件,转化为消息 fn subscription(&self) -> Subscription<Message> { window::close_events().map(Message::WindowClosed) } } impl Window { /// 创建单个窗口默认配置 fn new(count: usize) -> Self { Self { title: format!("Window_{count}"), scale_input: "1.0".to_string(), current_scale: 1.0, // 循环取用内置全部主题,每个窗口主题不一样 theme: Theme::ALL[count % Theme::ALL.len()].clone(), } } /// 单个窗口内部UI布局 fn view(&self, id: window::Id) -> Element<'_, Message> { // 缩放输入区域:文字提示 + 输入框,回车提交缩放 let scale_input_area = column![ text("Window scale factor:"), text_input("Window Scale", &self.scale_input) // 输入实时更新临时文本 .on_input(Message::ScaleInputChanged.with(id)) // 按下回车提交缩放数值 .on_submit(Message::ScaleChanged(id, self.scale_input.to_string())) ]; // 窗口标题输入区域,绑定唯一ID用于自动聚焦 let title_input_area = column![ text("Window title:"), text_input("Window Title", &self.title) .on_input(Message::TitleChanged.with(id)) .id(format!("input-{id}")) ]; // 新建窗口按钮 let btn_new_window = button(text("New Window")).on_press(Message::OpenWindow); // 垂直布局所有控件,间距50,宽度200,水平居中 let content = column![scale_input_area, title_input_area, btn_new_window] .spacing(50) .width(Fill) .align_x(Center) .width(200); // 外层滚动容器+内边距包裹内容 container(scrollable(center_x(content))).padding(10).into() } }

注:#![cfg_attr(windows, windows_subsystem = "windows")]
//加入上面一句:! Windows 编译后自动隐藏CMD黑色控制台窗口

四、运行项目

1. 开发模式运行(热编译、带日志)

cargo run

首次运行会下载编译 Iced 及其底层图形依赖,等待几分钟。 运行成功会弹出窗口。

2. 运行窗口

http://www.jsqmd.com/news/1217604/

相关文章:

  • 从-O0到-O3:深度解析GCC/Clang编译器优化等级与实战技巧
  • Adobe Creative Cloud Cleaner Tool使用指南与疑难解答
  • 深入解析SoC时钟管理:从DPLL原理到OMAP PRCM实战
  • Medium算法如何识别AI内容并影响推荐?实测27篇数据揭秘
  • XUtils3环境搭建与配置全指南
  • 【Deep Emotional Memory】情绪增强编码在RAG Agent记忆系统中的应用研
  • Android Handler消息机制详解与最佳实践
  • 基于西门子PLC与Unity3D的自动化立体仓库虚实交互系统开发实战
  • Python实战:从零开发天气查询工具
  • 杭州萧然医院环境好吗?
  • Python零基础入门:第一周学习指南与实战项目
  • C++实现埃拉托斯特尼筛法:高效求解20万以内质数
  • 动态规划题单(自用)
  • Spring + Dubbo + Zookeeper项目依赖注入
  • SonarQube 7.x API数据提取与集成实战指南
  • 从游戏物理到金融模型:微分方程数值求解实战指南
  • 单片机平衡车调试系统:从串口打印到数据可视化与PID调参实战
  • 现代C++核心特性实战:从C++11到C++20的效率提升与工程实践
  • 嵌入式PRCM编程实战:时钟、电源与复位管理详解
  • 工业相机图像采集卡技术解析与应用指南
  • 前后端分离架构下的JWT认证实践与优化
  • Android代码混淆实战:ProGuard与R8配置详解
  • PDF压缩神器!【PDFgear】完全免费,支持批量压缩、无损压缩
  • 电商选品ERP解决方案之商品数据批量采集落地实操
  • Proto反射机制详解与应用实践
  • Quartz.NET作业调度框架详解与实战应用
  • UE5自定义资源类型:构建模块化游戏数据系统的完整指南
  • Transformer-XL
  • 机器学习基础概念
  • Android自定义View实现模拟时钟的完整指南