QT实现串口助手中串口名的实时更新
问题及解决方案
当用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