Search code examples
qtqcomboboxqlineeditqcheckboxqsignalmapper

Qt QSignalMapper is emitting a signal for every item in the map


I have a function that receives a QList of a class I created. Let's call this hypothetical class "Stuff". So, this function receives a QList of Stuff

I iterate through the QList, and depending on the properties of the "Stuff" object, I will generate one of the following:

1) QLineEdit

2) QCheckBox

3) QComboBox

This isn't the actual code, but this is what I'm essentially doing:

void MyProgram::update(QList<Stuff> myStuffs)
{
  this->mSignalMapper = new QSignalMapper();

  foreach (Stuff stuff, myStuffs)
  {
    if (stuff.isInt())
    {
      QLineEdit* input = new QLineEdit();
      //There is code here to setup the QLineEdit and fill an initial value
      verticalLayout->addWidget(input); //verticalLayout is QVBoxLayout
      QObject::connect(input, SIGNAL(editingFinished()), this->mSignalMapper, SLOT(map()));
      this->mSignalMapper->setMapping(input, stuff.getMappingId());
      /*
       * NOTE: the stuff.getMappingId() function returns an int that is unique
       * to that stuff object.  I'm 100% sure each stuff object is getting
       * a unique mapping ID */
      QObject::connect(this->mSignalMapper, SIGNAL(mapped(int)), this, SLOT(onStuffChanged(int)));
    }
    else if (stuff.isBool())
    {
      QCheckBox* input = new QCheckBox();
      //There is code here to setup the QCheckBox and set an initial value
      verticalLayout->addWidget(input);
      QObject::connect(input, SIGNAL(stateChanged(int)), this->mSignalMapper, SLOT(map()));
      this->mSignalMapper->setMapping(input, stuff.getMappingId());
      QObject::connect(this->mSignalMapper, SIGNAL(mapped(int)), this, SLOT(onStuffChanged(int)));
    }
    else if (stuff.isStringList())
    {
      QComboBox* input = new QComboBox();
      //There is code here to setup the QComboBox and fill in values for the combo box
      verticalLayout->addWidget(input);
      QObject::connect(input, SIGNAL(activated(int)), this->mSignalMapper, SLOT(map()));
      this->mSignalMapper->setMapping(input, stuff.getMappingId());
      QObject::connect(this->mSignalMapper, SIGNAL(mapped(int)), this, SLOT(onStuffChanged(int)));
    }
  }
}

The problem is that if I trigger just one of the Widget's signals, by either editing the QLineEdit, or checking the check box, or changing the combo box value, the onStuffChanged(int) function is being called N times, where N = number of mSignalMapper's mappings.

What's going on here? If I loop through and create 10 widgets, clicking on just 1 of the 10 widgets calls the function 10 times, and each of those 10 times passes the unique int associated with only the 1 object I interacted with. So, if the 1st of 10 widgets was a checkbox with a unique int ID of 27, the onStuffChanged(int) function gets called 10 times with the parameter of 27 each time.


Solution

  • The problem is this line:

    QObject::connect(this->mSignalMapper, SIGNAL(mapped(int)), this, SLOT(onStuffChanged(int)));
    

    You are making the same connection N times (N = your "stuff" count), so every single mapped() signal triggers the onStuffChanged(int) slot N times also.

    Solution: Move this line outside the loop, to by called only once.