QT事件詳解
1.事件處理介紹
Qt 程序需要在
main()
函數創建一個QCoreApplication
對象,然後調用它的exec()
函數。這個函數就是開始 Qt 的事件循環。在執行exec()
函數之後,程序將進入事件循環來監聽應用程序的事件。當事件發生時,Qt 將創建一個事件對象。Qt 中所有事件類都繼承於QEvent
。在事件對象創建完畢後,Qt 將這個事件對象傳遞給QObject
的event()
函數。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()方法處理自定義事件
但是,自定義事件用在何處?具體什麼用處?還不得而知