QT事件詳解

QT事件詳解


1.事件處理介紹

​ Qt 程序需要在main()函數創建一個QCoreApplication對象,然後調用它的exec()函數。這個函數就是開始 Qt 的事件循環。在執行exec()函數之後,程序將進入事件循環來監聽應用程序的事件。當事件發生時,Qt 將創建一個事件對象。Qt 中所有事件類都繼承於QEvent。在事件對象創建完畢後,Qt 將這個事件對象傳遞給QObjectevent()函數。event()函數並不直接處理事件,而是按照事件對象的類型分派給特定的事件處理函數(event handler)。

#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QMouseEvent>
class EventLabel : public QLabel
{
protected:
        void mouseMoveEvent(QMouseEvent *event);     //聲明明三個事件函數
        void mousePressEvent(QMouseEvent *event);
        void mouseReleaseEvent(QMouseEvent *event);
};

void EventLabel::mouseMoveEvent(QMouseEvent *event)    //鼠標移動事件函數,顯示出鼠標的座標
{
    this->setText(QString("<center><h1>Move: (%1, %2)</h1></center>")
                          .arg(QString::number(event->x()), QString::number(event->y())));
}

void EventLabel::mousePressEvent(QMouseEvent *event)          //按下時,獲得鼠標座標
{
        this->setText(QString("<center><h1>Press: (%1, %2)</h1></center>")
                       .arg(QString::number(event->x()), QString::number(event->y())));
}

void EventLabel::mouseReleaseEvent(QMouseEvent *event)   //鬆開鼠標時,獲取鼠標的座標
{
        QString msg;
        msg.sprintf("<center><h1>Release: (%d, %d)</h1></center>",
                                event->x(), event->y());
        this->setText(msg);
}
//上述需要點擊鼠標移動才能出發mouseMoveEvent時間,可以設置爲什麼要點擊鼠標之後才能在mouseMoveEvent()函數中顯示鼠標座標值?這是因爲QWidget中有一個mouseTracking屬性,該屬性用於設置是否追蹤鼠標。只有鼠標被追蹤時,mouseMoveEvent()纔會發出。如果mouseTracking是 false(默認即是),組件在至少一次鼠標點擊之後,才能夠被追蹤,也就是能夠發出mouseMoveEvent()事件。如果mouseTracking爲 true,則mouseMoveEvent()直接可以被髮出
int main(int argc, char *argv[])
{
        QApplication app(argc, argv);
        EventLabel *label = new EventLabel;
        label->setWindowTitle("MouseEvent Demo");
        label->resize(300, 200);
    	label->setMouseTracking(true);
        label->show();
        return app.exec();
}

1.知識點:

//樣例QString.arg函數
this->setText(QString("<center><h1>Press: (%1, %2)</h1></center>")
              .arg(QString::number(event->x()), QString::number(event->y())));
//使用的是QString("[%1, %2]").arg(x, y);    
//使用 x 替換 %1,y 替換 %2,因此,這個語句生成的QString爲 [x, y]。
//樣例 static_cast<type-id>(expression)
//解釋:static_cast是一個c++運算符,功能是把一個表達式轉換爲某種類型,但沒有運行時類型檢查來保證轉換的安全性。  
int i;
float f = 166.97;
i = static_cast<int>(f);   //i值爲166

2.下面繼承分析

class CustomButton : public QPushButton
{
    Q_OBJECT
public:
    CustomButton(QWidget *parent) : QPushButton(parent)
    {
    }
protected:
    void mousePressEvent(QMouseEvent *event)
    {
        qDebug() << "CustomButton";
    }
};

class CustomButtonEx : public CustomButton
{
    Q_OBJECT
public:
    CustomButtonEx(QWidget *parent) : CustomButton(parent)
    {
    }
protected:
    void mousePressEvent(QMouseEvent *event)
    {
        qDebug() << "CustomButtonEx";
    }
};

class CustomWidget : public QWidget
{
    Q_OBJECT
public:
    CustomWidget(QWidget *parent) : QWidget(parent)
    {
    }
protected:
    void mousePressEvent(QMouseEvent *event)
    {
        qDebug() << "CustomWidget";
    }
};

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0) : QMainWindow(parent)
    {
        CustomWidget *widget = new CustomWidget(this);
        CustomButton *cbex = new CustomButton(widget);
        cbex->setText(tr("CustomButton"));
        CustomButtonEx *cb = new CustomButtonEx(widget);
        cb->setText(tr("CustomButtonEx"));
        QVBoxLayout *widgetLayout = new QVBoxLayout(widget);
        widgetLayout->addWidget(cbex);
        widgetLayout->addWidget(cb);
        this->setCentralWidget(widget);
    }
protected:
    void mousePressEvent(QMouseEvent *event)
    {
        qDebug() << "MainWindow";
    }
};

當CustButton繼承QPushButton;而CustButtonEx繼承CustButton;同時都在MainWidow中作爲控件顯示。僅僅作爲單機各個控件,都是僅僅打印自己重寫的mousePressEvent()事件;如果想要讓孩子同時可以執行父親相同的事件,需要重新添加父親的事件 CustButton::mousePressEvent(ent); 添加到孩子執行的事件函數中;

其次,還有另一中方案,就是使用 accept()ignore() 函數,其中 ent->accept() 是默認狀態,不做處理,僅僅執行自己重寫部分。 ignore() 說明我們想讓事件繼續傳播,所以就傳遞給父類組件,同時啓動父類的事件。

事件的傳播是在組件層次上面的,而不是依靠類繼承機制。

2.事件過濾器

準確地說事件過濾器就是在父類窗口上,查詢子類控件的事件的類型進而處理的一種方式

介紹:

這個函數返回一個 bool 類型,如果你想將參數 event 過濾出來,比如,不想讓它繼續轉發,就返回 true,否則返回 false。事件過濾器的調用時間是目標對象(也就是參數裏面的watched對象)接收到事件對象之前。也就是說,如果你在事件過濾器中停止了某個事件,那麼,watched對象以及以後所有的事件過濾器根本不會知道這麼一個事件。

class MainWindow : public QMainWindow 
{
public:
	MainWindow();
protected:
	bool eventFilter(QObject *obj, QEvent *event);
private:
	QTextEdit *textEdit;
}


MainWindow :: MainWindow()
{
    textEdit = new QTextEdit;
    setCentraWidget(textEdit);
    //installEventFilter 是由需要過濾的實例化對象調用的,以父類爲基礎
    textEdit->installEventFilter(this);
}

bool MainWindow :: eventFilter(QObject *obj, QEvent *event)
{
    if(obj == textEdit)
    {
        if(event->type() == QEvent::KeyPress)
        {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        	qDebug("Ate key press");
            return true;	
            //也就是過濾掉textEdit的KeyPress事件
        }
        else
        {
            return false;
        }
    }
    else
    {
        //pass the event on to the parent class
        return QMainWindow::eventFilter(obj, event);
    }
}

eventFilter()函數相當於創建了過濾器,然後我們需要安裝這個過濾器。安裝過濾器需要調用QObject::installEventFilter()`函數

//用於安裝過濾器函數
void QObject::installEventFilter(QObject *filterObj);
//用於移除過濾器
void QObject::removeEventFilter(QObject *filterObj);

如果我們向一個對象安裝多個事件處理器,只能調用多次 installEventFilter() 函數,如果一個對象存在多個事件過濾器,那麼最後一個安裝的第一個執行,也就是後進先執行的順序。

注意:

注意,如果你在事件過濾器中 delete 了某個接收組件,務必將函數返回值設爲 true。否則,Qt 還是會將事件分發給這個接收組件,從而導致程序崩潰

事件過濾器和被安裝過濾器的組件必須在同一線程,否則,過濾器將不起作用。另外,如果在安裝過濾器之後,這兩個組件到了不同的線程,那麼,只有等到二者重新回到同一線程的時候過濾器纔會有效

3.自定義事件舉例

自定義事件處理流程

首先,繼承QEvent;

其次,定義事件類型,通過registerEventType()函數進行自動創建一個全局唯一的事件類型;

再其次,使用sendEvent()[同步處理] 和 postEvent()函數[異步處理] 發送事件;

最後,重寫QObject::event()方法處理自定義事件

但是,自定義事件用在何處?具體什麼用處?還不得而知

參考網址:自定義事件

參考網址:自定義事件-程序已實現

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章