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://netpt.net/forum.php?mod=viewthread&tid=65
編譯執行,我們可以看出
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
程式碼見:https://gitee.com/flash008/qt_window/tree/master/000600/
相關文章
- Qt Quick 訊息列表檢視元件QTUI元件
- QStyledItemDelegate 和QTreeView實現滑鼠hover訊息QTView
- Qt實現系統托盤訊息QT
- 鍵盤控制滑鼠 windows QtWindowsQT
- 【RocketMQ】訊息拉模式分析MQ模式
- dotnet X11 視窗之間傳送滑鼠訊息 模擬滑鼠輸入
- Giraph原始碼分析(三)—— 訊息通訊原始碼
- 分散式訊息通訊Kafka(二) - 原理分析分散式Kafka
- 訊息佇列常見問題分析佇列
- 深入訊息中介軟體選型分析
- RabbitMQ,RocketMQ,Kafka 訊息模型對比分析MQKafka模型
- 好訊息 OR 壞訊息
- Qt監聽Windows鎖屏、解鎖、休眠、喚醒、登入、登出訊息QTWindows
- 訊息中介軟體(RabbitMq、Kafka)分析比較MQKafka
- 原始碼分析:Android訊息處理機制原始碼Android
- 【RocketMQ原始碼分析】深入訊息儲存(2)MQ原始碼
- 【RocketMQ原始碼分析】深入訊息儲存(3)MQ原始碼
- [原始碼分析] 訊息佇列 Kombu 之 Hub原始碼佇列
- [原始碼分析] 訊息佇列 Kombu 之 Consumer原始碼佇列
- 訊息機制篇——初識訊息與訊息佇列佇列
- RocketMQ 訊息整合:多型別業務訊息-普通訊息MQ多型型別
- Qt 判斷滑鼠在某一控制元件上QT控制元件
- 【Qt開發】實現系統托盤,托盤選單,托盤訊息QT
- Giraph原始碼分析(七)—— 新增訊息統計功能原始碼
- RocketMQ中PullConsumer的訊息拉取原始碼分析MQ原始碼
- RocketMQ中Broker的訊息儲存原始碼分析MQ原始碼
- 從原始碼分析RocketMq訊息的儲存原理原始碼MQ
- Kafka原始碼分析(三) - Server端 - 訊息儲存Kafka原始碼Server
- RocketMQ 訊息整合:多型別業務訊息——定時訊息MQ多型型別
- Qt ModbusTCP通訊QTTCP
- Qt usb通訊QT
- vscode原始碼分析【七】主程式啟動訊息通訊服務VSCode原始碼
- 利用redis的hash結構搭建訊息服務(發訊息,訂閱訊息,消費訊息,退訂)Redis
- RocketMQ 原理:訊息儲存、高可用、訊息重試、訊息冪等性MQ
- 訊息中介軟體—RocketMQ訊息消費(三)(訊息消費重試)MQ
- node事件迴圈和訊息佇列簡單分析事件佇列
- MQTT-保留訊息和遺囑訊息MQQT
- 訊息中介軟體—RocketMQ訊息傳送MQ