尚经起名网

您现在的位置是:首页 >

企业资讯

抖音刷赞 - 快手刷赞网推广链接

时间:2024-05-19 09:52 评论
1、修改托盘图标托盘hover时所弹出的框,主要用于显示未接受的消息,可以快速的浏览用户消息,并响应用户的交互,为了和鼠标右键菜单有所区分,在本小节中我把托盘有消息时hover所弹出的界面统称为hover弹框。下边是我自定义窗口的Show函数代码,参数pos是托盘图标的中心位置。3、如果你的系统是win7,包含之后的系统,那么你获取托盘图标的代码看起来像下面这样...

自助下单地址(拼多多砍价,ks/qq/dy赞等业务):点我进入

上一篇文章讲到自定义Qt托盘,但不是使用QSystemTrayIcon类,而是我们完全自己自定义的类。 我们只需要处理这个类的鼠标悬停、鼠标左键点击、鼠标右键点击和鼠标左键点击即可。 双击按键,完全模拟qq的托盘风格。 在文章的最后,我还提供了一个demo下载链接,这是一个处理鼠标悬停事件,模拟鼠标离开和进入事件的完全可操作的demo。 本节将逐步讲解如何实现一个完美的托盘,包括托盘菜单的显示、托盘工具提示以及托盘悬停时的弹出框显示。

在阅读本文之前,同学们应该阅读Qt Custom Tray一文。 本文对win32的几个API进行了说明。 虽然不详细,但是讲了它们的作用,解释了一些用法。 .

在讲解本文内容之前,先贴一段代码,也算是对之前内容的回顾。 该接口属于 QAbstractNativeEventFilter 类。 如果这个接口要处理app消息,需要用qApp指针注册CSystemTrayIcon对象,具体代码在上一节介绍过,这里不再多说。

 1 bool CSystemTrayIcon::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(message);
 6 
 7         if (pMsg->message == WM_TRAYNOTIFY)
 8         {
 9             switch (pMsg->lParam)
10             {
11             case WM_MOUSEMOVE:
12                 m_PTrayPos.OnMouseMove();
13                 break;
14             case WM_MOUSEHOVER:
15             {
16                 HandleMouseHover();
17             }
18             break;
19             case WM_MOUSELEAVE:
20             {
21                 HandleMouseLeave();
22             }
23             break;
24             case WM_LBUTTONUP:
25             {
26                 TrayActivateSlot(QSystemTrayIcon::Trigger);
27             }
28             break;
29             case NIN_BALLOONUSERCLICK: //用户单击气泡处理
30             {
31 
32             }
33             break;
34             case WM_LBUTTONDBLCLK:
35             {
36                 emit DblClickTray();
37             }
38             break;
39             case WM_RBUTTONUP:
40             {
41                 m_MenuPopPos = QCursor::pos();
42                 emit ShowPopupWidget(m_MenuPopPos, false);
43                 m_Menu->show();
44                 *result = 0;
45             }
46             break;
47             }
48         }
49     }
50 
51     return false;
52 }

这个本地事件过滤器可以处理通过这个应用程序的所有事件,所以它可以处理鼠标移动到托盘的消息。 有了悬停消息,我们可以模拟enter和leave这两个消息(enter和leave windows托盘区没有提供离开消息),其他的鼠标事件直接获取即可,我们只需要自己具体处理即可以后做生意。

1.菜单

酷鱼桌面托盘图标_托盘图标_qt 托盘图标

一个完美的托盘往往都有右键菜单,而右键菜单也是托盘的一个重要功能。 如果要实现自定义托盘菜单,请看qt菜单项自定义一文。 本文介绍自定义菜单。 应该可以满足大部分人的需求,至少像360或者电脑管家一样实现右键菜单是没问题的。

上面给出的链接可以实现一个自定义的漂亮的菜单项。 接下来主要说一下右键菜单的显示问题。 首先,我要说明一个问题。 右键菜单显示的位置应该就是我们右击的位置。 之所以强调这句话,是因为我们后面讲解鼠标悬停弹出框的时候,会和右键菜单有所不同。 Qt的菜单也是一个窗口。 它继承自QWidget,但菜单包含Qt::Popup 属性。 当它失去焦点时,它会自动隐藏。

在托盘区域点击鼠标右键,我们响应WM_RBUTTONUP消息,然后显示右键菜单。 这个时候,我们需要做一件事。 我们必须保证我们显示的右键菜单在屏幕上。 我对此没有太多疑问。 话说,一切靠代码,代码逻辑比较简单。 首先将菜单移动到鼠标右键点击的位置,然后判断鼠标是否在界面中。 如果需要移动,水平移动菜单的宽度,垂直移动菜单。 窗口的高度,如何移动需要判断窗口的哪一侧超出了屏幕。

说了这么多,改正代码其实还是比较简单的,如下:

 1 QPoint MenuWholePos(const QWidget * widget, const QPoint & proposal)//获取能完全显示菜单的位置
 2     {
 3         QRect wRect = widget->rect();
 4         if (QDesktopWidget * desktop = qApp->desktop())
 5         {
 6             QRect rect = desktop->screenGeometry(desktop->primaryScreen());
 7             wRect.moveTo(proposal);
 8 
 9             if (rect.contains(QPoint(wRect.left(), 1)) == false)
10             {
11                 wRect.translate(widget->width(), 0);
12             }
13 
14             if (rect.contains(QPoint(wRect.right(), 1)) == false)
15             {
16                 wRect.translate(-widget->width(), 0);
17             }
18 
19             if (rect.contains(QPoint(1, wRect.bottom())) == false)
20             {
21                 wRect.translate(0, -widget->height());
22             }
23 
24             if (rect.contains(QPoint(1, wRect.top())) == false)
25             {
26                 wRect.translate(0, widget->height());
27             }
28         }
29 
30         return wRect.topLeft();
31     }

当收到QEvent::Show的消息后,我们将窗口移动到正确的位置,一个完美的右键菜单就完成了。

qt 托盘图标_托盘图标_酷鱼桌面托盘图标

2.托盘信息

说到托盘信息,就不得不说到NOTIFYICONDATA结构体,它存储了托盘的基本信息,包括托盘图标、托盘工具提示、托盘句柄、托盘注意消息id等一系列成员。 本节Qt自定义如何创建和删除托盘图标在托盘中有提到,具体如何修改其他信息我就简单说一下,因为NOTIFYICONDATA结构的百度百科已经详细提到了,所以我只给出这里的一般描述。

1.修改托盘图标

 1 HICON CSystemTrayIcon::CreateIcon()
 2 {
 3     const HICON oldIcon = m_TrayHIcon;
 4     const QIcon icon = m_TrayIcon;
 5 
 6     if (icon.isNull())
 7     {
 8         return oldIcon;
 9     }
10     const int iconSizeX = GetSystemMetrics(SM_CXSMICON);
11     const int iconSizeY = GetSystemMetrics(SM_CYSMICON);
12     const QSize size = icon.actualSize(QSize(iconSizeX, iconSizeY));
13     const QPixmap pm = icon.pixmap(size);
14     if (pm.isNull())
15     {
16         return oldIcon;
17     }
18     m_TrayHIcon = qt_pixmapToWinHICON(pm);
19 
20     return m_TrayHIcon;
21 }

1 m_NotifyIconData.hIcon = CreateIcon();
2 
3         m_ToolTips = QStringLiteral("");
4 
5         if (!m_ToolTips.isNull())
6             qStringToLimitedWCharArray(m_ToolTips, m_NotifyIconData.szTip, sizeof(m_NotifyIconData.szTip) / sizeof(wchar_t));
7 
8 Shell_NotifyIcon(NIM_MODIFY, &m_NotifyIconData);

修改托盘图标的主要步骤是构造NOTIFYICONDATA结构,然后将uFlags设置为NIF_ICON,使hIcon字段有效。 我们说的是将QImage处理后的图像句柄传递给hIcon,调用Shell_NotifyIcon接口修改托盘。

2.修改托盘工具提示

qt 托盘图标_酷鱼桌面托盘图标_托盘图标

修改托盘的提示信息其实和修改图标是一样的。 首先需要弄清楚修改托盘的提示信息,然后设置uFlags标志,并重新设置NOTIFYICONDATA结构的具体成员信息,最后调用shell接口修改托盘。 代码如下不贴

3.托盘悬停窗口

托盘悬停时弹出的框主要用于显示未接受的消息。 它可以快速浏览用户消息并响应用户交互。 为了区别于右键菜单,本节我会在托盘有消息时悬停。 弹出界面统称为悬停弹框。

1、首先根据UI设计师的要求,定制一个漂亮的托盘悬停弹框。 这个弹框一般包含消息项qt 托盘图标,类似于QQ好友消息。 这个窗口应该支持与我们的自定义托盘类的交互,并与托盘图标的闪烁保持同步。 如果比较图标闪烁,则表示有消息,当鼠标悬停时,会弹出未接消息提示框

2.当托盘菜单需要显示悬停窗口时,我们显示弹出框

3、在第一节菜单内容中,我重点关注了菜单显示位置的问题。 hover弹框也存在这个问题,所以我先说明一下hover弹框的规则。 下面的规则都是基于任务栏的。 当屏幕在下方时发生。

4.如果任务栏在屏幕的上左右两侧,处理方法类似

下面是我的自定义窗口的Show函数代码,参数pos是托盘图标的中心位置。

 1 void CMessagePopupWidget::Show(const QPoint & pos)
 2 {
 3     m_TrayIconVerCenter = pos;
 4     m_CanHide = false;
 5     QRect wRect = this->rect();
 6     if (QDesktopWidget * desktop = qApp->desktop())
 7     {
 8         QRect rect = desktop->screenGeometry(desktop->primaryScreen());
 9         wRect.moveTo(m_TrayIconVerCenter);
10 
11         switch (MissionToolBar())
12         {
13         case 1:
14         {
15             int missionHeight = MissionToolHeight();
16             QPoint pos(wRect.topLeft().x() - wRect.width() / 2, missionHeight);
17             move(pos);
18         }
19         break;
20         case 2:
21         {
22             if (rect.contains(QPoint(wRect.right(), 1)) == false)
23             {
24                 wRect.translate(-this->width(), 0);
25             }
26             QRect r = desktop->availableGeometry(desktop->primaryScreen());
27             move(wRect.topLeft() + QPoint(-(m_TrayIconVerCenter.x() - r.width()), -wRect.height() / 2));
28         }
29         break;
30         case 3:
31         {
32             QRect r = desktop->availableGeometry(desktop->primaryScreen());
33             QPoint pos(wRect.topLeft().x() - wRect.width() / 2, r.height() - wRect.height());
34             move(pos);
35         }
36         break;
37         default:
38         {
39             int missionWidth = MissionToolWidth();
40             move(wRect.topLeft() + QPoint(missionWidth - m_TrayIconVerCenter.x(), -wRect.height() / 2));
41         }
42         }
43     }
44 
45     show();
46 }

上面的代码是不是比较简单,呵呵,其实还可以。 关于上面如何获取任务栏高宽的方法,我就不贴代码了。 有兴趣的同学可以自行百度。

接下来,我要添加如何获取任务栏图标的坐标

1、首先说一下Shell_NotifyIconGetRect接口。 微软明确表示只有在win7之后才会提供这个接口,所以自定义托盘如果要运行在xp系统和win7(win10)系列系统上,那么就需要进行兼容性处理。

2、下面是判断接口,判断指定动态库是否包含指定接口

 1 void * common::LibraryContainsInterface(LPWSTR lpDesc, LPCSTR pGuid)
 2 {
 3     HINSTANCE hinstLib = ::LoadLibrary(lpDesc);
 4     if (hinstLib != nullptr)
 5     {
 6         void* proc = GetProcAddress(hinstLib, pGuid);
 7         return proc;
 8     }
 9     FreeLibrary(hinstLib);
10 
11     return NULL;
12 }

3、如果你的系统是win7,包括以后的系统,那么你获取托盘图标的代码如下

 1 static PtrShell_NotifyIconGetRect Shell_NotifyIconGetRect
 2         = (PtrShell_NotifyIconGetRect)LibraryContainsInterface(L"shell32", "Shell_NotifyIconGetRect");
 3     if (Shell_NotifyIconGetRect)
 4     {
 5         NOTIFYICONIDENTIFIER notify;
 6         notify.cbSize = sizeof notify;
 7         notify.hWnd = (HWND)m_TrayMessageWidget->winId();
 8         notify.uID = 1;
 9         notify.guidItem = GUID_NULL;
10 
11         RECT rect;
12         HRESULT hr = Shell_NotifyIconGetRect(¬ify, &rect);
13 
14         return QRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
15     }

4、如果你的系统是xpqt 托盘图标,或者更早的系统,那么Shell_NotifyIconGetRect接口是不可用的。 如果使用它,会直接导致程序失败,我们的代码如下

 1 struct AppData
 2     {
 3         HWND hwnd;
 4         UINT uID;
 5     };
 6 
 7     QRect ret;
 8 
 9     TBBUTTON buttonData;
10     DWORD processID = 0;
11     HWND trayHandle = FindWindow(L"Shell_TrayWnd", NULL);
12 
13     //find the toolbar used in the notification area
14     if (trayHandle) {
15         trayHandle = FindWindowEx(trayHandle, NULL, L"TrayNotifyWnd", NULL);
16         if (trayHandle) {
17             HWND hwnd = FindWindowEx(trayHandle, NULL, L"SysPager", NULL);
18             if (hwnd) {
19                 hwnd = FindWindowEx(hwnd, NULL, L"ToolbarWindow32", NULL);
20                 if (hwnd)
21                     trayHandle = hwnd;
22             }
23         }
24     }
25 
26     if (!trayHandle)
27         return ret;
28 
29     GetWindowThreadProcessId(trayHandle, &processID);
30     if (processID <= 0)
31         return ret;
32 
33     HANDLE trayProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ, 0, processID);
34     if (!trayProcess)
35         return ret;
36 
37     int buttonCount = SendMessage(trayHandle, TB_BUTTONCOUNT, 0, 0);
38     LPVOID data = VirtualAllocEx(trayProcess, NULL, sizeof(TBBUTTON), MEM_COMMIT, PAGE_READWRITE);
39 
40     if (buttonCount < 1 || !data) {
41         CloseHandle(trayProcess);
42         return ret;
43     }
44 
45     //search for our icon among all toolbar buttons
46     for (int toolbarButton = 0; toolbarButton < buttonCount; ++toolbarButton) {
47         SIZE_T numBytes = 0;
48         AppData appData = { 0, 0 };
49         SendMessage(trayHandle, TB_GETBUTTON, toolbarButton, (LPARAM)data);
50 
51         if (!ReadProcessMemory(trayProcess, data, &buttonData, sizeof(TBBUTTON), &numBytes))
52             continue;
53 
54         if (!ReadProcessMemory(trayProcess, (LPVOID)buttonData.dwData, &appData, sizeof(AppData), &numBytes))
55             continue;
56 
57         bool isHidden = buttonData.fsState & TBSTATE_HIDDEN;
58 
59         if (m_NotifyIconData.hWnd == appData.hwnd && appData.uID == m_NotifyIconData.uID && !isHidden) {
60             SendMessage(trayHandle, TB_GETITEMRECT, toolbarButton, (LPARAM)data);
61             RECT iconRect = { 0, 0, 0, 0 };
62             if (ReadProcessMemory(trayProcess, data, &iconRect, sizeof(RECT), &numBytes)) {
63                 MapWindowPoints(trayHandle, NULL, (LPPOINT)&iconRect, 2);
64                 QRect geometry(iconRect.left + 1, iconRect.top + 1,
65                     iconRect.right - iconRect.left - 2,
66                     iconRect.bottom - iconRect.top - 2);
67                 if (geometry.isValid())
68                     ret = geometry;
69                 break;
70             }
71         }
72     }
73     VirtualFreeEx(trayProcess, data, 0, MEM_RELEASE);
74     CloseHandle(trayProcess);

以上代码我在xp、win7、iwn10上测试过,都没有问题。 我没有为本文提供演示。 最近太忙了,没时间整理。 之所以记录这些,是为了整理一下思路,帮助一些有问题的同学。看完这篇文章,一个自定义托盘逻辑的实现基本走完了,剩下的就是qwidget的大量应用,以及界面美化。

.