3

I was wondering either there is a possibility in Qt, co create a signal-slot connection, that will automatically break all other connections to this particular slot/ signal? I would appreciate all help.

Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
Łukasz Przeniosło
  • 2,725
  • 5
  • 38
  • 74

3 Answers3

4

Qt doesn't provide such functionality directly. Moreover, it's impossible to iterate signal-slot connections, so you can't even implement it yourself in general.

What you should be doing is keeping track of the connections that you initiate yourself, and removing them as appropriate.

For example:

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); }
};

The UniqueConnector allows only one connection to exist on its instance. So, for each unique connection, you need one UniqueConnector instance. The connection is removed upon destruction of the connector, unless you specify otherwise.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
1

So, you can use following scenario:

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()));
}
timocov
  • 1,088
  • 6
  • 16
1

I wrote this function very quickly and tested it, it seems that it really works! Yes, algorithm is not perfect, it probably can be improved, but it requires more time. Try this solution and tell result:

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);
}

Test:

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");

Output:

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

How to immitate SIGNAL SLOT macro.

Community
  • 1
  • 1
Jablonski
  • 18,083
  • 2
  • 46
  • 47
  • This of works. The cost is O(N^2), so it has to be kept in mind if you have lots of signals or slots. It also doesn't support connections to functors. It's better to track the connections yourself, though, since you're the one who set the connections to start with. – Kuba hasn't forgotten Monica Jul 23 '15 at 17:48
  • @KubaOber Yes, I agree with you. That's why I said that algorithms is not perfect, about another: I first of all tried to use Qt introspection and didn't remember about more generic way as you provided in your answer. – Jablonski Jul 23 '15 at 17:58
  • Sure, I think it's a good answer too, since it shows that you can't iterate the connections, and the best you can do (if you don't have the connection handle) is to iterate all possible connection kinds, and attempt to remove them individually. This is an acceptable workaround for existing code that wasn't changed yet to use a connection tracker. – Kuba hasn't forgotten Monica Jul 23 '15 at 18:34