Qt唯一连接



我想知道是否有可能在Qt,共同创建一个信号槽连接,这将自动打破所有其他连接到这个特定的槽/信号?

Qt不直接提供这样的功能。此外,迭代信号槽连接是不可能的,所以你甚至不能自己实现它。

你应该做的是跟踪你自己发起的连接,并在适当的时候删除它们。

例如:

enum class ConnectionDisposal { Dispose, Keep };
class UniqueConnector {
  Q_DISABLE_COPY(UniqueConnector)
  QMetaObject::Connection m_conn;
  ConnectionDisposal m_cd;
public:
  explicit UniqueConnector(ConnectionDisposal cd = ConnectionDisposal::Dispose) : 
    m_cd(cd) {}
  ~UniqueConnector() { if (m_cd == ConnectionDisposal::Dispose) disconnect(); }
  template <typename T, typename R>
  QMetaObject::Connection connect(const QObject * tx, T txf,
                                  const QObject * rx, R rxf,
                                  Qt::ConnectionType type = Qt::AutoConnection) {
    QObject::disconnect(m_conn);
    return m_conn = QObject::connect(tx, txf, rx, rxf, type);
  }
  template <typename T, typename R>
  QMetaObject::Connection connect(const QObject * tx, T txf, R rxf) {
    QObject::disconnect(m_conn);
    return m_conn = QObject::connect(tx, txf, rxf);
  }
  bool disconnect() { return QObject::disconnect(m_conn); }
};

UniqueConnector只允许在其实例上存在一个连接。因此,对于每个唯一连接,您需要一个UniqueConnector实例。除非您另有指定,否则在连接器被破坏时将删除连接。

那么,您可以使用以下场景:

if (!connect(senderObject, SIGNAL(signalName()), receiverObject, SLOT(slotName()), Qt::UniqueConnection))
{
    QMetaObject::disconnect(senderObject, senderObject->metaObject()->indexOfSignal(SIGNAL(signalName())),
                            NULL, receiverObject->metaObject()->indexOfSlot(SLOT(slotName())));
    connect(senderObject, SIGNAL(signalName()), receiverObject, SLOT(slotName()));
}

我很快地写了这个函数并测试了它,看起来它真的有效!是的,算法并不完美,它可能可以改进,但这需要更多的时间。尝试这个解决方案并告诉结果:

QMetaObject::Connection uniqueConnect(const QObject *sender, const char *signal, const QObject *receiver , const char *slot, Qt::ConnectionType type = Qt::AutoConnection)
{
    const QMetaObject * metaSender = sender->metaObject();
    const QMetaObject * metaReceiver = receiver->metaObject();
    int signalIndex = metaSender->indexOfSignal(signal);
    int slotIndex = metaReceiver->indexOfSlot(slot);
    //iterate throw all methods! and discover only signals and slots
    for (int i = 0; i < metaSender->methodCount(); ++i) 
    {
        for (int j = 0; j < metaReceiver->methodCount(); ++j) 
        {
            if(metaSender->method(i).methodType() == QMetaMethod::Signal)
            {
                if(metaReceiver->method(j).methodType() == QMetaMethod::Slot)
                {
                    //immitate SIGNAL SLOT macro, see more in the end of the answer.
                    QByteArray finalSignal = "2" + metaSender->method(i).methodSignature();
                    QByteArray finalSlot = "1" +  metaReceiver->method(j).methodSignature();
                    QObject::disconnect(sender,finalSignal.data(),receiver,finalSlot.data());
                }
            }
        }
    }
    return QObject::connect(sender,signal,receiver,slot,type);
}

测试:

QObject *obj = new QObject;
connect(obj,SIGNAL(objectNameChanged(QString)),this,SLOT(testFunc()));
connect(obj,SIGNAL(destroyed()),this,SLOT(testFunc()));
obj->setObjectName("newNAme");
uniqueConnect(obj,SIGNAL(objectNameChanged(QString)),this,SLOT(showMaximized()));
obj->setObjectName("more");
输出:

testFunc called once!!!
...maximized window...

如何模拟SIGNAL SLOT宏

最新更新