Qt - QSignalMapper(信号映射器)
QSignalMapper的使用
概述:
Qt的信号/槽机制给我们编程带来了很大的便利。在多数的情况下,我们只需要一个信号对应一个槽就可以了。但有时候我们也会遇到多个信号对应一个槽的情况。比如说做一个计算器的小程序,界面上有10个按钮分别对应0 - 9这几个数字。按下这几个按钮处理的逻辑是类似的,所以我们完全没有必要为每一个按钮都写一个槽来对应按钮按下的信号。但是假如我们把10个按钮的按下信号都连接到一个槽函数中,那么槽函数又要怎么区分此时按下的是哪个按钮呢?要知道按钮的按下信号是不带参数的。
这时候QSignalMapper就能派生用场了。它可以将对象的信号转发一个槽函数中。那么具体该怎么使用呢?举个例子吧:
首先用QtCreator创建一个基于QWidget的程序,使用UI设计师在界面上拖6个按钮。
接下来我们将使用QSignalMapper将这6个按钮的信号都连接到一个槽函数中。
1、创建QSignalMapper对象,并将它的mapped信号连接到一个槽函数中,这个槽就是我们用来处理6个按钮的地方。
mSignalMapper = new QSignalMapper(this);
connect(mSignalMapper,SIGNAL(mapped(QWidget*)),this,SLOT(slotSignalMap(QWidget*)));
mapped信号有4个不同参数的版本,根据需求自行选择。
2、将QSignalMapper与按钮关联起来,使用QSignalMapper的setMapping函数。
mSignalMapper->setMapping(ui->pushButton,ui->pushButton);
mSignalMapper->setMapping(ui->pushButton_2,ui->pushButton_2);
mSignalMapper->setMapping(ui->pushButton_3,ui->pushButton_3);
mSignalMapper->setMapping(ui->pushButton_4,ui->pushButton_4);
mSignalMapper->setMapping(ui->pushButton_5,ui->pushButton_5);
mSignalMapper->setMapping(ui->pushButton_6,ui->pushButton_6);
setMapping有4个重载函数,第二个参数类型与mapped信号的参数一一对应。
3、将按钮的按下信号与QSignalMapper关联起来。前面做了那么多不就是为了信号连在一起吗。
connect(ui->pushButton,SIGNAL(pressed()),mSignalMapper,SLOT(map()));
connect(ui->pushButton_2,SIGNAL(pressed()),mSignalMapper,SLOT(map()));
connect(ui->pushButton_3,SIGNAL(pressed()),mSignalMapper,SLOT(map()));
connect(ui->pushButton_4,SIGNAL(pressed()),mSignalMapper,SLOT(map()));
connect(ui->pushButton_5,SIGNAL(pressed()),mSignalMapper,SLOT(map()));
connect(ui->pushButton_6,SIGNAL(pressed()),mSignalMapper,SLOT(map()));
这一步将各个按钮的信号都连接到QSignalMapper的map()槽函数中。然后发射到第1步的slotSignalMap槽中。
4、在slotSignalMap处理
void Widget::slotSignalMap(QWidget* w)
{
QPushButton* button = qobject_cast<QPushButton*>(w);
if(NULL != button)
{
qDebug()<<"click: "<< button->text();
}
}
由于我们在关联按钮和QSignalMapper时(setMapping)将按钮对象传入,在mapped信号触发时传给槽函数slotSignal。我们只需要把这个参数QWidget转换为QPushButton对象就自然知道此时按下的是哪个按钮了。
效果如图:
使用QSignalMapper可以帮我们解决信号转发的问题,不过感觉还是有点局限性,我们可以看到信号最终是通过QSignalMapper的mapped()转发出去的,如果我的QPushButton的两个不同的信号都用QSignalMapper转发会怎样呢?
connect(ui->pushButton,SIGNAL(pressed()),mSignalMapper,SLOT(map())); //pressed(信号)
connect(ui->pushButton,SIGNAL(clicked(bool)),mSignalMapper,SLOT(map())); //clicked(信号)
我们将第一个按钮的clicked和pressed两个信号都通过mSignalMapper转发出去,点击按钮打印出了两个"click:1"。
说明两个信号都在同一个槽函数中处理了,这肯定不是我们想要的。我们既想要不同对象的同一个信号在同一个槽函数中处理,又不希望一个对象的不同信号在同一个槽函数中处理。怎么办?
看来还需要sender()函数来帮忙了。通过在槽函数中调用sender()来获取信号的发送者。将上面的例子改一改,先定义一个slotClicked槽函数
void Widget::slotClicked(bool bcheck)
{
QPushButton* button = qobject_cast<QPushButton*>(sender()); //将sender()转为QPushButton
if(NULL != button)
{
qDebug()<<"fearlazy click:"<<button->text()<<bcheck;
}
}
在槽函数中使用sender()获取此时槽函数对应的信号发送者,返回类型是QObject。 这样我们可以直接将不同对象的信号连接到同一个槽上,还能分清楚是谁触发的。
connect(ui->pushButton,SIGNAL(clicked(bool)),this,SLOT(slotClicked(bool)));
connect(ui->pushButton_2,SIGNAL(clicked(bool)),this,SLOT(slotClicked(bool)));
connect(ui->pushButton_3,SIGNAL(clicked(bool)),this,SLOT(slotClicked(bool)));
connect(ui->pushButton_4,SIGNAL(clicked(bool)),this,SLOT(slotClicked(bool)));
connect(ui->pushButton_5,SIGNAL(clicked(bool)),this,SLOT(slotClicked(bool)));
connect(ui->pushButton_6,SIGNAL(clicked(bool)),this,SLOT(slotClicked(bool)));
看看运行效果:
最后我们再添加一个slotPressed()槽函数,并让第一个按钮的pressed()信号连接该槽函数。
void Widget::slotPressed()
{
QPushButton* button = qobject_cast<QPushButton*>(sender());
if(NULL != button)
{
qDebug()<<"fearlazy pressed:"<<button->text(); //打印出presse了谁
}
}
connect(ui->pushButton,SIGNAL(pressed()),this,SLOT(slotPressed()));//在Widget的构造函数中连接
点击第一个按钮看看效果如何?
可以看到pressed和clicked信号都触发了,而且两个信号在不同的槽函数中处理。
示例所有代码:
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QSignalMapper>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
public slots:
void slotSignalMap(QWidget* w);
void slotClicked(bool bcheck);
void slotPressed();
private:
Ui::Widget *ui;
QSignalMapper* mSignalMapper;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
//1.创建QSignalMapper对象,并将它的mapped信号连接到一个槽函数中,这个槽就是我们用来处理6个按钮的地方。
mSignalMapper = new QSignalMapper(this);
connect(mSignalMapper,SIGNAL(mapped(QWidget*)),this,SLOT(slotSignalMap(QWidget*)));
//2.将QSignalMapper与按钮关联起来,使用QSignalMapper的setMapping函数。
mSignalMapper->setMapping(ui->pushButton,ui->pushButton);
mSignalMapper->setMapping(ui->pushButton_2,ui->pushButton_2);
mSignalMapper->setMapping(ui->pushButton_3,ui->pushButton_3);
mSignalMapper->setMapping(ui->pushButton_4,ui->pushButton_4);
mSignalMapper->setMapping(ui->pushButton_5,ui->pushButton_5);
mSignalMapper->setMapping(ui->pushButton_6,ui->pushButton_6);
//3.将按钮的按下信号与QSignalMapper关联起来。前面做了那么多不就是为了信号连在一起吗。
connect(ui->pushButton,SIGNAL(pressed()),mSignalMapper,SLOT(map()));//pressed(信号)
connect(ui->pushButton_2,SIGNAL(pressed()),mSignalMapper,SLOT(map()));
connect(ui->pushButton_3,SIGNAL(pressed()),mSignalMapper,SLOT(map()));
connect(ui->pushButton_4,SIGNAL(pressed()),mSignalMapper,SLOT(map()));
connect(ui->pushButton_5,SIGNAL(pressed()),mSignalMapper,SLOT(map()));
connect(ui->pushButton_6,SIGNAL(pressed()),mSignalMapper,SLOT(map()));
//=========================使用sender()函数区分信号===================================
connect(ui->pushButton,SIGNAL(clicked(bool)),this,SLOT(slotClicked(bool))); //clicked(信号)
connect(ui->pushButton_2,SIGNAL(clicked(bool)),this,SLOT(slotClicked(bool)));
connect(ui->pushButton_3,SIGNAL(clicked(bool)),this,SLOT(slotClicked(bool)));
connect(ui->pushButton_4,SIGNAL(clicked(bool)),this,SLOT(slotClicked(bool)));
connect(ui->pushButton_5,SIGNAL(clicked(bool)),this,SLOT(slotClicked(bool)));
connect(ui->pushButton_6,SIGNAL(clicked(bool)),this,SLOT(slotClicked(bool)));
connect(ui->pushButton,SIGNAL(pressed()),this,SLOT(slotPressed()));//pressed(信号)
connect(ui->pushButton_2,SIGNAL(pressed()),this,SLOT(slotPressed()));
connect(ui->pushButton_3,SIGNAL(pressed()),this,SLOT(slotPressed()));
connect(ui->pushButton_4,SIGNAL(pressed()),this,SLOT(slotPressed()));
connect(ui->pushButton_5,SIGNAL(pressed()),this,SLOT(slotPressed()));
connect(ui->pushButton_6,SIGNAL(pressed()),this,SLOT(slotPressed()));
//=========================使用sender()函数区分信号===================================
}
Widget::~Widget()
{
delete ui;
}
//4.定义槽函数,在slotSignalMap处理
void Widget::slotSignalMap(QWidget* w)
{
QPushButton* button = qobject_cast<QPushButton*>(w);
if(NULL != button)
{
qDebug()<<"click: "<< button->text();
}
}
void Widget::slotClicked(bool bcheck)
{
QPushButton* button = qobject_cast<QPushButton*>(sender()); //将sender()转为QPushButton
if(NULL != button)
{
qDebug()<<"fearlazy click:"<<button->text()<<bcheck;
}
}
void Widget::slotPressed()
{
QPushButton* button = qobject_cast<QPushButton*>(sender());
if(NULL != button)
{
qDebug()<<"fearlazy pressed:"<<button->text(); //打印出presse了谁
}
}
============================================================================
1、Qt多个信号连接同一个槽的方法: https://baijiahao.baidu.com/s?id=1611719867424585107&wfr=spider&for=pc
2、QSignalMapper(信号映射器):https://blog.csdn.net/m0_73443478/article/details/128332338
============================================================================