您的位置:

深入探究Qt中的QSignalMapper

一、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对象相关联。