【Qt筆記】事件

事件(event)是由系統或者 Qt 自己在不一樣的時刻發出的。當用戶按下鼠標、敲下鍵盤,或者是窗口須要從新繪製的時候,都會發出一個相應的事件。一些事件在對用戶操做作出響應時發出,如鍵盤事件等;另外一些事件則是由系統自動發出,如計時器事件。框架

 

事件也就是咱們一般說的「事件驅動(event drive)」程序設計的基礎概念。事件的出現,使得程序代碼不會按照原始的線性順序執行。想一想看,從最初的 C 語言開始,咱們的程序就是以一種線性的順序執行代碼:這一條語句執行以後,開始執行下一條語句;這一個函數執行事後,開始執行下一個函數。這種相似「批處理」的程序設計風格顯然不適合於處理複雜的用戶交互。咱們來想象一下用戶交互的情景:咱們設計了一堆功能放在界面上,用戶點擊了「打開文件」,因而開始執行打開文件的操做;用戶點擊了「保存文件」,因而開始執行保存文件的操做。咱們不知道用戶究竟想進行什麼操做,所以也就不能預測接下來將會調用哪個函數。若是咱們設計了一個「文件另存爲」的操做,若是用戶不點擊,這個操做將永遠不會被調用。這就是所謂的「事件驅動」,咱們的程序的執行順序再也不是線性的,而是由一個個事件驅動着程序繼續執行。沒有事件,程序將阻塞在那裏,不執行任何代碼。函數

在 Qt 中,事件的概念彷佛同信號槽相似。的確如此,通常來講,使用 Qt 組件時,咱們並不會把主要精力放在事件上。由於在 Qt 中,咱們關心的更多的是事件關聯的一個信號。好比,對於QPushButton的鼠標點擊,咱們不須要關心這個鼠標點擊事件,而是關心它的clicked()信號的發出。這與其餘的一些 GUI 框架不一樣:在 Swing 中,你所要關心的是JButtonActionListener這個點擊事件。由此看出,相比於其餘 GUI 框架,Qt 給了咱們額外的選擇:信號槽。this

可是,Qt 中的事件和信號槽卻並非能夠相互替代的。信號由具體的對象發出,而後會立刻交給由connect()函數鏈接的槽進行處理;而對於事件,Qt 使用一個事件隊列對全部發出的事件進行維護,當新的事件產生時,會被追加到事件隊列的尾部。前一個事件完成後,取出後面的事件進行處理。可是,必要的時候,Qt 的事件也能夠不進入事件隊列,而是直接處理。信號一旦發出,對應的槽函數必定會被執行。可是,事件則可使用「事件過濾器」進行過濾,對於有些事件進行額外的處理,另外的事件則不關心。總的來講,若是咱們使用組件,咱們關心的是信號槽;若是咱們自定義組件,咱們關心的是事件。由於咱們能夠經過事件來改變組件的默認操做。好比,若是咱們要自定義一個可以響應鼠標事件的EventLabel,咱們就須要重寫QLabel的鼠標事件,作出咱們但願的操做,有可能還得在恰當的時候發出一個相似按鈕的clicked()信號(若是咱們但願讓這個EventLabel可以被其它組件使用)或者其它的信號。設計

在前面咱們也曾經簡單提到,Qt 程序須要在main()函數建立一個QCoreApplication對象,而後調用它的exec()函數。這個函數就是開始 Qt 的事件循環。在執行exec()函數以後,程序將進入事件循環來監聽應用程序的事件。當事件發生時,Qt 將建立一個事件對象。Qt 中全部事件類都繼承於QEvent。在事件對象建立完畢後,Qt 將這個事件對象傳遞給QObjectevent()函數。event()函數並不直接處理事件,而是按照事件對象的類型分派給特定的事件處理函數(event handler)。關於這一點,咱們會在之後的章節中詳細說明。code

在全部組件的父類QWidget中,定義了不少事件處理的回調函數,如keyPressEvent()keyReleaseEvent()mouseDoubleClickEvent()mouseMoveEvent()mousePressEvent()mouseReleaseEvent()等。這些函數都是 protected virtual 的,也就是說,咱們能夠在子類中從新實現這些函數。下面來看一個例子:對象

#include <QApplication>
#include <QMouseEvent>
#include <QLabel>

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);
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    EventLabel *label = new EventLabel;
    label->setWindowTitle("MouseEvent Demo");
    label->resize(300, 200);
    label->show();

    return a.exec();
}

咱們編譯運行上面的代碼,就能夠理解到有關事件的使用方法。繼承

EventLabel繼承了QLabel,覆蓋了mousePressEvent()mouseMoveEvent()MouseReleaseEvent()三個函數。咱們並無添加什麼功能,只是在鼠標按下(press)、鼠標移動(move)和鼠標釋放(release)的時候,把當前鼠標的座標值顯示在這個Label上面。因爲QLabel是支持 HTML 代碼的,所以咱們直接使用了 HTML 代碼來格式化文字。隊列

QStringarg()函數能夠自動替換掉QString中出現的佔位符。其佔位符以 % 開始,後面是佔位符的位置,例如 %1,%2 這種。事件

QString("[%1, %2]").arg(x, y);

語句將會使用 x 替換 %1,y 替換 %2,所以,這個語句生成的QString爲 [x, y]。get

mouseReleaseEvent()函數中,咱們使用了另一種QString的構造方法。咱們使用相似 C 風格的格式化函數sprintf()來構造QString

運行上面的代碼,當咱們點擊了一下鼠標以後,label 上將顯示鼠標當前座標值。

爲何要點擊鼠標以後才能在mouseMoveEvent()函數中顯示鼠標座標值?這是由於QWidget中有一個mouseTracking屬性,該屬性用於設置是否追蹤鼠標。只有鼠標被追蹤時,mouseMoveEvent()纔會發出。若是mouseTracking是 false(默認便是),組件在至少一次鼠標點擊以後,纔可以被追蹤,也就是可以發出mouseMoveEvent()事件。若是mouseTracking爲 true,則mouseMoveEvent()直接能夠被髮出。知道了這一點,咱們就能夠在main()函數中直接設置下:

EventLabel *label = new EventLabel;
label->setWindowTitle("MouseEvent Demo");
label->resize(300, 200);
label->setMouseTracking(true);
label->show();

這樣子就沒有這個問題了。

相關文章
相關標籤/搜索