I have a QT class instance, called C, (C inherits QOBJECT) that sends a signal S.
In my program, other QT classes instances X are created and destroyed when the program runs. These other classes connect and disconnect S, i.e. they run:
connect(C,SIGNAL(S()), this, SLOT(my_func())); // <this> is an instance of X
or
disconnect(C,SIGNAL(S()), this, SLOT(my_func()));
In class C, the calculation of whether S should be emitted (and the data associated to it - not shown here) is rather complicated, so I would like the instance of class C (which emits the signal) to be notified when one(or more) object are connected (listening) to S or when all are disconnected.
I have read about the connectNotify and disconnectNotify functions, but their usage is discouraged. Besides the documentation does not state very clearly if there is a one to one relationship between the number of (dis)connectNotify calls and the number of "listener" to the signal (or can one single connectNotify be called for more than one listener?).
Can I just count positively (count++) the number of connectNotify and negatively (count--) the number of disconnectNotify and just react to non-zero value?
Any better way to do this?
First, I think you've got it right that connectNotify and disconnectNotify can be used for this purpose - each connect event will be counted properly, even if it is a duplicate from the same object.
You can also double check this with QObject::receivers
int QObject::receivers ( const char * signal ) const [protected]
Returns the number of receivers connected to the signal. Since both
slots and signals can be used as receivers for signals, and the same
connections can be made many times, the number of receivers is the
same as the number of connections made from this signal. When calling
this function, you can use the SIGNAL() macro to pass a specific
signal: if (receivers(SIGNAL(valueChanged(QByteArray))) > 0) {
QByteArray data;
get_the_value(&data); // expensive operation
emit valueChanged(data); } As the code snippet above illustrates, you can use this function to avoid emitting a signal that
nobody listens to. Warning: This function violates the object-oriented
principle of modularity. However, it might be useful when you need to
perform expensive initialization only if something is connected to a
signal.
My suggestion would be to write a simple test program. Override connectNotify and disconnectNotify to increment/decrement a counter, but also use receivers to verify that the counter is correct. Try connecting multiple times, disconnecting multiple times, disconnecting even if there is no connection, etc.
Something to be careful of: connect and disconnect are thread-safe; I'm not sure if the matching Notify functions are safe also.
Since Qt 5.0, you can do this more easily with the QObject::isSignalConnected function. Example from the documentation:
static const QMetaMethod valueChangedSignal = QMetaMethod::fromSignal(&MyObject::valueChanged);
if (isSignalConnected(valueChangedSignal)) {
QByteArray data;
data = get_the_value(); // expensive operation
emit valueChanged(data);
}
Related
To simplify the question, let's say that i have a QTimer, which will trigger its timeout event every 3000ms.
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&](){
// do sth(rely on a public data structure)
});
timer.start(3000);
The operation inside the lambda connected to the timeout event is relied on a public data structure.
And the application hold an QUdpSocket, and connect the readyRead signal with a slot function.
QUdpSocket socket;
socket.bind(45454, QUdpSocket::ReuseAddressHint);
QObject::connect(&socket, &QUdpSocket::readyRead, [&](){
//manipulate the public data structure
}
As you can see, the lambda connected to the readyRead signal manipulate the public data structure that the first lambda rely on.
So my question is, i want the function that connectted to the readyRead signal has the highest "priority", that is, even in the Qt's event loop are dealing with the timeout slot now, it can be interrupted and start the readyRead slot immediately, then after it finish, resume the timeout slot function. Is there any way to do so?
(My homework is to simulate the IEEE802.11 exposed/hidden node problem, it requires that i have to constantly listen the channel before/during sending a packet.)
(Is explicitly call QCoreApplication::processEvent will help?)
I am not familiar with the IEEE802.11 exposed/hidden node problem, however i don't think you can "interrupt" your code as you're describing.
One possible way to handle with this would be running the code for the readyRead and the timeout slots on different threads and then use some kind of synchronisation mechanism (QMutex comes to mind) to access the public data (and get its state).
i.e.
add some kind of uniqueid to identify the public_data current status
timeout_slot will acquire a lock, read the public data in a local copy and release the lock, then continue to manipulate local structure and finally before release would acquire lock again and check that the uniqueid has not been changed, if so commit your work, otherwise you'd have to start over.
readyRead_slot would acquire the lock, update the uniqueid and then continue working
I'm using Qt 5.9.2 with Visual Studio 2015 and QtDesigner for programming a Windows GUI application. I tried connecting one of my actions via the following call:
connect(ui.myAction, &QAction::triggered, memberPtrToObjX_, &ClassX::Run);
However ClassX::Run is not always triggered after clicking on myAction in the menubar. Investigating into this problem, I figured, that the same signal-slot connection using lambda syntax works:
connect(ui.myAction, &QAction::triggered, [this](bool run) { memberPtrToObjX_->Run(run); });
I'm pretty sure, that both calls are syntactically correct. Besides both calls return a valid QMetaObject::Connection, if I save the return value and check with operator bool().
Obviously I could just stick with the working lambda-version, but I'm confused and would prefer knowing the reason behind my "solution". Is there any functional difference between these two calls, that explains the different behaviour?
The two calls of QObject::connect() (exposed by the OP) behave differently in the case that this->memberPtrToObjX_ is modified after the call of connect().
The first
connect(ui.myAction, &QAction::triggered, memberPtrToObjX_, &ClassX::Run);
calls
QMetaObject::Connection QObject::connect( const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection).
Creates a connection of the given type from the signal in the sender object to the method in the receiver object. Returns a handle to the connection that can be used to disconnect it later.
Hence, the current pointer in this->memberPtrToObjX_ is connected as signal receiver. If this->memberPtrToObjX_ is modified after connect() this doesn't have any effect to the signal connection.
The second
connect(ui.myAction, &QAction::triggered, [this](bool run) { memberPtrToObjX_->Run(run); });
calls
QMetaObject::Connection QObject::connect( const QObject *sender, PointerToMemberFunction signal, Functor functor).
Creates a connection from signal in sender object to functor, and returns a handle to the connection.
Hence, the (functor behind the) lambda is connected as receiver. The lambda resolves the pointer in this->memberPtrToObjX_ at the time it is executed i.e. when the signal is triggered.
The second difference (which originally was uncovered in the comment of G.M.) is the connection type:
The first version uses the default value Qt::AutoConnection (as it is not defined explicitly). The version with the lambda uses always Qt::DirectConnection instead.
The difference appears if the pointee in this->memberPtrToObjX_ does not "live" in the same thread. In this case, the Qt::AutoConnection is resolved to Qt::QueuedConnection instead of Qt::DirectConnection.
I assumed that the pointee in this->memberPtrToObjX_ would "live" in the same QThread. If not, the second version (with the lambda) becomes very questionable as it calls the a member function of the object "living" in a different thread (where it is hard to tell what that thread is doing at this time). This only seems to work better but is very possibly a "time bomb".
I am currently trying to understand the new QT5 signal/slot syntax
connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
receiver->updateValue("senderValue", newValue);
});
Now my question is where is the address of the receiver SLOT in the above expression ? I wanted to know this because what happens if a signal is in threadA and the slot is in thread B and I wanted it to be a queued connection ?
A slot is a piece of code, it doesn't "live" in a thread - a thread might run it or not, but the code itself doesn't belong to any thread. (If the slot is a member function, then the Qt object defined as the receiver belongs to a Qt thread - that's a property of the object, not the function.)
In the code you have above, the compiler generates an object that:
captures receiver by value ([=])
has a function-call operator that can be called with a reference to a const QString.
That object is passed to connect along with the other two arguments. It's not a QObject, so it doesn't have an owning thread in the Qt sense. What you need to make sure of is that:
what receiver points to stays alive for as long as that signal is connected
receiver->updateValue(...) is thread-safe - it will be called in sender's context/thread.
If receiver->updateValue needs to be called in receiver's thread/context, then do not use that syntax for the connect call, use the one where you specify both sender and receiver, and the connection type.
WMIQuery::wmiquery(WMI::WMITable* table, const QString& query, WMI::ProgressIndicator* progressIndicator)
This is the Function signature. and I am calling it through QtConcurrent::run
QFuture<quint32> future = QtConcurrent::run(WMI::WMIQuery::wmiquery, _table, query);
The architecture is quite simple.
Expected number of rows that will be returned by the query is known.
query is ran parallelly and on each record fetch a row is added to table: WMI::WMITable*
WMI::WMITable is a Simple QObject Table Data Structure .
it emits rowsAboutToBeInserted(QModelIndex, int, int) and rowsInserted(QModelIndex, int, int) upon row addition.
On the other hand ProgressIndicator in instantiated on main thread and the table is passed to its ctor . it gets the expected total number of rows from WMI::WMIQuery::wmiquery() through ProgressIndicator::setRecordCount(quint64 count).
it has a slot rowAdded() which emits the progress out of 100 by doing some simple mathematics. In its ctor it connects
connect(_table, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowAdded()));
What I think. as WMI::WMIQuery::wmiquery() i running on a different thread (on QThreadPool) this connection is a cross thread queued connection . am I correct ?
I am getting the following error at runtime
QObject::connect: Cannot queue arguments of type 'QModelIndex'
(Make sure 'QModelIndex' is registered using qRegisterMetaType().)
What should I do ? as my SLOT(rowAdded()) does not require the 3 arguments of SIGNAL(rowsInserted(QModelIndex,int,int)) should I make another signal like rowInserted() and emit it whenever I am emitting rowsInserted(QModelIndex,int,int) and use this SIGNAL instead for this coinnection
You may ask why I am using model like signals like rowsInserted(QModelIndex,int,int) in the table data structure. cause I do also have a model that is connected to this table. which will also be updated row by row. however I think that is immater in this regard.
Before emitting a signal across a thread boundary with a non-trivial argument type (like QModelIndex), you must first call this:
qRegisterMetaType<QModelIndex>("QModelIndex");
That prepares Qt to be able to emit the signal across a thread boundary.
Normally you would do this in main() or somewhere that only runs once, before calling emit, but after your QApplication has been instantiated.
This is only necessary for types that are non-trivial. For example, a signal like this would not require you to call qRegisterMetaType()
signals:
void mySignal(int foo, int bar);
But a signal like this does require qRegisterMetaType():
signals:
void mySignal(QModelIndex);
For more info, see the Qt docs here: http://doc.qt.nokia.com/latest/qmetatype.html#qRegisterMetaType
I know this is rather late, but I wanted to be sure someone mentioned it: QModelIndex is not meant to be queued, for the same reason that it's not meant to be stored and used later in other ways. That is, if the model changes before you use the QModelIndex, you will get undefined behavior. If you need queued events with model indices, you should probably use QPersistentModelIndex. Not really relevant to the original question, but may be of use to someone who lands here.
Say I have a Qt application where I have something like this:
connect(A, SIGNAL(a()), B, SLOT(b1()));
connect(A, SIGNAL(a()), B, SLOT(b2()));
...
void B::b1() {
A->disconnect();
}
If a() is emitted, will the slot B::b2() still be called after disconnecting all slots from A in B::b1(), since they both respond to the same signal? Assume that both objects live in the same thread, so we have a direct connection.
I know that the disconnect() disconnects all signal connections from A, but I am not sure if the emit just schedules both the b1 and b2 slots to be called and then calls them, so that a change to the connections has no effect until the two slots (and therefore the emit) return. So it could be implemented like:
emit:
make temprorary copy of signal's slot table
foreach element in temporary slot table: call
disconnect:
clear signal's slot table
Otherwise you could run into datastructure integrity problems.
A quick experiment shows that the second slot is not called.
However, I'm not sure if this is something that Qt promises.
Qt does document that certain checks are made during the iteration of connected receivers, but it's but that's pretty loose and non-specific (and I haven't come across anything better in my short search); from http://doc.qt.io/qt-5/signalsandslots.html:
This is the overhead required to locate the connection object, to safely iterate over all connections (i.e. checking that subsequent receivers have not been destroyed during the emission)
So they loosely document that certain checks are made while a signal is emitted, such as whether a receiver has been destroyed. I think checking whether the connection has been disconnected seems like a similar kind of situation, but it would be nice if that were explicitly documented.
Since it sounds like this will be a problem for you (you want all the signals to get through, right?), you may be able to work around this problem by ensuring that the signal to slot b1() is connected last. Qt does promise that slots will be called by a signal in connection order, so if you arrange for the slot that does the disconnect to be the last one, all the other slots will be seen.
I don't know how easy this might be to arrange, but it also seems kind of strange that an object would disconnect everything from inside another object's slot function (so there's probably some other refactoring that can solve this problem as well).
If none of that is acceptable, it wouldn't be too hard to come up with a proxy object for A's signals that has the behavior you need. There would be a single:
connect(A, SIGNAL(a()), proxy_obj, SLOT(proxy_slot()));
to connect A's signal to the proxy, then B can connect to the proxy's signal (which the proxy would be emit when the proxy's slot got called):
connect(proxy_obj, SIGNAL(a()), B, SLOT(b1()));
connect(proxy_obj, SIGNAL(a()), B, SLOT(b2()));
Since A's disconnect will not affect the connections that the proxy has to B, the proxy will ensure that all of B's slots get called when signal A->a() is emitted.
class A_proxy : public QObject
{
Q_OBJECT
public slots:
void proxy_slot() {
emit a();
}
signals:
void a();
};
disconnect reference page answers your question.
It depends on how you call it, and the way you call it (without any parameter), it will disconnect all signals on object A, therefore the slot B:b2 will not be called after disconnect.