初探Qt信号与槽机制

3.3 按键响应 - 初识信号与槽
3.3.1 信号与槽基本介绍
提出疑问,界面上已经有按键了,怎么操作才能让用户按下按键后有操作上的反应呢?
在 Qt 中, 信号和槽机制 是一种非常强大的事件通信机制。这是一个重要的概念,特别是对于初学者来 说,理解它对于编写 Qt 程序至关重要。
概要

  1. 信号 (Signals) :是由对象在特定事件发生时发出的消息。例如, QPushButton 有一个
    clicked() 信号,当用户点击按钮时发出。
  2. (Slots) :是用来响应信号的方法。一个槽可以是任何函数,当其关联的信号被发出时,该槽函数 将被调用。
  3. 连接信号和槽 :使用 QObject::connect() 方法将信号连接到槽。当信号发出时,关联的槽函数
    会自动执行。
    3.3.2 按键 QPushButton 设置信号与槽
    在 Qt 中,有几种不同的方式来设置按键信号与槽的连接,主要包括:
    Qt的信号和槽机制是其事件处理系统的核心。这种机制允许对象之间的通信,而不需要它们知道对方的 具体实现。以下是Qt信号和槽的几种常见连接方式的简要概述,我将它们整理成表格形式以便于理解:
    这些方式各有优劣,选择哪种方式取决于具体的应用场景、代码风格以及个人偏好。例如,直接使用 QObject::connect 是最通用的方式,而使用 Lambda 表达式可以在同一位置编写信号处理逻辑,提高 代码的可读性。使用函数指针的方式则在编译时提供更好的类型检查。自动连接通常在使用Qt Designer 设计UI 时比较方便。
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//在构造函数中进行信号与槽的绑定
//第二种方式:QObject::connect(sender, SIGNAL(signal()), receiver,
SLOT(slot()));
QObject::connect(ui->btnCon, SIGNAL(clicked()), this,
SLOT(on_btnCon_clickedMyself()));
//第三方式:lambda表达式:QObject::connect(sender, &Sender::signal, [=]() { /*
lambda body */ });
QObject::connect(ui->btnLambda, &QPushButton::clicked,[=](){
std::cout << "btnLambdaClicked" << std::endl;
});
//第四种方式:QObject::connect(sender, &Sender::signal, receiver,
&Receiver::slot);
QObject::connect(ui-
>btnFortch,&QPushButton::clicked,this,&Widget::on_fortch_clicked);
}
Widget::~Widget()
{
delete ui;
}
//第一种方式:通过uiDesigner
void Widget::on_btnui_clicked()
{
std::cout << "UIBtnClicked" << std::endl;
}
void Widget::on_btnCon_clickedMyself()
{
std::cout << "btnConClicked" << std::endl;
}
void Widget::on_fortch_clicked()
{
std::cout << "btnForthClicked" << std::endl;
}

3.3.3 自定义信号与槽
在 Qt 中,自定义信号与槽是实现对象间通信的一种机制。信号和槽是 Qt 对象通信的核心特性,使得一个
对象能够在发生某种事件时通知其他对象。自定义信号与槽的实现步骤如下:

  1. 定义信号 :在 Qt 中,信号是由 signals 关键字声明的类成员函数。它们不需要实现,只需声明。例
    如:
cpp 复制代码
class MyClass : public QObject {
Q_OBJECT
public:
MyClass();
signals:
void mySignal(int value);
};

在上面的例子中, MyClass 有一个名为 mySignal 的信号,它带有一个整型参数。
定义槽 :槽可以是任何普通的成员函数,但通常在类定义中用 slots 关键字标识。槽可以有返回类型,
也可以接受参数,但它们的参数类型需要与发出信号的参数类型匹配。例如:

cpp 复制代码
class MyClass : public QObject {
Q_OBJECT
public slots:
void mySlot(int value);
};

在这个例子中,我们定义了一个名为 mySlot 的槽,它接收一个整型参数。
连接信号与槽 :使用 QObject::connect 函数将信号与槽连接起来。当信号被发射时,连接到这个信号的槽将被调用。

cpp 复制代码
MyClass *myObject = new MyClass();
connect(myObject, SIGNAL(mySignal(int)), myObject, SLOT(mySlot(int)));

这行代码连接了 myObject 的 mySignal 信号到同一个对象的 mySlot 槽。
发射信号 :使用 emit 关键字发射信号。当信号被发射时,所有连接到这个信号的槽都会被调用。

cpp 复制代码
emit mySignal(123);

这将触发所有连接到 mySignal 的槽。
自定义信号和槽是 Qt 编程中非常强大的特性,它们使得组件之间的通信变得灵活而松耦合。通过信和 槽,可以方便地实现各种复杂的事件驱动逻// 辑。

cpp 复制代码
//widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <iostream>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
void mysignal();
void mysignalparams(int value);
private slots:
void myslot();
void myslotparams(int value);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(this,SIGNAL(mysignal()),this,SLOT(myslot()));
connect(this,SIGNAL(mysignalparams(int)),this,SLOT(myslotparams(int)));
emit mysignal();
emit mysignalparams(100);
}
Widget::~Widget()
{
delete ui;
}
void Widget::myslot()
{
std::cout << "myslot" << std::endl;
}
void Widget::myslotparams(int value)
{
qDebug() << "myslotparams";
qDebug() << value ;
}

QDebug()
QDebug 是 Qt 框架中用于输出调试信息的一个类。它提供了一种方便的方式来输出文本到标准输出(通 常是控制台),这对于调试 Qt 应用程序非常有用。 QDebug 类可以与 Qt 的信号和槽机制一起使用,使 得在响应各种事件时能够输出有用的调试信息。
使用 QDebug 的一个典型方式是通过 qDebug() 函数,它返回一个 QDebug 对象。然后,可以使用流操 作符 << 来输出各种数据类型。例如:

cpp 复制代码
qDebug() << "This is a debug message";
int value = 10;
qDebug() << "The value is" << value;

当执行这些代码时,它们会在应用程序的控制台输出相应的文本。这对于检查程序的运行状态、变量的 值或者跟踪程序的执行流程非常有帮助。
还可以使用 qDebug() 来输出自定义类型,只要为这些类型提供了适当的输出操作符重载。此外, Qt 还 提供了 qInfo() , qWarning() , qCritical() 和 qFatal() 函数,用于输出不同级别的信息,分别用
于普通信息、警告、关键错误和致命错误。这有助于对日志信息进行级别划分,从而更好地控制输出内容。

cpp 复制代码
QMetaObject::connectSlotsByName: No matching signal for on_pushButton_clicked()
QMetaObject::connectSlotsByName: No matching signal for on_btnCon_clickedMyself()
QObject::connect: Parentheses expected, signal QPushButton::clicked in ..\signalAndSlot\widget.cpp:11
QObject::connect:  (sender name:   'btnCon')
QObject::connect:  (receiver name: 'Widget')
Can't find filter element
Can't find filter element
16:25:47: E:\QT\build-signalAndSlot-Desktop_Qt_5_12_12_MinGW_32_bit-Debug\debug\signalAndSlot.exe exited with code 0

你提供的这段日志信息是 Qt 程序运行时的调试输出 ,主要涉及 信号与槽机制 的错误提示。下面我将逐条解释这些错误信息的含义,并给出可能的原因和解决方法。


🧾 错误日志分析

1. QMetaObject::connectSlotsByName: No matching signal for on_pushButton_clicked()

  • 含义

    • Qt 尝试自动连接名为 on_pushButton_clicked() 的槽函数(即按钮点击事件)。
    • 但没有找到对应的信号(比如 QPushButton::clicked())来绑定这个槽函数。
  • 常见原因

    • 没有在 .ui 文件中正确命名控件为 pushButton
    • 或者你在代码中动态创建了按钮,但没有手动连接信号和槽。
    • 或者你写了 on_pushButton_clicked() 函数,但并没有通过 connect() 或 UI 自动连接机制进行绑定。
  • 解决办法

    • 如果你使用的是 Qt Designer 设计界面,请确保按钮的 objectNamepushButton

    • 如果你是手动创建按钮,请使用如下方式手动连接:

      复制代码
      connect(ui->pushButton, &QPushButton::clicked, this, &Widget::on_pushButton_clicked);

2. QMetaObject::connectSlotsByName: No matching signal for on_btnCon_clickedMyself()

  • 含义

    • 同样是找不到与槽函数 on_btnCon_clickedMyself() 对应的信号。
  • 常见原因

    • 你定义了一个自定义的槽函数名 on_btnCon_clickedMyself(),但 Qt 默认只识别标准命名格式如 on_<控件名>_<信号名>()
    • 例如:on_btnCon_clicked() 是合法的;而 on_btnCon_clickedMyself() 不符合默认规则,不会被自动连接。
  • 解决办法

    • 建议使用标准命名方式,如:

      复制代码
      void on_btnCon_clicked();
    • 或者如果你坚持要使用自定义名称,必须手动连接信号和槽

      复制代码
      connect(ui->btnCon, &QPushButton::clicked, this, &Widget::on_btnCon_clickedMyself);

3. QObject::connect: Parentheses expected, signal QPushButton::clicked in ..\signalAndSlot\widget.cpp:11

  • 含义

    • 在第 11 行调用 connect() 时,参数中的信号写法不正确。
    • 可能你漏掉了括号或者写成了 QPushButton::clicked 而不是 &QPushButton::clicked
  • 错误示例

    复制代码
    connect(ui->btnCon, QPushButton::clicked, this, Widget::on_btnCon_clicked); // ❌ 错误
  • 正确写法

    复制代码
    connect(ui->btnCon, &QPushButton::clicked, this, &Widget::on_btnCon_clicked); // ✅ 正确

4. QObject::connect: (sender name: 'btnCon')

5. QObject::connect: (receiver name: 'Widget')

  • 含义
    • 这只是调试信息,显示发送者是名字为 btnCon 的对象(按钮),接收者是 Widget 类的对象(主窗口)。

6. Can't find filter element ×2

  • 含义

    • 这个警告通常出现在你尝试使用 事件过滤器(eventFilter) ,但传入了一个未注册或无效的 QObject

    • 或者你在某些地方调用了类似:

      复制代码
      ui->someWidget->installEventFilter(this);

      但是没有实现 eventFilter() 函数,或者传入了错误的对象。

  • 解决办法

    • 检查是否使用了 installEventFilter()
    • 如果使用了,请确保实现了 bool eventFilter(QObject *obj, QEvent *event) 函数。
    • 或者移除未使用的事件过滤代码。

7. exited with code 0

  • 含义
    • 程序正常退出,没有崩溃,也没有异常终止。
    • 所以虽然有警告,但程序本身没有致命错误。

✅ 总结:你需要检查以下几点

问题 检查点 建议
No matching signal for ... 是否控件名一致?是否手动连接? 使用标准命名或手动连接
Parentheses expected 是否漏掉了 & 或写错了信号名? 检查 connect() 写法
Can't find filter element 是否误用了 eventFilter 检查 installEventFilter()eventFilter() 实现

✅ 示例修复代码片段

复制代码
// widget.h
private slots:
    void on_pushButton_clicked();               // 标准命名,可自动连接
    void on_btnCon_clickedMyself();             // 自定义命名,需手动连接

// widget.cpp
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 手动连接自定义命名的槽函数
    connect(ui->btnCon, &QPushButton::clicked, this, &Widget::on_btnCon_clickedMyself);
}

void Widget::on_pushButton_clicked()
{
    qDebug() << "Standard button clicked";
}

void Widget::on_btnCon_clickedMyself()
{
    qDebug() << "Custom slot function called";
}

如果你愿意贴出你的完整 .h.cpp 文件,我可以帮你具体定位错误位置并修改。

需要我继续协助吗?

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<iostream>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

private slots:
    void on_pushButton_clicked();
    void on_btnCon_clickedMyself();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

当然可以!下面是对这段 Qt Widgets 应用程序的头文件 widget.h逐行详细注释解释,帮助你理解这个类的结构、功能和 Qt 相关机制。


🔁 头文件卫士(防止重复包含)

复制代码

cpp

深色版本

复制代码
#ifndef WIDGET_H
#define WIDGET_H

说明:这是"头文件卫士"(Include Guard),用于防止该头文件被多次包含导致重复定义。

  • 如果 WIDGET_H 没有被定义过,就继续编译以下内容;
  • 否则跳过整个头文件内容。

📦 引入标准库头文件

复制代码

cpp

深色版本

复制代码
#include <QWidget>

说明 :引入 Qt 提供的 QWidget 类,它是所有 GUI 控件的基础类。当前 Widget 类将继承自它。

复制代码

cpp

深色版本

复制代码
#include <iostream>

说明 :引入 C++ 标准输入输出流库,可能用于调试输出(如 std::cout)。


🧱 声明 UI 命名空间(由 .ui 文件生成)

复制代码

cpp

深色版本

复制代码
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

说明

  • QT_BEGIN_NAMESPACEQT_END_NAMESPACE 是 Qt 的命名空间宏,用于将 Qt 的类放在 Qt 命名空间中(在某些配置下启用)。
  • namespace Ui { class Widget; } 是对 Ui::Widget 类的前置声明。
    • 这个类是由 .ui 设计文件自动生成的,里面包含了界面控件(如按钮、标签等)。
    • .cpp 文件中会通过 ui->控件名 来访问这些控件。

🏗️ 定义 Widget

复制代码

cpp

深色版本

复制代码
class Widget : public QWidget
{
    Q_OBJECT

说明

  • 定义一个类 Widget,它继承自 QWidget,是一个自定义的窗口部件。
  • Q_OBJECT 是一个宏,必须出现在类定义中,如果你希望这个类支持:
    • 信号与槽机制(signals/slots)
    • 动态属性(dynamic properties)
    • 国际化支持(tr() 函数)
    • 元对象系统(Meta-Object)

🧩 构造函数与析构函数

复制代码

cpp

深色版本

复制代码
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

说明

  • Widget(QWidget *parent = nullptr);:构造函数,默认参数为 nullptr,表示没有父窗口。
  • ~Widget();:析构函数,用于释放资源(如界面指针 ui)。

💡 私有槽函数(Private Slots)

复制代码

cpp

深色版本

复制代码
private slots:
    void on_pushButton_clicked();
    void on_btnCon_clickedMyself();

说明

  • private slots: 表示这些函数是 Qt 的槽函数(Slot),可以响应信号(Signal),比如按钮点击事件。
  • 这两个函数的作用分别是:
    • on_pushButton_clicked():当名为 pushButton 的按钮被点击时自动调用(基于 Qt 的自动连接命名规则)。
    • on_btnCon_clickedMyself():这是一个自定义命名的槽函数 ,不会被 Qt 自动识别绑定,需要手动连接信号与槽

💾 私有成员变量

复制代码

cpp

深色版本

复制代码
private:
    Ui::Widget *ui;

说明

  • 这是一个指向 Ui::Widget 的指针,用于访问你在 Qt Designer 中设计的图形界面。
  • 实际上,.ui 文件会被 Qt 自动转换成 ui_widget.h 文件,在 .cpp 文件中通过 ui->控件名 访问界面上的按钮、标签等。

✅ 结束头文件卫士

复制代码

cpp

深色版本

复制代码
};
#endif // WIDGET_H

说明 :结束 #ifndef WIDGET_H 的条件编译块,确保这个头文件不会被多次包含。


📌 总结结构图

组件 内容
头文件卫士 防止重复包含
引用头文件 #include <QWidget>#include <iostream>
UI 前置声明 namespace Ui { class Widget; }
类定义 class Widget : public QWidget
支持 Qt 特性 Q_OBJECT
构造/析构函数 初始化窗口、释放资源
槽函数 on_pushButton_clicked()on_btnCon_clickedMyself()
UI 成员指针 Ui::Widget *ui;,用于操作界面控件

🧠 小贴士

  • 如果你使用了 Qt Designer 设计界面,请确保按钮的 objectNamepushButtonbtnCon,这样 Qt 才能正确找到并绑定槽函数。

  • 对于 on_btnCon_clickedMyself() 这样的自定义命名槽函数,你需要在 .cpp 文件中手动连接信号与槽:

    复制代码

    cpp

    深色版本

    复制代码
    connect(ui->btnCon, &QPushButton::clicked, this, &Widget::on_btnCon_clickedMyself);

如果你还需要我帮你分析 .cpp 文件或修复信号槽相关错误,欢迎继续提问 😊

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //在构造函数中进行槽和信号的绑定
    //QObject::connect(sender,SIGNAL(signal()),receiver, SLOT(slot()));
    QObject::connect(ui->btnCon, SIGNAL(clicked), this, SLOT(on_btnCon_clickedMyself()));
}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_pushButton_clicked()
{
    std::cout << "UIBTNClicked" << std::endl;
}

void Widget::on_btnCon_clickedMyself()
{
    std::cout << "btnConClicked" << std::endl;
}

这段代码是一个 Qt Widgets 应用程序的实现文件(widget.cpp ,用于定义主窗口类 Widget 的构造函数、析构函数和两个槽函数。它实现了基本的 信号与槽机制 来响应按钮点击事件。

下面是对整段代码的逐行详细注释解释,帮助你理解每一行的作用以及 Qt 中的信号与槽机制。


📁 引入头文件

复制代码
#include "widget.h"

包含当前类的头文件,声明了 Widget 类的基本结构、成员函数和成员变量。

复制代码
#include "ui_widget.h"

引入由 Qt Designer 生成的 UI 头文件。

  • 这个文件是根据 .ui 文件自动生成的,里面定义了界面上的所有控件(如按钮、标签等)。
  • Ui::Widget 是一个命名空间中的类,包含了这些控件的指针。

🔧 构造函数实现

复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)          // 调用父类 QWidget 的构造函数
    , ui(new Ui::Widget)       // 动态创建一个 Ui::Widget 对象
{
    ui->setupUi(this);         // 初始化界面布局,把 .ui 设计的界面加载到当前窗口
  • 创建 Widget 窗口对象;
  • 使用 ui->setupUi(this) 把你在 Qt Designer 中设计的 .ui 界面加载进来。
复制代码
    // 在构造函数中进行槽和信号的绑定
    // QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
    QObject::connect(ui->btnCon, SIGNAL(clicked), this, SLOT(on_btnCon_clickedMyself()));
}

说明

  • 使用 Qt 的经典宏方式连接信号与槽。
  • btnCon 按钮被点击时,会触发 clicked() 信号,并调用 on_btnCon_clickedMyself() 槽函数。
  • 注意:这种写法使用的是 Qt4 的 SIGNAL/SLOT 宏语法 ,不推荐在新项目中使用(没有编译期检查),建议改用 Qt5 的 Lambda 或函数指针方式

🧼 析构函数实现

复制代码
Widget::~Widget()
{
    delete ui; // 手动释放 ui 指针指向的对象所占用的内存,防止内存泄漏
}

析构函数在对象销毁时自动调用,用于清理资源。


💡 槽函数实现

1. on_pushButton_clicked()

复制代码
void Widget::on_pushButton_clicked()
{
    std::cout << "UIBTNClicked" << std::endl;
}
  • 这是一个标准命名的槽函数:on_<控件名>_<信号名>()
  • 如果你在 .ui 文件中有一个名为 pushButton 的按钮,Qt 会自动将它的 clicked() 信号连接到这个函数。
  • 作用:当按钮被点击时输出 "UIBTNClicked" 到控制台。

2. on_btnCon_clickedMyself()

复制代码
void Widget::on_btnCon_clickedMyself()
{
    std::cout << "btnConClicked" << std::endl;
}
  • 这是一个自定义命名的槽函数
  • 因为名字不是标准格式 on_<控件名>_clicked(),所以不会自动连接,必须手动调用 connect()
  • 在构造函数中已经通过 QObject::connect(...)btnConclicked() 信号绑定到了这个函数。
  • 作用:当按钮 btnCon 被点击时输出 "btnConClicked" 到控制台。

✅ 总结输出结果(当你点击按钮时)

按钮 输出内容
pushButton 被点击 UIBTNClicked
btnCon 被点击 btnConClicked

⚠️ 建议改进:使用 Qt5 推荐的连接方式

目前你使用的是 Qt4 的 SIGNAL()SLOT() 宏方式,这种方式在运行时才会报错,不利于调试。

推荐改写成 Qt5 的方式

复制代码
connect(ui->btnCon, &QPushButton::clicked, this, &Widget::on_btnCon_clickedMyself);

或者使用 Lambda 表达式:

复制代码
connect(ui->btnCon, &QPushButton::clicked, this, [=]() {
    std::cout << "btnConClicked (Lambda)" << std::endl;
});

📌 最佳实践总结

内容 建议
信号与槽连接方式 使用 Qt5 的函数指针或 Lambda 表达式
槽函数命名 标准命名可自动绑定,否则需手动连接
控件命名一致性 .ui 文件中控件的 objectName 必须与代码一致
内存管理 手动删除 ui 指针,避免内存泄漏

如果你还想了解:

  • 如何使用 Qt Designer 添加按钮并自动绑定槽函数
  • 如何添加更多控件并在代码中操作它们
  • 如何使用布局管理器(Layout)来美化界面

欢迎继续提问,我可以帮你一步步完善你的 Qt 程序 😊

bash 复制代码
E:\QT\build-signalAndSlot-Desktop_Qt_5_12_12_MinGW_32_bit-Debug\debug\moc_widget.cpp:80: error: undefined reference to `Widget::on_pushButton_clicked()'
debug/moc_widget.o: In function `ZN6Widget18qt_static_metacallEP7QObjectN11QMetaObject4CallEiPPv':
E:\QT\build-signalAndSlot-Desktop_Qt_5_12_12_MinGW_32_bit-Debug/debug/moc_widget.cpp:80: undefined reference to `Widget::on_pushButton_clicked()'

你遇到的这个错误:

复制代码

深色版本

复制代码
undefined reference to `Widget::on_pushButton_clicked()'

链接阶段 (Linking)报错,说明编译器在生成最终可执行文件时找不到函数 void Widget::on_pushButton_clicked() 的定义。


🚨 错误原因分析

✅ 你在头文件中声明了该函数:

复制代码

cpp

深色版本

复制代码
private slots:
    void on_pushButton_clicked();

❌ 但你在 .cpp 文件中没有定义它 或者 定义拼写不一致

你贴出的 .cpp 文件中:

复制代码

cpp

深色版本

复制代码
void Widget::on_pushButton_clicked()
{
    std::cout << "UIBTNClicked" << std::endl;
}

看起来你是写了这个函数的。那为什么还会报错呢?


🔍 可能原因总结

✅ 原因 1:未重新构建项目(qmake 没有重新生成 moc 文件)

Qt 使用元对象编译器(moc)来处理信号与槽机制。如果你:

  • 添加了新的槽函数;
  • 修改了类的结构并忘记重新运行 qmake;

就可能导致 moc_widget.cpp 中仍然引用了一个不存在或未正确编译的函数。

🔧 解决方法:

  1. 清理项目(Build → Clean All)
  2. 重新运行 qmake(Build → Run qmake)
  3. 重新构建项目(Build → Build All)

✅ 原因 2:槽函数拼写错误或签名不一致

确保你的函数签名完全匹配,包括:

  • 返回类型(必须是 void
  • 函数名(必须是 on_pushButton_clicked()
  • 参数列表(必须为空)

✅ 正确:

复制代码

cpp

深色版本

复制代码
void Widget::on_pushButton_clicked()
{
    // ...
}

❌ 错误示例:

复制代码

cpp

深色版本

复制代码
void on_pushButton_clicked() // 没有作用域 Widget::
void Widget::on_pushButton_clicked(int) // 多了参数

✅ 原因 3:.pro 文件中缺少 QT += widgets

如果你的 .pro 文件中没有包含 Widgets 模块:

复制代码

qmake

深色版本

复制代码
QT += core gui

而没有:

复制代码

qmake

深色版本

复制代码
QT += widgets

会导致一些 Qt Widgets 类(如 QPushButton)无法正确链接。

🔧 解决方法: 修改 .pro 文件为:

复制代码

qmake

深色版本

复制代码
QT += core gui widgets

然后清理、运行 qmake、重新构建。


✅ 原因 4:IDE 缓存问题 / 构建路径混乱

有时 IDE(比如 Qt Creator)缓存了旧版本的 moc 文件或中间文件。

🔧 解决方法:

  • 删除项目的 build 目录(或 Debug/Release 子目录)
  • 重新打开项目并重新构建

✅ 最终检查清单

检查项 是否完成
函数是否正确定义?
函数签名是否完全一致?
是否清理并重新构建项目?
.pro 文件中是否包含 QT += widgets
是否删除 build 并重新构建?

✅ 示例修复后的完整 .cpp 文件

复制代码

cpp

深色版本

复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    connect(ui->btnCon, &QPushButton::clicked, this, &Widget::on_btnCon_clickedMyself);
}

Widget::~Widget()
{
    delete ui;
}

// 必须正确定义这个槽函数
void Widget::on_pushButton_clicked()
{
    std::cout << "UIBTNClicked" << std::endl;
}

void Widget::on_btnCon_clickedMyself()
{
    std::cout << "btnConClicked" << std::endl;
}

✅ 推荐使用 Qt5 的连接方式(更安全)

复制代码

cpp

深色版本

复制代码
connect(ui->pushButton, &QPushButton::clicked, this, &Widget::on_pushButton_clicked);

而不是使用宏方式(容易出错):

复制代码

cpp

深色版本

复制代码
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(on_pushButton_clicked()));

如果你已经确认以上所有内容,但问题依旧,请贴出你的 .pro 文件和完整的项目结构,我可以进一步帮你诊断。

bash 复制代码
/****************************************************************************
** Meta object code from reading C++ file 'widget.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.12.12)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/

#include "../../signalAndSlot/widget.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'widget.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.12.12. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif

QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_Widget_t {
    QByteArrayData data[6];
    char stringdata0[92];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_Widget_t, stringdata0) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
static const qt_meta_stringdata_Widget_t qt_meta_stringdata_Widget = {
    {
QT_MOC_LITERAL(0, 0, 6), // "Widget"
QT_MOC_LITERAL(1, 7, 23), // "on_btnCon_clickedMyself"
QT_MOC_LITERAL(2, 31, 0), // ""
QT_MOC_LITERAL(3, 32, 16), // "on_btnui_clicked"
QT_MOC_LITERAL(4, 49, 21), // "on_pushButton_clicked"
QT_MOC_LITERAL(5, 71, 20) // "on_btnLambda_clicked"

    },
    "Widget\0on_btnCon_clickedMyself\0\0"
    "on_btnui_clicked\0on_pushButton_clicked\0"
    "on_btnLambda_clicked"
};
#undef QT_MOC_LITERAL

static const uint qt_meta_data_Widget[] = {

 // content:
       8,       // revision
       0,       // classname
       0,    0, // classinfo
       4,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       0,       // signalCount

 // slots: name, argc, parameters, tag, flags
       1,    0,   34,    2, 0x08 /* Private */,
       3,    0,   35,    2, 0x08 /* Private */,
       4,    0,   36,    2, 0x08 /* Private */,
       5,    0,   37,    2, 0x08 /* Private */,

 // slots: parameters
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,
    QMetaType::Void,

       0        // eod
};

void Widget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<Widget *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->on_btnCon_clickedMyself(); break;
        case 1: _t->on_btnui_clicked(); break;
        case 2: _t->on_pushButton_clicked(); break;
        case 3: _t->on_btnLambda_clicked(); break;
        default: ;
        }
    }
    Q_UNUSED(_a);
}

QT_INIT_METAOBJECT const QMetaObject Widget::staticMetaObject = { {
    &QWidget::staticMetaObject,
    qt_meta_stringdata_Widget.data,
    qt_meta_data_Widget,
    qt_static_metacall,
    nullptr,
    nullptr
} };


const QMetaObject *Widget::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

void *Widget::qt_metacast(const char *_clname)
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_Widget.stringdata0))
        return static_cast<void*>(this);
    return QWidget::qt_metacast(_clname);
}

int Widget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    _id = QWidget::qt_metacall(_c, _id, _a);
    if (_id < 0)
        return _id;
    if (_c == QMetaObject::InvokeMetaMethod) {
        if (_id < 4)
            qt_static_metacall(this, _c, _id, _a);
        _id -= 4;
    } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
        if (_id < 4)
            *reinterpret_cast<int*>(_a[0]) = -1;
        _id -= 4;
    }
    return _id;
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE

你贴出的这段内容是 Qt 元对象编译器(moc)自动生成的代码 ,具体是 moc_widget.cpp 文件的内容。它是由你的头文件 widget.h 自动生成的,用于支持 Qt 的信号与槽机制。


📌 这段代码的作用

这段代码的主要作用是:

  • 注册类信息 :让 Qt 知道 Widget 类的存在。
  • 支持信号与槽机制 :将你在 .h 文件中声明的槽函数(slots)绑定到具体的实现上。
  • 元数据管理:包括函数名、参数类型、调用方式等信息。

🔍 逐部分解释

✅ 1. 自动生成警告和版本检查

复制代码

cpp

深色版本

复制代码
/****************************************************************************
** Meta object code from reading C++ file 'widget.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.12.12)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
  • 这是 moc 自动生成的代码,不要手动修改!
  • 它基于你的 widget.h 文件生成。
  • 如果你修改了 widget.h 中的类结构(如添加新槽函数),moc 会重新生成这个文件。

✅ 2. 版本兼容性检查

复制代码

cpp

深色版本

复制代码
#include "../../signalAndSlot/widget.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'widget.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.12.12. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif
  • 检查是否包含必要的 Qt 基础类;
  • 检查 moc 版本是否匹配当前使用的 Qt 版本;
  • 如果版本不一致或缺少 <QObject>,就会报错。

✅ 3. 字符串表(方法名)

复制代码

cpp

深色版本

复制代码
struct qt_meta_stringdata_Widget_t {
    QByteArrayData data[6];
    char stringdata0[92];
};
#define QT_MOC_LITERAL(idx, ofs, len) ...

创建一个字符串表,保存所有注册的方法名称,比如:

  • "on_btnCon_clickedMyself"
  • "on_pushButton_clicked"

这些名字在运行时会被用来查找对应的槽函数。


✅ 4. 方法元数据(Meta Data)

复制代码

cpp

深色版本

复制代码
static const uint qt_meta_data_Widget[] = {
 // content:
       8,       // revision
       0,       // classname
       0,    0, // classinfo
       4,   14, // methods
       ...
};

描述类中的方法信息,例如有多少个方法、它们的参数数量、返回类型等。


✅ 5. 静态元调用函数(qt_static_metacall)

复制代码

cpp

深色版本

复制代码
void Widget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<Widget *>(_o);
        switch (_id) {
        case 0: _t->on_btnCon_clickedMyself(); break;
        case 1: _t->on_btnui_clicked(); break;
        case 2: _t->on_pushButton_clicked(); break;
        case 3: _t->on_btnLambda_clicked(); break;
        }
    }
}
  • 这是一个由 Qt 调用的内部函数;
  • 当某个信号触发后,Qt 会根据 _id 来决定调用哪一个槽函数;
  • 例如:
  • _id == 2 → 调用 on_pushButton_clicked()
  • _id == 0 → 调用 on_btnCon_clickedMyself()

✅ 6. 注册元对象(staticMetaObject)

复制代码

cpp

深色版本

复制代码
QT_INIT_METAOBJECT const QMetaObject Widget::staticMetaObject = { {
    &QWidget::staticMetaObject,
    qt_meta_stringdata_Widget.data,
    qt_meta_data_Widget,
    qt_static_metacall,
    nullptr,
    nullptr
} };
  • 这是 Qt 内部用于识别类信息的核心结构体;
  • 它告诉 Qt:
  • 我这个类是从哪个父类继承来的;
  • 有哪些槽函数可以响应信号;
  • 如何调用这些函数;

✅ 7. 辅助函数(metaObject(), qt_metacast(), qt_metacall())

复制代码

cpp

深色版本

复制代码
const QMetaObject *Widget::metaObject() const { ... }
void *Widget::qt_metacast(const char *_clname) { ... }
int Widget::qt_metacall(QMetaObject::Call _c, int _id, void **_a) { ... }
  • metaObject():获取当前对象的元对象指针;
  • qt_metacast():用于动态类型转换(类似 dynamic_cast);
  • qt_metacall():处理元调用(如通过信号触发槽函数);

⚠️ 错误分析:结合你的构建错误

你之前遇到的错误是:

复制代码

深色版本

复制代码
undefined reference to `Widget::on_pushButton_clicked()'

而从这段 moc 代码来看:

复制代码

cpp

深色版本

复制代码
case 2: _t->on_pushButton_clicked(); break;

说明:

  • Qt 已经知道你需要调用 on_pushButton_clicked() 这个函数
  • 但链接器找不到它的定义 → 你没有在 .cpp 文件中正确定义这个函数

✅ 解决方案总结

步骤 操作
1 确保 widget.cpp 中有如下函数定义:
复制代码

cpp

深色版本

复制代码
void Widget::on_pushButton_clicked()
{
    std::cout << "UIBTNClicked" << std::endl;
}

| 2 | 清理项目并重新构建: | | - Build → Clean All | | - Build → Run qmake | | - Build → Build All |

| 3 | 检查 .pro 文件是否包含 widgets 模块: |

复制代码

qmake

深色版本

复制代码
QT += core gui widgets

| 4 | 删除 build 目录或 debug/release 子目录,重新构建整个项目。 |


✅ 小贴士

  • 如果你使用的是 Qt Designer 设计界面,并且按钮名为 pushButton,那么 on_pushButton_clicked() 是自动绑定的;
  • 如果你改了控件名(如改为 btnPush),但没改槽函数名,会导致绑定失败;
  • 使用 Qt5 的连接方式更安全,推荐替代宏写法:
复制代码

cpp

深色版本

复制代码
connect(ui->pushButton, &QPushButton::clicked, this, &Widget::on_pushButton_clicked);

如果你愿意贴出完整的 .h.cpp 文件,我可以帮你进一步定位问题。

需要我继续协助吗?😊

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //第二种在构造函数中进行槽和信号的绑定
    //QObject::connect(sender,SIGNAL(signal()),receiver, SLOT(slot()));
    QObject::connect(ui->btnCon, SIGNAL(clicked()), this, SLOT(on_btnCon_clickedMyself()));

    //第三方式:lambda表达式:QObject::connect(sender, &Sender::signal, [=]() { /*lambda body */ });
    QObject::connect(ui->btnLambda, &QPushButton::clicked, [=]() { std::cout << "btnLambdaClicked" << std::endl; });
}

Widget::~Widget()
{
    delete ui;
}



void Widget::on_btnCon_clickedMyself()
{
    std::cout << "btnConClicked" << std::endl;
}


void Widget::on_btnui_clicked()
{
     std::cout << "UIBTNClicked" << std::endl;
}

根据你提供的 widget.cpp 文件内容,看起来你在尝试使用不同的方式连接信号和槽。不过,从你的描述和代码来看,问题可能在于缺少对 on_pushButton_clicked() 函数的定义,而这是你在 .h 文件中声明的一个槽函数。

分析与修正

  1. 确保所有声明的槽函数都有定义

    • 你提到在构建时遇到了 undefined reference to 'Widget::on_pushButton_clicked()' 的错误。
    • 在你提供的 .cpp 文件中并没有看到 on_pushButton_clicked() 函数的定义。
  2. 添加缺失的槽函数定义

    • 如果你确实需要这个槽函数,请确保在 widget.cpp 中定义它。
  3. 检查其他槽函数的命名是否正确

    • 确保槽函数名和信号名与 .ui 文件中的控件名一致,或者如果你手动连接信号和槽,则确保连接正确。

修改后的 widget.cpp

以下是基于你提供的代码进行调整后的版本,包括添加了 on_pushButton_clicked() 函数的定义:

复制代码

cpp

深色版本

复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 第二种在构造函数中进行槽和信号的绑定(旧式写法)
    QObject::connect(ui->btnCon, SIGNAL(clicked()), this, SLOT(on_btnCon_clickedMyself()));

    // 使用Qt5的新式写法(推荐)
    // 注意:这里假设 ui->btnLambda 和 ui->pushButton 都是有效的控件对象名称
    QObject::connect(ui->btnLambda, &QPushButton::clicked, [=]() { 
        std::cout << "btnLambdaClicked" << std::endl; 
    });

    // 手动连接 pushButton 的点击事件到 on_pushButton_clicked 槽
    QObject::connect(ui->pushButton, &QPushButton::clicked, this, &Widget::on_pushButton_clicked);
}

Widget::~Widget()
{
    delete ui;
}

// 定义 on_pushButton_clicked 槽函数
void Widget::on_pushButton_clicked()
{
    std::cout << "UIBTNClicked" << std::endl;
}

// 已有的槽函数定义
void Widget::on_btnCon_clickedMyself()
{
    std::cout << "btnConClicked" << std::endl;
}

void Widget::on_btnui_clicked()
{
     std::cout << "UIBTNClicked" << std::endl;
}

关键点

  • 确保所有槽函数都有定义 :特别是那些在头文件中声明但在实现文件中未定义的函数,如 on_pushButton_clicked
  • 使用 Qt5 推荐的连接方式 :相比于旧式的 SIGNALSLOT 宏,使用函数指针或 Lambda 表达式的方式更加安全且支持编译期类型检查。
  • 检查控件名称的一致性 :确保 .ui 文件中控件的 objectName 属性与代码中引用的控件名称相匹配。

请确认你的 .ui 文件中有名为 pushButton, btnCon, btnLambda 的控件,并且它们的 objectName 设置正确。如果这些控件名称不正确,你需要相应地调整代码中的控件引用名称。

相关推荐
Aderversa1 小时前
C++开源协程库async_simple有栈协程源码分析
c++·协程
uyeonashi1 小时前
【QT】窗口详解
开发语言·c++·qt·学习
Hello eveybody2 小时前
C++介绍整数二分与实数二分
开发语言·数据结构·c++·算法
空白木各2 小时前
evo工具
c++
编码小笨猪2 小时前
浅谈Linux中一次系统调用的执行过程
linux·服务器·c++
几道之旅3 小时前
零基础RT-thread第二节:按键控制
c语言·stm32
Jay_5153 小时前
C语言环形数组(循环队列)详解:原理、实现与应用
c语言·学习·嵌入式·环形数组
jmlinux3 小时前
从 C 语言计算器到串口屏应用
c语言·开发语言
yuren_xia3 小时前
RabbitMQ 知识详解(Java版)
java·rabbitmq·java-rabbitmq
Mallow Flowers4 小时前
Python训练营-Day31-文件的拆分和使用
开发语言·人工智能·python·算法·机器学习