QT中的信号与槽
- 一、信号与槽函数的作用
- 二、如何关联信号与槽函数
- 1、借助集成开发环境,右键转到槽函数
- 示例代码:
- 2、调用connect函数手动关联信号与槽函数
- 三、扩展
- 四、总结信号与槽的特点
- 1、一个类如果要使用信号以及槽函数,那么该类的定义中必须加上Q_OBJECT宏定义
- 2、同一个槽函数,可以被不同对象的信号关联
- 3、同一个信号,可以关联不同的槽函数
- 4、信号的发送者跟信号之间必须是所属关系(发送者对应的类里面必须有这个信号)
- 5、信号带参数,槽函数可以带参数,也可以不带参数
- 五、获取信号的发送者
- 示例代码:
- 六、自定义信号,发送信号
- 示例代码:
一、信号与槽函数的作用
用来在组件和组件,组件和窗口之间通信
- 信号:QT中给每个组件都定义了信号,每个信号都有它特定的触发条件(QT助手查询到每个信号的触发条件)
QT中所有的信号,本质上都是个函数,信号只有声明,没有源代码
比如:以按钮为例,常用的信号如下
[signal] void QAbstractButton::clicked(bool checked = false) //当按钮按下去,然后松开的时候自动触发这个信号
[signal] void QAbstractButton::released() //按钮松开会触发该信号
[signal] void QAbstractButton::pressed() //按钮按下去触发该信号
- 槽函数:当某个信号触发的时候,跟这个信号对应的槽函数会被自动调用执行
二、如何关联信号与槽函数
关联信号与槽函数:就是使选择的信号跟槽函数形成一一对应关系
1、借助集成开发环境,右键转到槽函数
- 头文件中会自动生成槽函数的声明
private slots: //声明了一个私有的槽函数
void on_pushButton_clicked(); //槽函数的原型声明
- 在源码.cpp文件中自动生成的槽函数的定义
on_对象的名字_信号的名字();
示例代码:
// login.cpp
#include "loginwin.h"
#include "ui_loginwin.h"
#include <QLabel>
#include <QFont>
#include <QLineEdit>
#include <QPushButton>
#include <iostream>
#include <QDebug>
using namespace std;
loginwin::loginwin(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::loginwin)
{
ui->setupUi(this);
//通过写代码把登录界面的ui做出来
//第一步:标签
//父窗口:这个组件(控件)等一会在哪个窗口上显示,这个窗口就是父窗口
QLabel *lb1=new QLabel("学生管理系统",this);
//设置按钮的坐标,宽高
/*
规律:任何组件,属性都有对应的设置方法,方法的名字统一叫做setxxx()
比如:属性geometry --》对应的方法setGeometry()
*/
lb1->setGeometry(200,10,400,100);
QFont myfont("楷体",24);
lb1->setFont(myfont);
//设置样式
lb1->setStyleSheet("color:rgb(255,0,0);");
//第二步:两个单行输入框
QLineEdit *le1=new QLineEdit(this);
QLineEdit *le2=new QLineEdit(this);
//设置坐标,宽高
le1->setGeometry(220,130,300,50);
le2->setGeometry(220,220,300,50);
//设置默认提示文字
le1->setPlaceholderText("请输入用户名");
le2->setPlaceholderText("请输入密码");
//设置用户名,密码位数
le1->setMaxLength(8);
le2->setMaxLength(8);
//设置密码隐藏
le2->setEchoMode(QLineEdit::Password);
//设置样式
le1->setStyleSheet("border:2px solid#ff0000;");
le2->setStyleSheet("border:2px solid#ff0000;");
//设置字体
le1->setFont(myfont);
le2->setFont(myfont);
//第三步:两个按钮
QPushButton *bt1=new QPushButton("登录",this);
QPushButton *bt2=new QPushButton("注册",this);
bt1->setFont(myfont);
bt2->setFont(myfont);
//设置坐标,宽高
bt1->setGeometry(200,300,100,50);
bt2->setGeometry(450,300,100,50);
//给主窗口设置边框图片
this->setStyleSheet("QMainWindow{border-image: url(C:/Users/Administrator/Desktop/share/4.jpeg);}");
}
loginwin::~loginwin()
{
delete ui;
}
//跟登录按钮有关的槽函数
//槽函数的定义--》代码由程序员来编写
void loginwin::on_pushButton_clicked()
{
//C++的输出
//cout<<"登录按钮被点击(按下去,然后松开)"<<endl;
//cout<<"loginbt clicked"<<endl;
//QT特有的输出,自动换行,不需要回车
qDebug()<<"登录按钮被点击(按下去,然后松开)";
}
//跟release信号有关的槽函数
void loginwin::on_pushButton_released()
{
qDebug()<<"登录按钮松开";
}
//跟pressed信号有关的槽函数
void loginwin::on_pushButton_pressed()
{
}
// login.h
#ifndef LOGINWIN_H
#define LOGINWIN_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class loginwin; }
QT_END_NAMESPACE
class loginwin : public QMainWindow
{
Q_OBJECT
public:
loginwin(QWidget *parent = nullptr);
~loginwin();
//翻译qt程序--》uic和moc(qt程序除了c++代码之外还有qt特有的(例如信号与槽),所有需要翻译成c++代码)
//slots是QT特有的关键字,C++中没有
private slots: //声明私有的槽函数
//在源码.cpp文件中自动生成的槽函数的定义,命名有规律
//on_对象的名字_信号的名字();
void on_pushButton_clicked(); //槽函数的原型声明
void on_pushButton_released(); //槽函数的原型声明
void on_pushButton_pressed(); //槽函数的原型声明
private:
Ui::loginwin *ui;
};
#endif // LOGINWIN_H
2、调用connect函数手动关联信号与槽函数
有些场合无法右键转到槽(比如:按钮不是ui设计师拖过去,而是通过写代码new出来)
- 写法1:
connect(QObject *sender, PointerToMemberFunction signal,QObject *receiver, PointerToMemberFunction method)
- 写法2
connect(发送者指针,&类的名字::信号的名字,接收者,&类的名字::槽函数)
参数:sender --》信号的发送者,指针
signal --》发送什么信号
receiver --》信号的接收者
method --》需要调用的槽函数
槽函数命名时候,可以跟自动生成的槽函数命名规律保持一致,也可以自行单独命名
比如:connect(ui->handlebt,SIGNAL(clicked(bool)),this,SLOT(fun())); //关联了handlebt这个按钮的clicked信号和槽函数fun()
QT使用技巧:头文件中声明了函数,右键选择refactor,可以在.cpp生成函数的定义
三、扩展
QObject --》QT中所有类的基类
作为形参的好处:实参可以传递任何类型(因为QT中所有的类都是QObject的子类)
四、总结信号与槽的特点
1、一个类如果要使用信号以及槽函数,那么该类的定义中必须加上Q_OBJECT宏定义
Q_OBJECT:QT把它称为元对象系统,作用支持信号与槽这种机制
2、同一个槽函数,可以被不同对象的信号关联
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 同一个槽函数,可以被不同对象的信号关联
connect(ui->btn1, SIGNAL(clicked()), this, SLOT(btn()));
connect(ui->btn2, SIGNAL(clicked()), this, SLOT(btn()));
connect(ui->btn3, SIGNAL(clicked()), this, SLOT(btn()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::btn()
{
qDebug()<<"按钮按下";
}
//===========================================//
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void btn();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
3、同一个信号,可以关联不同的槽函数
槽函数的调用顺序跟关联的先后顺序一致,先关联的先调用
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//同一个信号,可以关联不同的槽函数
//槽函数的调用顺序关联的先后顺序一致,先关联的先调用
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(btn2()));
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(btn1()));
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(btn3()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::btn1()
{
qDebug()<<"按下btn1";
}
void MainWindow::btn2()
{
qDebug()<<"按下btn2";
}
void MainWindow::btn3()
{
qDebug()<<"按下btn3";
}
//=======================================================//
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void btn1();
void btn2();
void btn3();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
4、信号的发送者跟信号之间必须是所属关系(发送者对应的类里面必须有这个信号)
信号的接收者跟槽函数之间必须是所属关系(接收者对应的类里面必须有这个槽函数)
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//自己手动管理按钮的clicked信号跟自己写的槽函数
/*
信号的发送者跟信号之间必须是所属关系(发送者对应的类里面必须有这个信号)
信号的接收者跟槽函数之间必须是所属关系(接收者对应的类里面必须有这个槽函数)
*/
//错误1:信号发送者跟信号之间不是所属关系
//connect(ui->pushButton,SIGNAL(textChanged(QString)),this,SLOT(fun()));
//错误2:信号接收者跟槽函数之间不是所属关系
//connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(myfun()));
connect(ui->pushButton,SIGNAL(clicked()),ui->lineEdit,SLOT(fun()));
}
MainWindow::~MainWindow()
{
delete ui;
}
//自己手写的槽函数
void MainWindow::fun()
{
qDebug()<<"按钮被点击了";
}
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
//自己手写槽函数,命名可以跟右键转到槽一样的命名,也可以随意命名
void fun();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
5、信号带参数,槽函数可以带参数,也可以不带参数
区别:
- 槽函数带参数,参数必须跟信号保持一致,表示槽函数接收信号传递过来的参数
- 槽函数不带参数,表示不接收信号传递过来的参数
注意:
- 如果信号没有任何参数,此时槽函数也不可以有任何参数
- 信号带了参数,connect写的时候不要写参数的名字,只需要写参数的类型
- 槽函数带了参数,connect写的时候不要写参数的名字,只需要写参数的类型
五、获取信号的发送者
作用:当多个组件对象共用一个槽函数的时候,程序员需要知道是哪个组件对象触发的信号,此时就要获取信号的发送者
QObject *sender() const
返回值:返回一个指针,该指针指向信号的发送者
QT中转换函数
qobject_cast(QObject *object) //把父类的指针转换成子类地址
示例代码:
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 同一个槽函数,可以被不同对象的信号关联
connect(ui->btn1, SIGNAL(clicked()), this, SLOT(btn()));
connect(ui->btn2, SIGNAL(clicked()), this, SLOT(btn()));
connect(ui->btn3, SIGNAL(clicked()), this, SLOT(btn()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::btn()
{
// qDebug()<<"按钮按下";
// 获取信号的发送者,并判断哪个按钮被按下
QObject *is_sender = sender();
if(is_sender == ui->btn1)
qDebug()<<"按钮1按下";
else if(is_sender == ui->btn2)
qDebug()<<"按钮2按下";
else if(is_sender == ui->btn3)
qDebug()<<"按钮3按下";
// QT中转换函数的使用
QPushButton *btn = qobject_cast<QPushButton *>(is_sender);
btn->setStyleSheet("background-color:rgb(0,255,0);");
}
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void btn();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
六、自定义信号,发送信号
//自定义信号语法格式
signals:
// 自定义信号;
void mysignal(参数); //参数的类型个数依据需要自行添加
//发送信号
//注意注意注意:自定义的信号必须程序员主动调用emit发送,不能自动触发的
emit mysignal(实参);
示例代码:
// 第一个界面:mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "info_win.h"
#include <QString>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
// 登录按钮的槽函数
void MainWindow::on_pushButton_clicked()
{
// 获取输入的用户名和密码
QString account = ui->lineEdit->text();
QString pswd = ui->lineEdit_2->text();
if(account == "aaa" && pswd == "123")
{
//创建第二个界面对象,必须传递this指针(把第一个界面的地址传给第二个界面)
info_win *info_w = new info_win(this);
// 将第一个界面的账号和密码传递给第二个界面 --信号发送
connect(this, SIGNAL(sendSign(QString, QString)), info_w, SLOT(recvSlot(QString, QString)));
emit sendSign(account, pswd);
qDebug()<<"第一界面的地址是"<<this;
// 显示第二个界面
info_w->show();
this->hide();
}
else
{
qDebug()<<"账号或密码错误,重新输入";
}
}
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals: //自定义信号
void sendSign(QString, QString);
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H