将参数绑定到信号/插槽

时间:2010-07-02 13:27:45

标签: c++ qt

我基本上有多个事件信号,我想连接到同一个插槽。我想知道的是如何将基于字符串的参数传递到同一个插槽,以便插槽知道这个信号来自哪个。一种替代方案是制作与信号一样多的插槽,然后以1:1的方式连接它们,但考虑到所有处理的代码非常相似,这是有效的。我尝试过这样做,但是我遇到了一些错误:

connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString)));
connect(button1,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade("button1")));
connect(button2,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade("button2")));

错误与我在最后2个命令中传递的参数有关。backgroundTypeChoiceMade声明如下:

void backgroundTypeChoiceMade(QString);

有人可以告诉我上述代码中的错误是什么吗?

6 个答案:

答案 0 :(得分:8)

您可以使用QSignalMapper。虽然QSignalMapper是你问题的答案,但我认为jon hanson的回答是你应该采取的方式。你可以通过这种方式获得更清晰的代码。

答案 1 :(得分:8)

四种方法。一个人不会吮吸。

  1. QSignalMapper。可以工作,但会造成混乱的代码。
  2. 命名插槽。对任何大量发件人都是乱码的,对于动态生成的发件人(例如列表中的按钮)不起作用。
  3. sender() - 进行比较。可以处理动态发件人,但仍然有点难看。
  4. 对发件人进行子类化。不好意思。给你一直想要的东西:参数化信号
  5. 特别是当您使用少量信号和发件人类型以及动态生成发件人时,将发件人子类化是最干净的方式。这使您可以重载现有信号以包含所需的任何参数。

    现在,连接信号和插槽正常工作:

    Keypad::Keypad(QWidget *parent) : QWidget(parent)
    {
        for (int i = 0; i < 10; ++i)
        {
            // KeypadButton keeps track of the identifier you give it
            buttons[i] = new KeypadButton(i, this);
            // And passes it as a signal parameter. Booyah.
            connect(buttons[i], SIGNAL(clicked(int)), this, SIGNAL(digitClicked(int)));
        }
        createLayout();
    }
    
    void Keypad::digitClicked(int digit)
    {
        // The slot can find the clicked button with ease:
        dial(button[i]); // or whatever
        //...
    }
    

    并且额外的代码在您永远不必再触摸的子类中是不可见的。

    有关子类化QPushButton以发出clicked(int)信号的示例实现,请参阅http://doc.qt.digia.com/qq/qq10-signalmapper.html#thesubclassapproach。还讨论了所有四种方法 - 命名槽(“普通解决方案”),sender(),子类化和信号映射器。

    警告:显然最适合少量的发件人类型。但通常情况就是如此。在这种情况下,这是值得的。

答案 2 :(得分:5)

使用单独的插槽效率低下?如果在槽处理程序中存在共性,则将其移动到函数中,例如,扩展ereOn的例子:

void YourClass::YourClass() :
  m_button1(new QPushButton()),
  m_button2(new QPushButton())
{
  connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot1()));
  connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot2()));
}

void YourClass::common(int n)
{
}

void YourClass::yourSlot1()
{
    common (1);
}

void YourClass::yourSlot2()
{
    common (2);
}

答案 3 :(得分:4)

您不能将常量传递给connect(),因为有效参数是在执行时推断的,而不是编译时间。

但是,虽然这违反了OO原则,但您可以使用QObject::sender()来指向发射器QObject

以下示例:

void YourClass::YourClass() :
  m_button1(new QPushButton()),
  m_button2(new QPushButton())
{
  connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot()));
  connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot()));
}

void YourClass::yourSlot()
{
  if ((QPushButton* button = dynamic_cast<QPushButton*>(sender()))
  {
    // Now button points to a QPushButton* that you can compare with the pointers you already have

    if (button == m_button1)
    {
      // Whatever
    } else
    if (button == m_button2)
    {
      // Whatever
    }
  }
}

如果您有许多按钮,您也可以通过为每个按钮提供标识符来使用QSignalMapper

答案 4 :(得分:0)

如果你真的不想使用QSignalMapper,你可以这样做:

class SignalForwarderWithString: public QObject
{
    Q_OBJECT
public:
    SignalForwarderWithString(QString data = "", QObject *parent = 0) : QObject(parent), _data(data) {}
    QString _data;
signals:
    void forward(QString);
public slots:
    void receive() { emit forward(_data); }
};

...
connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString)));

SignalForwarderWithString *sfws;
sfws = new SignalForwarderWithString("button1", this);
connect(button1,SIGNAL(clicked()), sfws, SLOT(receive(QString)));
connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString)));

sfws = new SignalForwarderWithString("button2", this);
connect(button2,SIGNAL(clicked()), sfws, SLOT(receive(QString)));
connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString)));

但是QSignalMapper也很简单......

QSignalMapper *mapper = new QSignalMapper(this);
connect(button1, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(button1, "button 1");
connect(button2, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(button2, "button 2");
// you might have to tweak the argument type for your slot...
connect(mapper, SIGNAL(mapped(const QString &), this, SLOT(backgroundTypeChoiceMade(QString)));

答案 5 :(得分:0)

现在,您可以在连接时真正绑定值。 Qt5增加了对此的支持。

示例:

connect(sender, &Sender::valueChanged,
    tr1::bind(receiver, &Receiver::updateValue, "senderValue", tr1::placeholder::_1));

请参阅more info

注意:你当然可以使用std :: bind或boost :: bind代替tr1 :: bind。