淺談Qt事件的路由機制:鼠標事件


  請注意,本文是探討文章而不是教程,是根據實驗和分析得出的結果,可能是錯的,因此歡迎別人來探討和糾正。

  這幾天對於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信號並沒有被觸發。

  今天就到這吧,我寫本文的原因不是教別人,而是闡述疑惑與觀點,因為一句話說不清,在這里求教育求指正,有什么錯誤盡管提,謝謝各位了。

  過幾天再和大家討論下鍵盤事件的路由,拖放事件的看法,以及過濾器的一些疑惑與測試。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM