一般点击一个按钮,并且想将预先定好的参数一同发送出去时,由于按钮的点击事件clicked()并没有参数,那么按照一般的做法就会先定义一个槽与clicked()信号关联,然后获取参数,再通过自定义的信号将该参数发送出去。
这个过程无疑是繁琐的,为此,Qt提供了QSignalMapper这个类来解决这个问题。同时,这个类可以连接多个按钮,匹配发送信号的对象对应的整数、字符串,窗口指针,继承于QObject的对象参数重新发送它们。
现在我们创建一个类似计算器的窗口,添加如下代码:
SignalMapperWidget.h
#include <QtWidgets/QWidget>
class SignalMapperWidget : public QWidget
{
Q_OBJECT
public:
SignalMapperWidget(QWidget* parent);
};
SignalMapperWidget.cpp
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QLineEdit>
#include <QtCore/QSignalMapper>
#include "SignalMapperWidget.h"
SignalMapperWidget::SignalMapperWidget(QWidget* parent)
: QWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout(this);
//创建编辑框,用于显示点击按钮的文字,并且文字在右边显示
QLineEdit* edit = new QLineEdit(this);
edit->setAlignment(Qt::AlignRight);
vLayout->addWidget(edit);
//创建信号匹配器
QSignalMapper* signalMapper = new QSignalMapper(this);
QGridLayout *gridLayout = new QGridLayout;
for (int i = 0; i < 10; ++i)
{
QString txt = QString::number(i);
QPushButton *button = new QPushButton(txt);
connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(button, txt);//将按钮和要发送的字符串配对
gridLayout->addWidget(button, i / 3, i % 3);
}
//连接配对信号和设置文字槽
connect(signalMapper, SIGNAL(mapped(QString)), edit, SLOT(setText(QString)));
vLayout->addLayout(gridLayout);
resize(200, 200);
}
main.cpp
#include <QtWidgets/QApplication>
#include "SignalMapperWidget.h"
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
SignalMapperWidget widget(nullptr);
widget.show();
return app.exec();
}
运行效果:
QSignalMapper源码分析
查看QSignalMapper的源码,发现定义很简单。setMapping有四个重载函数。
class Q_CORE_EXPORT QSignalMapper : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QSignalMapper)
public:
explicit QSignalMapper(QObject *parent = nullptr);
~QSignalMapper();
void setMapping(QObject *sender, int id);
void setMapping(QObject *sender, const QString &text);
void setMapping(QObject *sender, QWidget *widget);
void setMapping(QObject *sender, QObject *object);
void removeMappings(QObject *sender);
QObject *mapping(int id) const;
QObject *mapping(const QString &text) const;
QObject *mapping(QWidget *widget) const;
QObject *mapping(QObject *object) const;
Q_SIGNALS:
void mapped(int);
void mapped(const QString &);
void mapped(QWidget *);
void mapped(QObject *);
public Q_SLOTS:
void map();
void map(QObject *sender);
private:
Q_DISABLE_COPY(QSignalMapper)
Q_PRIVATE_SLOT(d_func(), void _q_senderDestroyed())
};
先看其中的一个setMapping函数。往内部的hash表中添加映射关系。其他的三个重载函数的实现也一样。
void QSignalMapper::setMapping(QObject *sender, const QString &text)
{
Q_D(QSignalMapper);
d->stringHash.insert(sender, text);
connect(sender, SIGNAL(destroyed()), this, SLOT(_q_senderDestroyed()));
}
再看map槽函数。当sender的信号发出时,会调用到map槽函数,然后槽函数在内部先查找是否存在这个sender,接着将信号发送出去。
void QSignalMapper::map(QObject *sender)
{
Q_D(QSignalMapper);
if (d->intHash.contains(sender))
emit mapped(d->intHash.value(sender));
if (d->stringHash.contains(sender))
emit mapped(d->stringHash.value(sender));
if (d->widgetHash.contains(sender))
emit mapped(d->widgetHash.value(sender));
if (d->objectHash.contains(sender))
emit mapped(d->objectHash.value(sender));
}
可以看到,其实现很简单,就是一个转发的功能。