初识Qt · 信号与槽 · 自定义和参数

目录

前言:

自定义槽

自定义信号

参数

connect小结


前言:

目前接触到的函数是connect,当我们使用的时候,我们发现connect关联的信号和槽,即便是函数,也是Qt中已经存在的,即内置函数,那么我们是否能够自己自定义信号或者说是自己自定义槽函数呢?

当然是可以的。

其实这么说都有点废话了,应该槽函数我们已经自定义了,对于内置槽函数我们也看过了,即锯齿状的是槽函数,波纹状的是信号。

那么本文,我们将来学习,如何自定义。


自定义槽

假定我们现在实现一个功能就是,点击按钮,会使得widget的标题发生改变,那么自然使用connect函数,在前面我们也介绍过槽函数必须放在public slot中,就像:

cpp 复制代码
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,使用第二种定义槽函数的方式:

就这两步,我们就已经成功生成了一个自定义的槽函数:

cpp 复制代码
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整体的框架就是一个麻烦事儿,毕竟需要做既定的操作,所以定义方面,我们不能干预。

剩下的就是对于函数本身的要求了,简单来说就是这样:

cpp 复制代码
class Widget : public QWidget
{
    Q_OBJECT

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

signals:
       void MySignal();
};

使用的时候仍然使用connect函数,但是问题来了,我们如何发出该信号呢?

这里使用到的就是一个关键字了,emit,虽然但是不适用该关键字也是没事儿的

以下是一个示例:

cpp 复制代码
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关键字,这个东西使用起来没有啥用。


参数

对于信号和槽来说是可以带参数的, 毕竟在上文我们就提及到了,自定义信号是可以构成重载的。

当槽和信号被绑定在了一起之后,这两个函数的参数应该是大致一致的

至于为什么是大致一致,这里留一个伏笔~

首先,我们不妨先见见:

cpp 复制代码
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++函数声明似乎也是这样~

cpp 复制代码
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了。

那么如果我这样修改一下:

cpp 复制代码
void mysignal(const QString&, int);
cpp 复制代码
   mysignal("Hello Qt!!",12);

你会发现运行结果是一摸一样的,这就是为什么我说是大致一致即可,这个的意思是指:

信号的参数个数应该大于等于槽的参数个数

这是Qt中的一个非常特别的点。

好了,让我们回到最开始的点:

cpp 复制代码
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的了。

就像这样:、

cpp 复制代码
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;
};
cpp 复制代码
#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";
}

感谢阅读!

相关推荐
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner4 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz9 天前
QML Hello World 入门示例
qt
xcyxiner12 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner13 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00616 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术16 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript