03-信号与槽.md 30 KB


title: 信号与槽 tags: [嵌入式Linux, Qt, 信号与槽, signal, slot, connect, Q_OBJECT, moc, 元对象系统, QtCreator, 快捷键, IMX6ULL] created: 2026-09-18 updated: 2026-09-18

pdf_ref: "《【正点原子】I.MX6U嵌入式Qt开发指南V1.2》第五章 Qt信号与槽、第六章 Qt Creator的使用技巧;例程 Embedded-Qt-Tutorial/Qt/01/03_signal_slot_example"

信号与槽

💡 关联知识:[[05-Qt应用开发/02-Qt入门与项目结构]](Q_OBJECTui_mainwindow.h、Qt Designer 的可视化连接都在这一篇收口)、[[05-Qt应用开发/04-Qt控件详解]](每个控件都有大量内置信号与槽);延伸阅读:[[编程学习项目/使用Qt和C++构造UI界面/4. 信号和槽机制]]

信号与槽(Signal & Slot)是 Qt 编程的基础,也是 Qt 的一大创新。有了它,界面各组件之间的交互变得直观而简单。本篇从机制、自定义信号、自定义槽、连接、内置信号槽讲到 Qt Creator 的快捷键与帮助文档,并给出一个完整可运行的自定义信号槽例程。

本章起,教程不再使用 Qt Designer 开发,新建项目时取消勾选 *.ui 文件。原因是:控件少时 Designer 方便直观,但控件一多就不好管理,每次加控件都要打开 .ui;而且信号槽连接藏在生成的 setupUi() 里或设计器的连接模式里,不如代码直观。统一用代码画界面,能锻炼布局能力和代码逻辑能力。


1. Qt 信号与槽机制(5.1)

1.1 信号与槽是什么

概念 定义 例子
信号(Signal) 在特定情况下被发射的事件 PushButton 被鼠标单击时发射 clicked();ComboBox 列表项变化时发射 CurrentIndexChanged()
槽(Slot) 对信号响应的函数 与信号关联后,信号发射时自动执行

槽与普通 C++ 函数的相同点:可以定义在类的任何部分(publicprivateprotected),可以有任意参数,也可以被直接调用。不同点:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行

GUI 程序设计的主要内容,就是对界面上各组件信号的响应——只需知道什么情况下发射哪些信号,合理地去响应和处理即可。

1.2 connect 基本格式

信号与槽关联用 QObject::connect() 函数实现:

QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

connect()QObject 类的静态函数,而 QObject 是所有 Qt 类的基类,实际调用时可以忽略前面的限定符:

connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
参数 含义
sender 发射信号的对象名称
SIGNAL(signal()) 信号名称,信号可看作特殊函数,需要带括号,有参数时要写参数
receiver 接收信号的对象名称
SLOT(slot()) 槽函数名称,需要带括号,有参数时要写参数

SIGNALSLOT 是 Qt 的,用于指明信号和槽,并把它们的参数转换为相应的字符串。

在 Qt Designer 例程 02_designer_example 编译生成的 ui_mainwindow.h(位于 build 目录)里,setupUi() 中有这样一句:

QObject::connect(pushButton, SIGNAL(clicked()), MainWindow, SLOT(close()));

它的作用就是把 pushButtonclicked() 信号与窗体 MainWindowclose() 槽关联,所以单击按钮就会执行窗口的 close()这解答了上一篇"可视化连接藏在哪里"的疑问——它在 UI 文件编译后生成的代码里。

1.3 信号与槽的规则

规则 说明 示例
一个信号可连接多个槽 槽按连接建立的顺序依次执行 connect(btn, SIGNAL(clicked()), this, SLOT(hide()));connect(btn, SIGNAL(clicked()), this, SLOT(close()));
多个信号可连接同一个槽 任一信号发射都执行该槽 三个 pushButton 的 clicked() 都连到 close()
一个信号可连接另外一个信号 信号发射时也发射另一个信号 connect(btn, SIGNAL(objectNameChanged(QString)), this, SIGNAL(windowTitleChanged(QString)));
参数匹配 严格情况下信号与槽的参数个数和类型需一致,至少信号的参数不能少于槽的参数;不匹配会编译错误或运行错误 有参数的信号连接无参槽通常可以,反之不行
必须加 Q_OBJECT 使用信号与槽的类,类定义中必须加入宏 Q_OBJECT(特别重要) 见下方 mainwindow.h
执行时机 信号发射时,关联槽通常被立即执行,像正常调用函数;所有槽执行完,才执行发射信号处后面的代码 顺序执行

"一个信号连多个槽"的补充:如果两个槽一个是 hide()、一个是 close(),由于 close() 写在后面且执行后程序退出,hide() 其实已无意义——但它确实先执行了,因为连接在前。

"一个信号连接另外一个信号"意味着 connect 万物皆可连,非常好用。

1.4 信号槽的发送与接收方向

flowchart LR
    subgraph 发送方
        S["sender 对象"] --> SIG["signal() 信号"]
    end
    subgraph 接收方
        R["receiver 对象"] --> SL["slot() 槽 / signal() 信号"]
    end
    SIG -->|connect| SL

要点:发送者与发送的信号在一起,接收者与接收的槽/信号在一起,顺序不能在 connect() 里写乱。

1.5 断开连接 disconnect

初学者基本用不到断开连接,用到时用 disconnect(),它被重载了好几个函数,典型原型:

bool QObject::disconnect(const QObject *sender, const char *signal,
                         const QObject *receiver, const char *method)
需求 静态函数写法 非静态重载写法
断开与 myObject 连接的一切信号或槽 disconnect(myObject, 0, 0, 0); myObject->disconnect();
断开所有连接到特定信号的东西 disconnect(myObject, SIGNAL(mySignal()), 0, 0); myObject->disconnect(SIGNAL(mySignal()));
与指定的接收者断开连接 disconnect(myObject, 0, myReceiver, 0); myObject->disconnect(myReceiver);

其中 0 表示"任意匹配"(C 风格的 NULL)。

1.6 connect 的返回值

connect() 返回一个 QMetaObject::Connection 对象,可用于判断连接是否成功、或稍后断开设定的这一条连接:

QMetaObject::Connection c =
        connect(pushButton, SIGNAL(clicked()), this, SLOT(pushButtonClicked()));
if (c) {
    /* 连接成功 */
}
disconnect(c);   // 只断开这一条连接

相比 disconnect(myObject, 0, 0, 0) 这种"一刀切"式断开,保存并断开具体的 Connection 更精确。

1.7 信号槽 vs 回调函数 vs 事件

⚠️ 来源说明:本节不属于《I.MX6U嵌入式Qt开发指南》内容,为扩展知识。

对比项 信号与槽 回调函数(函数指针) Qt 事件(event)
耦合度 发送者只负责 emit,不关心谁接收,低耦合 调用者必须持有并注册回调指针,耦合较高 QApplication 分发到目标对象
连接数量 一个信号可连多个槽;多个信号可连同一槽 通常一个回调只能挂一个函数 一个事件到达一个对象
类型安全 函数指针写法编译期检查;字符串写法运行期检查 编译期检查 需重写 event() 或具体 xxxEvent()
跨线程 QueuedConnection 自动支持 需自行加锁/投递 需自行 postEvent
典型场景 控件交互、模块解耦 标准库/底层库回调 重绘、鼠标键盘、自定义事件

信号槽本质上是 Qt 在"回调"之上加了一层元对象系统,换来松耦合 + 多对多 + 跨线程三个好处,代价是 moc 生成代码和轻微的性能开销。


2. 元对象系统与 moc(原理)

⚠️ 来源说明:本节不属于《I.MX6U嵌入式Qt开发指南》内容,为扩展知识。

原书第五章强调"使用信号槽的类必须加 Q_OBJECT"并提到了 ui_mainwindow.h 等生成文件,但未展开 moc 的内部原理。

2.1 为什么信号只需声明、无需定义

信号在类里用 signals: 关键字声明,只写函数原型,不写函数体。这是因为信号不是由你实现的,而是由 Qt 的 moc(Meta-Object Compiler,元对象编译器) 根据声明自动生成实现代码。

流程如下:

  1. qmake 扫描工程;
  2. 对包含 Q_OBJECT 宏的头文件调用 moc,生成 moc_*.cpp
  3. moc_*.cpp 与你的 *.cpp 一起编译链接。

    flowchart LR
    H["mainwindow.h\nclass MainWindow : QObject { Q_OBJECT; signals: ... }"] -->|moc| M["moc_mainwindow.cpp\n元对象表 + 信号实现 + qt_metacall"]
    M --> C["与 mainwindow.cpp 一起编译"]
    C --> BIN["可执行程序"]
    

2.2 moc 生成的核心内容

生成内容 作用
staticMetaObject 类的元对象,保存类名、信号槽签名、父类信息等
信号的实现函数 调用 QMetaObject::activate() 发射信号
qt_metacall 按索引分发信号与槽调用
metaObject() 覆写 让运行时能查询类型信息

因此 Qt 能在运行时通过字符串签名SIGNAL(...)/SLOT(...) 宏最终转为字符串)查找并调用对应函数,这就是信号槽跨对象、跨线程也能工作的基础。

2.3 Q_OBJECT 与其它 Qt 扩展

  • Q_OBJECT:启用元对象系统,位置必须在类定义的第一行
  • signals::声明信号区,映射为 public,只能声明不能定义。
  • slots: / public slots: / private slots::声明槽区,本质是普通函数,加宏后可被 connect 按字符串查找。
  • emit:发射信号的关键字,其实是空宏,仅用于语义标记;也可以省略不写,但不建议。
  • Q_OBJECT 缺失是新手最常见的错误之一,症状往往是 connect 编译通过但运行时不触发,或报 undefined reference to vtable

3. 如何在项目里创建信号(5.2)

新建项目 03_signal_slot_example,步骤同 3.6,取消勾选 *.ui 文件(原因见本章开头)。

信号只需声明,无需定义,所以只需在 mainwindow.h 里声明即可。下面创建一个 void pushButtonTextChanged(); 信号——命名最好贴合信号本身的含义,这个信号表示"按钮文本发生改变后"。

mainwindow.h(添加信号后):

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
/* 引入QPushButton */
#include <QPushButton>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:
    /* 声明一个信号,只需声明,无需定义 */
    void pushButtonTextChanged();

};
#endif // MAINWINDOW_H

说明(第 16~18 行):声明一个信号 void pushButtonTextChanged();,可以看到信号无需 public 等关键字修饰


4. 如何在项目中创建槽(5.3)

创建槽的方法:在 mainwindow.h声明槽,在 mainwindow.cpp实现槽。声明槽必须写定义(函数体实现),否则编译会报错。

槽的特点:

  1. 槽可以是任何成员函数、普通全局函数、静态函数;
  2. 槽函数和信号的参数和返回值要一致

由于第 5.2 节声明的信号 void pushButtonTextChanged(); 无返回值、无参数,所以声明的槽函数也必须 void 且无参数。此外再声明一个 QPushButton 对象 pushButton(可简写 btn,按习惯即可,只要不让人看不懂),以及一个处理按钮点击的槽。

mainwindow.h(添加槽函数后):

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
/* 引入QPushButton */
#include <QPushButton>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();


signals:
    /* 声明一个信号,只需声明,无需定义 */
    void pushButtonTextChanged();

public slots:
    /* 声明一个槽函数 */
    void changeButtonText();

    /* 声明按钮点击的槽函数 */
    void pushButtonClicked();

private:
    /* 声明一个对象pushButton */
    QPushButton *pushButton;
};
#endif // MAINWINDOW_H

说明:

  • 第 21 行 public slots::声明一个槽函数 void changeButtonText();
  • 第 26 行:声明槽函数 void pushButtonClicked();
  • 第 30 行:声明一个 QPushButton 对象 pushButton

mainwindow.cpp 里实现这两个槽,并实例化 pushButton

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 设置窗体的宽为800,高为480 */
    this->resize(800,480);

    /* 实例化pushButton对象 */
    pushButton = new QPushButton(this);

    /* 调用setText()方法设定按钮的文本 */
    pushButton->setText("我是一个按钮");
}

MainWindow::~MainWindow()
{

}

/* 实现按钮点击槽函数 */
void MainWindow::pushButtonClicked()
{
    /* 使用emit发送信号 */
    emit pushButtonTextChanged();
}

/* 实现按钮文本改变的槽函数 */
void MainWindow::changeButtonText()
{
    /* 在槽函数里改变按钮的文本 */
   pushButton->setText("被点击了!");
}

逐行解释:

  • 第 7 行:设置程序窗体大小为 800×480,不设置则为最小分辨率。800×480 正是正点原子某款 RGB LCD 屏的分辨率,本教程统一按 800×480 开发;实际开发请以硬件实际分辨率为准,不建议低于该分辨率(显示内容过少)。
  • 第 10 行:在堆中实例化 pushButton,并指定父对象为 this。因为头文件里声明的是指针类型,所以用 newQPushButton(this) 的原型其实是 QPushButton(QWidget *parent = nullptr)
  • 第 13 行:setText() 设定按钮文本。
  • 第 26~30 行:实现按钮点击槽函数,用 emit 发送自定义信号 pushButtonTextChanged()
  • 第 33~37 行:实现按钮文本改变的槽函数,把按钮文本改为"被点击了!"。

QPushButton 的构造函数有多个重载(C++ 重载在 Qt 中的体现):

QPushButton(QWidget *parent = nullptr)
QPushButton(const QString &text, QWidget *parent = nullptr)
QPushButton(const QIcon &icon, const QString &text, QWidget *parent = nullptr)

5. 如何在项目中连接信号与槽(5.4)

第 5.2、5.3 节只是声明了信号和定义了槽,要真正生效还需 connect

connect(pushButton, SIGNAL(clicked()), this, SLOT(pushButtonClicked()));
connect(this, SIGNAL(pushButtonTextChanged()), this, SLOT(changeButtonText()));

如何判断发送者和接收者该写谁?

  • pushButtonClicked() 是本类定义的槽,changeButtonText() 也是本类的槽 → 接收者写 this
  • pushButtonTextChanged() 是本类定义的信号 → 发送者也写 this
  • 很多初学者被"信号的发送者和槽的接收者究竟写谁"绕住,细读信号槽定义和上面两条即可理清。

5.1 完整可运行例程

03_signal_slot_example.pro(注意 FORMS += 后为空,因为不使用 ui):

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

DEFINES += QT_DEPRECATED_WARNINGS

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS +=

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
/* 引入QPushButton */
#include <QPushButton>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:
    /* 声明一个信号,只需声明,无需定义 */
    void pushButtonTextChanged();

public slots:
    /* 声明一个槽函数 */
    void changeButtonText();

    /* 声明按钮点击的槽函数 */
    void pushButtonClicked();

private:
    /* 声明一个对象pushButton */
    QPushButton *pushButton;
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 设置窗体的宽为800,高为480 */
    this->resize(800,480);

    /* 实例化pushButton对象 */
    pushButton = new QPushButton(this);

    /* 调用setText()方法设定按钮的文本 */
    pushButton->setText("我是一个按钮");

    /* 信号与槽连接 */
    connect(pushButton, SIGNAL(clicked()), this, SLOT(pushButtonClicked()));
    connect(this, SIGNAL(pushButtonTextChanged()), this, SLOT(changeButtonText()));
}

MainWindow::~MainWindow()
{

}

/* 实现按钮点击槽函数 */
void MainWindow::pushButtonClicked()
{
    /* 使用emit发送信号 */
    emit pushButtonTextChanged();
}

/* 实现按钮文本改变的槽函数 */
void MainWindow::changeButtonText()
{
    /* 在槽函数里改变按钮的文本 */
   pushButton->setText("被点击了!");
}

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

5.2 执行流程

第 16、17 行完成连接后,整个流程如下:

sequenceDiagram
    participant U as 用户
    participant B as pushButton
    participant M as MainWindow
    U->>B: 单击按钮
    B->>M: clicked() 信号
    M->>M: pushButtonClicked() 槽执行
    M->>M: emit pushButtonTextChanged()
    M->>M: changeButtonText() 槽响应
    M->>B: setText("被点击了!")
    B-->>U: 按钮文本改变

即:点击按钮 → 触发 pushButtonClicked() → 槽里发送 pushButtonTextChanged() 信号 → changeButtonText() 槽响应 → 按钮文本由"我是一个按钮"变成"被点击了!"。

5.3 构建运行

cd 03_signal_slot_example
/opt/Qt5.12.9/5.12.9/gcc_64/bin/qmake 03_signal_slot_example.pro
make
./03_signal_slot_example

或在 Qt Creator 中直接 Ctrl + R。程序虽简单,但完整实现了自定义信号与自定义槽


6. 学会使用 Qt 类的信号与槽(5.5)

Qt 里有大量自定义好的信号与槽,基本够用;找不到想要的信号与槽时,再按 5.2~5.4 节自定义。

怎么知道某个类有哪些信号与槽?

  1. 代码跳转:在 connect 语句里按住 Ctrl 再点击 clicked(),跳转到该信号的定义处。例如进入 QPushButton 的定义,可看到它不止 clicked,还有很多其他信号和槽函数。返回上一步用 Alt + 左方向键
  2. 帮助文档:实际开发中更常用。Qt 帮助文档里能看到类的 Properties、Public Functions、Public Slots、Signals 等,是最权威的资料(下一节详述)。

7. connect 的其它写法与连接类型

⚠️ 来源说明:本节不属于《I.MX6U嵌入式Qt开发指南》内容,为扩展知识。

原书第五章只讲解 Qt4/字符串式 SIGNAL/SLOT 写法,并声明"Qt4 与 Qt5 写法有区别、本教程对 Qt4 写法不做讲解"。本节介绍的函数指针写法、lambda 槽、连接类型为 Qt5 扩展知识。

7.1 两种连接写法对比

写法一:字符串签名(原书用法,继续有效)

connect(pushButton, SIGNAL(clicked()), this, SLOT(pushButtonClicked()));
  • 编译期不检查信号/槽是否存在,拼写错误要到运行时才在控制台报 "No such signal/slot"。
  • 参数用字符串描述,宏展开后作为元对象查找的 key。

写法二:函数指针(Qt5 推荐,编译期检查)

connect(pushButton, &QPushButton::clicked, this, &MainWindow::pushButtonClicked);
  • 编译期即可发现拼写错误、参数不匹配,更安全。
  • 不需要 SIGNAL/SLOT 宏,不需要写参数类型字符串。
  • 重载信号需要 qOverloadstatic_cast 消歧,例如 &QComboBox::currentIndexChanged 有重载版本。
  • 不能用于"信号连信号"中目标也是信号的情况?——可以,函数指针同样支持信号连信号。
对比项 SIGNAL/SLOT 字符串写法 函数指针写法
错误检查时机 运行期 编译期
是否需要 SIGNAL/SLOT 需要 不需要
重载信号 字符串自动区分 qOverload/static_cast
可否给槽传 lambda 可以
Qt5/Qt6 支持 支持(保留) 推荐

7.2 槽可以是 lambda 表达式

函数指针写法下,接收者可以省略,直接用 lambda 作为槽:

connect(pushButton, &QPushButton::clicked, this, [=]() {
    pushButton->setText("被点击了!");
});
  • [=] 按值捕获外部变量,[this] 捕获当前对象指针。
  • lambda 槽无法用 SIGNAL/SLOT 字符串写法连接。
  • 简洁,非常适合一次性、简单的响应逻辑。

7.3 连接类型 Qt::ConnectionType

connect 的第 5 个可选参数控制投递方式:

connect(sender, SIGNAL(sig()), receiver, SLOT(slot()), Qt::ConnectionType type);
枚举值 行为 线程关系要求
Qt::AutoConnection(默认) 发送者与接收者同线程→Direct;跨线程→Queued 通用
Qt::DirectConnection 立即在发送者所在线程直接调用槽 任意
Qt::QueuedConnection 把调用封装为事件投递到接收者线程的事件队列,异步执行 接收者线程须有事件循环
Qt::BlockingQueuedConnection 同 Queued,但发送者阻塞直到槽执行完 同线程使用会死锁
Qt::UniqueConnection 与上述之一按位或使用,防止重复连接同一对信号槽 通用
flowchart TD
    SIG["信号发射"] --> SAME{"发送者与接收者\n是否同线程?"}
    SAME -->|同线程| D["DirectConnection\n立即调用"]
    SAME -->|跨线程| Q["QueuedConnection\n投递到接收者事件队列"]
    Q --> EV["接收者事件循环取出\n在接收者线程执行槽"]

要点:默认 AutoConnection 已能覆盖绝大多数场景;跨线程时 Qt 自动用队列连接,因此不要手动 new 线程对象后直接在错误线程调用其函数UniqueConnection 常用来避免同一连接被建立多次导致槽被重复执行。


8. Qt Creator 的使用技巧(第六章)

8.1 快捷键(6.1)

想知道某个按钮的快捷键,把鼠标移到按钮上即可看到提示(需在英文输入法下)。

一般快捷键

功能 快捷键
新建文件或项目(N) Ctrl + N
关闭当前窗口/文件 Ctrl + W
关闭所有文件 Ctrl + Shift + W
关闭当前文件(Windows) Ctrl + F4
运行 Ctrl + R
返回上一级(常用于跳转代码) Alt + ←
进入下一级(常用于跳转代码) Alt + →
Qt 自动排版对齐代码 Ctrl + I
减小字体 Ctrl + -(或 Ctrl + 滚轮向下
增大字体 Ctrl + +(或 Ctrl + 滚轮向上
重置字体大小 Ctrl + 0
折叠 Ctrl + <

常用编辑快捷键

功能 快捷键
展开 Ctrl + >
复制行 Ctrl + Ins
复制到行下 Ctrl + Alt + Down
复制到行上 Ctrl + Alt + Up
在当前行上方插入新行 Ctrl + Shift + Enter
在当前行下方插入新行 Ctrl + Enter
查看剪切板历史 Ctrl + Shift + V
剪切行 Shift + Del
追加行 Ctrl + J
向下移动当前行 Ctrl + Shift + Down
向上移动当前行 Ctrl + Shift + Up
切换函数声明/定义 Ctrl + 鼠标左键 / Shift + F2
编辑信号和槽 F4
跳转至以 } 结尾的块 Ctrl + }
跳转至以 { 开始的块 Ctrl + {
打开类型层次窗口 Ctrl + Shift + T

快捷键可能与 Ubuntu/Windows 系统冲突。可在"工具 → 选项 → 环境 → 键盘"里修改:选中命令,按"Record"重新记录新组合键,再 Apply / OK。注意不要与系统快捷键重复(例如 Ctrl + M 显示红色即表示与系统冲突)。没有定义快捷键的命令,也可以按同样步骤自定义。

8.2 Qt 帮助文档的使用(6.2)

Qt 帮助文档在安装时已随 Qt 一起安装,位于"安装目录 / Qt5.12.9 / Docs /"下(html 文本,也可用浏览器打开);Qt Creator 内置了同一套文档。

在 Qt Creator 中查文档的步骤

  1. 点击左侧"帮助"。
  2. 把检索方式下拉选为"索引(Index)"。
  3. 输入关键字,如 QPushButton,也可只输入 QPush 匹配。
  4. 点击匹配项进入文档页。

如何阅读一个类的文档页

标题 内容
Properties 类有哪些属性
Public Functions 类有哪些公开函数(方法)
Public Slots 类有哪些可用槽
Signals 类有哪些信号
Detailed Description 详细说明与示例

查看某个方法的用法,直接单击进入即可。记得用 Alt + ← / Alt + → 在上下级文档间跳转。

Qt 帮助文档是最权威、最全面的中文/英文资料,任何 Qt 教程都没有它详细(尽管是英文);Qt 还自带大量示例可以参考。看文档要多练,并不是看会了就会了,实践起来才知道

flowchart LR
    A["不知道 QPushButton 有什么信号槽"] --> B["帮助 → 索引 → 输入 QPush"]
    B --> C["进入类文档页"]
    C --> D["Properties / Public Functions"]
    C --> E["Signals / Public Slots"]
    E --> F["Ctrl 点击 connect 中的函数名直接跳转"]

9. 常见错误排查

⚠️ 来源说明:本节不属于《I.MX6U嵌入式Qt开发指南》内容,为扩展知识。

现象 可能原因 排查手段
点击按钮没反应 漏写 connect,或连接写反了发送者/接收者 检查 connect(sender, SIGNAL(...), receiver, SLOT(...)) 顺序;确认在 ui->setupUi(this) 之后连接
编译报 undefined reference to vtable for ... 类里写了 Q_OBJECT 却没重新运行 qmake/moc Qt Creator 里执行"构建 → 执行 qmake",再重新构建;确认 .proHEADERS 包含该头文件
运行时报 QObject::connect: No such signal/slot SIGNAL/SLOT 字符串拼写错误、参数不一致、忘写括号 Ctrl 点击跳转核对签名;优先改用函数指针写法让编译期报错
槽执行了两次 同一对信号槽被 connect 了多次 Qt::UniqueConnection,或检查是否重复初始化
跨线程槽里操作界面崩溃 在子线程直接调用了 UI 对象 用信号槽(默认 AutoConnection)把结果投递回主线程再更新界面
自定义信号编译报 error: no matching function 信号只声明未定义?其实正常 信号本就不该写函数体;检查是否误把信号写成普通函数、或忘加 signals:
中文字符显示乱码 源文件编码或未配置中文输入 见 [[05-Qt应用开发/02-Qt入门与项目结构]] 的中文环境/输入法配置

10. 面试精选

⚠️ 来源说明:本节不属于《I.MX6U嵌入式Qt开发指南》内容,为扩展知识。

Q1:信号与槽是什么?它们和普通函数有什么关系?

:信号是在特定情况下被发射的事件(如按钮被点击发射 clicked());槽是对信号响应的函数。槽本身就是普通 C++ 函数,可以定义在 public/protected/private 下、可以有任意参数、也可以被直接调用;它的特殊之处仅在于能与信号关联,信号发射时被自动执行。信号则只声明不定义,由 moc 生成实现。

Q2:为什么使用信号与槽的类必须加 Q_OBJECT 宏?信号为什么只声明不实现?

Q_OBJECT 宏启用 Qt 的元对象系统,moc 依据它扫描并生成 moc_*.cpp,其中包含元对象表、信号的实现(内部调用 QMetaObject::activate)以及按索引分发调用的 qt_metacall 等。正因为信号由 moc 自动实现,类里只写函数原型即可,无需(也不能)自己写函数体。缺少 Q_OBJECT 会导致信号槽无法工作,常见报错是 undefined reference to vtable 或运行时 "No such signal/slot"。

Q3:SIGNAL/SLOT 宏写法与函数指针写法有什么区别?

SIGNAL/SLOT 宏把函数签名转成字符串,connect 在运行期按字符串查找匹配,编译期不检查,拼写错误要到运行时才发现;函数指针写法(如 connect(btn, &QPushButton::clicked, this, &MainWindow::slot))在编译期就能检查信号/槽是否存在、参数是否匹配,更安全,也能连接 lambda 作为槽;重载信号时函数指针需要 qOverloadstatic_cast 消歧。

Q4:信号与槽的参数、返回值有什么要求?一个信号能连接几个槽?连接顺序如何?

:槽函数的参数和返回值要与信号一致;严格情况下二者参数个数和类型需匹配,至少信号的参数不能少于槽的参数,否则会编译或运行错误。一个信号可以连接多个槽,也可以连接另一个信号;多个信号也可以连接同一个槽。一个信号连多个槽时,槽按连接建立时的顺序依次执行,并且所有槽执行完才继续执行发射信号处后面的代码(默认直接调用情形)。

Q5:跨线程使用信号槽时要注意什么?Qt::AutoConnection 的行为是什么?

connect 的第五个参数可指定连接类型。默认 Qt::AutoConnection 会判断发送者与接收者是否在同一线程:同线程按 DirectConnection 立即在发送者线程调用槽;跨线程按 QueuedConnection 把调用封装成事件投递到接收者线程的事件队列,由接收者的事件循环取出执行,从而保证槽在接收者线程执行,避免竞态。BlockingQueuedConnection 会阻塞发送者直到槽执行完,但不能用于同一线程,否则死锁UniqueConnection 与其它类型按位或,可防止重复连接。


内容来源:《I.MX6U嵌入式Qt开发指南V1.2》第五章 Qt信号与槽、第六章 Qt Creator的使用技巧,以及正点原子 Qt 开发指南例程源码 Embedded-Qt-Tutorial/Qt/01/03_signal_slot_example