Qt中的事件模型
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 class
return 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 after
the Qt includes to solve this problem.
*/
None = 0, // invalid event
Timer = 1, // timer event
MouseButtonPress = 2, // mouse button pressed
MouseButtonRelease = 3, // mouse button released
MouseButtonDblClick = 4, // mouse button double click
MouseMove = 5, // mouse move
KeyPress = 6, // key pressed
KeyRelease = 7, // key released
FocusIn = 8, // keyboard focus received
FocusOut = 9, // keyboard focus lost
FocusAboutToChange = 23, // keyboard focus is about to be lost
Enter = 10, // mouse enters widget
Leave = 11, // mouse leaves widget
Paint = 12, // paint widget
Move = 13, // move widget
Resize = 14, // resize widget
Create = 15, // after widget creation
Destroy = 16, // during widget destruction
Show = 17, // widget is shown
Hide = 18, // widget is hidden
Close = 19, // request to close widget
Quit = 20, // request to quit application
ParentChange = 21, // widget has been reparented
ParentAboutToChange = 131, // sent just before the parent change is done
ThreadChange = 22, // object has changed threads
WindowActivate = 24, // window was activated
WindowDeactivate = 25, // window was deactivated
ShowToParent = 26, // widget is shown to parent
HideToParent = 27, // widget is hidden to parent
Wheel = 31, // wheel event
WindowTitleChange = 33, // window title changed
WindowIconChange = 34, // icon changed
ApplicationWindowIconChange = 35, // application icon changed
ApplicationFontChange = 36, // application font changed
ApplicationLayoutDirectionChange = 37, // application layout direction changed
ApplicationPaletteChange = 38, // application palette changed
PaletteChange = 39, // widget palette changed
Clipboard = 40, // internal clipboard event
Speech = 42, // reserved for speech input
MetaCall = 43, // meta call event
SockAct = 50, // socket activation
WinEventAct = 132, // win event activation
DeferredDelete = 52, // deferred delete event
DragEnter = 60, // drag moves into widget
DragMove = 61, // drag moves in widget
DragLeave = 62, // drag leaves or is cancelled
Drop = 63, // actual drop
DragResponse = 64, // drag accepted/rejected
ChildAdded = 68, // new child widget
ChildPolished = 69, // polished child widget
ChildRemoved = 71, // deleted child widget
ShowWindowRequest = 73, // widget's window should be mapped
PolishRequest = 74, // widget should be polished
Polish = 75, // widget is polished
LayoutRequest = 76, // widget should be relayouted
UpdateRequest = 77, // widget should be repainted
UpdateLater = 78, // request update() later
EmbeddingControl = 79, // ActiveX embedding
ActivateControl = 80, // ActiveX activation
DeactivateControl = 81, // ActiveX deactivation
ContextMenu = 82, // context popup menu
InputMethod = 83, // input method
TabletMove = 87, // Wacom tablet event
LocaleChange = 88, // the system locale changed
LanguageChange = 89, // the application language changed
LayoutDirectionChange = 90, // the layout direction changed
Style = 91, // internal style event
TabletPress = 92, // tablet press
TabletRelease = 93, // tablet release
OkRequest = 94, // CE (Ok) button pressed
HelpRequest = 95, // CE (?) button pressed
IconDrag = 96, // proxy icon dragged
FontChange = 97, // font has changed
EnabledChange = 98, // enabled state has changed
ActivationChange = 99, // window activation has changed
StyleChange = 100, // style has changed
IconTextChange = 101, // icon text has changed. Deprecated.
ModifiedChange = 102, // modified state has changed
MouseTrackingChange = 109, // mouse tracking state has changed
WindowBlocked = 103, // window is about to be blocked modally
WindowUnblocked = 104, // windows modal blocking has ended
WindowStateChange = 105,
ReadOnlyChange = 106, // readonly state has changed
ToolTip = 110,
WhatsThis = 111,
StatusTip = 112,
ActionChanged = 113,
ActionAdded = 114,
ActionRemoved = 115,
FileOpen = 116, // file open request
Shortcut = 117, // shortcut triggered
ShortcutOverride = 51, // shortcut override request
WhatsThisClicked = 118,
ToolBarChange = 120, // toolbar visibility toggled
ApplicationActivate = 121, // deprecated. Use ApplicationStateChange instead.
ApplicationActivated = ApplicationActivate, // deprecated
ApplicationDeactivate = 122, // deprecated. Use ApplicationStateChange instead.
ApplicationDeactivated = ApplicationDeactivate, // deprecated
QueryWhatsThis = 123, // query what's this widget help
EnterWhatsThisMode = 124,
LeaveWhatsThisMode = 125,
ZOrderChange = 126, // child widget has had its z-order changed
HoverEnter = 127, // mouse cursor enters a hover widget
HoverLeave = 128, // mouse cursor leaves a hover widget
HoverMove = 129, // mouse cursor move inside a hover widget
// last event id used = 132
#ifdef QT_KEYPAD_NAVIGATION
EnterEditFocus = 150, // enter edit mode in keypad navigation
LeaveEditFocus = 151, // enter edit mode in keypad navigation
#endif
AcceptDropsChange = 152,
ZeroTimerEvent = 154, // Used for Windows Zero timer events
GraphicsSceneMouseMove = 155, // GraphicsView
GraphicsSceneMousePress = 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 changed
DynamicPropertyChange = 170, // A dynamic property was changed through setProperty/property
TabletEnterProximity = 171,
TabletLeaveProximity = 172,
NonClientAreaMouseMove = 173,
NonClientAreaMouseButtonPress = 174,
NonClientAreaMouseButtonRelease = 175,
NonClientAreaMouseButtonDblClick = 176,
MacSizeChange = 177, // when the Qt::WA_Mac{Normal,Small,Mini}Size changes
ContentsRectChange = 178, // sent by QWidget::setContentsMargins (internal)
MacGLWindowChange = 179, // Internal! the window of the GLWidget has changed
FutureCallOut = 180,
GraphicsSceneResize = 181,
GraphicsSceneMove = 182,
CursorChange = 183,
ToolTipChange = 184,
NetworkReplyUpdated = 185, // Internal for QNetworkReply
GrabMouse = 186,
UngrabMouse = 187,
GrabKeyboard = 188,
UngrabKeyboard = 189,
StateMachineSignal = 192,
StateMachineWrapped = 193,
TouchBegin = 194,
TouchUpdate = 195,
TouchEnd = 196,
#ifndef QT_NO_GESTURES
NativeGesture = 197, // QtGui native gesture
#endif
RequestSoftwareInputPanel = 199,
CloseSoftwareInputPanel = 200,
WinIdChange = 203,
#ifndef QT_NO_GESTURES
Gesture = 198,
GestureOverride = 202,
#endif
ScrollPrepare = 204,
Scroll = 205,
Expose = 206,
InputMethodQuery = 207,
OrientationChange = 208, // Screen orientation has changed
TouchCancel = 209,
ThemeChange = 210,
SockClose = 211, // socket closed
PlatformPanel = 212,
StyleAnimationUpdate = 213, // style animation target should be updated
ApplicationStateChange = 214,
WindowChangeInternal = 215, // internal for QQuickWidget and texture-based widgets
ScreenChangeInternal = 216,
PlatformSurface = 217, // Platform surface created or about to be destroyed
Pointer = 218, // Qt 5: QQuickPointerEvent; Qt 6: unused so far
TabletTrackingChange = 219, // tablet tracking state has changed
// GraphicsSceneLeave = 220,
WindowAboutToChangeInternal = 221, // internal for QQuickWidget and texture-based widgets
DevicePixelRatioChange = 222,
// 512 reserved for Qt Jambi's MetaCall event
// 513 reserved for Qt Jambi's DeleteOnMainThread event
User = 1000, // first user event id
MaxUser = 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 应用程序的行为。