Qt信号/插槽 - 消耗多个插槽中的一个?

时间:2015-07-23 16:23:04

标签: qt signals slot

有没有办法在连接到同一信号的许多插槽之一中消耗未来的插槽(并阻止它们执行)?

我的目标是向许多QObject发出带有消息的信号,并在消息所属的QObject找到它时消耗(停止迭代到未来的插槽)。

根据我在Qt Documentation中的理解:

  

如果有多个插槽连接到一个信号,则插槽将是   按照他们连接的顺序依次执行,   当信号发出时。

我希望能够从插槽中停止此过程。

建议?

2 个答案:

答案 0 :(得分:1)

不,没有办法做到这一点,你不应该这样想。无论连接到信号的插槽数是多少,发送方都应该执行相同的操作。这是信号槽机制的基本合同:发送器完全与接收器分离,并且不知道接收器。

您要做的是合格调度:有多个接收器,每个接收器可以处理一种或多种消息类型。实现它的一种方法如下:

  1. 发出(发出信号)a QEvent。这使您可以保持发送器和接收器之间的信号槽去耦。

  2. 然后,事件可由自定义事件调度程序使用,该事件调度程序知道哪些对象处理给定类型的事件。

  3. 对象以通常的方式发送给事件,并以event()方法接收。

  4. 下面的实现允许接收器对象存在于其他线程中。这就是为什么它需要能够克隆事件。

    class <QCoreApplication>
    class <QEvent>
    
    class ClonableEvent : public QEvent {
      Q_DISABLE_COPY(ClonableEvent)
    public:
      ClonableEvent(int type) : QEvent(static_cast<QEvent::Type>(type)) {}
      virtual ClonableEvent * clone() const { return new ClonableEvent(type()); }
    }
    Q_REGISTER_METATYPE(ClonableEvent*)
    
    class Dispatcher : public QObject {
      Q_OBJECT
      QMap<int, QSet<QObject*>> m_handlers;
    public:
      Q_SLOT void dispatch(ClonableEvent * ev) {
        auto it = m_handlers.find(ev->type());
        if (it == m_handlers.end()) return;
        for (auto object : *it) {
          if (obj->thread() == QThread::currentThread())
            QCoreApplication::sendEvent(obj, ev);
          else
            QCoreApplication::postEvent(obj, ev.clone());
        }
      }
      void addMapping(QClonableEvent * ev, QObject * obj) { 
        addMapping(ev->type(), obj);
      }
      void addMapping(int type, QObject * obj) {
        QSet<QObject*> & handlers = m_handlers[type];
        auto it = handlers.find(obj);
        if (it != handlers.end()) return;
        handlers.insert(obj);
        QObject::connect(obj, &QObject::destroyed, [this, type, obj]{
          unregister(type, obj);
        });
        m_handlers[type].insert(obj);
      }
      void removeMapping(int type, QObject * obj) {
        auto it = m_handlers.find(type);
        if (it == m_handlers.end()) return;
        it->remove(obj);
      }
    }
    
    class EventDisplay : public QObject {
      bool event(QEvent * ev) {
        qDebug() << objectName() << "got event" << ev.type();
        return QObject::event(ev);
      }
    public:
      EventDisplay() {}
    };
    
    class EventSource : public QObject {
      Q_OBJECT
    public:
      Q_SIGNAL void indication(ClonableEvent *);
    }
    
    #define NAMED(x) x; x.setObjectName(#x)
    int main(int argc, char ** argv) {
      QCoreApplication app(argc, argv);
      ClonableEvent ev1(QEvent::User + 1);
      ClonableEvent ev2(QEvent::User + 2);
      EventDisplay NAMED(dp1);
      EventDisplay NAMED(dp12);
      EventDisplay NAMED(dp2);
    
      Dispatcher d;
      d.addMapping(ev1, dp1);  // dp1 handles only ev1
      d.addMapping(ev1, dp12); // dp12 handles both ev1 and ev2
      d.addMapping(ev2, dp12);
      d.addMapping(ev2, dp2);  // dp2 handles only ev2
    
      EventSource s;
      QObject::connect(&s, &EventSource::indication, &d, &Dispatcher::dispatch);
    
      emit s.indication(&ev1);
      emit s.indication(&ev2);
      return 0;
    }
    
    #include "main.moc"
    

答案 1 :(得分:0)

如果连接在一个线程中,我认为你可以抛出异常。但在这种情况下,你应该在发出信号时发现任何异常:

try {
    emit someSignal();
} catch(...) {
    qDebug() << "catched";
}

但我认为这是个坏主意。我将使用事件调度。