【问题标题】:Qt signal/slots - consume signal in one of many slots?Qt 信号/插槽 - 在多个插槽之一中消耗信号?
【发布时间】:2015-07-23 16:23:04
【问题描述】:

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

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

据我了解Qt Documentation

如果多个插槽连接到一个信号,插槽将 一个接一个地执行,按照它们连接的顺序, 当信号发出时。

我希望能够在一个槽内停止这个过程。

建议?

【问题讨论】:

    标签: qt signals slot


    【解决方案1】:

    不,没有办法,你不应该这样想。无论有多少插槽连接到信号,发送方都应该执行相同的操作。这就是信号槽机制的基本约定:发送者与接收者完全解耦,并且不知道接收者。

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

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

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

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

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

    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"
    

    【讨论】:

    • TODO:面对其他线程中的对象删除,此代码不是线程安全的。这是可以修复的。
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2012-09-11
      • 1970-01-01
      • 2020-10-01
      • 2011-08-16
      • 2012-07-04
      • 2014-03-07
      • 2012-10-15
      • 2015-07-18
      • 2011-09-06
      相关资源
      最近更新 更多