一、QSignalMapper用法
在Qt中,QSignalMapper是一个很有用的工具类,它可以将一个信号和多个对象进行关联。当该信号被触发时,QSignalMapper会发出自己的一个信号,同时在这个信号中传递与触发的对象关联的数据。
通常,我们可以使用QSignalMapper来处理一组控件的触发信号,比如QPushButton的clicked()信号,使用QSignalMapper可以使每个按钮触发的操作都不同,这给我们的程序带来了更灵活的设计。
接下来,我们来看一下QSignalMapper的使用方法。
// 创建QSignalMapper对象
QSignalMapper *mapper = new QSignalMapper(this);
// 创建QPushButton对象
QPushButton *btn1 = new QPushButton(tr("Button 1"));
QPushButton *btn2 = new QPushButton(tr("Button 2"));
// 信号与槽函数之间的关联
connect(btn1, SIGNAL(clicked()), mapper, SLOT(map()));
connect(btn2, SIGNAL(clicked()), mapper, SLOT(map()));
// 将按钮与数据相关联
mapper->setMapping(btn1, "Button 1 clicked");
mapper->setMapping(btn2, "Button 2 clicked");
// 连接QSignalMapper自己的信号
connect(mapper, SIGNAL(mapped(QString)), this, SLOT(handleButton(QString)));
上面的例子中,我们先创建了一个QSignalMapper对象,并创建了两个QPushButton对象。接着,使用connect()将两个QPushButton对象的clicked()信号与QSignalMapper对象的map()槽函数关联起来,将QPushButton对象与相关联的数据(这里是QString)使用setMapping()方法进行关联。最后连接QSignalMapper自己的mapped()信号到handleButton()槽函数上。
当我们点击QPushButton对象时,每个按钮触发的操作都会被区别对待,并且操作的具体内容会在handleButton()槽函数中处理。
二、QSignalMapper Mapping
在上一节中,我们使用setMapping()方法将QPushButton对象与QString数据相关联。实际上,QSignalMapper可以与任意类型的QObject对象进行相关联。
另外,使用QSignalMapper的一个重要技巧是将整个对象作为映射,而不是对象的一个属性。这意味着如果想要对一个控件发射信号并提供额外的信息,我们可以将该控件本身作为映射,在槽函数中获取所有需要的相关信息。
下面是一个基于QSignalMapper的完整的代码示例:
#include
#include
#include
#include
#include
#include
#include
class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = nullptr) : QWidget(parent) { // 创建QSignalMapper对象 mapper = new QSignalMapper(this); // 创建标签 QLabel *label = new QLabel(tr("Click any button")); layout()->addWidget(label); // 创建QPushButton对象 QVariantMap properties{{"text", tr("Button 1")}, {"tag", 1234}}; QPushButton *btn1 = createPushButton(properties); properties.replace("text", tr("Button 2"), "tag", 5678); QPushButton *btn2 = createPushButton(properties); // 信号与槽函数之间的关联 connect(btn1, SIGNAL(clicked()), mapper, SLOT(map())); connect(btn2, SIGNAL(clicked()), mapper, SLOT(map())); // 将按钮与数据相关联 mapper->setMapping(btn1, btn1); mapper->setMapping(btn2, btn2); // 连接QSignalMapper自己的信号 connect(mapper, SIGNAL(mapped(QObject*)), this, SLOT(handleButton(QObject*))); setLayout(new QVBoxLayout()); layout()->addWidget(btn1); layout()->addWidget(btn2); } public slots: void handleButton(QObject *obj) { QPushButton *button = qobject_cast
(obj); QVariantMap properties = button->property("attributes").toMap(); QMessageBox::information(this, "Info", QString("Button clicked!\n" "Text: %1\n" "Tag: %2\n").arg(properties["text"].toString(), properties["tag"].toInt())); } private: QPushButton* createPushButton(const QVariantMap &properties) { QPushButton *button = new QPushButton(this); button->setProperty("attributes", QVariant(properties)); button->setText(properties.value("text").toString()); return button; } QSignalMapper *mapper; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); }
上面例子中,我们创建了一个QPushButton对象,并关联了两个数据:text和tag。我们使用QVariantMap将数据存储在属性中,并使用setMapping()方法将QPushButton对象与自身相关联。在handleButton()槽函数中,我们使用qobject_cast()将QObject对象强制转换为QPushButton对象,并从其属性中检索相关信息进行处理。
三、QSignalMapper替代类
虽然QSignalMapper是一个非常实用的工具类,但在一些简单的应用程序中,它可能会显得过于笨重和复杂。Qt提供了许多替代QSignalMapper的方法。
QObject::sender()方法经常被用来获取发射信号的对象。当信号触发时,QObject::sender()会返回最后一个发射信号的对象。
于是,我们可以在槽函数中使用switch()或者if()等条件语句对不同的对象进行判断,并执行对应的操作。
下面以一个简单的例子来演示如何使用QObject::sender()方法替代QSignalMapper:
#include
#include
#include
#include
#include
#include
#include
class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = nullptr) : QWidget(parent) { QPushButton *btn1 = new QPushButton(tr("Button 1")); QPushButton *btn2 = new QPushButton(tr("Button 2")); connect(btn1, SIGNAL(clicked()), this, SLOT(handleButton())); connect(btn2, SIGNAL(clicked()), this, SLOT(handleButton())); setLayout(new QVBoxLayout()); layout()->addWidget(btn1); layout()->addWidget(btn2); } public slots: void handleButton() { QPushButton *button = qobject_cast
(sender()); if(button == nullptr) { return; } qDebug() << QString("Button %1 clicked!").arg(button->text()); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); }
上面例子中,我们创建了两个QPushButton对象,并使用QObject::sender()方法检索哪一个QPushButton发射了信号。在槽函数中使用qobject_cast()将QObject对象强制转换为QPushButton对象,并输出该QPushButton对象的文本。
四、QSignalMapper获取信号发射对象选取
有时候,我们想在控件中保存一些数据,而不想使用属性。在这种情况下,我们可以使用QWidget::setUserData()和QWidget::userData()来保存控件的相关信息。使用QSignalMapper是很方便的,我们可以将保存在QWidget::userData()中的数据直接传递到槽函数中。
下面是一段代码示例,演示如何使用QWidget::userData():
#include
#include
#include
#include
#include
#include
#include
class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = nullptr) : QWidget(parent) { QPushButton *btn1 = new QPushButton(tr("Button 1")); QPushButton *btn2 = new QPushButton(tr("Button 2")); btn1->setUserData(0, QVariant(1234)); btn2->setUserData(0, QVariant(5678)); connect(btn1, SIGNAL(clicked()), this, SLOT(handleButton())); connect(btn2, SIGNAL(clicked()), this, SLOT(handleButton())); setLayout(new QVBoxLayout()); layout()->addWidget(btn1); layout()->addWidget(btn2); } public slots: void handleButton() { QPushButton *button = qobject_cast
(sender()); if(button == nullptr) { return; } QVariant data = button->userData(0); if(data.isValid() && data.type() == QVariant::Int) { qDebug() << QString("Button %1 clicked! Data: %2").arg(button->text(), data.toInt()); } } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); }
上面例子中,我们创建了两个QPushButton对象,并使用QWidget::setUserData()将其与QVariant对象相关联。在槽函数中,我们使用qobject_cast()检索sender()对象,并使用QWidget::userData()获取已关联的数据。
五、总结
QSignalMapper是一个非常有用的工具类,可以使我们处理多个控件发射的信号更加灵活。使用QSignalMapper,可以将一个信号和多个对象进行关联。当该信号被触发时,QSignalMapper会发出自己的一个信号,同时在这个信号中传递与触发的对象关联的数据。
另外,QObject::sender()方法是一个方便的替代QSignalMapper的方式。当信号被触发时,可以使用QObject::sender()获取发射信号的对象,并使用qobject_cast()将QObject对象强制转换为实际的控件对象。
最后,在控件中保存数据时,可以使用QWidget::userData()和QWidget::setUserData()方法,将控件与QVariant对象相关联。