QT鼠标消息分析
本文主要探索以下几个知识点:
1.setMouseTracking的使用
2.widget的鼠标消息会上发给父窗口,其机制是怎样的,怎么阻止这种行为(WA_NoMousePropagation的使用)
3.WA_Hover有什么用,为什么有时需要这个.
4.和Win32窗口编程的一些区别(不熟悉Win32编程的自动略过)
先看看我们要测试的程序的样子:
如上图所示,控件的父子关系为:
青色对应的类为MyChildWidget
紫色对应的类为MoveableWidget
灰色对应的类为MainWindow也就是我们的主窗口.
接下来我们一步一步地,先把框架代码写好在项目上点右键,选择添加新文件
在弹出的对话框中选C++ C++ Class Choose...然后输入MoveableWidget,并从QWidget继承,如下图:
同样的手法,添加MyChildWidget
去到QT设计界面,拖一个Widget控件上去,修改下大小,并右键单击此控件,选择"提升为..."
提升为MoveableWidget,如下图:
在再拖动一个Widget控件到MoveableWidget里,修改下大小,显得小一些,然后点右键,选择"提升为..."
同样的手法,提升为MyChildWidget,最终的样子如下图:
到此,我们的架构就搭建完成了.
给MoveableWidget涂成紫色,继承函数paintEvent
void MoveableWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter p(this);
p.setPen(Qt::NoPen);
p.setBrush(Qt::darkMagenta);
p.drawRect(rect());
}
同样的手法给MyChildWidget涂成青色
void MyChildWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter p(this);
p.setPen(Qt::NoPen);
p.setBrush(Qt::cyan);
p.drawRect(rect());
}
运行一下,显示画面如下:
A)测试setMouseTracking
我们先修改MoveableWidget
构造函数中加入setMouseTracking(true);
MoveableWidget::MoveableWidget(QWidget *parent) : QWidget(parent)
{
setMouseTracking(true);
}
打印mouseMoveEvent
void MoveableWidget::mouseMoveEvent(QMouseEvent *event)
{
qDebug("MoveableWidget::mouseMoveEvent: x=%d, y=%d\n", event->x(), event->y());
}
注意,我并没有在MainWindow中测试,原因见:https://www.cnblogs.com/xingzaicpp/p/16669049.html
编译运行,我们可以看出
1)当鼠标移到MoveableWidget上,会出现打印,这表明setMouseTracking起作用了.
2)当鼠标移动到子窗口MyChildWidget时,打印停止
3)当鼠标移动到主窗口上,打印停止
B)测试WA_Hover
在A)测试中,我们注意到了一点,当鼠标移动到子窗口MyChildWidget上,打印停止,
如果我们想要这样的结果: 就算移动到子Widget上,MoveableWidget依然可以收到鼠标消息事件.
那么就需要WA_Hover出场了
上面是官方的说明,似乎没有说明个啥出来,我来补充下:
设置WA_Hover后,就会收到三种消息:
QEvent::HoverEnter //鼠标进入到控件
QEvent::HoverLeave //鼠标离开控件
QEvent::HoverMove //鼠标在控件上移动.
我们给MoveableWidget加上WA_Hover属性,代码如下:
MoveableWidget::MoveableWidget(QWidget *parent) : QWidget(parent)
{
setAttribute(Qt::WA_Hover, true);
setMouseTracking(true);
}
然后,继承event函数,增加一些打印:
bool MoveableWidget::event(QEvent *e)
{
if ( e->type() == QEvent::HoverEnter
|| e->type() == QEvent::HoverLeave
|| e->type() == QEvent::HoverMove )
{
QHoverEvent* pHoverEvent = static_cast<QHoverEvent *>(e);
const char* pType = "QEvent::HoverMove";
if (e->type() == QEvent::HoverEnter)
{
pType = "QEvent::HoverEnter";
}else if (e->type() == QEvent::HoverLeave)
{
pType = "QEvent::HoverLeave";
}
qDebug("type=%s (%d, %d)\n", pType, pHoverEvent->pos().x(), pHoverEvent->pos().y());
}
return QWidget::event(e);
}
编译运行,我们可以看出
1)当鼠标移到MoveableWidget上,Hover的消息会先打印,然后会打印mouseMoveEvent.
2)当鼠标移动到子窗口MyChildWidget时,只会打印Hover的消息
3)当鼠标移动到主窗口上,打印停止
4).字如其意,HoverEnter HoverMove HoverLeave是有规律的,一般来说先Enter,再Move,再Leave
C)让MoveableWidget可以被拖动
这里我们需要用到move函数,对于子Widget而言,要想拖动,必须使用父窗口的坐标系,所以需要mapToParent进行坐标转换.
完整的代码如下,拖动原理请自行慢慢体会.
void MoveableWidget::mousePressEvent(QMouseEvent *event)
{
qDebug("MoveableWidget::mousePressEvent: x=%d, y=%d\n", event->x(), event->y());
if (event->button() == Qt::LeftButton)
{
m_pointClickPos = event->pos();
m_bCanMove = true;
}
}
void MoveableWidget::mouseMoveEvent(QMouseEvent *event)
{
qDebug("MoveableWidget::mouseMoveEvent: x=%d, y=%d\n", event->x(), event->y());
if (m_bCanMove && (event->buttons() == Qt::LeftButton))
{
QPoint diff = event->pos() - m_pointClickPos;
qDebug("diff(%d, %d)\n", diff.x(), diff.y());
QPoint ptMove = mapToParent(diff);
qDebug("move(%d, %d)\n", ptMove.x(), ptMove.y());
move(ptMove);
}
}
void MoveableWidget::mouseReleaseEvent(QMouseEvent *event)
{
qDebug("MoveableWidget::mouseMoveEvent: x=%d, y=%d\n", event->x(), event->y());
m_bCanMove = false;
}
编译运行,我们可以看出
1)当鼠标点击在MoveableWidget上,按住可以实现拖动.
2)当鼠标点击在子窗口MyChildWidget时,按住也可以实现拖动,神奇
D)证实子窗口可以把鼠标消息发给父亲窗口
通过C)的测试,我们已经可以看出,子窗口可以发鼠标消息给窗口.这里我们要关注一个属性WA_NoMousePropagation,为此,我们在MyChildWidget加入如下的代码:
MyChildWidget::MyChildWidget(QWidget *parent) : QWidget(parent)
{
bool b = testAttribute(Qt::WA_NoMousePropagation);
qDebug("b = %d\n", b);
setAttribute(Qt::WA_NoMousePropagation, !b);
}
打印出的信息为:
b = 0为此,我们知道,默认情况WA_NoMousePropagation为0,表示会把鼠标消息发给父窗口
如果把WA_NoMousePropagation设置为1, 则不会把鼠标消息发给父亲窗口了.
编译运行,我们可以看出:
1)当鼠标移动到子窗口MyChildWidget时,没有Hover的鼠标消息了
2)当鼠标点击在子窗口MyChildWidget时,也无法拖动.
这充分说明,MyChildWidget没有把鼠标消息发给父亲窗口MoveableWidget
E)测试MyChildWidget中加入鼠标处理函数
去掉对Qt::WA_NoMousePropagation属性的修改,也就是默认给父亲窗口传坐标
E.1: MyChildWidget只继承mousePressEvent
void MyChildWidget::mousePressEvent(QMouseEvent *event)
{
qDebug("MyChildWidget::mousePressEvent: x=%d, y=%d\n", event->x(), event->y());
}
编译运行,我们可以看出:
1)当鼠标移动到子窗口MyChildWidget时,Hover消息正常
2)当鼠标点击在子窗口MyChildWidget时,也无法拖动.
于是,我们可以大胆的猜测,如果MyChildWidget处理了鼠标消息,那么就不会给MoveableWidget处理了.
E.2: MyChildWidget继承mousePressEvent/mouseMoveEvent/mouseReleaseEvent
void MyChildWidget::mousePressEvent(QMouseEvent *event)
{
qDebug("MyChildWidget::mousePressEvent: x=%d, y=%d\n", event->x(), event->y());
}
void MyChildWidget::mouseMoveEvent(QMouseEvent *event)
{
qDebug("MyChildWidget::mouseMoveEvent: x=%d, y=%d\n", event->x(), event->y());
}
void MyChildWidget::mouseReleaseEvent(QMouseEvent *event)
{
qDebug("MyChildWidget::mouseReleaseEvent: x=%d, y=%d\n", event->x(), event->y());
}
编译运行,我们可以看出:
1)当鼠标移动到子窗口MyChildWidget时,Hover消息正常
2)当鼠标点击在子窗口MyChildWidget时,也无法拖动.
E.3: MyChildWidget继承mousePressEvent/mouseMoveEvent/mouseReleaseEvent,但加上event->ignore();
void MyChildWidget::mousePressEvent(QMouseEvent *event)
{
qDebug("MyChildWidget::mousePressEvent: x=%d, y=%d\n", event->x(), event->y());
event->ignore();
}
void MyChildWidget::mouseMoveEvent(QMouseEvent *event)
{
qDebug("MyChildWidget::mouseMoveEvent: x=%d, y=%d\n", event->x(), event->y());
event->ignore();
}
void MyChildWidget::mouseReleaseEvent(QMouseEvent *event)
{
qDebug("MyChildWidget::mouseReleaseEvent: x=%d, y=%d\n", event->x(), event->y());
event->ignore();
}
编译运行,我们可以看出:
1)当鼠标移动到子窗口MyChildWidget时,Hover消息正常
2)当鼠标点击在子窗口MyChildWidget时,可以拖动.
充分说明, MyChildWidget调用event->ignore();后,和他没有继承处理鼠标消息的行为是一样的.
F)和Win32编程的区别(不熟悉Win32编程的请忽略)
Win32编程,对于一个窗口来说,您不并需要调用类似setMouseTracking这样的函数,默认就会收到WM_MOUSEMOVE消息.
Win32编程,如果一个窗口想要获取窗口外的鼠标消息,需要在鼠标点击时调用SetCapture,鼠标抬起或者WM_CAPTURECHANGED时调用ReleaseCapture
Win32编程,当你用ALT+TAB切换任务时,通常,你的鼠标焦点会丢失.
QT编程, 看起来, WIDGET已经为你调用了SetCapture,而且,当你用ALT+TAB切换任务时,鼠标消息还是发给你的.来个图说明下
[思考]
Qt开发中触发鼠标悬停事件
请参考https://blog.csdn.net/chinley/article/details/95404282
代码见: http://q1024.com/files/qt_window-master.zip 000600目录
本文来自博客园,作者:xingzaicpp,转载请注明原文链接:https://www.cnblogs.com/xingzaicpp/p/16669060.html