Qt图形界面编程【2】(信号与槽)

365 阅读7分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

一、信号和槽

1.1 相关概念

==信号槽==是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。

我们主要研究的是四个东西:

  • 信号的发起者
  • 信号
  • 信号接收者
  • 槽函数

图片.png

比如:按钮按下后让当前窗口关闭:

  1. 信号发送者:按钮
  2. 信号:按钮被按下
  3. 信号接收者:窗口
  4. 槽函数:关闭窗口

接收者接收到信号的处理方式函数:connect

connect(const QObject *sender, const char *signal, 
        const QObject *receiver, const char *method)
功能:接收者接收到指定信号的处理操作
参数:
    sender:信号的发起者
    signal:指定的信号,根据实际情况来确定信号
            信号可以使用系统的,也可以自定义
    receiver:信号接收方
    method:槽函数,根据实际情况来确定槽函数
           槽函数可以使用系统的,也可以自定义

1.2 系统自带信号和槽函数

#include "widget.h"
#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //**************设置窗口属性***************
    this->setWindowTitle("按钮操作");
    this->setFixedSize(800, 600);

    //**************创建按钮并设置属性************
    QPushButton *buttonReg = new QPushButton("注册", this);
    QPushButton *buttonLog = new QPushButton("登录", this);
    QPushButton *buttonQuit = new QPushButton("退出", this);

    buttonReg->setFixedSize(100, 50);
    buttonLog->setFixedSize(100, 50);
    buttonQuit->setFixedSize(100, 50);

    buttonReg->move(150, 200);
    buttonLog->move(300, 200);
    buttonQuit->move(450, 200);

    //**************根据按钮的信号设置处理方式*****************
    //Qt5.0以上版本使用一下形式
    connect(buttonQuit, &QPushButton::clicked, this, &QWidget::close);
    //Qt4.x版本只能使用一下形式
    //connect(buttonQuit, SIGNAL(clicked()), this, SLOT(close()));
}

Widget::~Widget()
{
}

1.3 用户自定义槽函数

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT

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

    //一般成员函数作为槽函数需要用slots声明一下
public slots:
    void buttonLogSlot();
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include <QPushButton>
#include <QDebug>

void buttonRegSlot()
{
    qDebug()<< "注册按钮点击了";
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //**************设置窗口属性***************
    this->setWindowTitle("按钮操作");
    this->setFixedSize(800, 600);

    //**************创建按钮并设置属性************
    QPushButton *buttonReg = new QPushButton("注册", this);
    QPushButton *buttonLog = new QPushButton("登录", this);
    QPushButton *buttonQuit = new QPushButton("退出", this);
    QPushButton *buttonCancel = new QPushButton("取消", this);

    buttonReg->setFixedSize(100, 50);
    buttonLog->setFixedSize(100, 50);
    buttonQuit->setFixedSize(100, 50);
    buttonCancel->setFixedSize(100, 50);

    buttonReg->move(150, 200);
    buttonLog->move(300, 200);
    buttonQuit->move(450, 200);
    buttonCancel->move(150, 300);

    //**************根据按钮的信号设置处理方式*****************
    //---系统自带信号和槽函数---
    //点击退出按钮让窗口关闭
    //Qt5.0以上版本使用一下形式
    connect(buttonQuit, &QPushButton::clicked, this, &QWidget::close);
    //Qt4.x版本只能使用一下形式
    //connect(buttonQuit, SIGNAL(clicked()), this, SLOT(close()));

    //---自定义槽函数---
    //1、使用全局函数定义槽函数
    connect(buttonReg, &QPushButton::clicked, this, buttonRegSlot);
    //2、使用成员函数定义槽函数
    connect(buttonLog, &QPushButton::clicked, this, buttonLogSlot);
    //3、使用lambda表达式实现槽函数
    connect(buttonCancel, &QPushButton::clicked, this, [=](){
        qDebug()<< "取消按钮点击了";
    });
}

Widget::~Widget()
{
}

void Widget::buttonLogSlot()
{
    qDebug()<< "登录按钮点击了";
}

1.4 实现窗口切换

1.4.1 添加新的窗口

在当前工程的名字位置鼠标右键选择“Add New”,在弹出的窗口中选择“C++”中的“C++ Class

https://note.youdao.com/yws/public/resource/d23ff061138554293b27d88e32be9553/xmlnote/114CFAC2464144F2AC9D12236A18F6F9/3C947399FE6943E2B8FEBC961FAC9209/85129

设置类名,并设置父类

https://note.youdao.com/yws/public/resource/d23ff061138554293b27d88e32be9553/xmlnote/114CFAC2464144F2AC9D12236A18F6F9/47601E8243D44EAB8787EBF8A4642CE7/85126

一路下一步点击完成即可

图片.png

1.4.2 实现页面之间切换并传值

newwindow.h

#ifndef NEWWINDOW_H
#define NEWWINDOW_H

#include <QWidget>

class Newwindow : public QWidget
{
    Q_OBJECT
public:
    explicit Newwindow(QWidget *parent = nullptr);

    //自定义信号,返回值为void,
    //不需要自己实现,参数可以有,也可以没有
signals:
    void mysignal();
    void mysignal1(int val);

};

#endif // NEWWINDOW_H

newwindow.cpp

#include "newwindow.h"
#include <QPushButton>

Newwindow::Newwindow(QWidget *parent) : QWidget(parent)
{
    this->setWindowTitle("这是另一个窗口");
    this->setFixedSize(600, 300);

    QPushButton *button = new QPushButton("上一页", this);
    button->setFixedSize(100, 50);
    button->move(50, 50);

    connect(button, &QPushButton::clicked, [=](){
        //发信号,当前窗口发送的信号
        emit this->mysignal();
    });

    QPushButton *button1 = new QPushButton("FRONT", this);
    button1->setFixedSize(100, 50);
    button1->move(150, 50);

    connect(button1, &QPushButton::clicked, [=](){
        //发信号的同时传值
        emit this->mysignal1(888);
    });
}

widget.h

ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT

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

    //一般成员函数作为槽函数需要用slots声明一下
public slots:
    void buttonLogSlot();
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include <QPushButton>
#include <QDebug>
#include "newwindow.h"

void buttonRegSlot()
{
    qDebug()<< "注册按钮点击了";
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //**************设置窗口属性***************
    this->setWindowTitle("按钮操作");
    this->setFixedSize(800, 600);

    //**************创建按钮并设置属性************
    QPushButton *buttonReg = new QPushButton("注册", this);
    QPushButton *buttonLog = new QPushButton("登录", this);
    QPushButton *buttonQuit = new QPushButton("退出", this);
    QPushButton *buttonCancel = new QPushButton("取消", this);
    QPushButton *buttonNext = new QPushButton("下一页", this);

    buttonReg->setFixedSize(100, 50);
    buttonLog->setFixedSize(100, 50);
    buttonQuit->setFixedSize(100, 50);
    buttonCancel->setFixedSize(100, 50);
    buttonNext->setFixedSize(100, 50);

    buttonReg->move(150, 200);
    buttonLog->move(300, 200);
    buttonQuit->move(450, 200);
    buttonCancel->move(150, 300);
    buttonNext->move(300, 300);

    //**************根据按钮的信号设置处理方式*****************
    //---系统自带信号和槽函数---
    //点击退出按钮让窗口关闭
    //Qt5.0以上版本使用一下形式
    connect(buttonQuit, &QPushButton::clicked, this, &QWidget::close);
    //Qt4.x版本只能使用一下形式
    //connect(buttonQuit, SIGNAL(clicked()), this, SLOT(close()));

    //---自定义槽函数---
    //1、使用全局函数定义槽函数
    connect(buttonReg, &QPushButton::clicked, this, buttonRegSlot);
    //2、使用成员函数定义槽函数
    connect(buttonLog, &QPushButton::clicked, this, buttonLogSlot);
    //3、使用lambda表达式实现槽函数
    connect(buttonCancel, &QPushButton::clicked, this, [=](){
        qDebug()<< "取消按钮点击了";
    });

    //页面切换
    //创建新窗口
    Newwindow *newwindow = new Newwindow;
    //使用connect函数切换窗口
    connect(buttonNext, &QPushButton::clicked, [=](){
        //隐藏当前窗口
        this->hide();
        //开启新的窗口
        newwindow->show();
    });

    //接收新窗口发送的信号,隐藏新窗口,开辟原窗口
    connect(newwindow, &Newwindow::mysignal, this, [=](){
        newwindow->hide();
        this->show();
    });

    //************************************************

    //页面切换之间可以通过信号和槽实现数据的传递
    connect(newwindow, &Newwindow::mysignal1, [=](int val){
        newwindow->hide();
        this->show();
        qDebug()<< val;
    });
}

Widget::~Widget()
{
}

void Widget::buttonLogSlot()
{
    qDebug()<< "登录按钮点击了";
}

1.5 信号与槽总结

自定义信号槽需要注意的事项:

  1. 发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数Lambda 表达式等无需接收者的时候除外);
  2. 信号和槽函数返回值是 void
  3. 信号只需要声明,不需要实现
  4. 槽函数需要声明,也需要实现
  5. 槽函数是普通的成员函数,作为成员函数,会受到 publicprivateprotected 的影响;
  6. 使用 emit 在恰当的位置发送信号;
  7. 使用connect()函数连接信号和槽。
  8. 任何成员函数static 函数全局函数Lambda 表达式都可以作为槽函数
  9. ==信号槽要求信号和槽的参数一致==,所谓一致,是参数类型一致。如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。

信号槽的拓展:

  1. 一个信号可以和多个槽相连 如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。
  2. 多个信号可以连接到一个槽 只要任意一个信号发出,这个槽就会被调用。
  3. 一个信号可以连接到另外的一个信号 当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。
  4. 槽可以被取消链接 这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。
  5. 信号槽可以断开 利用disconnect关键字是可以断开信号槽的
  6. 使用Lambda 表达式 在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。 在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。

图片.png