rust gtk 桌面应用 demo

《精通Rust》里介绍了 GTK+框架的开发,这篇博客记录并扩展一下。rust 可以用于桌面应用开发,我还挺惊讶的,大学的时候也有学习过 VC++,对桌面编程一直都很感兴趣,而且一直有一种妄念,总觉得自己能开发一款很好用的桌面程序,就和总觉得自己能彩票中大奖一样。

环境安装

可能你会需要安装 gtk+3。如果执行 cargo build 的时候提示你找不到 gdk-3.0,那你就需要手动安装一下:
在这里插入图片描述

不过,也不需要提前安装这些依赖。当我们执行 cargo build 编译的时候,结合 rust 的错误提示进行按需安装是比较稳妥的。

功能开发

在《精通Rust》书中的16章节,书中的 Demo 忽略了一个非常重要的细节,就是省略了依赖包的声明,没有依赖包的声明,代码就缺少了灵魂,编译都没有办法通过。

我在考虑要不要使用 GPT 自动生成一下源码,省的麻烦。自动生成代码还是放到最后吧…可以通过这个过程来熟悉一下 rust 的函数。

std::process::exit

这个函数并不陌生,使用一个 code 来退出当前的进程。要想在程序中正常调用这个函数,需要导入如下的头声明:

use std::process;

gtk::Window

代码中使用到的组件都来自于 gtk 包,为了方便起见,可以将 gtk 下的声明全局导入

use gtk::*

std::sync::mpsc::Sender

用来通过 channel 实现异步通讯的能力,代码中用来做数据通讯,有 send 和对应的 try_recv 的两个动作。如果我们不引入这个包,cargo build 还会给我们另一个可选建议 glib::Sender,不过这个函数的解释中提到,两个方式是类似的。

use std::sync::mpsc::Sender

std::sync::mpsc::Receiver

有消息的发送,就应该有消息的接受

use std::sync::mpsc::Receiver

std::sync::mpsc::channel

函数用来创建 Sender、Receive,和上面两个函数是一体的,它创建的是一个异步队列。创建同步队列需要调用 std::sync::mpsc::sync_channel方法。

use std::sync::mpsc::channel

mod

rust 在相同的目录下,不同文件中声明的结构体是无法相互引用的,需要通过 mod 来解决。mod 主要用来解决项目内代码组织的问题,use mod xxx 会尝试去加载当前目录下的 xxx.rs 文件的代码。

在 main.rs 中的 mod hackernews 就是用来加载 hackernews.rs 中声明的导出方法或结构体。如果你将这行代码删除,程序就会找不到文件中声明的 Story 结构体。

use crate::hackernews::Story;

std::sync::Arc

全称是 Atomically Reference Counted,表示线程安全的引用计数器。Arc 表示一个指针,指向堆空间的 T 值。同时,有一个附属的引用计数。

use std::sync::Arc;

reqwest::Client

一个异步的 HTTP 请求客户端,用来发送 HTTP 请求。在说明文档中明确强调:我们不需要使用 Rc 或者 Arc 去包装这个类型,内部已经使用 Arc 包装过了。

use reqwest::Client;

glib::source::timeout_add

函数用于固定间隔执行闭包函数,示例中的作用是固定间隔尝试接受消息。我发现,rust 依赖包的做法特别接近 javascript 。

函数第一个参数的类型是 core::time::Duration,这个时间概念和 Go 语言相近,不过 rust 表示的是秒+毫秒的单位,构造时间的时候可以传递秒和毫秒两个数值。

use glib::source::timeout_add;

std::ops::ControlFlow

代码示例中 ControlFlow::Continue(true),目前来看这个 Continue 的含义并不明确,感觉不到它的价值。

use std::ops::ControlFlow

gtk::Box | gtk::prelude::BoxExt

其中,gtk::prelude::BoxExt 属于 trait 属性,rust 中的 trait 等同于 go 语言的 interface 类型,也是实现多态的手段之一

impl App {pub fn new() -> (App, Receiver<Msg>) {if gtk::init().is_err() {println!("Failed to init hews window");process::exit(1);}let (tx, rx) = channel();let window = gtk::Window::new(gtk::WindowType::Toplevel);let sw = ScrolledWindow::new(None, None);let stories = gtk::Box::new(gtk::Orientation::Vertical, 20);let spinner = gtk::Spinner::new();let header = Header::new(stories.clone(), tx.clone());stories.pack_start(&spinner, false, false, 2);sw.add(&stories);window.add(&sw);window.set_default_size(600, 350);window.set_titlebar(&header.header);window.connect_delete_event(move |_, _| {main_quit();Inhibit(false);});}pub fn launch(&self, rx: Receiver<Msg>) {self.window.show_all();let client = Arc::new(reqwest::Client::new());self.fetch_posts(client.clone());self.run_event_loop(rx, client);}fn fetch_posts(&self, client: Arc<Client>) {self.spinner.start();self.tx.send(Msg::Loading).unwrap();let tx_clone = self.tx.clone();top_stories(client, 10, &tx_clone);}fn run_event_loop(&self, rx: Receiver<Msg>, client: Arc<Client>) {let container = self.stories.clone();let spinner = self.spinner.clone();let header = self.header.clone();let tx_clone = self.tx.clone();timeout_add(100, move || {match rx.try_recv() {Ok(Msg::NewStory(s)) => App::render_story(s, &container),Ok(Msg::Loading) => header.disable_refresh(),Ok(Msg::Loaded) => {spinner.stop();header.enable_refresh();}Ok(Msg::Refresh) => {spinner.start();spinner.show();(&tx_clone).send(Msg::Loading).unwrap();top_stories(client.clone(), 10, &tx_clone)}Err(_) => {}}Continue(true)});gtk::main();}fn render_story(s: Stroy, stories: &gtk::Box) {let title_with_score = format!("{} ({})", s.title, s.score);let label = gtk::Label::new(&*title_with_score);let story_url = s.url.unwrap_or("N/A".to_string());let link_label = gtk::Label::new(&*story_url);let label_markup = format!("<a href=\"{}\">{}</a>", story_url, story_url);link_label.set_markup(&label_markup);stories.pack_start(&label, false, false, 2);stories.pack_start(&link_label, false, false, 2);stories.show_all();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/7893.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入学习 Redis - 深挖经典数据类型之 set

目录 前言 一、Set 类型 1.1、操作命令 sadd / smembers&#xff08;添加&#xff09; sismember&#xff08;判断存在&#xff09; scard&#xff08;获取元素个数&#xff09; spop&#xff08;删除元素&#xff09; smove&#xff08;移动&#xff09; srem&#x…

CSAP_ORD_BOM_MAINTAIN 销售订单BOM

[TOC] 1 BAPI&#xff1a;CSAP_ORD_BOM_MAINTAIN 2 Detail 2.1 Deleted line STPO_API03-FLDELETE ‘X’ 2 .2 Modified Line 2.21 Please specify below lines . STPO_API03-ITEM_NODE STPO-STLKN. STPO_API03-ITEM_COUNT STOP-STPOZ. 2.22 Example. CONSTANTS : C…

若依-前台无法正常启动,npm run dev失败

问题场景&#xff1a; 使用若依Vue前端分离版-基于SpringBoot的权限管理系统进行实战。 问题描述与解决 拉取若依项目后&#xff0c;根据官方开发文档&#xff08;项目readme文档&#xff09;进行依赖下载安装后&#xff0c;启动失败。 出现以下几个问题&#xff1a; 运行n…

VisDrone2019上训练YOLOv5(用ultralytics)

使用的package library&#xff1a; ultralytics 环境&#xff1a;python3.8, torch1.7.0 把ultralytics代码git clone 到本地 git clone https://github.com/ultralytics/ultralytics/ 本地新建一个train.py,内容写下面这些。 from ultralytics import YOLO# Load a model…

Golang time 包以及日期函数

time 包 在 golang 中 time 包提供了时间的显示和测量用的函数。 time.Now()获取当前时间 可以通过 time.Now()函数获取当前的时间对象&#xff0c;然后获取时间对象的年月日时分秒等信息。 示例代码如下&#xff1a; package mainimport ("fmt""time" )…

Redis原理篇(二)

Redis原理 Redis数据结构 Redis网络模型 RESP协议 Redis内存回收 Redis原理篇 一、原理篇-Redis数据结构 1.1 Redis数据结构-动态字符串 我们都知道Redis中保存的Key是字符串&#xff0c;value往往是字符串或者字符串的集合。可见字符串是Redis中最常用的一种数据结构。 不…

网络安全 Day19-计算机网络基础知识04(网络协议)

计算机网络基础知识04&#xff08;网络协议&#xff09; 1. ARP1.1 ARP通讯原理1.2 arp欺骗1.3 ARP欺骗与预防1.4 排查ARP病毒 2. DHCP工作原理&#xff08;自动分配内网IP&#xff09;3. TCP协议三次握手、四次挥手原理4. DNS协议工作原理 1. ARP Linux查看arp&#xff1a;ar…

聊聊STM32 ADC的话题

STM32 微控制器系列提供了多个模拟数字转换器&#xff08;ADC&#xff09;模块&#xff0c;用于实现模拟信号的采集和转换为数字信号。ADC 在很多应用中都是非常重要的&#xff0c;例如传感器数据采集、电压测量等。 在 STM32 中&#xff0c;ADC 可以通过 STM32HAL 库提供的函…

自动驾驶感知系统-超声波雷达

超声波雷达&#xff0c;是通过发射并接收40kHz的超声波&#xff0c;根据时间差算出障碍物距离。其测距精度是1~3cm.常见的超声波雷达有两种&#xff1a;第一种是安装在汽车前后保险杠上的&#xff0c;用于测量汽车前后障碍物的驻车雷达或倒车雷达&#xff0c;称为超声波驻车辅助…

时间复杂度和空间复杂度

在计算机科学中&#xff0c;算法的效率是一个重要的概念。算法的效率可以通过复杂度来度量&#xff0c;其中包括时间复杂度和空间复杂度。 了解算法的复杂度对于程序员来说非常重要。在解决实际问题时&#xff0c;我们需要选择合适的算法来保证程序的性能和效率。因此&#xff…

【前端笔记】本地运行cli项目报错ERR_OSSL_EVP_UNSUPPORTED

报错原因 Node版本>17.x&#xff0c;本地npm run 起项目后会发现终端报错&#xff0c;具体有以下2块关键信息&#xff1a; Error: error:0308010C:digital envelope routines::unsupported和 opensslErrorStack: [ error:03000086:digital envelope routines::initializa…

SpringCloud学习路线(9)——服务异步通讯RabbitMQ

一、初见MQ &#xff08;一&#xff09;什么是MQ&#xff1f; MQ&#xff08;MessageQueue&#xff09;&#xff0c;意思是消息队列&#xff0c;也就是事件驱动架构中的Broker。 &#xff08;二&#xff09;同步调用 1、概念&#xff1a; 同步调用是指&#xff0c;某一服务…

Dubbo服务提供者失效踢出原理解析

Dubbo服务提供者失效踢出原理解析 在分布式系统中&#xff0c;服务提供者的失效是一个常见而且重要的问题。Dubbo作为一款优秀的分布式服务框架&#xff0c;提供了失效踢出机制来及时剔除不可用的服务提供者&#xff0c;确保系统的稳定性和可用性。本文将深入探讨Dubbo服务提供…

ProtoBuf入门概念

目录&#xff1a; 序列化概念ProtoBuf是什么ProtoBuf的使⽤特点安装ProtoBuf如何学习ProtoBuf 1.序列化概念 序列化和反序列化 序列化&#xff1a;把对象转换为字节序列的过程称为对象的序列化。反序列化&#xff1a;把字节序列恢复为对象的过程称为对象的反序列化。 什么…

【搜索引擎Solr】Apache Solr 神经搜索

Sease[1] 与 Alessandro Benedetti&#xff08;Apache Lucene/Solr PMC 成员和提交者&#xff09;和 Elia Porciani&#xff08;Sease 研发软件工程师&#xff09;共同为开源社区贡献了 Apache Solr 中神经搜索的第一个里程碑。 它依赖于 Apache Lucene 实现 [2] 进行 K-最近邻…

找不同的字符

题目&#xff1a; 给定两个字符串 s 和 t &#xff0c;它们只包含小写字母。 字符串 t 由字符串 s 随机重排&#xff0c;然后在随机位置添加一个字母。 请找出在 t 中被添加的字母。 解题思路 把字符串转换成字典&#xff0c;key为字符&#xff0c;value为字符出现的次数&…

【Python笔记】Python + xlrd + pymysql读取excel文件数据并且将数据插入到MySQL数据库里面

这篇文章&#xff0c;主要介绍Python xlrd pymysql读取excel文件数据并且将数据插入到MySQL数据库里面。 目录 一、Python读取excel 1.1、安装xlrd库 1.2、打开excel工作簿 1.3、获取sheet工作表 1.4、操作row数据行 1.5、操作column数据列 1.6、操作单元格 二、读取…

Bean 作用域和生命周期

1.通过⼀个案例来看 Bean 作⽤域的问题 假设现在有⼀个公共的 Bean&#xff0c;提供给 A ⽤户和 B ⽤户使⽤&#xff0c;然⽽在使⽤的途中 A ⽤户却“悄悄”地修 改了公共 Bean 的数据&#xff0c;导致 B ⽤户在使⽤时发⽣了预期之外的逻辑错误。 1.1 被修改的 Bean 案例 公…

【N32L40X】学习笔记04-gpio中断库

gpio中断 该函数库的目的就是在统一的地方配置&#xff0c;将配置的不同项放置在一个结构体内部使用一个枚举来定义一个的别名 NVIC 寄存器 NVIC 相关的寄存器定义了可以在 core_cm4.h 文件中找到。我们直接通过程序的定义来分 析 NVIC 相关的寄存器&#xff0c;其定义如下…

RocketMQ(1.NameServer源码)

NameServer功能简述 主要功能如下 服务注册与发现&#xff1a;Nameserver扮演了RocketMQ集群中服务注册中心的角色。当RocketMQ中的Broker、Producer和Consumer启动时&#xff0c;它们会向Nameserver注册自己的网络地址和角色信息。Nameserver维护着集群中所有活跃实例的信息…