• Qt 系统托盘(加hover效果)


    原文链接:http://www.cnblogs.com/qnkk123/p/6840944.html 

    最近项目需要添加系统托盘,本来Qt的QSystemTrayIcon可以实现的,但是要求要添加hover效果,并显示未读消息(就和qq的托盘差不多,移动上去显示未读列表),加了这个要求QSystemTrayIcon就没法实现了,最后使用的是NOTIFYICONDATA实现的,记录下。

    1.创建一个系统托盘:

       NOTIFYICONDATA  m_nid;
       CMsgTrayPos     m_traypos;
       QLabel         *m_pSysIcon;
        qApp->installNativeEventFilter(this);
        //创建托盘图标
        m_pSysIcon = new QLabel;
        m_nid.cbSize = sizeof m_nid;
        m_nid.hIcon = qt_pixmapToWinHICON(QIcon(":/style/blue/signin/logo.png").pixmap(16, 16));
        m_nid.hWnd = HWND(m_pSysIcon->winId());
        m_nid.uCallbackMessage = WM_TRAYNOTIFY;
        m_nid.uID = 1;
        m_nid.uFlags = NIF_ICON | NIF_MESSAGE;
    
        Shell_NotifyIcon(NIM_ADD, &m_nid);
        m_traypos.SetNotifyIconInfo(HWND(this->winId()), 1, WM_TRAYNOTIFY);    

    2,这就创建好系统托盘了,接着就是鼠标事件函数:

    bool CMainWindow::nativeEventFilter(const QByteArray & eventType, void * message, long * result)
    {
        if (eventType == "windows_generic_MSG" || eventType == "windows_dispatcher_MSG")
        {
            MSG * pMsg = reinterpret_cast<MSG *>(message);
            if (pMsg->message == WM_TRAYNOTIFY)
            {
                switch (pMsg->lParam)
                {
                case WM_MOUSEMOVE:
                    m_traypos.OnMouseMove();
                    break;
                case WM_MOUSEHOVER:
                {
                    if (m_pSysNaviWidget->newMessageCount() > 0) // 有消息则移动上去显示未读列表
                    {
                        QPoint point = cursor().pos();
                        m_pSysNaviWidget->show();
                        m_pSysNaviWidget->move(point.x() - 200, point.y() - 150);
                    }
                }
                    break;
                case WM_MOUSELEAVE:
                {
              // 如果hover之后移动到未读列表,则不消失,移动到其他地方则隐藏未读列表 QPoint point
    = cursor().pos(); QPoint naviPoint = m_pSysNaviWidget->pos(); if (point.x() > naviPoint.x() && point.x() < naviPoint.x() + 200 && point.y() > naviPoint.y() && point.y() < naviPoint.y() + 150) { } else { m_pSysNaviWidget->hide(); } } break; case WM_LBUTTONDBLCLK: { //ShowWindow(HWND(this->winId()), SW_SHOW); 界面会假死 showWindow(); break; } case WM_LBUTTONDOWN: //m_Menu->show(); break; case WM_RBUTTONDOWN: { creatMenu(); m_pPop_menu->exec(QCursor::pos()); } break; } } } return false; }

    3、现在hover就可以显示未读列表了,下一步是有消息时闪烁托盘图标,设置的是定时器,事件到就调用下面这个函数

    void CMainWindow::onFlickerSysIcon(bool bFlicker)
    {
        if (bFlicker)
        {
            m_nid.hIcon = qt_pixmapToWinHICON(QIcon(":/style/blue/signin/logo.png").pixmap(16, 16));
        }
        else
        {
            m_nid.hIcon = NULL;
        }
    
        Shell_NotifyIcon(NIM_MODIFY, &m_nid); // 修改图标
    }

    4、闪烁搞定就是右键菜单

    void CMainWindow::initSysMenu()
    {
        //创建菜单、菜单项
        m_pPop_menu = new QMenu();
        m_pControl_action = new QAction("打开主面板", m_pPop_menu);
        m_pSetting_action = new QAction("设置", m_pPop_menu);
        m_pOpinion_action = new QAction("意见反馈", m_pPop_menu);
        m_pExit_action = new QAction("退出", m_pPop_menu);
    
        //连接信号与槽
        connect(m_pControl_action, SIGNAL(triggered()), this, SLOT(onControlAction()));
        connect(m_pSetting_action, SIGNAL(triggered()), this, SLOT(onSettingAction()));
        connect(m_pOpinion_action, SIGNAL(triggered()), this, SLOT(onOpinionAction()));
        connect(m_pExit_action, SIGNAL(triggered()), this, SLOT(onBtnQuitSelected()));
    }
    void CMainWindow::creatMenu()
    {
        m_pPop_menu->addAction(m_pControl_action);
        m_pPop_menu->addSeparator();
        m_pPop_menu->addAction(m_pSetting_action);
        m_pPop_menu->addAction(m_pOpinion_action);
        m_pPop_menu->addAction(m_pExit_action);
    }

    右键是上面的鼠标事件函数中的WM_RBUTTONDOWN;

    5.从托盘显示界面,本来是使用 ShowWindow(HWND(this->winId()), SW_SHOW); 但是显示的界面会假死,不能操作,所以我选择了个折中的方式:

    void CMainWindow::showWindow()
    {
        if (!isVisible())
        {
            hide();
            show();
        }
    }

    6.退出程序是删除托盘:

      Shell_NotifyIcon(NIM_DELETE, &m_nid);

    7.需要到的其他文件:

    #ifndef CTRAYPOS_H
    #define CTRAYPOS_H
    
    #include <windows.h>
    
    class CTrayPos
    {
    private:
        POINT                m_ptMouse;
        
        
        HANDLE                m_hThread;
        HANDLE                m_hExitEvent;
        
        BOOL                m_bTrackMouse;
    
        CRITICAL_SECTION    m_cs;
    
        
    public:
        CTrayPos();
        virtual ~CTrayPos();
        
        static UINT CALLBACK TrackMousePt(PVOID pvClass);
        VOID OnMouseMove();
        BOOL IsMouseHover();
        
    protected:
        virtual VOID OnMouseHover() = 0;
        virtual VOID OnMouseLeave() = 0;
    };
    
    class CMsgTrayPos : public CTrayPos
    {
    private:
        HWND    m_hNotifyWnd;
        UINT    m_uID;
        UINT    m_uCallbackMsg;
    
    public:
        CMsgTrayPos(HWND hwnd=NULL, UINT uID=0, UINT uCallbackMsg=0);
        ~CMsgTrayPos();
    
        VOID SetNotifyIconInfo(HWND hwnd, UINT uID, UINT uCallbackMsg);
    
    protected:
        VOID OnMouseHover();
        VOID OnMouseLeave();
    };
    
    #endif
    #include <process.h>
    
    #include "CTraypos.h"
    
    
    
    CTrayPos::CTrayPos()
    {
        UINT    uThreadId;
    
        m_bTrackMouse = FALSE;
        m_hExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
        m_hThread = (HANDLE) _beginthreadex(NULL, 0, CTrayPos::TrackMousePt, this, 0, &uThreadId);
        InitializeCriticalSection(&m_cs);
    }
    
    CTrayPos::~CTrayPos()
    {
        if(m_hThread != NULL)
        {
            SetEvent(m_hExitEvent);
            if(WaitForSingleObject(m_hThread, 5000) == WAIT_TIMEOUT)
            {
                TerminateThread(m_hThread, 0);
            }
    
            CloseHandle(m_hThread);
            m_hThread = NULL;
        }
    
        if(m_hExitEvent != NULL)
        {
            CloseHandle(m_hExitEvent);
            m_hExitEvent = NULL;
        }
    
        DeleteCriticalSection(&m_cs);
    }
    
    UINT CALLBACK CTrayPos::TrackMousePt(PVOID pvClass)
    {
        POINT        ptMouse;
        CTrayPos    *pTrayPos = (CTrayPos *) pvClass;
    
        while(WaitForSingleObject(pTrayPos->m_hExitEvent, 100) == WAIT_TIMEOUT)
        {
    
            if(pTrayPos->m_bTrackMouse == TRUE)
            {
                GetCursorPos(&ptMouse);
                
                if(ptMouse.x != pTrayPos->m_ptMouse.x || ptMouse.y != pTrayPos->m_ptMouse.y)
                {
                    pTrayPos->m_bTrackMouse = FALSE;
                    pTrayPos->OnMouseLeave();
                }
            }
        }
    
        return 0;
    }
    
    VOID CTrayPos::OnMouseMove()
    {
        EnterCriticalSection(&m_cs);
    
        GetCursorPos(&m_ptMouse);
        if(m_bTrackMouse == FALSE)
        {
            OnMouseHover();
            m_bTrackMouse = TRUE;
        }
    
        LeaveCriticalSection(&m_cs);
    }
    
    BOOL CTrayPos::IsMouseHover()
    {
        return m_bTrackMouse;
    }
    
    //////////////////////////////////////////////////////////////////////////
    
    CMsgTrayPos::CMsgTrayPos(HWND hwnd, UINT uID, UINT uCallbackMsg)
        : CTrayPos()
    {
        SetNotifyIconInfo(hwnd, uID, uCallbackMsg);
    }
    
    CMsgTrayPos::~CMsgTrayPos()
    {
    }
    
    VOID CMsgTrayPos::SetNotifyIconInfo(HWND hwnd, UINT uID, UINT uCallbackMsg)
    {
        m_hNotifyWnd = hwnd;
        m_uID = uID;
        m_uCallbackMsg = uCallbackMsg;
    }
    
    VOID CMsgTrayPos::OnMouseHover()
    {
        if(m_hNotifyWnd != NULL && IsWindow(m_hNotifyWnd))
            PostMessage(m_hNotifyWnd, m_uCallbackMsg, m_uID, WM_MOUSEHOVER);
    }
    
    VOID CMsgTrayPos::OnMouseLeave()
    {
        if(m_hNotifyWnd != NULL && IsWindow(m_hNotifyWnd))
            PostMessage(m_hNotifyWnd, m_uCallbackMsg, m_uID, WM_MOUSELEAVE);
    }

    总结:本来打算用QSystemTrayIcon的Tooltip事件来完成hover的,但是事件调用没效果,最后的效果图:

              



  • 相关阅读:
    如何取消隐藏工作簿,使工作簿可见
    Android小知识总结
    Android内存泄露总结
    Ubuntu 升级VisualBox后无法启动 Kernel driver not installed (rc=-1908)
    Eclipse颜色主题插件:Eclipse Color Theme
    使用Json的注意事项
    android中正确导入第三方jar包
    设计模式--单例模式学习
    比较好的学习网址总结
    二叉树学习总结(Java实现)
  • 原文地址:https://www.cnblogs.com/qnkk123/p/6840944.html
Copyright © 2020-2023  润新知