初识Qt · 信号与槽 · 自定义和参数
目录
前言:
自定义槽
自定义信号
参数
connect小结
前言:
目前接触到的函数是connect,当我们使用的时候,我们发现connect关联的信号和槽,即便是函数,也是Qt中已经存在的,即内置函数,那么我们是否能够自己自定义信号或者说是自己自定义槽函数呢?
当然是可以的。
其实这么说都有点废话了,应该槽函数我们已经自定义了,对于内置槽函数我们也看过了,即锯齿状的是槽函数,波纹状的是信号。
那么本文,我们将来学习,如何自定义。
自定义槽
假定我们现在实现一个功能就是,点击按钮,会使得widget的标题发生改变,那么自然使用connect函数,在前面我们也介绍过槽函数必须放在public slot中,就像:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
public slots:
void Headle();
private:
Ui::Widget *ui;
QPushButton* button;
};
但是实际上现在Qt5版本和6版本是不用这样写的了。我们直接给它当成正常的成员函数就可以了。
对于slots关键字来说,它是Qt中自己扩展的关键字,并不是C++中的标准语法,而在Qt中广泛使用了元编程技术,所以qmake在构建Qt项目的时候,就会专门扫描这种关键字,用来代码生成代码,不过现在似乎用不着这个关键字了~
这是一种自定义槽函数的方式。
我们现在在UI界面定义出一个pushbutton,使用第二种定义槽函数的方式:
就这两步,我们就已经成功生成了一个自定义的槽函数:
void Widget::on_pushButton_clicked()
{
}
这个时候你看命名也是非常形象的,on,在哪里控件上面,pushButton,代表控件的objectname,clicked代表的是发出的信号。
所以Qt在这里的命名是非常nice的~其中这个on一般是前缀。
自定义信号
Qt不仅支持自定义槽函数,也支持自定义信号,但是实际上呢,Qt的自定义信号在开发中用的是非常少见的,因为在GUI界面中,用户能执行的操作咱们都是可以穷举出来的。
比如给你一个光标,你能对它执行的操作无非就是移动,点击,所以自定义信号实际上那么必要。但是我们还是简单学习一下~
对于信号来说,它是一个非常非常特殊的函数:
1.不需要自己实现定义
2.只需要写声明
3.返回值只能是void
4.参数没有要求,可以支持重载
5.需要使用signals关键字
首先,为什么不需要自己实现定义呢~因为这个函数的定义是Qt在qmake的过程中自己编译的,不需要程序员去干预,也不能让程序员去干预,因为你自定义了个信号,本身配合Qt整体的框架就是一个麻烦事儿,毕竟需要做既定的操作,所以定义方面,我们不能干预。
剩下的就是对于函数本身的要求了,简单来说就是这样:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
void MySignal();
};
使用的时候仍然使用connect函数,但是问题来了,我们如何发出该信号呢?
这里使用到的就是一个关键字了,emit,虽然但是不适用该关键字也是没事儿的。
以下是一个示例:
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* button = new QPushButton(this);
emit MySignal();
connect(this, &Widget::MySignal, this,&Widget::on_pushButton_clicked);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
this->setWindowTitle("Hello qt!");
}
点击按钮之后的效果就是这样。
当然了,自定义信号可以在任意位置发送,不一定要在构造函数里面发送,并且,我们可以不使用emit关键字,这个东西使用起来没有啥用。
参数
对于信号和槽来说是可以带参数的, 毕竟在上文我们就提及到了,自定义信号是可以构成重载的。
当槽和信号被绑定在了一起之后,这两个函数的参数应该是大致一致的。
至于为什么是大致一致,这里留一个伏笔~
首先,我们不妨先见见:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
public slots:
void HandSig(const QString&);
signals:
void mysignal(const QString&);
private:
Ui::Widget *ui;
};
先自定义出我们的信号和槽函数,在Qt这里的参数是可以不用写变量名的,笔者依稀记得在C++函数声明似乎也是这样~
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(this, &Widget::mysignal, this, &Widget::HandSig);
mysignal("Hello Qt!!");
}
Widget::~Widget()
{
delete ui;
}
void Widget::HandSig(const QString &text)
{
this->setWindowTitle(text);
}
此时窗口刚刚运行Title就会变成Hello Qt了。
那么如果我这样修改一下:
void mysignal(const QString&, int);
mysignal("Hello Qt!!",12);
你会发现运行结果是一摸一样的,这就是为什么我说是大致一致即可,这个的意思是指:
信号的参数个数应该大于等于槽的参数个数
这是Qt中的一个非常特别的点。
好了,让我们回到最开始的点:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
public slots:
void HandSig(const QString&);
signals:
void mysignal(const QString&, int);
private:
Ui::Widget *ui;
};
对于Qt中的类想要使用信号槽的这个机制,即能在类中定义槽函数和信号,就必须要在类最开始的地方写这个宏。
当然,这个宏我们暂时先不研究,这是我们现阶段研究不上来的。
connect小结
对于connect来说,或者说信号和槽的这个机制,在所有的GUI开发框架中是一个非常特殊的存在.比如在网页开发中,相应用户的操作非常简单,使用回调函数就可以了,不像connect函数,虽然能一对多吧~但是现在看来好像不太行了?
因为Qt最开始的时候,一个最大的卖点就是connect,一个信号可以关联到多个槽函数,但是现在实际上很少有这样操作的,毕竟A信号关联了B C两个槽函数之后,万一触发了这个B,C也触发的同时给系统带崩溃了呢?
所以Qt最开始的设想实际上是想让信号和槽解耦合,实现一个低耦合,高内聚的一个特点。
比如网页开发,一个事件对应一个处理函数,Qt想多对多,就像数据库的关联表一样~
但是现在好像,这个卖点不太行了。
当然了,Qt中还有很多值得我们去学习的点,不能只抓住这个多对多的点不妨,即便现在一对一就够用了,但是当时Qt的这个特点可是非常nb的了。
就像这样:、
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
void mySignal1();
void mySignal2();
void mySignal3();
public slots:
void mySlot1();
void mySlot2();
void mySlot3();
private:
Ui::Widget *ui;
};
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(this, &Widget::mySignal1, this, &Widget::mySlot1);
connect(this, &Widget::mySignal1, this, &Widget::mySlot2);
connect(this, &Widget::mySignal2, this, &Widget::mySlot1);
connect(this, &Widget::mySignal2, this, &Widget::mySlot3);
}
Widget::~Widget()
{
delete ui;
}
void Widget::mySlot1()
{
qDebug() << "mySlot1";
}
void Widget::mySlot2()
{
qDebug() << "mySlot2";
}
void Widget::mySlot3()
{
qDebug() << "mySlot3";
}
感谢阅读!