11

I'm making a little chat messenger program, which needs a list of chat channels the user has joined. To represent this list graphically, I have made a list of QPushButtons, which all represent a different channel. These buttons are made with the following method, and that's where my problem kicks in:

void Messenger::addToActivePanels(std::string& channel)
{
    activePanelsContents = this->findChild<QWidget *>(QString("activePanelsContents"));
    pushButton = new QPushButton(activePanelsContents);
    pushButton->setObjectName("pushButton");
    pushButton->setGeometry(QRect(0, 0, 60, 60));
    pushButton->setText("");
    pushButton->setToolTip(QString(channel.c_str()));
    pushButton->setCheckable(true);
    pushButton->setChecked(false);
    connect(pushButton, SIGNAL(clicked()), this, SLOT(switchTab(channel)));
}

(activePanelContents is a QWidget that holds the list.)

The point is that each button should call the switchTab(string& tabname) method when clicked, including the specific channel's name as variable. This implementation doesn't work though, and I haven't been able to find out how to properly do this.

casperOne
  • 73,706
  • 19
  • 184
  • 253
Neko
  • 3,550
  • 7
  • 28
  • 34

6 Answers6

8

For strings and integers, you can use QSignalMapper. In your Messenger class, you would add a QSignalMapper mapper object, and your function would look like:

void Messenger::addToActivePanels(std::string& channel)
{
    activePanelsContents = this->findChild<QWidget *>(QString("activePanelsContents"));
    pushButton = new QPushButton(activePanelsContents);
    // ...
    connect(pushButton, SIGNAL(clicked()), &mapper, SLOT(map()));
    mapper.setMapping(pushButton, QString(channel.c_str()));
}

and after you have added all channels to your active panels, you call

connect(&mapper, SIGNAL(mapped(const QString &)), this, SLOT(switchTab(const QString &)));
Daniel Gallagher
  • 6,915
  • 25
  • 31
  • This doesn't seem to work... switchTab() simply never gets called, although no errors are given anywhere at all. – Neko Feb 09 '11 at 21:51
  • Does `switchTab()` take a `const QString &`? If it doesn't, you need to change it so it does. Qt doesn't allow you to pass `std::string` (or, in general, any type not registered with `qRegisterMetaType`) through signals and slots. Typos inside `SIGNAL` and `SLOT` are not detected until runtime, because those macros turn their arguments into strings. Chances are Qt is printing an error message to the console when it tries to make the connection to `switchTab(const QString &)`. – Daniel Gallagher Feb 09 '11 at 23:24
3

Use QSignalMapper to pass variables;

        QSignalMapper* signalMapper = new QSignalMapper (this) ;
        QPushButton *button = new QPushButton();


         signalMapper -> setMapping (button, <data>) ;
         connect (signalMapper, SIGNAL(mapped(QString)), this,   
                   SLOT(buttonClicked(QString))) ;

in slot i.e

void class::buttonClicked(QString data){

    //use data
    // to get sender 
    QSignalMapper *temp = (QSignalMapper *)this->sender();
    QPushButton *btn = (QPushButton *)temp->mapping(data);
    // use btn

}

Hope my ans may help you

Sharanabasu Angadi
  • 4,304
  • 8
  • 43
  • 67
  • Thanks for this answer. I was using QSignalMapper with QTNetworkRequest and didn't know i had to use Mapper->mapping(data) to obtain the object in such a case. I was simply using qobject_cast(sender) which only works when you have no parameter. – fkl Feb 14 '13 at 06:13
1

Don't use the sender method unless you absolutely have to. It ties the function directly to being used only as a slot (can't be called directly). Retain the behavior of having the function accept a string and simply make a mechanism by which you can call it.

One method, among others you might find, is to leverage use of QSignalMapper. It will map objects to values and regenerate signals of the appropriate signature.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
1

I would do it with "relay" objects:

Create TabSwitchRelay which is a sub-class of QObject with this constructor:

TabSwitchRelay::TabSwitchRelay(QObject *parent, Messanger * m, const QString & c)
: QObject(parent), m_messanger(m), m_channel(c)
{
}

It also has a slot clicked():

void TabSwitchRelay::clicked()
{
    m_messager->switchTab(m_channel);
}

Now replace the line in your code that does connect with this:

TabSwitchRelay * tabRelay = new TabSwitchRelay(pushButton, this, channel);
connect(pushButton, SIGNAL(clicked()), tabRelay, SLOT(clicked()));

It's not tested but you get teh basic idea.

Stephen Chu
  • 12,724
  • 1
  • 35
  • 46
0

if you're using Qt5, you can do it through lambda:

connect( sender, &Sender::valueChanged,  [=](){ myMethod(5); } );

void myMethod(int value)
{
  // do stuff
}
Moia
  • 2,216
  • 1
  • 12
  • 34
0

You could try having your switchTab slot take no argument and use QObject::sender to get the object that sent the signal.

Messenger::switchTab()
{
  QObject* sender = this->sender();
  QPushButton* button = qobject_cast<QPushButton*>(sender);
  if(button)
  {
    // Do stuff...
  }
}
spbots
  • 1,513
  • 1
  • 15
  • 22