問題及解決方案
當用USB在電腦上插拔,自制的簡易串列埠助手中串列埠名稱不會實時更新,因此為了實現更新串列埠名,這裡記錄一下實現過程
解決方案:將Windows的裝置管理訊息傳送給QT進行處理(需要包含windows.h
),自定義子類繼承QWidget、QAbstractNativeEventFilter,QAbstractNativeEventFilter類中的純虛擬函式bool nativeEventFilter()
如果使用重寫[virtual protected] bool QWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)
,並不會接收到來的Windows的訊息,nativeEvent
有bug。引用:https://stackoverflow.com/questions/26652783/qtnativeevent-calls
First: you don't need to call
nativeEvent
method directly. It is a callback that is called by Qt. You may override it.Second. There are known bugs in Qt5 with processing
nativeEvent
. So be careful with it. But, as I know, there are problemd only with native child widgets.Second. There are known bugs in Qt5 with processing
nativeEvent
. So be careful with it. But, as I know, there are problemd only with native child widgets.Third. Solution: create your own
QAbstractNativeEventFilter
. Hint, how to use it (because it is not well-documented):QAbstractEventDispatcher::instance()->installNativeEventFilter(yourFilter);
原始碼
myeventfilter.h的內容如下:
#ifndef MYEVENTFILTER_H
#define MYEVENTFILTER_H
#include <QAbstractNativeEventFilter>
#include <QWidget>
class MyNativeFilter : public QWidget, public QAbstractNativeEventFilter
{
Q_OBJECT
public:
MyNativeFilter();
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);
signals:
void DeviceChanged();
};
#endif // MYEVENTFILTER_H
這裡繼承QWidget是為了使用訊號與槽機制,新增了宏Q_OBJECT
,需要重新Build(構建)一下專案,否則會出現錯誤:error: undefined reference to
vtable for`。有關QAbstractNativeEventFilter的介紹,參照官方文件:https://doc.qt.io/qt-5/qabstractnativeeventfilter.html
myeventfilter.cpp的內容如下,必須需要包含標頭檔案windows.h
、dbt.h
#include "myeventfilter.h"
#include <windows.h>
#include <dbt.h>
MyNativeFilter::MyNativeFilter()
{
}
bool MyNativeFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
Q_UNUSED(eventType);
Q_UNUSED(result);
MSG *msg = static_cast<MSG*>(message);
if(msg->message == WM_DEVICECHANGE)
{
if(msg->wParam == DBT_DEVICEARRIVAL ||
msg->wParam == DBT_DEVICEREMOVECOMPLETE) //新增了裝置或移除了裝置
{
emit DeviceChanged(); //發出裝置修改的訊號
}
}
return false;
}
MSG是在winuser.h
中宣告的結構體
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
DWORD lPrivate;
} MSG, *PMSG, *NPMSG, *LPMSG;
這裡只說明message和wParam兩個結構體成員
- message:型別:UINT,訊息的識別符號。 應用程式只能使用低字;高字由系統保留。
- wParam:型別WPARAM,關於訊息的附加資訊。 確切含義取決於訊息成員的值。
message的取值:Wm/裝置管理訊息 https://learn.microsoft.com/zh-cn/windows/win32/winmsg/about-messages-and-message-queues
wParam的取值:WM_DEVICECHANGE 訊息 (Winuser.h) - Win32 apps | Microsoft Learn
微軟官網文件:https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/ns-winuser-msg
官方文件上寫到:在重新實現此功能時,如果您想過濾掉訊息,即停止進一步處理,請返回true;否則返回false。因為這裡需要進一步處理msg,所以這裡返回false。
In your reimplementation of this function, if you want to filter the message out, i.e. stop it being handled further, return true; otherwise return false.
使用方法
自定義類SerialPortWidget,在serialportidget.cpp中實現,serialportidget.cpp部分程式碼如下:
#include <QCoreApplication>
#include "myeventfilter.h" //包含事件過濾器標頭檔案
MyNativeFilter *nativefilter = new MyNativeFilter;
qApp->installNativeEventFilter(nativefilter); //設定本地事件過濾器
//qApp是一個宏,等價於QCoreApplication::instance()
//從MyNativeFilter類中發射出的訊號DeviceChanged,在serialportidget.cpp中進行訊號的處理
connect(nativefilter, &MyNativeFilter::DeviceChanged, [=]{
if(serialport->isOpen())
serialport->close();
//獲取串列埠資訊,進行串列埠名重新整理
QList<QSerialPortInfo> infos = QSerialPortInfo::availablePorts();
serialPortComboBox->clear();
for (const QSerialPortInfo &info : infos) //foreach遍歷串列埠資訊
serialPortComboBox->addItem(info.portName()); //獲取串列埠名
});
執行效果
參考連結:
https://stackoverflow.com/questions/26652783/qtnativeevent-calls
https://blog.csdn.net/zzzw0/article/details/104367345
https://blog.csdn.net/u010168781/article/details/105298677