Rust系统托盘图标库ksni的使用,ksni提供跨平台系统托盘图标和菜单功能集成
Rust系统托盘图标库ksni的使用
ksni是一个实现了KDE/freedesktop StatusNotifierItem规范的Rust库,提供了跨平台的系统托盘图标和菜单功能集成。
基本示例
以下是ksni的基本使用示例:
use ksni::TrayMethods; // 提供spawn方法
#[derive(Debug)]
struct MyTray {
selected_option: usize,
checked: bool,
}
impl ksni::Tray for MyTray {
fn id(&self) -> String {
env!("CARGO_PKG_NAME").into()
}
fn icon_name(&self) -> String {
"help-about".into()
}
fn title(&self) -> String {
if self.checked { "CHECKED!" } else { "MyTray" }.into()
}
fn menu(&self) -> Vec<ksni::MenuItem<Self>> {
use ksni::menu::*;
vec![
SubMenu {
label: "a".into(),
submenu: vec![
SubMenu {
label: "a1".into(),
submenu: vec![
StandardItem {
label: "a1.1".into(),
..Default::default()
}
.into(),
StandardItem {
label: "a1.2".into(),
..Default::default()
}
.into(),
],
..Default::default()
}
.into(),
StandardItem {
label: "a2".into(),
..Default::default()
}
.into(),
],
..Default::default()
}
.into(),
MenuItem::Separator,
RadioGroup {
selected: self.selected_option,
select: Box::new(|this: &mut Self, current| {
this.selected_option = current;
}),
options: vec![
RadioItem {
label: "Option 0".into(),
..Default::default()
},
RadioItem {
label: "Option 1".into(),
..Default::default()
},
RadioItem {
label: "Option 2".into(),
..Default::default()
},
],
..Default::default()
}
.into(),
CheckmarkItem {
label: "Checkable".into(),
checked: self.checked,
activate: Box::new(|this: &mut Self| this.checked = !this.checked),
..Default::default()
}
.into(),
StandardItem {
label: "Exit".into(),
icon_name: "application-exit".into(),
activate: Box::new(|_| std::process::exit(0)),
..Default::default()
}
.into(),
]
}
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
let tray = MyTray {
selected_option: 0,
checked: false,
};
let handle = tray.spawn().await.unwrap();
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
// 我们可以修改托盘
handle.update(|tray: &mut MyTray| tray.checked = true).await;
// 永久运行
std::future::pending().await
}
完整示例代码
use ksni::TrayMethods;
#[derive(Debug)]
struct AppTray {
counter: u32,
is_active: bool,
}
impl ksni::Tray for AppTray {
fn id(&self) -> String {
"my-app-tray".into()
}
fn icon_name(&self) -> String {
if self.is_active {
"network-wired-activated"
} else {
"network-wired"
}.into()
}
fn title(&self) -> String {
format!("Counter: {}", self.counter)
}
fn menu(&self) -> Vec<ksni::MenuItem<Self>> {
use ksni::menu::*;
vec![
StandardItem {
label: format!("Click count: {}", self.counter),
enabled: false,
..Default::default()
}.into(),
MenuItem::Separator,
CheckmarkItem {
label: "Active".into(),
checked: self.is_active,
activate: Box::new(|this: &mut Self| {
this.is_active = !this.is_active;
}),
..Default::default()
}.into(),
StandardItem {
label: "Increment".into(),
icon_name: "list-add".into(),
activate: Box::new(|this: &mut Self| {
this.counter += 1;
}),
..Default::default()
}.into(),
StandardItem {
label: "Reset".into(),
icon_name: "edit-clear".into(),
activate: Box::new(|this: &mut Self| {
this.counter = 0;
}),
..Default::default()
}.into(),
MenuItem::Separator,
StandardItem {
label: "Quit".into(),
icon_name: "application-exit".into(),
activate: Box::new(|_| std::process::exit(0)),
..Default::default()
}.into(),
]
}
}
#[tokio::main]
async fn main() {
let tray = AppTray {
counter: 0,
is_active: true,
};
let handle = tray.spawn().await.unwrap();
// 每5秒更新一次计数器
let mut interval = tokio::time::interval(std::time::Duration::from_secs(5));
loop {
interval.tick().await;
handle.update(|tray| {
tray.counter += 1;
}).await;
}
}
功能支持
- [x] org.kde.StatusNotifierItem
- [x] com.canonical.dbusmenu
- [x] org.freedesktop.DBus.Introspectable
- [x] org.freedesktop.DBus.Properties
- [x] 单选项目
- [ ] 文档
- [x] 异步支持
- [x] 可变菜单项
许可证
这是免费且不受限制的软件,发布到公共领域。任何人都可以自由复制、修改、发布、使用、编译、出售或分发此软件,无论是源代码形式还是编译后的二进制形式,用于任何目的,商业或非商业,以及通过任何方式。
1 回复
Rust系统托盘图标库ksni的使用指南
ksni是一个跨平台的Rust库,用于在系统托盘中创建和管理图标及菜单。它支持Linux、Windows和macOS等主流操作系统,提供了一个统一的API来创建系统托盘应用。
基本特性
- 跨平台支持(Linux/Windows/macOS)
- 支持自定义图标
- 可添加右键菜单
- 支持图标状态变化
- 轻量级实现
使用方法
添加依赖
首先在Cargo.toml
中添加ksni依赖:
[dependencies]
ksni = "0.1"
基本示例
下面是一个创建简单系统托盘图标的基本示例:
use ksni::{Tray, TrayService, MenuItem};
use std::sync::Arc;
struct MyTray {
// 可以在这里添加你的应用状态
}
impl Tray for MyTray {
fn icon_name(&self) -> String {
// 返回图标名称或路径
"input-mouse".to_string() // 这是一个示例,使用系统图标
}
fn title(&self) -> String {
"My App".to_string()
}
fn menu(&self) -> Vec<MenuItem<Self>> {
vec![
MenuItem {
label: "退出".to_string(),
activate: Arc::new(|_| std::process::exit(0)),
..Default::default()
}
]
}
}
fn main() {
let tray = MyTray {};
let service = TrayService::new(tray);
service.spawn();
// 保持程序运行
loop {
std::thread::sleep(std::time::Duration::from_secs(1));
}
}
完整示例代码
下面是一个完整的ksni系统托盘应用示例,包含自定义图标、动态更新和复杂菜单功能:
use ksni::{Tray, TrayService, MenuItem, TrayUpdate};
use std::sync::Arc;
use std::time::Duration;
// 自定义托盘结构体
struct AppTray {
counter: u32, // 示例状态
icon_index: u8, // 当前图标索引
}
impl Tray for AppTray {
fn icon_name(&self) -> String {
// 根据状态返回不同图标
match self.icon_index {
0 => "face-smile".to_string(),
1 => "face-sad".to_string(),
_ => "face-laugh".to_string(),
}
}
fn title(&self) -> String {
format!("计数器: {}", self.counter)
}
fn menu(&self) -> Vec<MenuItem<Self>> {
vec![
MenuItem {
label: "增加计数".to_string(),
activate: Arc::new(|tray| {
println!("增加计数");
tray.counter += 1;
}),
..Default::default()
},
MenuItem {
label: "减少计数".to_string(),
activate: Arc::new(|tray| {
println!("减少计数");
tray.counter -= 1;
}),
..Default::default()
},
MenuItem::separator(),
MenuItem {
label: "切换图标".to_string(),
submenu: vec![
MenuItem {
label: "笑脸".to_string(),
activate: Arc::new(|tray| {
tray.icon_index = 0;
}),
..Default::default()
},
MenuItem {
label: "哭脸".to_string(),
activate: Arc::new(|tray| {
tray.icon_index = 1;
}),
..Default::default()
},
MenuItem {
label: "大笑".to_string(),
activate: Arc::new(|tray| {
tray.icon_index = 2;
}),
..Default::default()
},
],
..Default::default()
},
MenuItem::separator(),
MenuItem {
label: "退出".to_string(),
activate: Arc::new(|_| std::process::exit(0)),
..Default::default()
}
]
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 创建托盘实例
let tray = AppTray {
counter: 0,
icon_index: 0,
};
// 创建托盘服务
let service = TrayService::new(tray);
let handle = service.handle();
// 启动托盘服务
service.spawn()?;
// 在后台线程中动态更新托盘
std::thread::spawn(move || {
let mut count = 0;
loop {
std::thread::sleep(Duration::from_secs(5));
handle.update(|tray: &mut AppTray| {
count += 1;
tray.counter = count;
TrayUpdate::Title(format!("已运行: {}秒", count * 5))
});
}
});
// 保持主线程运行
loop {
std::thread::sleep(Duration::from_secs(1));
}
}
平台注意事项
- Linux: 需要DBus服务,大多数桌面环境都支持
- Windows: 原生支持
- macOS: 使用NSStatusItem实现
错误处理
ksni可能会在初始化时失败,特别是在缺少必要依赖的环境中。建议添加适当的错误处理:
fn main() -> Result<(), Box<dyn std::error::Error>> {
let tray = MyTray {};
let service = TrayService::new(tray);
service.spawn()?;
// 保持程序运行
loop {
std::thread::sleep(std::time::Duration::from_secs(1));
}
}
ksni提供了一种简单而强大的方式来为Rust应用程序添加系统托盘功能,使得创建后台服务或实用程序变得更加容易。