請注意,本文是探討文章而不是教程,是根據實驗和分析得出的結果,可能是錯的,因此歡迎別人來探討和糾正。
這幾天對於Qt的事件較為好奇,平時並不怎麼常用,一般都是用信號,對於事件的處理,一般都是需要響應鍵盤按鍵事件的時候,也用得毫無問題,因此也沒怎麼注意過,翻了下一般qt的教材《精通Qt4編程(第二版)》,裡面12.1是這麼說的。
當用戶按下一個鼠標鍵時,這個事件首先被發給當前擁有焦點的窗口部件。
看到這裡,我第一反應是,真的是這樣嗎,我表示十分地好奇,於是就趕忙試驗了一下。代碼比較簡單,沒有注釋,相信都能看懂,main函數省略,沒什麼改動。
父窗口.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
namespace Ui {
class Dialog;
}
class myButton;
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
myButton* btn;protected:
void mousePressEvent(QMouseEvent *);
// bool eventFilter(QObject *, QEvent *);
};
#endif // DIALOG_H
父窗口.CPP
#include "dialog.h"
#include "ui_dialog.h"
#include "mybutton.h"
#include <QApplication>
#include <QDebug>
#include <QMouseEvent>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
btn=new myButton(tr("測試父按鈕"),this);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::mousePressEvent(QMouseEvent *e)
{
qDebug()<<"void Dialog::mousePressEvent(QMouseEvent *)";
}
子窗口.h
#ifndef MYBUTTON_H
#define MYBUTTON_H
#include <QPushButton>
class myButton : public QPushButton
{
Q_OBJECT
public:
myButton(const QString &str,QWidget *parent);
signals:
public slots:
protected:
void mousePressEvent(QMouseEvent *e);
};
#endif // MYBUTTON_H
父窗口.cpp
#include "mybutton.h"
#include <QEvent>
#include <QDebug>
myButton::myButton(const QString &str, QWidget *parent) :
QPushButton(str,parent)
{
}
void myButton::mousePressEvent(QMouseEvent *e)
{
qDebug()<<"void myButton::mousePressEvent(QMouseEvent *e)";
}
子窗口和父窗口都安裝了鼠標按下事件的響應函數,好了,試一下吧。

結果是這樣的,可以看到,是父窗口的事件處理器被觸發了,而焦點窗口應該是子窗口的按鈕,那麼是否是Qt的某些機制,使得鼠標按鍵造成了焦點的轉移呢,難道其實焦點在按下鼠標之後轉移到了父窗口上?還是還試一下吧。
把父窗口和子窗口的mousePressEvent修改為
qDebug()<<"void Dialog::mousePressEvent(QMouseEvent *)"<<hasFocus();
qDebug()<<"void myButton::mousePressEvent(QMouseEvent *e)"<<hasFocus();
再來運行下,看下結果

可以看到,父窗口依舊是沒有焦點的,所以上面的猜測是錯誤的嗎?
繼續向自己提問,是否是因為父窗口的原因呢,如果鼠標按下的是子窗口會怎麼樣,繼續嘗試
在父窗口的構造函數最後增加一行代碼
setFocus();
繼續嘗試,在子窗口按鈕上按下鼠標,會是什麼結果呢。
結果很奇怪,在父窗口中點擊鼠標,確實是獲得焦點的,然後再點子窗口,按鈕子窗口也獲得了焦點,然後再點父窗口,再也獲得不到焦點了。
繼續寫代碼測試,這次子類化一個QWidget,然後重寫mousePressEvent,因為QWidget默認是分發事件的,因此稍作修改,讓他阻斷事件。
void myWidget::mousePressEvent(QMouseEvent *e)
{
e->accept();
qDebug()<<"void myWidget::mousePressEvent(QMouseEvent *e)"<<hasFocus();
}
然後新窗口構造函數中對新窗口稍微修改下樣式,以區分2個窗口
QHBoxLayout *layout=new QHBoxLayout;
QLabel *label=new QLabel(tr("新窗口"));
layout->addWidget(label);
setLayout(layout);
resize(100,100);
在父窗口中注釋掉原來的button子窗口,new一個新的widget子窗口,現在父窗口和子窗口都是QWidget
w=new myWidget(this);
w->setStyleSheet("background-color:red;");
運行下看看,發現兩個窗口都沒有焦點

在父窗口構造函數中加上
setFocus();
然後交叉點擊父窗口和子窗口,你會發現焦點永遠在父窗口上,如果setFous在子窗口,就永遠在子窗口,如果都加了,就按照代碼順序。

可見,這是按鈕一個默認機制,焦點的奪回,如果別的窗體設置了焦點,然後點擊鼠標按下按鈕,按鈕會奪回焦點,再也不還了,除非你再次設置焦點。
再次嘗試下,發現即使焦點在子窗口,點擊父窗口,消息不會路由到子窗口上。
在這裡得出一個結論,鼠標事件的獲得與處理,是根據鼠標點擊的窗體所決定的,而不是像書本上說的路由到焦點所在窗體(鍵盤事件則確實如此,以後再談),如果子窗口處理鼠標事件的時候,使用ignore分發(默認是accept截獲),會再向父窗口傳遞,直到事件被截獲或者到達頂層窗口。
QPushButton等有click信號的窗體控件,如果處理mousePressEvent,click信號還會被響應嗎
父窗口的構造函數修改為
ui->setupUi(this);
//w=new myWidget(this);
//w->setStyleSheet("background-color:red;");
btn=new myButton(tr("測試父按鈕"),this);
父窗口的mousePressEvent修改為
void Widget::mousePressEvent(QMouseEvent *e)
{
qDebug()<<"void Widget::mousePressEvent(QMouseEvent *e)"<<hasFocus();
}
子窗口的按鈕構造函數為空白,mousePressEvent修改為(注意,此時子窗口已經變回按鈕了)
void myButton::mousePressEvent(QMouseEvent *e)
{
qDebug()<<"void myButton::mousePressEvent(QMouseEvent *e)";
}
然後給按鈕子窗口安裝一個槽函數,響應按鈕的cilck信號,槽函數自由發揮,能顯示是否被調用就行

可以看到,如果處理了鼠標按鍵後調用父類方法,是可以響應click消息的,如果注釋掉return,則沒有click消息,可以自行測試。
最後一個問題,我不調用父類方法,而是使用ignore分發,能有click消息嗎?
注釋掉return,如果你剛才沒注釋掉的話,在按鈕的mousePressEvent裡加上一句代碼
e->ignore();
然後運行程序,點擊按鈕

可以看見,父窗口收到了子窗口傳來的鼠標事件,但是click信號並沒有被觸發。
今天就到這吧,我寫本文的原因不是教別人,而是闡述疑惑與觀點,因為一句話說不清,在這裡求教育求指正,有什麼錯誤盡管提,謝謝各位了。
過幾天再和大家討論下鍵盤事件的路由,拖放事件的看法,以及過濾器的一些疑惑與測試。