输入关键词开始搜索

Qt 事件系统

事件 vs 信号槽

事件 (Event)信号槽 (Signal/Slot)
层级底层(操作系统 → Qt)上层(QObject 之间)
方向自顶向下传播任意对象间通信
处理event() / eventFilter()connect()
典型鼠标点击、键盘按键、重绘按钮点击、数据到达

信号槽底层由事件系统驱动,但日常开发中大多数场景用信号槽就够了。

事件循环

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    // ...
    return app.exec();  // 进入事件循环
}

// 事件循环伪代码:
while (!shouldQuit) {
    QEvent event = getNextEvent();   // 从操作系统取事件
    sendEvent(widget, &event);        // 发给目标控件
}

QWidget 事件处理五层

// 层 1: 专用事件处理器(最常用)
void MyWidget::mousePressEvent(QMouseEvent *event) override {
    if (event->button() == Qt::LeftButton) {
        // 处理左键点击
    }
    QWidget::mousePressEvent(event);  // ⚠️ 调用父类,否则无法继续传播
}

// 层 2: event() — 事件总入口(少用)
bool MyWidget::event(QEvent *event) override {
    if (event->type() == QEvent::KeyPress) {
        auto *ke = static_cast<QKeyEvent *>(event);
        // 拦截所有按键
    }
    return QWidget::event(event);  // 分发到专用处理器
}

// 层 3: 事件过滤器 — 拦截子控件事件
class Monitor : public QObject {
    bool eventFilter(QObject *obj, QEvent *event) override {
        if (event->type() == QEvent::MouseButtonPress) {
            qDebug() << "clicked on" << obj;
        }
        return QObject::eventFilter(obj, event);  // false = 继续传播
    }
};
// 安装:child->installEventFilter(monitor);

// 层 4: 全局事件过滤器(应用程序级)
// qApp->installEventFilter(globalMonitor);

// 层 5: notify() — QApplication 级(极少用)
// 重写 QApplication::notify()

常用事件类型

// 鼠标事件
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;   // 需 setMouseTracking(true)
void wheelEvent(QWheelEvent *e) override;

// 键盘事件
void keyPressEvent(QKeyEvent *e) override;
void keyReleaseEvent(QKeyEvent *e) override;

// 窗口事件
void resizeEvent(QResizeEvent *e) override;
void closeEvent(QCloseEvent *e) override;       // 拦截关闭
void showEvent(QShowEvent *e) override;         // 首次显示前

// 重绘
void paintEvent(QPaintEvent *e) override;

// 拖放
void dragEnterEvent(QDragEnterEvent *e) override;
void dropEvent(QDropEvent *e) override;

自定义事件

// 1. 定义事件类型
const QEvent::Type MyCustomEvent = static_cast<QEvent::Type>(
    QEvent::registerEventType()
);

// 2. 自定义事件类(可携带数据)
class DataEvent : public QEvent {
public:
    static const QEvent::Type Type;
    QString message;

    DataEvent(const QString &msg)
        : QEvent(Type), message(msg) {}
};
const QEvent::Type DataEvent::Type =
    static_cast<QEvent::Type>(QEvent::registerEventType());

// 3. 发送事件
// sendEvent — 同步(等处理完才返回)
QApplication::sendEvent(receiver, new DataEvent("hello"));

// postEvent — 异步(加入事件队列,立即返回)
QApplication::postEvent(receiver, new DataEvent("hello"));

// 4. 接收事件
bool Receiver::event(QEvent *event) override {
    if (event->type() == DataEvent::Type) {
        auto *de = static_cast<DataEvent *>(event);
        qDebug() << de->message;
        return true;  // 已处理
    }
    return QWidget::event(event);
}

事件过滤器实战

// 场景:让 LineEdit 响应回车键
class EnterFilter : public QObject {
    bool eventFilter(QObject *obj, QEvent *event) override {
        if (event->type() == QEvent::KeyPress) {
            auto *ke = static_cast<QKeyEvent *>(event);
            if (ke->key() == Qt::Key_Return) {
                emit enterPressed();  // 自定义信号
                return true;           // 拦截事件
            }
        }
        return false;  // 继续传播
    }
signals:
    void enterPressed();
};

auto *filter = new EnterFilter(edit);
edit->installEventFilter(filter);
connect(filter, &EnterFilter::enterPressed, [=]() {
    // 处理回车
});

常见陷阱

// 陷阱 1: 忘记调用父类事件处理器
void MyWidget::mousePressEvent(QMouseEvent *e) override {
    // 处理...但没调 QWidget::mousePressEvent(e)
    // → setFocus、右键菜单等默认行为丢失
}

// 陷阱 2: eventFilter 返回 true 会吞掉事件
bool eventFilter(QObject *obj, QEvent *event) override {
    if (event->type() == QEvent::KeyPress)
        return true;  // ❌ 按键被吃了,子控件永远收不到
    return false;
}

// 陷阱 3: postEvent 的内存管理
QApplication::postEvent(obj, new MyEvent);  // ✅ Qt 自动 delete
// sendEvent 也一样 — 不要手动 delete 事件对象