Qt学习Day2:信号槽
1. 信号槽
信号槽是Qt在C++基础上新增的特性,类似于其他编程语言中回调函数的概念。信号槽使用起来非常方便,可以实现Qt对象之间的通信。
使用的前提条件有两个:
● 对象必须继承自QObject类,QObject是Qt所有对象的基类,也是QWidget的基类
● 类中要包含Q_OBJECT宏
2. 函数原型
// 连接信号槽
// 参数1:发射者,信号函数的发出者,因果关系中因的名词对象
// 参数2:信号函数,需要使用SIGNAL包裹信号函数的名称,因果关系中因的动词,第一次使用时通常需要查询
// 参数3:接收者,槽函数的执行者,因果关系中果的执行名词对象
// 参数4:槽函数,需要使用SLOT包裹槽函数的名称,因果关系中果的动词,第一次使用时通常需要查询QObject::connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method) [static]
3. 连接
为了学习方便,逐步分为三种连接方式,非官方分类。
3.1 自带信号→自带槽
最简单的一种信号槽连接情况,因为信号函数和槽函数都是Qt内置的,程序员只需要查询后建立连接即可。
【例子】点击按钮,关闭窗口。
分析:
发射者——按钮对象,
信号函数——void QAbstractButton::clicked(bool checked = false) [signal]
接收者——窗口对象
槽函数——bool QWidget::close() [slot]
dialog.h
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = 0);~Dialog();private:QPushButton* btn;
};#endif // DIALOG_H
dialog.cpp
#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent)
{resize(300,300);btn = new QPushButton("关闭窗口",this);btn->move(100,100);// 连接信号槽时需要确保发射者和接收者对象均创建完成connect(btn,SIGNAL(clicked()),this,SLOT(close()));
}Dialog::~Dialog()
{delete btn;
}
3.2 自带信号 → 自定义槽
3.1节中的连接方式并不能实现一些特殊功能的槽函数,这种情况下需要程序员手动编写槽函数,这种方式也是使用最多的一种连接方式。
槽函数是一种特殊的成员函数,因此也受权限修饰符的影响,几乎不影响信号槽的使用,但是会影响作为成员的调用。
【例子】
点击按钮,窗口向右下角移动并输出坐标。
dialog.h
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>
#include <QDebug>class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = 0);~Dialog();private:QPushButton* btn;// 声明槽函数
private slots: // 私有槽函数void btnClickedSlot();
};#endif // DIALOG_H
dialog.cpp
#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent)
{resize(300,300);btn = new QPushButton("关闭窗口",this);btn->move(100,100);// 发射者:btn// 信号函数:clicked// 接收者:this// 槽函数:btnClickedSlotconnect(btn,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
}// 定义槽函数
void Dialog::btnClickedSlot()
{// 获取当前的坐标int x = this->x();int y = this->y();x += 10;y += 10;// 移动窗口move(x,y);qDebug() << x << y;
}Dialog::~Dialog()
{delete btn;
}
3.3 自定义信号 → 槽
本节内容属于强行教学,因为自定义信号主要在后期解决特殊情况下的问题。
信号函数是一种特殊的函数,只有声明,没有定义,且不能调用只能配合emit关键字发送,也没有权限。
【例子】点击按钮,关闭窗口。
在3.1节中使用一个信号槽解决此问题,也是问题的最优解;本节使用两个信号槽连接解决此问题,在第一个连接的槽函数中发射一个自定义信号,使用这个自定义信号再触发关闭窗口的功能。
dialog.h
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = 0);~Dialog();private:QPushButton* btn;private slots:void btnClickedSlot();// 声明信号函数
signals:void mySignal(); // 自定义信号函数
};#endif // DIALOG_H
dialog.cpp
#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent)
{resize(300,300);btn = new QPushButton("关闭窗口",this);btn->move(100,100);// 点击按钮 → 自定义槽函数connect(btn,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));// 使用自定义信号触发关闭窗口的效果connect(this,SIGNAL(mySignal()),this,SLOT(close()));}void Dialog::btnClickedSlot()
{// 发射自定义信号emit mySignal();
}Dialog::~Dialog()
{delete btn;
}
4.传参
【例子】点击按钮,按钮上显示点击的次数。
正常的解法是使用成员变量计数。
dialog.h
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>class Dialog : public QDialog
{Q_OBJECTpublic: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(300,300);btn = new QPushButton("0",this);btn->move(100,50);connect(btn,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
}void Dialog::btnClickedSlot()
{// 次数+1count++;// int → QStringQString text = QString::number(count);// 设置按钮当前点击的次数btn->setText(text);
}Dialog::~Dialog()
{delete btn;
}
为了演示信号槽传参,强行使用信号槽进行参数传递。
dialog.h
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>
#include <QDebug>class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = 0);~Dialog();private:QPushButton* btn;int count; // 点击次数private slots:void btnClickedSlot(); // 点击按钮的槽函数void countSlot(int); // 此槽函数表示可以接收一个int参数signals:// 此信号函数可以携带一个int参数发射void countSignal(int);
};#endif // DIALOG_H
dialog.cpp
#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent),count(0) // 构造初始化列表
{resize(300,300);btn = new QPushButton("0",this);btn->move(100,50);connect(btn,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));connect(this,SIGNAL(countSignal(int)),this,SLOT(countSlot(int)));
}void Dialog::btnClickedSlot()
{// 次数+1count++;// 发射一个自定义信号,携带count参数emit countSignal(count);
}// 接收参数的槽函数
void Dialog::countSlot(int count)
{qDebug() << &this->count << &count;// int → QStringQString text = QString::number(count);btn->setText(text);
}Dialog::~Dialog()
{delete btn;
}
信号与槽参数关系:
1. 理论上可以传递任意个数的参数
2. 信号的参数个数必须大于等于槽的参数个数
3. 参数类型必须一一匹配
5.对应关系
一对多:同一个信号可以连接到多个槽函数。
dialog.h
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>
#include <QDebug>class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = 0);~Dialog();private:QPushButton* btn;QPushButton* btn2;private slots:void customSlot1();void customSlot2();void customSlot3();
};#endif // DIALOG_H
dialog.cpp
#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent)
{resize(300,300);btn = new QPushButton("一对多",this);btn2 = new QPushButton("一对多",this);btn->move(100,100);btn2->move(100,150);// 一个信号连接多个槽函数connect(btn,SIGNAL(clicked()),this,SLOT(customSlot1()));connect(btn,SIGNAL(clicked()),this,SLOT(customSlot2()));// 断开连接
// disconnect(btn,SIGNAL(clicked()),
// this,SLOT(customSlot2()));// 一个信号连接一个槽connect(btn2,SIGNAL(clicked()),this,SLOT(customSlot3()));
}void Dialog::customSlot1()
{qDebug() << "A";
}void Dialog::customSlot2()
{qDebug() << "B";
}void Dialog::customSlot3()
{// 当做是普通的成员函数直接调用customSlot1();customSlot2();
}Dialog::~Dialog()
{delete btn;
}
多对一:多个信号可以同时连接一个槽函数。
dialog.h
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>
#include <QDebug>class Dialog : public QDialog
{Q_OBJECTpublic: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(300,300);btn1 = new QPushButton("A",this);btn2 = new QPushButton("B",this);btn1->move(100,100);btn2->move(100,150);connect(btn1,SIGNAL(clicked()),this,SLOT(btnsClickedSlot()));connect(btn2,SIGNAL(clicked()),this,SLOT(btnsClickedSlot()));
}void Dialog::btnsClickedSlot()
{// 可以使用sender函数获取发射者对象if(btn1 == sender()){qDebug() << "疯狂星期四1";}else if(btn2 == sender()){qDebug() << "疯狂星期四2";}
}Dialog::~Dialog()
{delete btn1;delete btn2;
}