請注意,本文是探討文章而不是教程,是根據實驗和分析得出的結果,可能是錯的,因此歡迎別人來探討和糾正。
這幾天對於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信號並沒有被觸發。
今天就到這吧,我寫本文的原因不是教別人,而是闡述疑惑與觀點,因為一句話說不清,在這裡求教育求指正,有什麼錯誤盡管提,謝謝各位了。
過幾天再和大家討論下鍵盤事件的路由,拖放事件的看法,以及過濾器的一些疑惑與測試。