一、创建一个基于 QWidget 的项目,不带有界面设计器:
二、在主窗口下添加两个按钮对象,连接按钮发出的信号和槽函数:
mywidget.h:
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
// 引入按钮头文件
#include <QPushButton>
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = 0);
~MyWidget();
private:
// 声明两个按钮对象
QPushButton b1; // 普通对象
QPushButton *b2; // 指针对象
};
#endif // MYWIDGET_H
mywidget.cpp:
#include "mywidget.h"
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
{
// 通过 setParent 方法指定父对象
b1.setParent(this);
b1.setText("Button1");
b1.resize(80, 40);
b1.move(200, 100);
// 通过构造函数方法指定父对象
b2 = new QPushButton(this);
b2->setText("Button2");
b2->resize(80, 40);
b2->move(30, 20);
/*
* 连接信号和槽:点击按钮的时候,关闭当前窗口
* 参数1:信号的发送者(指针类型)
* 参数2:发送的信号;&发送者的名字::信号名称
* 参数3:信号的接收者(指针类型)
* 参数4:处理信号的函数(叫做槽函数);&接收者的名字::槽函数名称
*/
connect(&b1, &QPushButton::clicked, this, &MyWidget::close);
// 连接信号和槽:按下按钮的时候,当前窗口最大化显示
connect(b2, &QPushButton::pressed, this, &MyWidget::showMaximized);
// 重置主窗口的大小
this->resize(600, 400);
}
MyWidget::~MyWidget()
{
}
其中信号 &QPushButton::clicked、&QPushButton::pressed,
和槽函数 &MyWidget::close、&MyWidget::showMaximized 都是 Qt 定义好的;
可以在 Qt 帮助文档里查找:
三、我们也可以自定义槽函数:
自定义槽函数的特点:槽函数的本质就是一个普通函数,在 Qt5 里面,自定义槽函数可以是成员函数、普通全局函数、或者静态函数都行;槽函数的返回值和参数需要和信号一致;
注意:信号和槽函数都没有返回值,但是可以带参数;
在 mywidget.h 中声明一个自定义的槽函数:
在 mywidget.cpp 中连接信号和自定义槽函数:
注意:一个信号可以连接多个槽函数;
四、自定义信号:
案例:添加一个子窗口,在主窗口上点击按钮的时候,主窗口隐藏,子窗口显示;
点击子窗口上的按钮的时候,子窗口隐藏,主窗口显示;
1、在 Qt 项目上添加子窗口:
2、在子窗口头文件中添加自定义信号:
childwidget.h 文件为:
#ifndef CHILDWIDGET_H
#define CHILDWIDGET_H
#include <QWidget>
#include <QPushButton>
class ChildWidget : public QWidget
{
Q_OBJECT
public:
explicit ChildWidget(QWidget *parent = 0);
signals:
// 自定义信号必须由 signals 关键字声明;
// 自定义信号没有返回值,但可以有参数;
// 自定义信号就是函数的声明,只需声明,无需定义;
// 发送信息使用关键字 emit;
void mySignal();
public slots:
// Qt5 里面自定义槽函数不是必须由 public slots 声明;
// 但是 Qt4 里面槽函数必须由 public slots 声明;
void sendSignal();
private:
// 创建一个按钮对象
QPushButton btn;
};
#endif // CHILDWIDGET_H
3、在子窗口中点击按钮时,发送自定义信号:
childwidget.cpp 文件为:
#include "childwidget.h"
ChildWidget::ChildWidget(QWidget *parent) :
QWidget(parent)
{
this->resize(400, 300); // 设置窗口大小
this->setWindowTitle("子窗口"); // 设置窗口标题
btn.setParent(this);
btn.setText("切换到主窗口");
btn.move(200, 100);
// 点击按钮,调用自定义槽函数(sendSignal),发送自定义信号(mySignal)
connect(&btn, &QPushButton::clicked, this, &ChildWidget::sendSignal);
}
// 自定义槽函数
void ChildWidget::sendSignal()
{
// 发送自定义信号
emit mySignal();
}
4、在主窗口中添加子窗口对象,并自定义槽函数处理子窗口发出的信号:
修改 mywidget.h 文件为:
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
#include <QPushButton>
// 在主窗口中引入子窗口的头文件
#include "childwidget.h"
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = 0);
~MyWidget();
// 自定义槽函数
void mySlot();
// 处理自定义信号的槽函数
void dealSignal();
private:
QPushButton b1;
// 在主窗口中声明子窗口对象
ChildWidget child;
};
#endif // MYWIDGET_H
5、点击主窗口上的按钮,隐藏主窗口,并显示子窗口;
同时连接子窗口发出的信号和槽函数,当接收到信号时,显示主窗口,隐藏子窗口:
修改 mywidget.cpp 文件为:
#include "mywidget.h"
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
{
this->resize(400, 300); // 设置窗口大小
this->setWindowTitle("主窗口"); // 设置窗口标题
b1.setParent(this);
b1.setText("切换到子窗口");
b1.move(200, 100);
// 点击按钮,调用自定义槽函数(mySlot),隐藏主窗口,显示子窗口
connect(&b1, &QPushButton::clicked, this, &MyWidget::mySlot);
// 连接子窗口的自定义信号和主窗口中的自定义槽函数
// 参数1:信号的发送者(子窗口)
// 参数2:发送的信号(子窗口自定义的信号)
// 参数3:信号的接收者(主窗口)
// 参数4:处理信号的自定义槽函数
connect(&child, &ChildWidget::mySignal, this, &MyWidget::dealSignal);
}
// 自定义槽函数
void MyWidget::mySlot()
{
// 隐藏主窗口
this->hide();
// 显示子窗口
child.show();
}
// 处理信号的自定义槽函数
void MyWidget::dealSignal()
{
// 显示主窗口
this->show();
// 隐藏子窗口
child.hide();
}
MyWidget::~MyWidget()
{
}
五、带参数的自定义信号和自定义槽函数:
在上面案例的子窗口头文件(childwidget.h)中自定义一个带参信号:
并在子窗口上点击按钮的时候,发送带参信号:
在主窗口头文件(mywidget.h)中自定义一个相对应的带参槽函数:
在主窗口中处理无参和有参的信号:
// 因为信号和槽函数都重载了,所以直接使用 connect 连接信号和槽的时候会报错,
// 因为存在二义性,不确定调用的是无参的,还是有参的;
// 可以使用函数指针来解决:
// 无参数的信号和槽函数的函数指针:
void(ChildWidget::*noParamSignal)(void) = &ChildWidget::mySignal;
void(MyWidget::*noParamSlot)(void) = &MyWidget::dealSignal;
// 有参数的信号和槽函数的函数指针:
void(ChildWidget::*haveParamSignal)(int, QString) = &ChildWidget::mySignal;
void(MyWidget::*haveParamSlot)(int, QString) = &MyWidget::dealSignal;
// 连接无参数的自定义信号和无参数的槽函数
// 函数指针 noParamSignal 就表示无参的信号,
// 函数指针 noParamSlot 就表示无参的槽函数;
connect(&child, noParamSignal, this, noParamSlot);
// 连接带参数的自定义信号和带参数的槽函数
connect(&child, haveParamSignal, this, haveParamSlot);
测试结果如下:
老师的视频在演示的时候,qDebug() 输出 QString 数据是中文时会出现乱码,如下:
但我的可以直接输出中文;如果中文输出乱码,可以将 QString 类型转换成 char* 类型:
六、上面使用 函数指针 的方式区别无参和有参信号的方法是 Qt5 之后才有的,在 Qt4 的时候是另一种方法区分无参和有参的信号,如下:
// 连接无参的信号和无参的槽函数
connect(&child, SIGNAL(mySignal()), this, SLOT(dealSignal()));
// 连接有参的信号和有参的槽函数
connect(&child, SIGNAL(mySignal(int,QString)), this, SLOT(dealSignal(int,QString)));
Qt4 的这种方式用起来比较方便,但是有一个缺点:SIGNAL 和 SLOT 宏会将信号名称和槽函数名称转换成字符串,在项目编译的时候不进行错误检查;也就是说信号名称和槽函数名称写错了,也可以编译通过,但是程序会在运行的时候直接崩掉。