Qt Event

事件循环

事件循环(Event Loop)是 Qt 框架的核心机制,主要负责管理应用程序的事件分发、信号槽调用、定时器、网络通信等异步操作。

事件循环是一个无限循环,不断检查是否有新的事件(如鼠标点击、键盘输入、网络数据到达)需要处理,并分发给对应的对象,主要组成部分:

  • 事件队列(Event Queue):存储所有待处理的事件,按照先进先出的原则处理
  • 事件分发器:(Event Dispatcher):负责从队列中取出事件,将事件发送到目标对象
  • 事件处理器:(Event Handler):对象中处理特定事件的方法(如 mousePressEvent())

在 Qt 应用程序中,Qt 的主事件循环由 QCoreApplication::exec() 或 QApplication::exec() 启动,一般位于 main() 函数的末尾。

c++ 复制代码
int main(int argc, char *argv[]) 
{
    QApplication app(argc, argv);

    MainWindow window;
    window.show();

    return app.exec(); // 进入主事件循环
}

如果没有 app.exec(),程序会立即退出,UI 无法显示,信号槽也无法正常工作。

对于非 GUI 程序(如后台服务),使用 QCoreApplication::exec()。

事件循环的应用场景

保持 UI 响应

事件循环允许 GUI 程序在等待用户输入时不会卡死(例如一个窗口调用show方法以模态框的方式显示后不会阻塞(这一点跟winform、c++builder的form不太一样));如果没有事件循环,UI 会冻结,无法响应用户操作。

异步网络通信

QTcpSocket、QUdpSocket 依赖事件循环来接收数据。

c++ 复制代码
QTcpSocket socket;
socket.connectToHost("127.0.0.1", 1800);
QObject::connect(&socket, &QTcpSocket::readyRead, [&]() {
	qDebug() << "Data received:" << socket.readAll();
});

readyRead 信号只能在事件循环运行时触发。

定时器

QTimer 依赖事件循环

c++ 复制代码
QTimer timer;
timer.setInterval(1000); // 1秒触发一次
QObject::connect(&timer, &QTimer::timeout, []() {
	qDebug() << "timer triggered!";
});
timer.start(); // 需要事件循环才能运行

跨线程通信

如果对象在不同线程,信号槽调用会通过目标线程的事件循环进行调度

c++ 复制代码
Worker worker;
worker.moveToThread(&workerThread); // 将 worker 移到子线程
QObject::connect(this, &MainWindow::startWork, &worker, &Worker::doWork);
workerThread.start();

当 emit startWork() 时,doWork() 会在 workerThread 的事件循环中被调用。

手动控制事件循环

局部事件循环

有时需要临时进入事件循环(如等待用户输入)

c++ 复制代码
QEventLoop loop;
QTimer::singleShot(3000, &loop, &QEventLoop::quit); // 3秒后退出
loop.exec(); // 阻塞,直到 quit() 被调用

多线程与事件循环

每个线程可以有独立的事件循环

c++ 复制代码
class WorkerThread : public QThread {
protected:
    void run() override 
    {
        QTimer timer;
    	connect(&timer, &QTimer::timeout, []() { qDebug() << "Thread tick"; });
        timer.start(1000);
        exec(); // 启动子线程的事件循环
    }
};

主线程的事件循环由 QApplication::exec() 启动。

子线程的事件循环由 QThread::exec() 启动。

跨线程信号槽依赖目标线程的事件循环。

扩展:关于qt线程的一些总结

  • QThread 的run函数相当于程序main函数,只有在run函数中调用的函数才算是在子线程中执行;
  • 建议使用moveToThread,这样简单且安全,不推荐继承QThread

事件

Qt 的事件系统是一个事件驱动架构 ,它让程序能够响应用户操作(鼠标、键盘等)、系统消息(窗口调整、定时器)、以及自定义事件。所有事件都被封装为 QEvent 对象,通过事件循环分发给对应的 QObject(尤其是 QWidget)处理。

Qt 的事件驱动实际上是面向对象的事件传递系统(继承 + 虚函数),与.net框架的事件驱动有很大区别(.net的事件驱动类似与Qt的信号、槽机制,属于发布/订阅模式)。

QEvent

  • 所有事件的基类。
  • 定义了 Type 枚举,表示事件类型,比如鼠标事件、键盘事件、定时器事件、绘图事件等。
  • 支持事件"接受/忽略"机制,控制事件是否继续传递。
c++ 复制代码
void QEvent::accept();   // 设置事件已处理
void QEvent::ignore();   // 设置事件未处理
bool QEvent::isAccepted() const;

QObject::event(QEvent *event)

  • 事件处理的统一入口。
  • 默认实现根据事件类型调用对应的虚函数,如 mousePressEvent(), keyPressEvent()
  • 可重写此函数实现自定义事件处理逻辑。
c++ 复制代码
bool MyWidget::event(QEvent *event) override {
    if (event->type() == QEvent::KeyPress) {
        // 自定义键盘事件处理
        return true;
    }
    return QWidget::event(event);  // 调用父类默认处理
}

事件过滤器(Event Filter)

  • 允许一个对象监听和拦截另一个对象的事件。
  • 通过 installEventFilter() 安装。
  • 通过重写 eventFilter() 实现事件拦截与处理。
c++ 复制代码
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);//MainWindow中有一个QTextEdit控件,拦截它的键盘按下的事件。
}
 
bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
    if (obj == textEdit)  {
        if (event->type() == QEvent::KeyPress) {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
            qDebug() << "you press" << keyEvent->key();
            //事件不再进行传播,拦截
            return true;
        }  else {
            return false;//继续传播
        }
    } 
    else  {
        //当不确定是否继续传播时,按照父类的方法来处理
        //即调用父类的evenFilter函数
        return QMainWindow::eventFilter(obj, event);
    }
}
相关推荐
用户8055336980319 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner20 小时前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner9 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00612 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术12 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript