AKTK-Lab

Back

信号槽机制#

信号槽是一种类型安全的对象间通信的机制。当一个对象状态发生变化时,发出一个信号 signal通知事件,其他对象可以连接这个信号到一个 slot 槽函数上响应这些事件,从而实现松耦合的通信。信号槽机制替代传统的、不安全的回调函数(如函数指针),槽函数可以是虚函数。

实现原理:单线程主要是由观察者模式与函数指针的联合使用,多线程本质上是事件机制

连接方法:UI信号槽自动连接、Lamda表达式、自定义信号槽

连接方式:

  • 自动连接(Auto Connection) :默认方式,Qt 会自动决定信号与槽是同步还是异步调用,取决于信号和槽是否在同一线程。
  • 直接连接(Direct Connection) :信号发出时直接在发出者所在的线程中调用槽。
  • 队列连接(Queued Connection) :将信号的调用排入目标对象线程的事件队列,槽会在目标线程中执行。
  • 阻塞队列连接(BlockingQueuedConnection) :(信号和槽必须在不同的线程中,否则就产生死锁):这个是完全同步队列,只有槽线程执行完成才会返回,否则发送线程也会一直等待,相当于是不同的线程可以同步起来执行
  • 独立连接(Unique Connection) :确保信号和槽之间的连接是唯一的,避免重复连接。

优点:

  • 松耦合 :信号与槽之间不直接依赖,便于扩展和修改。
  • 线程安全 :在多线程环境下,Qt 会自动处理信号与槽的同步。
  • 易于维护 :信号与槽的方式使得代码结构清晰,维护性好。

缺点:

  • 性能开销 :信号和槽的调用机制会增加一定的性能开销。
  • 调试困难 :如果连接不当,可能会导致难以追踪的问题。

一个信号可以连接多个槽,信号发射时,所有连接的槽会依次被调用。一个槽可以响应多个信号,只要它的参数签名与信号匹配。

自定义信号和槽:

  • 自定义信号:在类的 signals 部分使用 signals 关键字定义
  • 自定义槽:在类的 public slotsprivate slots 部分定义

元对象系统(Meta-Object System)#

https://www.bilibili.com/video/BV12GqWYkEZ4/?spm_id_from=333.337.search-card.all.click&vd_source=9d28f0e4734f1bde4c84c3169e3a429d

元对象系统是 Qt 提供的一种机制,用于支持信号与槽、动态属性、类型识别等功能。 它依赖于 Q_OBJECT 宏,并通过元对象编译器(MOC)生成元对象信息,使得 Qt 可以在运行时获取对象的类型信息。

  • 组成:QObject 类,MOC,信号与槽机制
  • 作用:提供信号槽机制实现对象之间的高效通信,动态属性、类型识别的功能

Q_OBJECT#

Q_OBJECT 宏启用 Qt 的元对象系统,支持信号与槽机制、动态属性、对象反射等功能。每个使用信号与槽的类都必须声明该宏,以便 MOC 生成相关的元对象代码。

元对象编译器(MOC)#

MOC 是 Qt 提供的工具,负责处理含有 Q_OBJECT 宏的类。它会生成包含元对象信息的代码,以支持信号与槽、动态属性等功能。生成的代码会被编译并链接到最终的应用程序中。

内存管理#

Qt 使用 父子对象机制 来自动管理内存。每个对象如果有父对象,父对象会在销毁时自动销毁所有子对象。没有父对象的对象需要手动调用 delete 来销毁

对象树机制#

Qt 的 对象树机制 是一种父子对象关系管理方式。每个 QObject 对象都有一个父对象,父对象会自动销毁其所有子对象。父子关系在内存管理中非常重要,可以简化资源管理,避免手动 delete

  • 父对象销毁了子对象一起被销毁,避免内存泄漏
  • 智能指针 QSharedPointer QWeakPointer,处理对象之间引用的复杂情况
  • 内存分配策略及优化

deleteLater() 延迟销毁对象#

deleteLater() 函数将对象的销毁操作推迟到当前事件处理完毕后执行,避免在事件处理过程中删除对象引起的崩溃或未定义行为。常用于异步删除对象。

事件处理机制#

Qt 的事件机制基于事件循环和事件队列,所有事件都会先进入队列,然后依次分发到合适的控件。

事件传递的顺序:

  1. 事件首先由事件源对象接收并放入事件队列
  2. 事件队列中的事件会依次传递给目标对象,首先传递到事件接收对象,每个继承 QObject 的都可以通过重写事件处理函数来处理特定的事件
  3. 如果该对象处理了事件,则停止传递,否则,事件会传递到该对象的父对象,直到根对象
  4. 如果事件一直没有被处理,Qt会使用默认的事件处理机制

自定义事件处理:继承 QEvent

event()eventFilter()#

  • event() 是每个 QObject 的成员函数,主要用于处理传递给对象的事件。可以通过重写此函数来处理不同类型的事件(如鼠标、键盘等)
  • eventFilter() 用于设置事件过滤器,拦截传递到目标控件的事件,在事件到达目标控件前对其进行处理。它是通过安装到目标对象上的方式来拦截事件的

事件过滤器的实现#

  1. 创建一个类继承 QObject 并重写 eventFilter()
  2. 使用 installEventFilter() 方法将事件过滤器安装到目标控件上,这样目标控件的事件就会先经过过滤器

GUI常用事件函数#

  • paintEvent() :用于绘制控件的外观,当控件需要重新绘制时(如大小变化或需要更新显示)会调用此函数。
  • resizeEvent() :当控件的大小发生变化时调用,通常用来调整控件的布局或重新绘制内容。
  • mousePressEvent() :当鼠标按下时调用,用于处理鼠标点击事件,如点击按钮或拖动控件等。

事件循环#

Qt 的事件循环机制是指程序启动后,通过一个循环不断从事件队列中取出事件并分发处理的过程。

事件循环由 QEventLoop 实现,应用程序启动后进入事件循环,只有在事件循环运行时,Qt 的界面刷新、信号槽、定时器等机制才能正常工作。

exec() 启动事件循环#

  • QApplication::exec() :启动整个应用程序的主事件循环
  • QDialog::exec() :启动一个局部事件循环,使对话框以模态方式运行

当调用 exec() 后,程序会阻塞在此函数中,直到事件循环结束,函数才会返回

QCoreApplication::processEvents()#

手动处理当前事件队列中的事件,常用于在耗时操作中避免界面卡死 / 强制刷新界面 / 临时处理待处理的事件,它会立即处理当前队列中的事件,但不会进入完整的事件循环

缺点:

  • 频繁调用可能导致逻辑混乱
  • 不能替代事件循环
  • 更推荐使用多线程或异步方式解决耗时问题

定时器#

定时器的实现方式#

  1. QTimer 最常用的定时器方式,基于事件循环,支持单次和周期定时
  2. QObject::startTimer() / timerEvent() 通过对象启动一个底层定时器,在 timerEvent() 中处理超时事件,适合轻量级定时需求
  3. QBasicTimer 是对 startTimer() 的封装,效率较高,常用于对性能要求较高的场景
  4. QElapsedTimer 用于测量时间间隔,不触发超时事件,主要用于性能统计和耗时测量
  5. 系统定时器(结合线程)在子线程中配合 QTimer 或系统接口实现更高精度的定时控制

QTimer#

QTimer 是 Qt 中最常用的定时器类,使用方式主要包括以下几步:

  1. 创建 QTimer 对象
  2. 连接 timeout() 信号到槽函数
  3. 调用 start() 启动定时器,设置时间间隔(毫秒)

QTimer 定时器启动后,每到达设定的时间间隔就会触发 timeout 信号,在槽函数中处理定时任务

QTimer timer; // 定时器定时刷新
QObject::connect(&timer, &QTimer::timeout, timer_task);
timer.start(5000); // 5s 执行一次 事件驱动
cpp

可以使用 setSingleShot(true) 设置为单次定时器,也可以使用静态函数 QTimer::singleShot() 实现一次性延时执行

startTimer()#

startTimer()QObject 提供的底层接口,而 QTimer 是对定时器机制的高级封装。

主要区别有:

  • 使用方式不同:startTimer() 需要重写 timerEvent() 来处理定时事件,QTimer 使用信号与槽机制
  • 功能丰富程度不同:QTimer 支持单次定时、暂停、重启等功能,startTimer() 功能相对简单
  • 可读性和维护性:QTimer 更符合 Qt 编程风格,startTimer() 更偏底层,适合对性能敏感的场景

一般情况下推荐使用 QTimer ,除非有特殊性能或底层控制需求。

隐式共享#

对于两份内容相同的数据,不用重新开辟一份内存来存储,而是使用一份浅拷贝即只拷贝一个指针,这样两份数据就共用一份内存和数据,浅拷贝一次引用计数就增加,计数变成0的时候就删除共享的数据。

Qt part.1: Qt的基础概念和常用机制
https://astro-pure.js.org/blog/qt-basic
Author Akatsuki
Published at June 6, 2026