Qt是否可以定义一个函数指针来代替信号?

时间:2017-04-26 10:23:39

标签: c++ qt qt-signals

我有很多信号都具有相同的参数但执行不同的功能。

所有信号的连接和断开代码都是相同的,信号连接的插槽处理程序也是如此。

而不是一遍又一遍地编写这段代码。我想使用函数指针或类似的东西来分配信号,然后有一个公共代码块来执行连接或断开连接。

以下代码只是为了说明我所描述的内容,它不是有效的,也不会编译。

    void (*pfnSignal)(quint8, QString);
    switch( eSigID ) {
    case SIGNAL_A:
        pfnSignal = signalA; 
        break;
    case SIGNAL_B:
        pfnSignal = signalB;
        break;
    default:
        pfnSignal = NULL;           
    }
    if ( pfnSignal != NULL ) {
      QObject::connect(pobjRef, pfnSignal, this, SLOT(handler(quint8, QString)));
    }

3 个答案:

答案 0 :(得分:6)

在Qt5中,这可以轻松完成,因为它允许使用new pointer to member function syntax进行连接。

// Using decltype to avoid figuring out the ugly pointer-to-member-function syntax. 
// Assumes all signals have the same arguments.    
decltype<&ThatClass::someSignal> pfnSignal = nullptr;
switch( eSigID ) {
case SIGNAL_A:
    pfnSignal = &ThatClass::signalA; 
    break;
case SIGNAL_B:
    pfnSignal = &ThatClass::signalB;
    break;          
}

if (pfnSignal) {
    connect(pobjRef, pfnSignal, this, &ThisClass::handler);
}

但实际上,Qt4甚至可以实现这一点,因为SIGNAL宏的类型为const char*

const char *pfnSignal = nullptr;
switch( eSigID ) {
case SIGNAL_A:
    pfnSignal = SIGNAL(signalA(quint8, QString)); 
    break;
case SIGNAL_B:
    pfnSignal = SIGNAL(signalB(quint8, QString)); 
    break;         
}
if (pfnSignal) {
  QObject::connect(pobjRef, pfnSignal, this, SLOT(handler(quint8, QString)));
}

答案 1 :(得分:3)

C ++ 11允许您编写非常简洁的Qt代码。

  1. 利用range-based for loops迭代指针。这些可以是指向小部件的指针,指向方法的指针等等:

    for (auto signal : {&Class::signal1, &Class:signal2})
       QObject::connect(sender, signal, receiver, slot);
    
  2. 利用lambda expressions捕获常量参数值:

    auto const cMySlot = [&](void (Sender::*signal)(int)){
      QObject::connect(sender, signal, receiver, slot);
    
  3. 然后:

    for (auto signal : {&Class::signal1, &Class:signal2}) cMySlot(signal);
    

    完整示例:

    // https://github.com/KubaO/stackoverflown/tree/master/questions/signals-simpler-43631464
    #include <QtWidgets>
    #include <initializer_list>
    
    class Receiver : public QLabel {
       Q_OBJECT
    public:
       Receiver(QWidget * parent = {}) : QLabel{parent} {}
       Q_SLOT void intSlot(int val) {
          setText(QStringLiteral("int = %1").arg(val));
       }
    };
    
    class Sender : public QWidget {
       Q_OBJECT
       QFormLayout m_layout{this};
       QPushButton btn1{"Send 1"}, btn2{"Send 5"}, btn3{"Send 10"};
    public:
       Sender(QWidget * parent = {}) : QWidget{parent} {
          m_layout.setMargin(1);
          for (auto w : {&btn1, &btn2, &btn3}) m_layout.addWidget(w);
          auto const clicked = &QPushButton::clicked;
          connect(&btn1, clicked, this, [this]{ emit signal1(1); });
          connect(&btn2, clicked, this, [this]{ emit signal2(5); });
          connect(&btn3, clicked, this, [this]{ emit signal3(10); });
       }
       Q_SIGNAL void signal1(int);
       Q_SIGNAL void signal2(int);
       Q_SIGNAL void signal3(int);
    };
    
    using Widgets = std::initializer_list<QWidget*>;
    
    int main(int argc, char **argv)
    {
       QApplication app{argc, argv};
       QWidget win;
       QVBoxLayout layout{&win};
       Sender sender;
       Receiver receiver;
       for (auto w : Widgets{&sender, &receiver}) layout.addWidget(w);
    
       // Factor out connection
       auto const cIntSlot = [&](void (Sender::*signal)(int)){
          QObject::connect(&sender, signal, &receiver, &Receiver::intSlot);
       };
       // Factor out connection on a list
       for (auto signal : {&Sender::signal1, &Sender::signal2, &Sender::signal3})
          cIntSlot(signal);
    
       win.show();
       return app.exec();
    }
    #include "main.moc"
    

答案 2 :(得分:2)

实际上,Thomas McGuire比我快。 (该死的。)虽然,我想补充这个答案,因为:

  1. 它提供了完整的样本。

  2. 它使用函子而不是信号处理程序的对象/成员函数指针。

  3. 因此,它可能是Thomas McGuire答案的补充。

    在Qt 5之前,信号由char*描述,应该非常简单。因此,我认为您的问题与Qt 5以来的新API有关。

    如果使用正确的方法指针类型,这也应该有效。我为QPushButtonQCheckBox执行此操作以进行演示,因为它们都来自QAbstractButton,而Snapshot of running sample又有两个具有相同签名的信号。对于您的解决方案,IMHO必须具有相同的信号签名。

    #include <QtWidgets>
    
    enum SigType { None, Click, Toggle };
    
    template <typename FUNCTOR>
    void installSignalHandler(
      QAbstractButton *pQBtn,
      SigType sigType,
      FUNCTOR sigSlot)
    {
      void (QAbstractButton::*pSignal)(bool) = nullptr;
      switch (sigType) {
        case Click: pSignal = &QAbstractButton::clicked; break;
        case Toggle: pSignal = &QAbstractButton::toggled; break;
      }
      if (pSignal) QObject::connect(pQBtn, pSignal, sigSlot);
    }
    
    int main(int argc, char **argv)
    {
      qDebug() << "Qt Version: " << QT_VERSION_STR;
      // main application
      QApplication app(argc, argv);
      // setup GUI
      QWidget qWin;
      QVBoxLayout qVBox(&qWin);
      QPushButton qBtn1("Button 1 -> Click");
      qVBox.addWidget(&qBtn1);
      QPushButton qBtn2("Button 2 -> Toggle");
      qVBox.addWidget(&qBtn2);
      QPushButton qBtn3("Button 3 -> None");
      qVBox.addWidget(&qBtn3);
      QCheckBox qTgl1("Toggle 1 -> Click");
      qVBox.addWidget(&qTgl1);
      QCheckBox qTgl2("Toggle 2 -> Toggle");
      qVBox.addWidget(&qTgl2);
      QCheckBox qTgl3("Toggle 3 -> None");
      qVBox.addWidget(&qTgl3);
      qWin.show();
      // install signal handlers
      installSignalHandler(&qBtn1, Click,
        [](bool) { qDebug() << "Button 1 received clicked."; });
      installSignalHandler(&qBtn2, Toggle,
        [](bool) { qDebug() << "Button 2 received toggled."; });
      installSignalHandler(&qBtn3, None, // will be actually never called
        [](bool) { qDebug() << "Button 3 received none."; });
      installSignalHandler(&qTgl1, Click,
        [](bool) { qDebug() << "CheckBox 1 received clicked."; });
      installSignalHandler(&qTgl2, Toggle,
        [](bool) { qDebug() << "CheckBox 2 received toggled."; });
      installSignalHandler(&qTgl2, None, // will be actually never called
        [](bool) { qDebug() << "CheckBox 3 received none."; });
      // run-time loop
      return app.exec();
    }
    

    使用VisualStudio进行编译和测试Windows 10(64位)上的Qt 5.6:

    https://books.sonatype.com/nexus-book/3.0/reference/raw.html#_uploading_files_to_hosted_raw_repositories