rust的rpc服务增加系统托盘及图标

声明

本文仅用以记录个人针对该问题的猜测及处理办法,欢迎交流探讨,但若由于采用或借鉴此方法而导致的任何问题,本人不承担任何责任。

目录

声明

问题描述

解决思路


问题描述

        最近用rust实现rpc服务,程序运行后是在控制台的,不需要界面,所以感觉有点杂乱,就想着放到托盘去吧

解决思路

 添加依赖:

执行 cargo add tray-icon tao anyhow

或直接在Cargo.toml中添加进去

[dependencies]
tray-icon = "0.13.5"
tao = "0.28.0"
anyhow = "1.0.83"

 在main.rs中添加引用

use tao::event_loop::{EventLoopBuilder, ControlFlow};
use tray_icon::{menu::{Menu, MenuItem, MenuEvent}, TrayIconBuilder, Icon, TrayIconEvent};
use anyhow::Result;

然后开始托盘部分代码

     
    let event_loop = EventLoopBuilder::new().build();
        
    let menu = Menu::new();
    // menu.append(&MenuItem::new("打开", true, None))?;
    menu.append(&MenuItem::new("退出", true, None))?;

    let icon = Icon::from_resource_name("logo.ico", None).unwrap_or(
        Icon::from_resource(111, None).unwrap_or(
            Icon::from_rgba(vec![0,0,0,0], 1, 1).unwrap()
        )
    );

    let _tray_icon = Some(
        TrayIconBuilder::new()
            .with_menu(Box::new(menu))
            .with_tooltip("RPC服务")
            .with_icon(icon)
            .build().unwrap(),
    );

    let menu_channel = MenuEvent::receiver();
    let _tray_channel = TrayIconEvent::receiver();
    
    let event_loop_proxy = event_loop.create_proxy();
    // std::thread::spawn(move || {
    //     loop {
    //         event_loop_proxy.send_event(()).ok();
    //         std::thread::sleep(time::Duration::from_millis(50));
    //     }
    // });
    tokio::spawn(async move{
        loop{
            event_loop_proxy.send_event(()).ok();
            tokio::time::sleep(std::time::Duration::from_millis(50)).await;
        }
    });

    event_loop.run(move |_event, _, control_flow| {
        *control_flow = ControlFlow::Wait;

        if let Ok(MenuEvent { id:_ }) = menu_channel.try_recv() {
            // if id.0 == "1001"{
            //     //点击“打开”菜单,启动主窗口进程
            //     // open_app();
            // }else
            {
                //退出托盘程序
                *control_flow = ControlFlow::Exit;
            }
        }
        
        // if let Ok(TrayIconEvent {click_type, id: _, x: _, y: _, icon_rect: _ }) = tray_channel.try_recv(){
        //     if let ClickType::Left = click_type{
        //         //打开主窗口进程
        //         open_app();
        //     }
        // }
    });

参考:tray-iconSlint多窗口 + Tray Icon + 窗口间通信

这里我直接借鉴了Slint多窗口 + Tray Icon + 窗口间通信的代码放这儿,只需要退出,所以把其他的屏蔽了

上面代码中ICON部分遇到点问题,ICON引用图标几个方法如下:

// 从图像数组获取
fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> ;
// 从路径获取
fn from_path<P: AsRef<std::path::Path>>(path: P,size: Option<(u32, u32)>,) ->Result<Self, BadIcon> ;
// 从资源文件获取,这里有两个方法
fn from_resource(ordinal: u16, size: Option<(u32, u32)>) -> Result<Self, BadIcon>
//和
fn from_resource_name(resource_name: &str, size: Option<(u32, u32)>) -> Result<Self, BadIcon>
//  从win下api句柄获取
fn from_handle(handle: isize)

最初用from_path,但是发现不是嵌入程序的,也就是图标文件需要放在执行文件目录下,万一被删掉,就异常或者失败用from_rgba生成一个黑子…………

所以考虑嵌入exe的方法,用from_resource,但不熟悉rc文件,这里icon引用一直失败,后面查找下才解决

说说rc资源文件,可以参考这里Windows 资源文件(.rc文件),说的很细

我这里最初没搞明白,直接

name ICON "logo.ico"

生成exe确实也有图标,但托盘那里拿不到,用from_resource_name获取logo.ico一直失败,没弄明白,后面改如下

#define LOGOICON  111
LOGOICON ICON "logo.ico"

然后用from_resource方法读取ordinal_id,就是#define的111,这里随意资源多的话要保证唯一性(我是这么理解的) 

再说说嵌入图标,这里参考给rust写的windows程序加个图标,但这里的库版本早,跟新版本有出入,我这里贴下

执行

cargo add  embed-resource

这里要将cargo.toml中embed-resource 部分移入

[build-dependencies]
embed-resource = "2.4.2"

或直接在build-dependencies中加上

在build.rs加入

fn main() -> Result<(), Box<dyn std::error::Error>> {
    embed_resource::compile("./logo.rc",embed_resource::NONE);
    tonic_build::configure()
    // .out_dir("src/")
    .build_server(true)
    .compile(
        &["hello.proto"],
        &["protos"],
    )?;
    
    Ok(())
}

上面我用到tonic做rpc,有return result,所以这里embed_resource要放在前面

到这里似乎已经完成了,编译运行,ok,托盘有了,退出正常,但我的rpc不响应……

检查下,上面有个event_loop,这里给阻塞了,所以得给rpc那里也在前面加个线程出来;

由于tonic依赖tokio,所以我将上面托盘那里(屏蔽掉的std::thread::spawn)也改用tokio,至此完美实现托盘

这里插几句

1. 托盘那里的icon可以不用,程序就变成幽灵了,托盘有个空的……哈哈

2. 上面去掉控制台,在main.rs文件开头加入

#![windows_subsystem = "windows"]

这里呢,如果把托盘部分去掉,程序会运行在后台,无界面,任务栏也没有,要关闭得去任务管理器

3. 上面icon加载部分,可以用from_rgba生成自定义图标在托盘显示,上面我是测试,出错的话,托盘图标是黑色方块的

4. rust刚学,不熟,勿喷,上面有点乱,主要是做个记录,看的看个热闹,如果你有收获,顺手帮点个赞

  • 22
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值