Qt中的事件模型
Qt 的事件模型是其核心机制之一,用于处理用户交互(如鼠标点击、键盘输入)以及系统事件(如窗口大小改变、定时器事件等)。以下是 Qt 事件模型的详细介绍:
1. 事件的定义
在Qt 中事件是通过QEvent和其子类来表示的,QEvent是一个抽象基类,定义了事件的基本结构,而具体的事件类型(如鼠标事件、键盘事件、定时器事件等),则通过继承QEvent来实现。
例如:
QMouseEvent:鼠标事件。
QKeyEvent:键盘事件。
QResizeEvent:表示窗口大小改变事件。
QTimerEvent:表示定时器事件。
在QtCreator的帮助中看到,QEvent被很多类继承:
2. 事件的产生
事件的产生通常由系统或Qt框架触发。
- 用户移动鼠标时,系统会生成一个鼠标移动事件。
- 窗口大小改变时,Qt 会生成一个窗口大小改变事件。
- 定时器超时时,Qt 会生成一个定时器事件。
3. 事件的传递机制
Qt 的事件模型基于事件的传递和处理机制。事件从产生到被处理,通常会经历以下过程:
3.1 事件的传递链:
事件从产生后,会按照一定的顺序传递给目标对象(通常是窗口或控件),传递顺序如下:
1.事件过滤器(Event Filter):如果某个事件安装了事件过滤器(通过installEventFilter()),事件首先传递给事件过滤器,事件过滤器可以首先对事件进行预处理或者拦截。
2事件处理器(Event Handler):如果事件没有被事件过滤器拦截,它会传递到目标对象的事件处理器。事件处理器是一个虚函数,通常以event()的形式存在。
3.默认事件处理器(Default Event Handler):如果事件没有被目标对象处理,事件会传递给父对象(父窗口)。如果父对象也没有处理事件,最终会传递到 Qt 的默认事件处理器,由 Qt 进行默认处理。
3.2 事件的处理方式
事件的处理方式主要分为以下几种:
1.**事件处理器(event()
函数)**每个QObject派生类都有一个event()对象,用于处理事件,如果需要自定义事件处理逻辑,可以重写该函数。
QObject类有一个event()的虚函数。
例如:
if(e->type() == QEvent::KeyPress){// 自定义键盘处理逻辑QMessageBox::information(nullptr,"键盘被点击","这是一个信息弹窗");// 表示事件已被处理return true;}// 调用父类事件处理器return QWidget::event(e);
2。事件过滤器(eventFilter()
函数):事件过滤器是一个全局的事件处理机制,可以拦截并处理其他对象的事件。通过重写 eventFilter()
函数并安装事件过滤器,可以实现对事件的预处理或拦截。
class MainWindow : public QMainWindow{public:MainWindow();protected:bool eventFilter(QObject *obj, QEvent *ev) override;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<QKeyEvent*>(event);qDebug() << "Ate key press" << keyEvent->key();return true;} else {return false;}} else {// pass the event on to the parent classreturn QMainWindow::eventFilter(obj, event);}}
3.专用事件处理函数
Qt 提供了一些专用的事件处理函数,如 mousePressEvent()
、keyPressEvent()
等。这些函数是 event()
函数的特例,用于处理特定类型的事件。如果需要处理特定事件,可以重写这些函数。
void mousePressEvent(QMouseEvent* e) override {// 自定义鼠标点击事件处理逻辑
}
4. 事件的类型
Qt 定义了多种事件类型,每种事件类型都有一个唯一的枚举值(QEvent::Type
)。以下是一些常见的事件类型:
QEvent::KeyPress
和QEvent::KeyRelease
:键盘按下和释放事件。QEvent::MouseButtonPress
和QEvent::MouseButtonRelease
:鼠标按钮按下和释放事件。QEvent::MouseMove
:鼠标移动事件。QEvent::Resize
:窗口大小改变事件。QEvent::Timer
:定时器事件。QEvent::Paint
:绘图事件。
在QEvent的源码中有:
enum Type {/*If you get a strange compiler error on the line with None,it's probably because you're also including X11 headers,which #define the symbol None. Put the X11 includes afterthe Qt includes to solve this problem.*/None = 0, // invalid eventTimer = 1, // timer eventMouseButtonPress = 2, // mouse button pressedMouseButtonRelease = 3, // mouse button releasedMouseButtonDblClick = 4, // mouse button double clickMouseMove = 5, // mouse moveKeyPress = 6, // key pressedKeyRelease = 7, // key releasedFocusIn = 8, // keyboard focus receivedFocusOut = 9, // keyboard focus lostFocusAboutToChange = 23, // keyboard focus is about to be lostEnter = 10, // mouse enters widgetLeave = 11, // mouse leaves widgetPaint = 12, // paint widgetMove = 13, // move widgetResize = 14, // resize widgetCreate = 15, // after widget creationDestroy = 16, // during widget destructionShow = 17, // widget is shownHide = 18, // widget is hiddenClose = 19, // request to close widgetQuit = 20, // request to quit applicationParentChange = 21, // widget has been reparentedParentAboutToChange = 131, // sent just before the parent change is doneThreadChange = 22, // object has changed threadsWindowActivate = 24, // window was activatedWindowDeactivate = 25, // window was deactivatedShowToParent = 26, // widget is shown to parentHideToParent = 27, // widget is hidden to parentWheel = 31, // wheel eventWindowTitleChange = 33, // window title changedWindowIconChange = 34, // icon changedApplicationWindowIconChange = 35, // application icon changedApplicationFontChange = 36, // application font changedApplicationLayoutDirectionChange = 37, // application layout direction changedApplicationPaletteChange = 38, // application palette changedPaletteChange = 39, // widget palette changedClipboard = 40, // internal clipboard eventSpeech = 42, // reserved for speech inputMetaCall = 43, // meta call eventSockAct = 50, // socket activationWinEventAct = 132, // win event activationDeferredDelete = 52, // deferred delete eventDragEnter = 60, // drag moves into widgetDragMove = 61, // drag moves in widgetDragLeave = 62, // drag leaves or is cancelledDrop = 63, // actual dropDragResponse = 64, // drag accepted/rejectedChildAdded = 68, // new child widgetChildPolished = 69, // polished child widgetChildRemoved = 71, // deleted child widgetShowWindowRequest = 73, // widget's window should be mappedPolishRequest = 74, // widget should be polishedPolish = 75, // widget is polishedLayoutRequest = 76, // widget should be relayoutedUpdateRequest = 77, // widget should be repaintedUpdateLater = 78, // request update() laterEmbeddingControl = 79, // ActiveX embeddingActivateControl = 80, // ActiveX activationDeactivateControl = 81, // ActiveX deactivationContextMenu = 82, // context popup menuInputMethod = 83, // input methodTabletMove = 87, // Wacom tablet eventLocaleChange = 88, // the system locale changedLanguageChange = 89, // the application language changedLayoutDirectionChange = 90, // the layout direction changedStyle = 91, // internal style eventTabletPress = 92, // tablet pressTabletRelease = 93, // tablet releaseOkRequest = 94, // CE (Ok) button pressedHelpRequest = 95, // CE (?) button pressedIconDrag = 96, // proxy icon draggedFontChange = 97, // font has changedEnabledChange = 98, // enabled state has changedActivationChange = 99, // window activation has changedStyleChange = 100, // style has changedIconTextChange = 101, // icon text has changed. Deprecated.ModifiedChange = 102, // modified state has changedMouseTrackingChange = 109, // mouse tracking state has changedWindowBlocked = 103, // window is about to be blocked modallyWindowUnblocked = 104, // windows modal blocking has endedWindowStateChange = 105,ReadOnlyChange = 106, // readonly state has changedToolTip = 110,WhatsThis = 111,StatusTip = 112,ActionChanged = 113,ActionAdded = 114,ActionRemoved = 115,FileOpen = 116, // file open requestShortcut = 117, // shortcut triggeredShortcutOverride = 51, // shortcut override requestWhatsThisClicked = 118,ToolBarChange = 120, // toolbar visibility toggledApplicationActivate = 121, // deprecated. Use ApplicationStateChange instead.ApplicationActivated = ApplicationActivate, // deprecatedApplicationDeactivate = 122, // deprecated. Use ApplicationStateChange instead.ApplicationDeactivated = ApplicationDeactivate, // deprecatedQueryWhatsThis = 123, // query what's this widget helpEnterWhatsThisMode = 124,LeaveWhatsThisMode = 125,ZOrderChange = 126, // child widget has had its z-order changedHoverEnter = 127, // mouse cursor enters a hover widgetHoverLeave = 128, // mouse cursor leaves a hover widgetHoverMove = 129, // mouse cursor move inside a hover widget// last event id used = 132#ifdef QT_KEYPAD_NAVIGATIONEnterEditFocus = 150, // enter edit mode in keypad navigationLeaveEditFocus = 151, // enter edit mode in keypad navigation
#endifAcceptDropsChange = 152,ZeroTimerEvent = 154, // Used for Windows Zero timer eventsGraphicsSceneMouseMove = 155, // GraphicsViewGraphicsSceneMousePress = 156,GraphicsSceneMouseRelease = 157,GraphicsSceneMouseDoubleClick = 158,GraphicsSceneContextMenu = 159,GraphicsSceneHoverEnter = 160,GraphicsSceneHoverMove = 161,GraphicsSceneHoverLeave = 162,GraphicsSceneHelp = 163,GraphicsSceneDragEnter = 164,GraphicsSceneDragMove = 165,GraphicsSceneDragLeave = 166,GraphicsSceneDrop = 167,GraphicsSceneWheel = 168,GraphicsSceneLeave = 220,KeyboardLayoutChange = 169, // keyboard layout changedDynamicPropertyChange = 170, // A dynamic property was changed through setProperty/propertyTabletEnterProximity = 171,TabletLeaveProximity = 172,NonClientAreaMouseMove = 173,NonClientAreaMouseButtonPress = 174,NonClientAreaMouseButtonRelease = 175,NonClientAreaMouseButtonDblClick = 176,MacSizeChange = 177, // when the Qt::WA_Mac{Normal,Small,Mini}Size changesContentsRectChange = 178, // sent by QWidget::setContentsMargins (internal)MacGLWindowChange = 179, // Internal! the window of the GLWidget has changedFutureCallOut = 180,GraphicsSceneResize = 181,GraphicsSceneMove = 182,CursorChange = 183,ToolTipChange = 184,NetworkReplyUpdated = 185, // Internal for QNetworkReplyGrabMouse = 186,UngrabMouse = 187,GrabKeyboard = 188,UngrabKeyboard = 189,StateMachineSignal = 192,StateMachineWrapped = 193,TouchBegin = 194,TouchUpdate = 195,TouchEnd = 196,#ifndef QT_NO_GESTURESNativeGesture = 197, // QtGui native gesture
#endifRequestSoftwareInputPanel = 199,CloseSoftwareInputPanel = 200,WinIdChange = 203,
#ifndef QT_NO_GESTURESGesture = 198,GestureOverride = 202,
#endifScrollPrepare = 204,Scroll = 205,Expose = 206,InputMethodQuery = 207,OrientationChange = 208, // Screen orientation has changedTouchCancel = 209,ThemeChange = 210,SockClose = 211, // socket closedPlatformPanel = 212,StyleAnimationUpdate = 213, // style animation target should be updatedApplicationStateChange = 214,WindowChangeInternal = 215, // internal for QQuickWidget and texture-based widgetsScreenChangeInternal = 216,PlatformSurface = 217, // Platform surface created or about to be destroyedPointer = 218, // Qt 5: QQuickPointerEvent; Qt 6: unused so farTabletTrackingChange = 219, // tablet tracking state has changed// GraphicsSceneLeave = 220,WindowAboutToChangeInternal = 221, // internal for QQuickWidget and texture-based widgetsDevicePixelRatioChange = 222,// 512 reserved for Qt Jambi's MetaCall event// 513 reserved for Qt Jambi's DeleteOnMainThread eventUser = 1000, // first user event idMaxUser = 65535 // last user event id};Q_ENUM(Type)
5. 事件的自定义
Qt 允许开发者自定义事件类型。自定义事件需要继承 QEvent
类,并定义自己的事件类型。例如:
#ifndef QCUSTOMEVENT_H
#define QCUSTOMEVENT_H#include <QObject>
#include<QEvent>class QCustomEvent : public QEvent
{Q_OBJECT
public:explicit QCustomEvent();~QCustomEvent();static const QEvent::Type EventType = static_cast<QEvent::Type>(QEvent::User + 1);signals:
};#endif // QCUSTOMEVENT_H
自定义事件可以通过 QCoreApplication::postEvent()
或 QCoreApplication::sendEvent()
发送。
6. 事件的发送方式
Qt 提供了两种发送事件的方式:
QCoreApplication::sendEvent()
:同步发送事件,事件会立即被处理,函数返回事件的处理结果。QCoreApplication::postEvent()
:异步发送事件,事件会被放入事件队列中,稍后由事件循环处理。
7. 事件循环(Event Loop)
事件循环是 Qt 事件模型的核心,负责从事件队列中取出事件并分发给目标对象。事件循环通过调用 exec()
函数启动,通常在应用程序的主函数中启动:
int main(int argc, char* argv[]) {QApplication app(argc, argv);MainWindow w;w.show();return app.exec(); // 启动事件循环
}
事件循环会不断从事件队列中取出事件,并调用目标对象的事件处理器进行处理。
8. 总结
Qt 的事件模型是一个高效且灵活的机制,通过事件的产生、传递和处理,实现了用户交互和系统事件的响应。开发者可以通过重写事件处理器、安装事件过滤器或自定义事件类型,灵活地扩展和定制 Qt 应用程序的行为。