9、QT基礎(chǔ)——Qt消息機(jī)制和事件

      網(wǎng)友投稿 1039 2025-03-31

      9.1 事件

      事件(event)是由系統(tǒng)或者 Qt 本身在不同的時(shí)刻發(fā)出的。當(dāng)用戶按下鼠標(biāo)、敲下鍵盤,或者是窗口需要重新繪制的時(shí)候,都會(huì)發(fā)出一個(gè)相應(yīng)的事件。一些事件在對(duì)用戶操作做出響應(yīng)時(shí)發(fā)出,如鍵盤事件等;另一些事件則是由系統(tǒng)自動(dòng)發(fā)出,如計(jì)時(shí)器事件。

      在前面我們也曾經(jīng)簡(jiǎn)單提到,Qt 程序需要在main()函數(shù)創(chuàng)建一個(gè)QApplication對(duì)象,然后調(diào)用它的exec()函數(shù)。這個(gè)函數(shù)就是開始 Qt 的事件循環(huán)。在執(zhí)行exec()函數(shù)之后,程序?qū)⑦M(jìn)入事件循環(huán)來(lái)監(jiān)聽?wèi)?yīng)用程序的事件。當(dāng)事件發(fā)生時(shí),Qt 將創(chuàng)建一個(gè)事件對(duì)象。Qt 中所有事件類都繼承于QEvent。在事件對(duì)象創(chuàng)建完畢后,Qt 將這個(gè)事件對(duì)象傳遞給QObject的event()函數(shù)。event()函數(shù)并不直接處理事件,而是按照事件對(duì)象的類型分派給特定的事件處理函數(shù)(event handler),關(guān)于這一點(diǎn),會(huì)在后邊詳細(xì)說(shuō)明。

      在所有組件的父類QWidget中,定義了很多事件處理的回調(diào)函數(shù),如

      keyPressEvent()

      keyReleaseEvent()

      mouseDoubleClickEvent()

      mouseMoveEvent()

      mousePressEvent()

      mouseReleaseEvent() 等。

      這些函數(shù)都是 protected virtual 的,也就是說(shuō),我們可以在子類中重新實(shí)現(xiàn)這些函數(shù)。下面來(lái)看一個(gè)例子:

      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("

      Move: (%1, %2)

      ").arg(QString::number(event->x()),

      QString::number(event->y())));

      }

      void EventLabel::mousePressEvent(QMouseEvent *event)

      {

      this->setText(QString("

      Press:(%1, %2)

      ").arg(QString::number(event->x()),

      QString::number(event->y())));

      }

      void EventLabel::mouseReleaseEvent(QMouseEvent *event)

      {

      QString msg;

      msg.sprintf("

      Release: (%d, %d)

      ",

      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()三個(gè)函數(shù)。我們并沒(méi)有添加什么功能,只是在鼠標(biāo)按下(press)、鼠標(biāo)移動(dòng)(move)和鼠標(biāo)釋放(release)的時(shí)候,把當(dāng)前鼠標(biāo)的坐標(biāo)值顯示在這個(gè)Label上面。由于QLabel是支持 HTML 代碼的,因此我們直接使用了 HTML 代碼來(lái)格式化文字。

      QString的arg()函數(shù)可以自動(dòng)替換掉QString中出現(xiàn)的占位符。其占位符以 % 開始,后面是占位符的位置,例如 %1,%2 這種。

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

      語(yǔ)句將會(huì)使用x替換 %1,y替換 %2,因此,生成的QString為[x, y]。

      在mouseReleaseEvent()函數(shù)中,我們使用了另外一種QString的構(gòu)造方法。我們使用類似 C 風(fēng)格的格式化函數(shù)sprintf()來(lái)構(gòu)造QString。

      運(yùn)行上面的代碼,當(dāng)我們點(diǎn)擊了一下鼠標(biāo)之后,label 上將顯示鼠標(biāo)當(dāng)前坐標(biāo)值。

      為什么要點(diǎn)擊鼠標(biāo)之后才能在mouseMoveEvent()函數(shù)中顯示鼠標(biāo)坐標(biāo)值?

      這是因?yàn)镼Widget中有一個(gè)mouseTracking屬性,該屬性用于設(shè)置是否追蹤鼠標(biāo)。只有鼠標(biāo)被追蹤時(shí),mouseMoveEvent()才會(huì)發(fā)出。如果mouseTracking是 false(默認(rèn)即是),組件在至少一次鼠標(biāo)點(diǎn)擊之后,才能夠被追蹤,也就是能夠發(fā)出mouseMoveEvent()事件。如果mouseTracking為 true,則mouseMoveEvent()直接可以被發(fā)出。

      知道了這一點(diǎn),我們就可以在main()函數(shù)中添加如下代碼:

      label->setMouseTracking(true);

      在運(yùn)行程序就沒(méi)有這個(gè)問(wèn)題了。

      9.2 event()

      事件對(duì)象創(chuàng)建完畢后,Qt 將這個(gè)事件對(duì)象傳遞給QObject的event()函數(shù)。event()函數(shù)并不直接處理事件,而是將這些事件對(duì)象按照它們不同的類型,分發(fā)給不同的事件處理器(event handler)。

      如上所述,event()函數(shù)主要用于事件的分發(fā)。所以,如果你希望在事件分發(fā)之前做一些操作,就可以重寫這個(gè)event()函數(shù)了。例如,我們希望在一個(gè)QWidget組件中監(jiān)聽 tab 鍵的按下,那么就可以繼承QWidget,并重寫它的event()函數(shù),來(lái)達(dá)到這個(gè)目的:

      bool CustomWidget::event(QEvent *e)

      {

      if (e->type() == QEvent::KeyPress) {

      QKeyEvent *keyEvent = static_cast(e);

      if (keyEvent->key() == Qt::Key_Tab) {

      qDebug() << "You press tab.";

      return true;

      }

      }

      return QWidget::event(e);

      }

      CustomWidget是一個(gè)普通的QWidget子類。我們重寫了它的event()函數(shù),這個(gè)函數(shù)有一個(gè)QEvent對(duì)象作為參數(shù),也就是需要轉(zhuǎn)發(fā)的事件對(duì)象。函數(shù)返回值是 bool 類型。

      如果傳入的事件已被識(shí)別并且處理,則需要返回 true,否則返回 false。如果返回值是 true,那么 Qt 會(huì)認(rèn)為這個(gè)事件已經(jīng)處理完畢,不會(huì)再將這個(gè)事件發(fā)送給其它對(duì)象,而是會(huì)繼續(xù)處理事件隊(duì)列中的下一事件。

      在event()函數(shù)中,調(diào)用事件對(duì)象的accept()和ignore()函數(shù)是沒(méi)有作用的,不會(huì)影響到事件的傳播。

      我們可以通過(guò)使用QEvent::type()函數(shù)可以檢查事件的實(shí)際類型,其返回值是QEvent::Type類型的枚舉。我們處理過(guò)自己感興趣的事件之后,可以直接返回 true,表示我們已經(jīng)對(duì)此事件進(jìn)行了處理;對(duì)于其它我們不關(guān)心的事件,則需要調(diào)用父類的event()函數(shù)繼續(xù)轉(zhuǎn)發(fā),否則這個(gè)組件就只能處理我們定義的事件了。為了測(cè)試這一種情況,我們可以嘗試下面的代碼:

      bool CustomTextEdit::event(QEvent *e)

      {

      if (e->type() == QEvent::KeyPress)

      {

      QKeyEvent *keyEvent = static_cast(e);

      if (keyEvent->key() == Qt::Key_Tab)

      {

      qDebug() << "You press tab.";

      return true;

      }

      }

      return false;

      }

      CustomTextEdit是QTextEdit的一個(gè)子類。我們重寫了其event()函數(shù),卻沒(méi)有調(diào)用父類的同名函數(shù)。這樣,我們的組件就只能處理 Tab 鍵,再也無(wú)法輸入任何文本,也不能響應(yīng)其它事件,比如鼠標(biāo)點(diǎn)擊之后也不會(huì)有光標(biāo)出現(xiàn)。這是因?yàn)槲覀冎惶幚淼腒eyPress類型的事件,并且如果不是KeyPress事件,則直接返回 false,鼠標(biāo)事件根本不會(huì)被轉(zhuǎn)發(fā),也就沒(méi)有了鼠標(biāo)事件。

      通過(guò)查看QObject::event()的實(shí)現(xiàn),我們可以理解,event()函數(shù)同前面的章節(jié)中我們所說(shuō)的事件處理器有什么聯(lián)系:

      //!!! Qt5

      bool QObject::event(QEvent *e)

      {

      switch (e->type()) {

      case QEvent::Timer:

      timerEvent((QTimerEvent*)e);

      break;

      case QEvent::ChildAdded:

      case QEvent::ChildPolished:

      case QEvent::ChildRemoved:

      childEvent((QChildEvent*)e);

      break;

      // ...

      default:

      if (e->type() >= QEvent::User) {

      customEvent(e);

      break;

      }

      return false;

      }

      return true;

      }

      這是 Qt 5 中QObject::event()函數(shù)的源代碼(Qt 4 的版本也是類似的)。我們可以看到,同前面我們所說(shuō)的一樣,Qt 也是使用QEvent::type()判斷事件類型,然后調(diào)用了特定的事件處理器。比如,如果event->type()返回值是QEvent::Timer,則調(diào)用timerEvent()函數(shù)。可以想象,QWidget::event()中一定會(huì)有如下的代碼:

      switch (event->type()) {

      case QEvent::MouseMove:

      mouseMoveEvent((QMouseEvent*)event);

      break;

      // ...

      }

      事實(shí)也的確如此。timerEvent()和mouseMoveEvent()這樣的函數(shù),就是我們前面章節(jié)所說(shuō)的事件處理器 event handler。也就是說(shuō),event()函數(shù)中實(shí)際是通過(guò)事件處理器來(lái)響應(yīng)一個(gè)具體的事件。這相當(dāng)于event()函數(shù)將具體事件的處理“委托”給具體的事件處理器。而這些事件處理器是 protected virtual 的,因此,我們重寫了某一個(gè)事件處理器,即可讓 Qt 調(diào)用我們自己實(shí)現(xiàn)的版本。

      由此可以見,event()是一個(gè)集中處理不同類型的事件的地方。如果你不想重寫一大堆事件處理器,就可以重寫這個(gè)event()函數(shù),通過(guò)QEvent::type()判斷不同的事件。鑒于重寫event()函數(shù)需要十分小心注意父類的同名函數(shù)的調(diào)用,一不留神就可能出現(xiàn)問(wèn)題,所以一般還是建議只重寫事件處理器(當(dāng)然,也必須記得是不是應(yīng)該調(diào)用父類的同名處理器)。這其實(shí)暗示了event()函數(shù)的另外一個(gè)作用:屏蔽掉某些不需要的事件處理器。正如我們前面的CustomTextEdit例子看到的那樣,我們創(chuàng)建了一個(gè)只能響應(yīng) tab 鍵的組件。這種作用是重寫事件處理器所不能實(shí)現(xiàn)的。

      9.3 事件過(guò)濾器

      有時(shí)候,對(duì)象需要查看、甚至要攔截發(fā)送到另外對(duì)象的事件。例如,對(duì)話框可能想要攔截按鍵事件,不讓別的組件接收到;或者要修改回車鍵的默認(rèn)處理。

      通過(guò)前面的章節(jié),我們已經(jīng)知道,Qt 創(chuàng)建了QEvent事件對(duì)象之后,會(huì)調(diào)用QObject的event()函數(shù)處理事件的分發(fā)。顯然,我們可以在event()函數(shù)中實(shí)現(xiàn)攔截的操作。由于event()函數(shù)是 protected 的,因此,需要繼承已有類。如果組件很多,就需要重寫很多個(gè)event()函數(shù)。這當(dāng)然相當(dāng)麻煩,更不用說(shuō)重寫event()函數(shù)還得小心一堆問(wèn)題。好在 Qt 提供了另外一種機(jī)制來(lái)達(dá)到這一目的:事件過(guò)濾器。

      QObject有一個(gè)eventFilter()函數(shù),用于建立事件過(guò)濾器。函數(shù)原型如下:

      virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );

      這個(gè)函數(shù)正如其名字顯示的那樣,是一個(gè)“事件過(guò)濾器”。所謂事件過(guò)濾器,可以理解成一種過(guò)濾代碼。事件過(guò)濾器會(huì)檢查接收到的事件。如果這個(gè)事件是我們感興趣的類型,就進(jìn)行我們自己的處理;如果不是,就繼續(xù)轉(zhuǎn)發(fā)。這個(gè)函數(shù)返回一個(gè) bool 類型,如果你想將參數(shù) event 過(guò)濾出來(lái),比如,不想讓它繼續(xù)轉(zhuǎn)發(fā),就返回 true,否則返回 false。事件過(guò)濾器的調(diào)用時(shí)間是目標(biāo)對(duì)象(也就是參數(shù)里面的watched對(duì)象)接收到事件對(duì)象之前。也就是說(shuō),如果你在事件過(guò)濾器中停止了某個(gè)事件,那么,watched對(duì)象以及以后所有的事件過(guò)濾器根本不會(huì)知道這么一個(gè)事件。

      我們來(lái)看一段簡(jiǎn)單的代碼:

      class MainWindow : public QMainWindow

      {

      public:

      MainWindow();

      protected:

      bool eventFilter(QObject *obj, QEvent *event);

      private:

      QTextEdit *textEdit;

      };

      MainWindow::MainWindow()

      {

      textEdit = new QTextEdit;

      setCentralWidget(textEdit);

      textEdit->installEventFilter(this);

      }

      bool MainWindow::eventFilter(QObject *obj, QEvent *event)

      {

      if (obj == textEdit) {

      if (event->type() == QEvent::KeyPress) {

      QKeyEvent *keyEvent = static_cast(event);

      qDebug() << "Ate key press" << keyEvent->key();

      return true;

      } else {

      return false;

      9、QT基礎(chǔ)——Qt消息機(jī)制和事件

      }

      } else {

      // pass the event on to the parent class

      return QMainWindow::eventFilter(obj, event);

      }

      }

      MainWindow是我們定義的一個(gè)類。我們重寫了它的eventFilter()函數(shù)。為了過(guò)濾特定組件上的事件,首先需要判斷這個(gè)對(duì)象是不是我們感興趣的組件,然后判斷這個(gè)事件的類型。在上面的代碼中,我們不想讓textEdit組件處理鍵盤按下的事件。所以,首先我們找到這個(gè)組件,如果這個(gè)事件是鍵盤事件,則直接返回 true,也就是過(guò)濾掉了這個(gè)事件,其他事件還是要繼續(xù)處理,所以返回 false。對(duì)于其它的組件,我們并不保證是不是還有過(guò)濾器,于是最保險(xiǎn)的辦法是調(diào)用父類的函數(shù)。

      eventFilter()函數(shù)相當(dāng)于創(chuàng)建了過(guò)濾器,然后我們需要安裝這個(gè)過(guò)濾器。安裝過(guò)濾器需要調(diào)用QObject::installEventFilter()函數(shù)。函數(shù)的原型如下:

      void QObject::installEventFilter ( QObject * filterObj )

      這個(gè)函數(shù)接受一個(gè)QObject *類型的參數(shù)。記得剛剛我們說(shuō)的,eventFilter()函數(shù)是QObject的一個(gè)成員函數(shù),因此,任意QObject都可以作為事件過(guò)濾器(問(wèn)題在于,如果你沒(méi)有重寫eventFilter()函數(shù),這個(gè)事件過(guò)濾器是沒(méi)有任何作用的,因?yàn)槟J(rèn)什么都不會(huì)過(guò)濾)。已經(jīng)存在的過(guò)濾器則可以通過(guò)QObject::removeEventFilter()函數(shù)移除。

      我們可以向一個(gè)對(duì)象上面安裝多個(gè)事件處理器,只要調(diào)用多次installEventFilter()函數(shù)。如果一個(gè)對(duì)象存在多個(gè)事件過(guò)濾器,那么,最后一個(gè)安裝的會(huì)第一個(gè)執(zhí)行,也就是后進(jìn)先執(zhí)行的順序。

      還記得我們前面的那個(gè)例子嗎?我們使用event()函數(shù)處理了 Tab 鍵:

      bool CustomWidget::event(QEvent *e)

      {

      if (e->type() == QEvent::KeyPress) {

      QKeyEvent *keyEvent = static_cast(e);

      if (keyEvent->key() == Qt::Key_Tab) {

      qDebug() << "You press tab.";

      return true;

      }

      }

      return QWidget::event(e);

      }

      現(xiàn)在,我們可以給出一個(gè)使用事件過(guò)濾器的版本:

      bool FilterObject::eventFilter(QObject *object, QEvent *event)

      {

      if (object == target && event->type() == QEvent::KeyPress)

      {

      QKeyEvent *keyEvent = static_cast(event);

      if (keyEvent->key() == Qt::Key_Tab) {

      qDebug() << "You press tab.";

      return true;

      } else {

      return false;

      }

      }

      return false;

      }

      事件過(guò)濾器的強(qiáng)大之處在于,我們可以為整個(gè)應(yīng)用程序添加一個(gè)事件過(guò)濾器。記得,installEventFilter()函數(shù)是QObject的函數(shù),QApplication或者QCoreApplication對(duì)象都是QObject的子類,因此,我們可以向QApplication或者QCoreApplication添加事件過(guò)濾器。這種全局的事件過(guò)濾器將會(huì)在所有其它特性對(duì)象的事件過(guò)濾器之前調(diào)用。盡管很強(qiáng)大,但這種行為會(huì)嚴(yán)重降低整個(gè)應(yīng)用程序的事件分發(fā)效率。因此,除非是不得不使用的情況,否則的話我們不應(yīng)該這么做。

      注意,

      事件過(guò)濾器和被安裝過(guò)濾器的組件必須在同一線程,否則,過(guò)濾器將不起作用。另外,如果在安裝過(guò)濾器之后,這兩個(gè)組件到了不同的線程,那么,只有等到二者重新回到同一線程的時(shí)候過(guò)濾器才會(huì)有效。

      9.4 總結(jié)

      Qt 的事件是整個(gè) Qt 框架的核心機(jī)制之一,也比較復(fù)雜。說(shuō)它復(fù)雜,更多是因?yàn)樗婕暗降暮瘮?shù)眾多,而處理方法也很多,有時(shí)候讓人難以選擇。現(xiàn)在我們簡(jiǎn)單總結(jié)一下 Qt 中的事件機(jī)制。

      Qt 中有很多種事件:鼠標(biāo)事件、鍵盤事件、大小改變的事件、位置移動(dòng)的事件等等。如何處理這些事件,實(shí)際有兩種選擇:

      所有事件對(duì)應(yīng)一個(gè)事件處理函數(shù),在這個(gè)事件處理函數(shù)中用一個(gè)很大的分支語(yǔ)句進(jìn)行選擇,其代表作就是 win32 API 的WndProc()函數(shù):

      LRESULT CALLBACK WndProc(HWND hWnd,

      UINT message,

      WPARAM wParam,

      LPARAM lParam)

      在這個(gè)函數(shù)中,我們需要使用switch語(yǔ)句,選擇message參數(shù)的類型進(jìn)行處理,典型代碼是:

      switch(message)

      {

      case WM_PAINT:

      // ...

      break;

      case WM_DESTROY:

      // ...

      break;

      ...

      }

      每一種事件對(duì)應(yīng)一個(gè)事件處理函數(shù)。Qt 就是使用的這么一種機(jī)制:

      mouseEvent()

      keyPressEvent()

      Qt 具有這么多種事件處理函數(shù),肯定有一個(gè)地方對(duì)其進(jìn)行分發(fā),否則,Qt 怎么知道哪一種事件調(diào)用哪一個(gè)事件處理函數(shù)呢?這個(gè)分發(fā)的函數(shù),就是event()。顯然,當(dāng)QMouseEvent產(chǎn)生之后,event()函數(shù)將其分發(fā)給mouseEvent()事件處理器進(jìn)行處理。

      event()函數(shù)會(huì)有兩個(gè)問(wèn)題:

      event()函數(shù)是一個(gè) protected 的函數(shù),這意味著我們要想重寫event(),必須繼承一個(gè)已有的類。試想,我的程序根本不想要鼠標(biāo)事件,程序中所有組件都不允許處理鼠標(biāo)事件,是不是我得繼承所有組件,一一重寫其event()函數(shù)?protected 函數(shù)帶來(lái)的另外一個(gè)問(wèn)題是,如果我基于第三方庫(kù)進(jìn)行開發(fā),而對(duì)方?jīng)]有提供源代碼,只有一個(gè)鏈接庫(kù),其它都是封裝好的。我怎么去繼承這種庫(kù)中的組件呢?

      event()函數(shù)的確有一定的控制,不過(guò)有時(shí)候我的需求更嚴(yán)格一些:我希望那些組件根本看不到這種事件。event()函數(shù)雖然可以攔截,但其實(shí)也是接收到了QMouseEvent對(duì)象。我連讓它收都收不到。這樣做的好處是,模擬一種系統(tǒng)根本沒(méi)有那個(gè)事件的效果,所以其它組件根本不會(huì)收到這個(gè)事件,也就無(wú)需修改自己的事件處理函數(shù)。這種需求怎么辦呢?

      這兩個(gè)問(wèn)題是event()函數(shù)無(wú)法處理的。于是,Qt 提供了另外一種解決方案:事件過(guò)濾器。事件過(guò)濾器給我們一種能力,讓我們能夠完全移除某種事件。事件過(guò)濾器可以安裝到任意QObject類型上面,并且可以安裝多個(gè)。如果要實(shí)現(xiàn)全局的事件過(guò)濾器,則可以安裝到QApplication或者QCoreApplication上面。這里需要注意的是,如果使用installEventFilter()函數(shù)給一個(gè)對(duì)象安裝事件過(guò)濾器,那么該事件過(guò)濾器只對(duì)該對(duì)象有效,只有這個(gè)對(duì)象的事件需要先傳遞給事件過(guò)濾器的eventFilter()函數(shù)進(jìn)行過(guò)濾,其它對(duì)象不受影響。如果給QApplication對(duì)象安裝事件過(guò)濾器,那么該過(guò)濾器對(duì)程序中的每一個(gè)對(duì)象都有效,任何對(duì)象的事件都是先傳給eventFilter()函數(shù)。

      事件過(guò)濾器可以解決剛剛我們提出的event()函數(shù)的兩點(diǎn)不足:

      首先,事件過(guò)濾器不是 protected 的,因此我們可以向任何QObject子類安裝事件過(guò)濾器;

      其次,事件過(guò)濾器在目標(biāo)對(duì)象接收到事件之前進(jìn)行處理,如果我們將事件過(guò)濾掉,目標(biāo)對(duì)象根本不會(huì)見到這個(gè)事件。

      事實(shí)上,還有一種方法,我們沒(méi)有介紹。Qt 事件的調(diào)用最終都會(huì)追溯到QCoreApplication::notify()函數(shù),因此,最大的控制權(quán)實(shí)際上是重寫QCoreApplication::notify()。這個(gè)函數(shù)的聲明是:

      virtual bool QCoreApplication::notify ( QObject * receiver,

      QEvent * event );

      該函數(shù)會(huì)將event發(fā)送給receiver,也就是調(diào)用receiver->event(event),其返回值就是來(lái)自receiver的事件處理器。注意,這個(gè)函數(shù)為任意線程的任意對(duì)象的任意事件調(diào)用,因此,它不存在事件過(guò)濾器的線程的問(wèn)題。不過(guò)我們并不推薦這么做,因?yàn)閚otify()函數(shù)只有一個(gè),而事件過(guò)濾器要靈活得多。

      現(xiàn)在我們可以總結(jié)一下 Qt 的事件處理,實(shí)際上是有五個(gè)層次:

      重寫paintEvent()、mousePressEvent()等事件處理函數(shù)。這是最普通、最簡(jiǎn)單的形式,同時(shí)功能也最簡(jiǎn)單。

      重寫event()函數(shù)。event()函數(shù)是所有對(duì)象的事件入口,QObject和QWidget中的實(shí)現(xiàn),默認(rèn)是把事件傳遞給特定的事件處理函數(shù)。

      在特定對(duì)象上面安裝事件過(guò)濾器。該過(guò)濾器僅過(guò)濾該對(duì)象接收到的事件。

      在QCoreApplication::instance()上面安裝事件過(guò)濾器。該過(guò)濾器將過(guò)濾所有對(duì)象的所有事件,因此和notify()函數(shù)一樣強(qiáng)大,但是它更靈活,因?yàn)榭梢园惭b多個(gè)過(guò)濾器。全局的事件過(guò)濾器可以看到 disabled 組件上面發(fā)出的鼠標(biāo)事件。全局過(guò)濾器有一個(gè)問(wèn)題:只能用在主線程。

      重寫QCoreApplication::notify()函數(shù)。這是最強(qiáng)大的,和全局事件過(guò)濾器一樣提供完全控制,并且不受線程的限制。但是全局范圍內(nèi)只能有一個(gè)被使用(因?yàn)镼CoreApplication是單例的)。

      Qt

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:社交媒體–潛在客戶的新目的地
      下一篇:識(shí)別全局,快人一步丨華為云態(tài)勢(shì)感知上新!
      相關(guān)文章
      激情五月亚洲色图| 亚洲一级毛片免费看| 亚洲AV无码精品国产成人| 亚洲一区在线免费观看| 亚洲精品日韩中文字幕久久久| 久久久久久亚洲精品中文字幕 | 在线播放亚洲第一字幕| 亚洲一区二区三区乱码A| 亚洲成人国产精品| mm1313亚洲精品国产| 亚洲av中文无码乱人伦在线观看| 亚洲综合无码一区二区痴汉| 亚洲大成色www永久网址| 亚洲午夜无码久久久久小说| 亚洲日本中文字幕天天更新| 亚洲日韩国产二区无码| 亚洲av永久无码天堂网| 日本亚洲中午字幕乱码| 国产亚洲精品仙踪林在线播放| 免费观看亚洲人成网站| 亚洲精品动漫人成3d在线| 中文字幕专区在线亚洲| 亚洲精品国产精品乱码不卡√| 亚洲AV无码日韩AV无码导航| 亚洲视频在线观看一区| 亚洲黄网在线观看| 亚洲妇女水蜜桃av网网站| 亚洲娇小性xxxx| 亚洲精华国产精华精华液网站| 色噜噜的亚洲男人的天堂| 亚洲精品视频免费| 亚洲视频人成在线播放| 亚洲av永久无码精品古装片 | 日本红怡院亚洲红怡院最新 | 亚洲第一成年男人的天堂| 精品亚洲国产成AV人片传媒| 亚洲人成免费电影| 亚洲精品GV天堂无码男同| 亚洲色婷婷综合开心网| 亚洲成a人片在线观看无码| 久久久久亚洲av无码专区喷水|