在現代Linux桌面環境上我們時常可以看到類似的消息框: 這些消息框常用在如下場景: 即時聊天軟體的新消息 鬧鐘定時提示 電池電量提示 郵件消息 長耗時操作的完成提示 在freedesktop.org的規範中這種消息框被稱為 ,中文名我們形象得稱其為“氣泡框”。通過調用D BUS服務 提供的介面即可 ...
在現代Linux桌面環境上我們時常可以看到類似的消息框:
這些消息框常用在如下場景:
- 即時聊天軟體的新消息
- 鬧鐘定時提示
- 電池電量提示
- 郵件消息
- 長耗時操作的完成提示
在freedesktop.org的規範中這種消息框被稱為Desktop Notification
,中文名我們形象得稱其為“氣泡框”。通過調用D-BUS服務org.freedesktop.Notifications
提供的介面即可顯示在桌面上。
所以我們先瞭解一下這個D-BUS服務。
org.freedesktop.Notifications概覽
一個氣泡框消息通常會包含如下的屬性:
名稱 | 說明 |
---|---|
Application Name | 標示發送消息的程式,最好使用程式全名 |
Replaces ID | 可選的消息ID,伺服器通過id控制消息框的渲染,通常不用關註 |
Icon | 顯示在氣泡框上的圖標 |
Summary | 標題,只能顯示一行,叫title應該更合適 |
Body | 消息體,支持部分HTML標簽;<b></b> ;<i></i> ;<u></u> ;<a></a> ;<img src=... alt=...> |
Actions | 顯示一些按鈕或者菜單(QAction),不過這一功能通常未被實現 |
Hints | 為消息體提供的額外數據,比如顯示在屏幕的位置(x,y坐標) |
Expiration Timeout | 氣泡框顯示的時長,單位毫秒;指定為-1時行為取決於實現;為0時氣泡框將一直顯示在桌面上直到用戶點擊 |
其中Icon
和Hints中的image_path
必須為本地絕對路徑或者file://
開頭的文件URL。另外使用桌面環境預定義圖標的名字也是可以的。
氣泡框還有三個緊急程度可供選擇:
名稱 | 值 | 說明 |
---|---|---|
Low | 0 | 預設值,可以設置如何顯示,應該設置一個合理的顯示時間以便氣泡框可以隱藏退出 |
Normal | 1 | 同low |
Critical | 2 | 代表重要通知,不應該自動過期隱藏 |
所有的氣泡框消息請求都是非同步的,通常構造請求併發送後用戶就可以不再關心後續的信息,如果有特殊需要則可以自定義處理org.freedesktop.Notifications
發送的信號。
得益於freedesktop.org的標準規範,包括KDE,GNOME,XFCE4在內的許多桌面環境都提供了對Desktop Notification
的支持,雖然外觀上可能存在一些差異但是創建氣泡框的方法是一樣的。
不過不用擔心,我們不會直接去使用D-BUS,因為已經有簡化的現成方案可供選擇了。下麵就讓我們一起看看這些方案。
方案一:調用外部命令
可能你已經知道了,我要介紹的命令就是notify-send
。
notify-send
幾乎被所有的桌面環境和發行版支持,它依賴於後面會介紹的libnotify和glib,如果你的系統上沒有安裝可以使用如下命令進行安裝:
debian/Ubuntu:
sudo apt install libnotify-bin
Arch Linux:
sudo pacman -S libnotify
安裝後可以用如下命令顯示氣泡框:
# notify-send title body [options]
notify-send test 'This is a desktop Notification test.' -t 10000
-t
參數設置超時時間。效果如下:
具體的參數可以參考這裡:https://ss64.com/bash/notify-send.html
方案二:通過編程方式實現
在Qt代碼中調用外部命令就可以顯示氣泡框,然而這種方式不夠靈活,所以我們需要使用前面提到的libnotify在我們的代碼里生成並顯示氣泡框。
libnotify對各個語言都提供了binding,可以參考這裡。
這裡我們選擇使用golang的binding:
package main
import ("github.com/mqu/go-notify")
func main() {
notify.Init("Hello world")
hello := notify.NotificationNew("Hello World!", "This is an example notification.","dialog-information")
hello.SetTimeout(5000)
hello.Show()
}
上面的代碼將會顯示一個可以在桌面停留5s的氣泡框:
不過如果每次都要使用一大串代碼才能顯示消息的話必然是低效的,而且需要換算時間至毫秒,所以我寫了一個幫助函數在notify.go:
// ShowNotification 顯示org.freedesktop.Notifications氣泡消息框
// duration == -1時使用預設delay
// duration == 0表示不設置超時,desktop notification將會一直顯示
func ShowNotification(title, text, image string, delay time.Duration) {
var notifyDelay int32
if delay == -1 {
notifyDelay = duration2millisecond(defaultNotifyDelay)
} else {
notifyDelay = duration2millisecond(delay)
// 不合法值(包括duration不足1ms),使用預設值進行替換
if notifyDelay == -1 {
notifyDelay = duration2millisecond(defaultNotifyDelay)
}
}
libnotify.Init(applicationName)
notify := libnotify.NotificationNew(title, text, image)
if notify == nil {
fmt.Fprintf(os.Stderr, "Unable to create a new notification\n")
return
}
notify.SetTimeout(notifyDelay)
notify.Show()
}
// duration2millisecond 將time.Duration轉換成millisecond
// duration不足1ms將返回-1
func duration2millisecond(duration time.Duration) int32 {
res := int32(duration / time.Millisecond)
if res < 0 {
return -1
}
return res
}
首先將時間值轉換成毫秒數,如果太小或者不合法就使用預設的停留時間。applicationName是程式的完整名稱。
因為氣泡框消息是非同步的,所以在調用了Show()
之後函數就會返回,後續操作xwindows都會幫我們處理,所以這個函數調用之後是立刻返回的,不會阻塞Qt的gui事件迴圈,可以放心的使用:
// download something success
ShowNotification("下載", "文件下載完成", "dialog-information", 5*time.Second)
這樣我們也可以輕鬆地在我們的Qt程式中使用氣泡消息框了。
參考:
https://developer.gnome.org/notification-spec/