程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 淺談Qt事件的路由機制:鼠標事件,淺談qt

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

編輯:C++入門知識

淺談Qt事件的路由機制:鼠標事件,淺談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信號並沒有被觸發。

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

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved