首页 > 其他分享 >Qt(信号槽)

Qt(信号槽)

时间:2024-10-18 20:46:57浏览次数:3  
标签:btn Qt void 信号 Dialog include 函数

1. 概念

之前的程序界面只能看,不能交互,信号槽可以让界面进行人机交互。

信号槽是Qt在C++基础上新增的特性,类似于其他编程中的回调机制,其目的是实现对象之间的通信。

使用信号槽需要具备两个先决条件:

  • 通信的对象必须继承自QObject

QObject是Qt所有对象的基类,内部规定了Qt最基础的对象特性。

  • 类中要包含Q_OBJECT宏

2. 函数原型

信号槽的建立是通过connect函数实现的。

// 信号槽连接函数
// 参数1:发射者,因发起的对象n.
// 参数2:信号函数,因的动作v.,需要使用SIGNAL()包裹函数名称
// 参数3:接收者,果执行的对象n.
// 参数4:槽函数,果的动作v.,需要使用SLOT()包裹函数名称
QObject::​connect(const QObject * sender, 
				    const char * signal, 
				    const QObject * receiver, 
				    const char * slot)                [static]

信号函数一定来自于发射者,槽函数一定来自于接收者。信号槽的连接必须在发射者对象和接收者对象创建完成后进行,如果成功连接后,发射者或接收者对象销毁了,连接断开。

信号槽的连接也可以程序员手动断开,只需要把connect函数改为disconnect即可,参数保持不变。

3. 连接方式

为了讲课,按照难易程度分为三种连接方式:

  • 自带信号 → 自带槽
  • 自带信号 → 自定义槽
  • 自定义信号 → 槽

3.1 自带信号 → 自带槽

这种连接方式是最简单的连接方式,因为信号函数和槽函数都是Qt内置的,程序员只需要找到连接的四个参数,直接连接即可。

【例子】点击按钮,关闭窗口。

分析:

参数1:发射者——按钮对象

参数2:信号函数——点击

void QAbstractButton::​clicked()                    [signals]

参数3:接收者——窗口对象

参数4:槽函数——关闭

bool QWidget::​close()                            [slot]

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    QPushButton *btn;
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    resize(400,400);
    btn = new QPushButton("关闭",this);
    btn->move(100,100);
    //  参数1:发射者——按钮对象
    //	参数2:信号函数——点击
    //	参数3:接收者——窗口对象
    //	参数4:槽函数——关闭
    connect(btn,SIGNAL(clicked()),this,SLOT(close()));
}

Dialog::~Dialog()
{
    delete btn;
}

3.2 自带信号 → 自定义槽

这种连接方式是使用的最多的一种连接方式,因为在实际开发中,Qt无法预设所有的槽函数情况,对于复杂的操作执行逻辑,需要程序员手动编写槽函数。

槽函数是一种特殊的成员函数。

【例子】点击按钮,窗口向右下角移动10个像素,并且后台输出当前窗口的位置。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>
#include <QDebug>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    QPushButton *btn;

    // 槽函数的声明
private slots: // 最小权限原则
   void mySlot(); // 自定义槽函数
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    resize(400,400);
    btn = new QPushButton("关闭",this);
    btn->move(100,100);
    // 连接信号槽
    connect(btn,SIGNAL(clicked()),
            this,SLOT(mySlot()));
}

void Dialog::mySlot()
{
    // 先获得当前坐标
    int x = this->x();
    int y = this->y();
    // 移动
    move(x+10,y+10);
    // 输出当前窗口位置
    qDebug() << this->x() << this->y();
}

Dialog::~Dialog()
{
    delete btn;
}

3.3 自定义信号

自定义信号通常用于解决一些对象之间“远距离”通信问题,本节属于强行使用,因此并不是问题最优解,只是为了展示自定义信号的使用方式。

【例子】点击按钮,按钮上显示点击的次数。

先忽略自定义信号,展示正常解法:

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>
#include <QDebug>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    QPushButton* btn;
    int count;

private slots:
    void btnClickedSlot(); // 点击按钮的槽函数
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent),count(0) // 构造初始化列表
{
    resize(400,400);
    btn = new QPushButton("0",this);
    btn->move(200,200);

    connect(btn,SIGNAL(clicked()),
            this,SLOT(btnClickedSlot()));
}

void Dialog::btnClickedSlot()
{
    count++;
    // int → QString
    QString text = QString::number(count);
    // 设置到按钮上显示
    btn->setText(text);
}

Dialog::~Dialog()
{
    delete btn;
}

基于上面的解法,强行增加自定义信号发射环节:

信号函数是一种特殊的函数,只有声明没有定义,声明后可以直接配合emit关键字发射。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>
#include <QDebug>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    QPushButton* btn;
    int count;

signals:
    void countSignal(); // 自定义信号

private slots:
    void btnClickedSlot(); // 点击按钮的槽函数
    void countSlot(); // 与自定义信号连接的槽函数
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent),count(0) // 构造初始化列表
{
    resize(400,400);
    btn = new QPushButton("0",this);
    btn->move(200,200);

    connect(btn,SIGNAL(clicked()),
            this,SLOT(btnClickedSlot()));
    connect(this,SIGNAL(countSignal()),
            this,SLOT(countSlot()));
}

void Dialog::btnClickedSlot()
{
    count++;
    // 发射自定义信号
    emit countSignal();
}

/**
 * @brief Dialog::countSlot
 * 中转之后更新按钮显示的槽函数
 */
void Dialog::countSlot()
{
    QString text = QString::number(count);
    btn->setText(text);
}

Dialog::~Dialog()
{
    delete btn;
}

4. 信号槽传参

信号槽可以进行参数传递,信号函数携带参数发射,槽函数可以收到此参数。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>
#include <QDebug>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    QPushButton* btn;
    int count;

signals:
    void countSignal(int); // 自定义信号

private slots:
    void btnClickedSlot(); // 点击按钮的槽函数
    void countSlot(int); // 与自定义信号连接的槽函数
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent),count(0) // 构造初始化列表
{
    resize(400,400);
    btn = new QPushButton("0",this);
    btn->move(200,200);

    connect(btn,SIGNAL(clicked()),
            this,SLOT(btnClickedSlot()));
    connect(this,SIGNAL(countSignal(int)),
            this,SLOT(countSlot(int)));
}

void Dialog::btnClickedSlot()
{
    count++;
    // 发射自定义信号(携带参数)
    emit countSignal(count);
}

/**
 * @brief Dialog::countSlot
 * count参数是信号函数发来的,不是成员变量
 * 中转之后更新按钮显示的槽函数
 */
void Dialog::countSlot(int count)
{
    QString text = QString::number(count);
    btn->setText(text);
}

Dialog::~Dialog()
{
    delete btn;
}

需要注意的是:

  • 理论上可以传递任意多个参数
  • 信号函数的参数个数必须大于等于槽函数的参数个数
  • 参数类型需要匹配

5. 对应关系

同一个信号可以连接到多个槽(一对多),多个信号也可以连接到同一个槽(多对一)。

5.1 一对多

槽函数也是成员函数,因此在一对多的连接关系中,把连接的多个槽函数可以看做是普通的成员函数,合并为一个槽函数。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>
#include <QDebug>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    QPushButton* btn1;
    QPushButton* btn2;

private slots:
    void btnClickedSlot1();
    void btnClickedSlot2();
    void btnClickedSlot3(); // 3 = 1 + 2
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    resize(400,400);
    btn1 = new QPushButton("一对多", this);
    btn1->move(200,100);
    btn2 = new QPushButton("一对一", this);
    btn2->move(100,200);

    // 一对多
    connect(btn1,SIGNAL(clicked()),
            this,SLOT(btnClickedSlot1()));
    connect(btn1,SIGNAL(clicked()),
            this,SLOT(btnClickedSlot2()));
    // 一对一
    connect(btn2,SIGNAL(clicked()),
            this,SLOT(btnClickedSlot3()));
}

void Dialog::btnClickedSlot1()
{
    qDebug() << "A";
}

void Dialog::btnClickedSlot2()
{
    qDebug() << "B";
}

void Dialog::btnClickedSlot3()
{
    // 直接把Slot1和Slot2两个函数当做普通的成员函数,
    // 使用this指针调用
    btnClickedSlot1();
    btnClickedSlot2();
}

Dialog::~Dialog()
{
    delete btn1;
}

5.2 多对一

多对一的连接方式下,槽函数无法区分信号来源,可以在槽函数中调用sender函数获取发射者对象,从而判断信号来源。

// 在槽函数中调用,返回发射者对象
QObject * QObject::​sender() const

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>
#include <QDebug>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    QPushButton* btn1;
    QPushButton* btn2;

private slots:
    void btnsClickedSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    resize(400,400);
    btn1 = new QPushButton("A",this);
    btn2 = new QPushButton("B",this);
    btn1->move(150,100);
    btn2->move(150,200);

    // 多对一
    connect(btn1,SIGNAL(clicked()),
            this,SLOT(btnsClickedSlot()));
    connect(btn2,SIGNAL(clicked()),
            this,SLOT(btnsClickedSlot()));
}

void Dialog::btnsClickedSlot()
{
    if(btn1 == sender())
    {
        qDebug() << "点击了按钮1";
    }else if(btn2 == sender())
    {
        qDebug() << "点击了按钮2";
    }
    qDebug() << "槽函数触发!";
}

Dialog::~Dialog()
{
    delete btn1;
    delete btn2;
}

标签:btn,Qt,void,信号,Dialog,include,函数
From: https://blog.csdn.net/2301_77143270/article/details/143061496

相关文章

  • QT/c++相关记录
     QT的大部分容器类(如QString、QVector等)都是使用隐式共享(implicitsharing)技术,这是通过写时复制(copy-on-write,COW)实现的优化模式。理解这一点的关键在于,Qt的容器类需要在对象拷贝时高效处理数据,而隐式共享则允许在栈上操作容器的同时,在需要时共享内部数据的堆上存储。......
  • Learn OpenGL In Qt之纹理
    竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生~公众号:C++学习与探索 | 个人主页:rainInSunny | 个人专栏:LearnOpenGLInQt文章目录纹理纹理坐标纹理环绕方式纹理采样多级渐远纹理纹理加载和创建加载纹理创建纹理应用纹理纹理纹理坐标  在前面的......
  • Verilog——控制信号的书写规范
    目录前言控制信号的书写规范前言这个专栏会专门讲一些Verilog的知识,后续会慢慢更新,欢迎关注Verilog专栏控制信号的书写规范//不推荐//Example1:if(A&&(B|C))//推荐//Example2:assignD=A&&(B|C);if(D)......
  • mqtt与云服务器
    mqtt目录mqtt回顾云服务器的操作MQTT协议--将官方库移植到工程--应用--可能会出现的问题:完整代码回顾--昨天我们写的AT指令是直接写在main中,在while循环的外面,没有很好的封装,所以今天我们写一个函数来封装AT指令 //要保证能在while循环中运行uint8_tstate......
  • 【QT】常用控件(二)
    个人主页~常用控件(一)~常用控件三、按钮类控件1、PushButtonwidget.hwidget.cpp2、RadioButton3、CheckBox四、显示类控件1、label三、按钮类控件1、PushButtonQPushButton继承自QAbstractButton,它是所有按钮的父类我们从这个按钮的属性表中可以看到,QPus......
  • canopen 导致qt上位机崩溃的原因分析
    今天开发过程中,在qt中移植canfestival协议栈后,上位机崩溃了,经过我的排查,发现了崩溃的原因。在协议栈中的代码如下UNS32RegisterSetODentryCallBack(CO_Data*d,UNS16wIndex,UNS8bSubindex,ODCallback_tCallback){UNS32errorCode;constindextable*odentry;o......
  • 在盲解卷中,解卷积时滤波器系数翻转,平移与信号相乘再相加。另一种是信号翻转,平移与滤波
    在盲解卷积中,有两种基本的方法来处理信号和滤波器系数:一种是将滤波器系数翻转、平移与信号相乘再相加,另一种是将信号翻转、平移与滤波器系数相乘再相加。这两种方法的区别主要在于处理信号和滤波器的顺序,以及它们对最终结果的影响。1.**滤波器系数翻转(FilterCoefficientFli......
  • QT实现滑动页面切换
    1.界面实现效果以下是具体的项目需要用到的效果展示。2.简介原理:使用Qt的QPropertyAnimation动画类,这里简单来说就是切换两个界面。这个widget里面可以放很多个待切换的界面,每次切换的时候将当前界面和切换后的界面显示,其他界面都隐藏,然后当前界面移动到主界面之外,下一......
  • 2-STM32F103+ML307(中移4G Cat1)OTA升级篇(自建物联网平台)-STM32通过ML307使用http或
    <p><iframename="ifd"src="https://mnifdv.cn/resource/cnblogs/ZLIOTB/ML307/myota.html"frameborder="0"scrolling="auto"width="100%"height="1500"></iframe></p>  说明前面......
  • qt页面设计
    1.Designer设计师(掌握)Designer是Qt内置的一款界面设计程序,设计的界面文件为.ui格式。C++程序员通常不会单独启动Designer,如果要在项目中使用Designer程序,只需要在新建项目时,勾选“创建界面文件”选项。这样的项目自带dialog.ui,双击dialog.ui可以直接使用Designer程序......