天天看點

C++ Qt全局異常處理器_實戰PyQt5:095-Qt中的事件處理機制Qt中的事件(event)事件分發event()函數事件過濾器QEvent中的成員函數處理Qt事件的思路簡單示例代碼本文知識點

C++ Qt全局異常處理器_實戰PyQt5:095-Qt中的事件處理機制Qt中的事件(event)事件分發event()函數事件過濾器QEvent中的成員函數處理Qt事件的思路簡單示例代碼本文知識點

Qt中的事件(event)

事件(event)由視窗系統或Qt自身産生,用以響應所發生的各類事情。例如:當使用者按下鍵盤或者點選滑鼠上的按鍵時,就是産生一個鍵盤或者滑鼠事件;當某個視窗第一次顯示的時候,就會産生一個繪制事件,告知視窗繪制它本身,使視窗可見。事件主要分兩種情況:

  • 與使用者互動時發生,比如按下滑鼠(mousePressEvent),敲擊鍵盤(keyPressEvent)等。
  • 系統内部自身産生,比如計時器事件(timerEvent)等。

QEvent類是所有事件類的基類,事件對象裡包含事件參數。在Qt中,事件作為一個對象,繼承自QEvent。

任何從QObject類派生的類的對象都可以通過QObject.event()函數來接收事件;事件産生時,Qt會建立一個合适的QEvent對象或其派生類對象,然後通過調用QObject類的event()函數将這個事件對象傳遞給特定的QObject對象或其派生類對象。

Qt 的主事件循環(QCoreApplication::exec())從事件隊列中擷取本地視窗系統事件,将它們轉化為 QEvents,然後将轉換後的事件發送給 QObjects。QObjects 通過調用它們的 QObjec.event() 函數接收事件。該函數可以在子類中重新實作,來處理自定義的事件以及添加額外的事件類型,QWidget.event() 就是一個很好的例子。

C++ Qt全局異常處理器_實戰PyQt5:095-Qt中的事件處理機制Qt中的事件(event)事件分發event()函數事件過濾器QEvent中的成員函數處理Qt事件的思路簡單示例代碼本文知識點

Qt中的事件類

Qt中的常見事件類型:

  • 鍵盤事件:處理鍵盤相關事件,比如按鍵等;
  • 滑鼠事件:處理滑鼠相關事件,比如滑鼠按下,釋放,輕按兩下等事件;
  • 拖放事件:拖放事件處理;
  • 定時器事件:處理定時器事件;
  • 視窗相關事件:處理視窗繪制,移動,改變尺寸,關閉,右鍵菜單等事件。

事件分發event()函數

事件的分發函數稱為事件處理器(event handler)。event()函數就是用來處理事件的分發。如果想在事件的分發之前進行一些操作,比如監聽某個按鍵的按下,就可以在event()函數裡處理:

class MyWidget(QWidget):         ......         def event(e):                  if e.key() == Qt.Key_Tab:                            print('按下了Tab鍵')                            return True                  #按原來的流程來處理事件的分發                  return QWidget.event(self, e)
           

在上面的代碼中,MyWidget是QWidget的子類,它重新實作了event()函數,該函數帶有一個QEvent類型的參數。當系統産生QEvent對象時,就會傳入這個函數并調用。如果傳入的事件被識别并處理,則傳回True,表示這個事件已經處理完畢,Qt不會将這個事件再分發出去。否則,就繼續将該事件分發出去。

Qt系統處理事件時,使用了一種機制,叫做事件傳播機制。意思是在子類(比如說一個按鈕QPushButton)中發生的事件,調用了子元件的event()函數之後,還會調用父元件(比如說QAbstractButton)的event()函數。event函數的傳回值就用于控制這一傳播過程。

C++ Qt全局異常處理器_實戰PyQt5:095-Qt中的事件處理機制Qt中的事件(event)事件分發event()函數事件過濾器QEvent中的成員函數處理Qt事件的思路簡單示例代碼本文知識點

Qt中的事件傳遞過程

事件過濾器

在一些應用場景中,需要攔截某個元件發生的事件,讓這個事件不再向其他元件傳播,這是可以為這個元件或其父元件安裝一個事件過濾器(eventFilter)來實作,在實際使用中。需要調用函數installEventFilter()為元件安裝過濾器,才能使用事件過濾器這個機制。安裝好事件過濾器之後,該元件和其子元件的事件就會被監聽。然後重寫其eventFilter函數,實作事件過濾。

QEvent中的成員函數

  • ignore(): 接收者忽略目前接收到的事件,但事件可能會傳遞給接收者的父元件;
  • accept(): 接收者期望處理當然事件;
  • isAccept(): 判斷目前事件是否被處理;
  • registerEventType(): 注冊并傳回一個自定義事件類型;
  • spontaneous(): 如果事件由應用程式之外産生,比如一個系統事件,傳回True,否則傳回False;
  • type(): 傳回事件的類型。QEvent.Type定義了Qt中有效的事件類型。

處理Qt事件的思路

在處理Qt的事件時,一般按照以下思路進行:

  • 重寫paintEvent、mousePressEvent等事件處理函數。這是最常見、最簡單的方式;
  • 重寫event函數。 event函數是所有對象的事件入口,在QObject和QWidget中的實作中,預設是把事件傳遞給特定的事件處理函數;
  • 在特定對象上面安裝事件過濾器。該過濾器僅過濾該對象接收到的事件;
  • 在QCoreApplication::instance()上面安裝事件過濾器。該過濾器将過濾所有對象的所有事件;
  • 重寫QCoreApplication::notify()函數。這是最強大的,和全局事件過濾器一樣提供完全控制,并且不受線程的限制。

簡單示例代碼

示例代碼示範了對一些基本事件的處理,完整代碼如下:

import sysfrom PyQt5 import QtCore, QtGui, QtWidgetsfrom PyQt5.QtCore import Qt, QEvent, QTimerfrom PyQt5.QtGui import QPainterfrom PyQt5.QtWidgets import (QApplication, QWidget, QMenu, QMessageBox) class DemoEvent(QWidget):    def __init__(self, parent=None):        super(DemoEvent, self).__init__(parent)         # 設定視窗标題        self.setWindowTitle('實戰PyQt5: QEvent事件示範')        # 設定視窗大小        self.resize(400, 320)         #初始化資料        self.doubleClicked = False   #滑鼠輕按兩下        self.key=''                  #按鍵值        self.text=''                 #文本資訊        self.message=''              #提示消息                #定時器,500毫秒後執行        QTimer.singleShot(500, self.onShowHelpInfo)            def onShowHelpInfo(self):        self.text = '點選這裡觸發追蹤滑鼠位置'        #觸發重繪事件,即觸發paintEvent函數        self.update()            #關閉應用時,會觸發closeEvent    def closeEvent(self, event):        if QMessageBox.information(self, '關閉應用',  '點選按鈕關閉應用'):            return True        #右鍵菜單事件    def contextMenuEvent(self, event):        #執行個體化一個菜單        menu = QMenu(self)                actionTest1 = menu.addAction('測試1')        actionTest1.triggered.connect(self.onMenuTest1)        actionTest2 = menu.addAction('測試2')        actionTest2.triggered.connect(self.onMenuTest2)                if not self.message:            menu.addSeparator()            actionTest3 = menu.addAction('測試3')            actionTest3.triggered.connect(self.onMenuTest3)                #在滑鼠出現的位置顯示菜單欄        menu.exec(event.globalPos())            def onMenuTest1(self):        self.message = '菜單選項1'        self.update()             def onMenuTest2(self):        self.message = '菜單選項2'        self.update()         def onMenuTest3(self):        self.message = '菜單選項3'        self.update()              #重繪視窗事件    def paintEvent(self, event):        text = self.text        pos = text.find('')        if pos >= 0:            text = text[0:pos]                #如果觸發了鍵盤按鍵,則在文本資訊中記錄相應的按鍵資訊        if self.key:            text += '按下了: {0}'.format(self.key)                    painter = QPainter(self)        painter.setRenderHint(QPainter.TextAntialiasing)                #居中繪制文本資訊        painter.drawText(self.rect(), Qt.AlignCenter, text)                 #如果有消息文本,則在底部居中繪制消息文本,3秒鐘口消息文本清空重繪        if self.message:            #顯示消息文本            painter.drawText(self.rect(), Qt.AlignBottom|Qt.AlignLeft, self.message)                    #3秒後觸發清空資訊的函數,并重繪事件        QTimer.singleShot(3000, self.clearMessage)            def clearMessage(self):        self.message = ''        self.update()             #滑鼠釋放事件    def mouseReleaseEvent(self, event):        #如果是輕按兩下釋放,就不跟蹤滑鼠移動        if self.doubleClicked:            self.doubleClicked = False        else:            self.setMouseTracking(not self.hasMouseTracking())            if self.hasMouseTracking():                self.text = '開啟滑鼠位置跟蹤功能.' +                     '請移動一下滑鼠!!!' +                     '單擊滑鼠可以關閉這個功能'            else:                self.text = '閉滑鼠跟蹤功能.' +                     '單擊滑鼠可以開啟這個功能'        self.update()            #滑鼠移動事件    def mouseMoveEvent(self, event):        if not self.doubleClicked:            #視窗坐标轉換為螢幕坐标            globalPos = self.mapToGlobal(event.pos())            self.text = '滑鼠位置: 視窗坐标為:QPoint({0}, {1}) 螢幕坐标為:QPoint({2}, {3})'                 .format(event.pos().x(), event.pos().y(), globalPos.x(), globalPos.y())            self.update()                #滑鼠輕按兩下事件    def mouseDoubleClickEvent(self, event):        self.doubleClicked = True        self.text = '你輕按兩下了滑鼠'        self.update()            #鍵盤按鍵事件    def keyPressEvent(self, event):        self.key = ''        if event.key() == Qt.Key_Home:            self.key = 'Home'        elif event.key() == Qt.Key_End:            self.key = 'End'        elif event.key() == Qt.Key_PageUp:            if event.modifiers() & Qt.ControlModifier:                self.key = "Ctrl+PageUp"            else:                self.key = "PageUp"        elif event.key() == Qt.Key_PageDown:            if event.modifiers() & Qt.ControlModifier:                self.key = "Ctrl+PageDown"            else:                self.key = "PageDown"        elif Qt.Key_A <= event.key() <= Qt.Key_Z:            if event.modifiers() & Qt.ShiftModifier:                self.key = "Shift+"            self.key += event.text()                #如果key有字元,不為空,則繪制字元        if self.key:            self.update()        #否則就繼續監視這個事件        else:            QWidget.keyPressEvent(self, event)        '''    重新實作其他事件,适用于PyQt沒有提供該事件的處理函數的情況,    Tab鍵由于涉及焦點切換,不會傳遞給keyPressEvent,    是以,需要在這裡重新定義。      '''         def event(self, event):        #如果有按鍵按下,并且按鍵是tab鍵        if (event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab):            self.key = "在event()中捕獲Tab鍵"            self.update()            return True        return QWidget.event(self, event) if __name__ == '__main__':    app = QApplication(sys.argv)    window = DemoEvent()    window.show()    sys.exit(app.exec())
           

運作效果如下圖:

C++ Qt全局異常處理器_實戰PyQt5:095-Qt中的事件處理機制Qt中的事件(event)事件分發event()函數事件過濾器QEvent中的成員函數處理Qt事件的思路簡單示例代碼本文知識點

QEvent事件示範

本文知識點

  • Qt的事件傳播和處理機制;
  • Qt中的各種事件;
  • 事件發生時,會生成一個QEvent對象,需要even函數進行分發,來調用相應的事件處理器;
  • Qt事件可能在處理後傳遞給父元件對象;
  • 事件過濾器(evenFilter)可以令事件進行攔截,阻止其傳播,進而實作某些功能。

喜歡本文内容就關注, 收藏,點贊,評論和轉發。

繼續閱讀