說起Qt,真是個不錯的ui庫,不僅僅ui做的好,其他方面也不差,在平台擴展方面也是非常的強大。這篇文章我將會分析下qt的托盤,QSystemTrayIcon是qt的托盤類,托盤類的用途是什么我就不說了,自行百科就好,關鍵問題是我們要實現自定義的托盤。
說起常用的客戶端軟件,qq,微信等聊天工具,有這么幾個托盤事件:
1、來消息圖標閃爍
2、氣泡消息提示
3、鼠標左鍵單擊、左鍵雙擊、右鍵單擊、滾動單擊
上述這三種事件QSystemTrayIcon類都完全能夠解決,但是托盤的hover事件卻無能為力,如圖1所示,途圖中是幫助文檔中的一段描述,指明了只有在x11系統中,可以捕獲到系統的tooltip事件,其他系統都無能為力,我自己也看了下qt的源碼,果真是這樣的,有興趣的同學可以自行在研究下。
圖1 幫助文檔
如圖2所示,qq有消息時,鼠標hover在圖盤彈出菜單,那么qq是怎么做的呢,既然qq都到做到了,這個功能我們自己想必肯定也能實現。
圖2 托盤hover彈框
好了,上邊說了這么多,僅僅是為了鋪墊我自己實現的托盤,完全脫離了qt中的托盤類QSystemTrayIcon,不過也不能說完全脫離,部分代碼還是從qt源碼中摘出來的。文章的最后我附上我自己用qt實現的自定義托盤和下載別人用mfc自定義實現的自定義托盤。
因為win32我自己也不是特別了解,因此我也是大概說下自定義托盤需要了解的東西,首先是NOTIFYICONDATA結構,這個結構百科講的特別詳細,看一下就知道怎么用,然后是Shell_NotifyIcon這個api,這個方法就是對托盤操作的接口。具體參數百科中說的很詳細,不過如果你不想看也無所謂,直接往下看也可以。對盤托的操作在windows平台下都是一樣的,關鍵問題是用qt怎么接受這個圖盤的hover和leave消息。
關於這個托盤的實現我也是從mfc的示例代碼中獲取的啟發,然后用qt方法實現,接下來我就直接說下用qt實現的步驟:
1、首先我們需要了解下QAbstractNativeEventFilter這個接口類,繼承這個接口類的類可以把自己注冊到app中,然后就能獲取到整個app
的事件,事件的處理函數為nativeEventFilter,該類有3個參數,具體可以參見這篇文字qt捕獲全局windows消息 這個文章中說的不全是對的,不過能抓取到app消息應該是沒問你的,本篇博客的demo也是印證了這個問題。注冊代碼如下:
1 qApp->installNativeEventFilter(this);
2、第二步就是創建托盤圖標,創建托盤圖標的時候,windows提供了api,代碼如下:
1 NOTIFYICONDATA nid; 2 QLabel *l = new QLabel; 3 nid.cbSize = sizeof nid; 4 nid.hIcon = qt_pixmapToWinHICON(QIcon(":/trayIcon/Resources/childrenWidget.ico").pixmap(16, 16)); 5 nid.hWnd = HWND(l->winId()); 6 nid.uCallbackMessage = WM_TRAYNOTIFY; 7 nid.uID = 1; 8 nid.uFlags = NIF_ICON | NIF_MESSAGE; 9 10 Shell_NotifyIcon(NIM_ADD, &nid);
此處代碼中有一個標簽l,創建他是因為創建圖標時需要一個接受鼠標事件的窗口句柄hWnd,如果沒有句柄,那么托盤也不能創建成功;其他成員的含義從變量的命名上應該也能理解,我重點說下uFlags這個變量,他其實本身沒有什么含義,主要是為了標示NOTIFYICONDATA結構中其他成員那個是有效的,這個也方便了我們后續對托盤圖標的修改。比如說修改tooltip、修改圖標等信息。uCallbackMessage是消息id,在我們后續處理的邏輯中會用到
3、鼠標事件處理
1 bool trayIcon::nativeEventFilter(const QByteArray & eventType, void * message, long * result) 2 { 3 if (eventType == "windows_generic_MSG" || eventType == "windows_dispatcher_MSG") 4 { 5 MSG * pMsg = reinterpret_cast<MSG *>(message); 6 if (pMsg->message == WM_TRAYNOTIFY) 7 { 8 switch (pMsg->lParam) 9 { 10 case WM_MOUSEMOVE: 11 m_traypos.OnMouseMove(); 12 break; 13 case WM_MOUSEHOVER: 14 m_Menu->show(); 15 break; 16 case WM_MOUSELEAVE: 17 m_Menu->hide(); 18 break; 19 case WM_LBUTTONDBLCLK: 20 // m_Menu->show(); 21 break; 22 case WM_LBUTTONDOWN: 23 // m_Menu->show(); 24 break; 25 case WM_RBUTTONDOWN: 26 // m_Menu->show(); 27 break; 28 } 29 } 30 } 31 32 return false; 33 }
上述代碼主要是針對鼠標事件的一個處理。WM_TRAYNOTIFY消息是我們開始的時候注冊到圖盤中的消息,當托盤發生鼠標事件的時候我們只需要關注自己注冊的消息,對於windows托盤稍微有了解的同學可能也知道,微軟沒有提供給我們托盤圖標的進入和離開事件,而僅僅提供了鼠標move的事件,不過僅僅有這一個事件我們就可以模擬出其他的事件來。細心的同學將會注意到 m_traypos.OnMouseMove();這句代碼,其實m_tryapos這個對象是一個move事件處理類,他可以模擬出鼠標hover和leave事件來。關於這個類的解釋我就不說了,是一個國外的大牛寫的,demo中有源文件。
4、程序退出時銷毀托盤圖標
Shell_NotifyIcon(NIM_DELETE, &m_NotifyIconData);
通過上述的代碼整理,簡單的托盤就可以實現了,因為我是自己做的demo,因此不是所有事件的處理了的,高級定制的功能如果有興趣的同學可以給我留言,或者私信我可以,如果是我實現了的,我將願意和大家一起分享。我下邊鏈接中的這個demo其實比較粗糙,就僅僅的可以實現鼠標在托盤圖標上的hover和leave請求。
最后我上兩張效果圖,圖3是mfc示例的鼠標hover截圖,圖4是qt示例的鼠標hover截圖
圖3 mfc示例demo
圖4 qt示例demo
注意:這個demo非常粗糙,不過我已經講明了怎么實現一個自己的托盤,關於需要怎么實現一個完美的托盤,同學們可以參考qt的源碼中qsystemtrayicon_win.cpp文件,該文件就是QSystemTrayIcon類的真正實現。
qt示例鏈接:http://download.csdn.net/detail/qq_30392343/9608076
mfc示例鏈接:http://download.csdn.net/detail/qq_30392343/9608078


