EDIT: I do not want to call the object destructor as suggested in this thread.
I have connected a button to a slot. This slot starts a process.
ui->btnActivate->setText("Start");
connect(ui->btnActivate, SIGNAL(clicked()),this, SLOT(startProcess()));
After the process finishes, I do
ui->btnActivate->setText("Close");
connect(ui->btnActivate, SIGNAL(clicked()),this, SLOT(close()));
But now the button starts the process and then run close. How can I disconnect the first connection before altering the buttons behaviour? I would like to avoid calling the Destructor
Simply use 1 of the 5 signatures of QObject::disconnect to simply remove a connection between 2 objects without destroying any of them.
Related
I have the following code that performs a background operation (scan_value) while updating a progress bar in the ui (progress). scan_value iterates over some value in obj, emitting a signal (value_changed) each time that the value is changed. For reasons which are not relevant here, I have to wrap this in an object (Scanner) in another thread. The Scanner is called when the a button scan is clicked. And here comes my question ... the following code works fine (i.e. the progress bar gets updated on time).
# I am copying only the relevant code here.
def update_progress_bar(new, old):
fraction = (new - start) / (stop - start)
progress.setValue(fraction * 100)
obj.value_changed.connect(update_progress_bar)
class Scanner(QObject):
def scan(self):
scan_value(start, stop, step)
progress.setValue(100)
thread = QThread()
scanner = Scanner()
scanner.moveToThread(thread)
thread.start()
scan.clicked.connect(scanner.scan)
But if I change the last part to this:
thread = QThread()
scanner = Scanner()
scan.clicked.connect(scanner.scan) # This was at the end!
scanner.moveToThread(thread)
thread.start()
The progress bar gets updated only at the end (my guess is that everything is running on the same thread). Should it be irrelevant if I connect the signal to a slot before of after moving the object receiving object to the Thread.
It shouldn't matter whether the connection is made before or after moving the worker object to the other thread. To quote from the Qt docs:
Qt::AutoConnection - If the signal is emitted from a different
thread than the receiving object, the signal is queued, behaving as
Qt::QueuedConnection. Otherwise, the slot is invoked directly,
behaving as Qt::DirectConnection. The type of connection is
determined when the signal is emitted. [emphasis added]
So, as long as the type argument of connect is set to QtCore.Qt.AutoConnection (which is the default), Qt should ensure that signals are emitted in the appropriate way.
The problem with the example code is more likely to be with the slot than the signal. The python method that the signal is connected to probably needs to be marked as a Qt slot, using the pyqtSlot decorator:
from QtCore import pyqtSlot
class Scanner(QObject):
#pyqtSlot()
def scan(self):
scan_value(start, stop, step)
progress.setValue(100)
EDIT:
It should be clarified that it's only in fairly recent versions of Qt that the type of connection is determined when the signal is emitted. This behaviour was introduced (along with several other changes in Qt's multithreading support) with version 4.4.
Also, it might be worth expanding further on the PyQt-specific issue. In PyQt, a signal can be connected to a Qt slot, another signal, or any python callable (including lambda functions). For the latter case, a proxy object is created internally that wraps the python callable and provides the slot that is required by the Qt signal/slot mechanism.
It is this proxy object that is the cause of the problem. Once the proxy is created, PyQt will simply do this:
if (rx_qobj)
proxy->moveToThread(rx_qobj->thread());
which is fine if the connection is made after the receiving object (i.e. rx_qobj) has been moved to its thread; but if it's made before, the proxy will stay in the main thread.
Using the #pyqtSlot decorator avoids this issue altogether, because it creates a Qt slot more directly and does not use a proxy object at all.
Finally, it should also be noted that this issue does not currently affect PySide.
My problem was solved by movinf the connection to the spot where the worker thread is initialized, in my case because I am accessing an object which only exists after instantiation of my Worker Object class which is in another thread.
Simply connect the signal after the self.createWorkerThread()
Regards
This has to do with the connection types of Qt.
http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html#connect
http://qt-project.org/doc/qt-4.8/qt.html#ConnectionType-enum
In case both objects live in the same thread, a standard connection type is made, which results in a plain function call. In this case, the time consuming operation takes place in the GUI thread, and the interface blocks.
In case the connection type is a message passing style connection, the signal is emitted using a message which is handled in the other thread. The GUI thread is now free to update the user interface.
When you do not specify the connection type in the connect function, the type is automatically detected.
I'm a pure Qt beginner and currently want to understand its basic concepts and how to use them in the right way. My question might appear as somehow "ragged" and I want to apologize for that in advance. This having said, the question is:
I want to implement a method, which "blocks" until the handling is finished (for what reason ever...). In order to do the internals, I have to use an asynchronous class, emmiting a signal when it has finished its job in the background. I want to use that in a blocking way:
QEventLoop myLoop;
workerClass x;
QObject::connect(x, SIGNAL(finished()), &myLoop, SLOT(quit()));
/* x is asnchrounous and will emit "finished" when ist job is done */
x.doTheJob();
myLoop.exec();
Will that work? According to some other postings around - it should. But what is going on exactly?
When a new instance of QEventLoop is just created, does it automatically mean, that signals emitted from myLoop are automatically handled within that new handler? Moreover, the loop starts some while after having started the job. What happens, when it emits already "finished", before myLopp is started?
I have been told by a collegue, that I could use
QEventLoop myLoop;
workerClass x;
QObject::connect(x, SIGNAL(finished()), &myLoop, SLOT(quit()));
/* doTheJob is a slot now */
QMetaObject::invokeMethod(x, "doTheJob", Qt::QueuedConnection);
myLoop.exec();
I understand, that in this case doTheJob is called from the event loop. But from which event loop? invokeMethod() is not told to invoke the method in a particular event loop (myLoop), so why should the event posted to myLoop instead into the "outer" loop? In fact, myLoop is not even running at that time...
I hope I was able to transport my question to be understandable by experts.
Many Thanks,
Michael
The key is QObject::connect:
QObject::connect(x, SIGNAL(finished()), &myLoop, SLOT(quit()));
This function says when finished() is emitted, myLoop.quit() should be called just after the event occurs. So in your example,
<--------thread A--------> | <--------thread B-------->
QEventLoop myLoop;
workerClass x;
x.doTheJob(); starts thread B
sleep in exec() workerClass::JobA();
sleeping..... workerClass::JobB();
sleeping..... workerClass::JobC();
sleeping..... workerClass::JobD();
sleeping..... workerClass::finished();
sleeping.....
wake up by quit();
myLoop.quit must be called after thread A is sleeping, otherwise thread A may sleep forever because quit may be called before sleeping. You have to find a way to specify this. But how? Take a look at QObject::connect, the last argument is
Qt::ConnectionType type = Qt::AutoConnection
And about Qt::AutoConnection,
(Default) If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used.
The signal is emitted in thread B and the receiver myLoop is in thread A, that means you are using Qt::QueuedConnection:
The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
The slot myLoop.quit is invoked when control returns to the event loop, myLoop.exec. In other words, quit is invoked only when exec is running. This is exactly what we want and everything works fine. Therefore, your first example always runs correctly because the signal and slot are connecting together using Qt::QueuedConnection (from default value Qt::AutoConnection).
Your second example works fine because QObject::connect is the same, too.
If you change it to Qt::DirectConnection, the story is different. quit is called in thread B, it is possible that quit is called before exec and thread A is going to sleep forever. So in this scenario you should never use Qt::DirectConnection.
The story is about QObject::connect and threads. QMetaObject::invokeMethod is not related to this, it simply calls a function when the thread executes an exec function.
on your first part of the question: yes that will work.
There's actually a class for it in Qxt.
in order to understand how that works, you need to understand how event handling in general works, and this particular hack, while being a bad idea in generall, is a great way to learn it.
let's imaging a simplified call stack with just one mainloop:
6 myslot()
5 magic
4 someEvent.emit
3 eventloop.exec
2 qapplication.exec
1 main
as long as you don't return from myslot(), the "magic" cannot call any other slots that are connected to the same signal, and no other signal can be processed.
Now, with the nested eventloop "signalwaiter" hack:
10 myslot2()
9 magic
8 someEvent2.emit
7 eventloop.exec
6 myslot()
5 magic
4 someEvent.emit
3 eventloop.exec
2 qapplication.exec
1 main
we still cannot continue processing the event that we're blocking, but we can process new events, such as that signal you're connecting to quit.
This is not re-entrant, and generally a very bad idea. You may well end up with very hard to debug code, because you might be calling slots from a stack you don't expect.
The way to avoid the fire-before-exec problem, is to connect your signal to a slot that sets a flag. if the flag is set before exec, just don't exec.
However, for the sake of explanation, lets's see how your method works (it does work):
when you use Qt::QueuedConnection, a signal emission is not a stacked call anymore. It looks more like this:
5 eventloop.putQueue
4 someSignal.emit
3 eventloop.exec
2 qapplication.exec
1 main
then emit returns
3 eventloop.exec
2 qapplication.exec
1 main
and eventloop puts your slot on the stack
5 myslot()
4 eventloop.pop
3 eventloop.exec
2 qapplication.exec
1 main
So at the time your slot is called, your signal has already been removed from the stack and all local variables are gone.
this is vastly different than DirectConnection, and important to understand for other stuff like QObject::deleteLater()
To answer the question which eventloop executes your queue: the one on top of the stack.
This question already has answers here:
Qt signals (QueuedConnection and DirectConnection)
(3 answers)
Closed 8 years ago.
I have read the doc of Qt and have the following question:
if I have:
class_A::on_button_click(){
...do some things...//no signal emit this part
emit signal_A();
}
class_B::on_signal_received(){
...do some things...//no signal emit this part
}
...
connect(class_A_object,SIGNAL(signal_A()),class_B_object,on_signal_received);
All the things here are in the main thread,
now when I click the button and trigger the first function,
the program will be executed from the first line of
class_A::on_button_click()
until the last line of
class_B::on_signal_received()
and during the process nothing else in the main thread will get the chance to be executed, is this correct?( "...do some things..." part don't emit any signals)
In other words, when is the moment that the control return to the event loop?
After finishing
class_A::on_button_click()
or
class_B::on_signal_received()
?
When your signal and slot are in the same thread (as mentioned by user3183610) your signal will be direct connection (the default for same-thread). This effectively runs similarly to a function call. The signal is not queued, but instead the slot that the signal is connected to executes immediately - so you can think of it as a direct function call to the slot.
You can, however, change this behavior by using the Qt::QueuedConnection optional parameter at the end of your connect call:
connect(class_A_object,SIGNAL(signal_A()),class_B_object,on_signal_received, Qt::QueuedConnection);
This will force the use of the queue, your signal will be queued in the event loop and then other pending signals will be executed in order (this is often more desirable then DirectConnection because you can more easily guarantee the order of events). I tend towards to use of queued connections for this reason (though I believe direct is very slightly more efficient).
So for your code there is no return to the event loop until after on_button_click(). During on_button_click() you emit the diret signal signal_x() and immediately on_signal_received() is called (by-passing the event loop), when this finishes it returns back to on_button_click() - just like a function call would :)
I have created Qt tree control( and its nodes ) in different thread than the main thread. In the main thread I want to show context menu for the clicked node, so I am connectiong the actions in the menu with appropriate slots in the main thread. The connect function returns true , but slot is never executed. If I explicitly say in connect function that this is Qt :: DirectConnection then everything works fine. Why is this ?
I I create my tree in main thread, everything also works fine , without having to say that this is Qt::DirectConnection .
See the documentation here.
The default connection type, Qt::AutoConnection, is the same as Qt::DirectConnection if the signal is sent from the same thread as the receiver slot, otherwise the behaviour is the same as Qt::QueuedConnection.
In the case where you create the widget in the main thread, you basically get the same behaviour as when you explicitly specify Qt::DirectConnection.
The behaviour of Qt::QueuedConnection is to call the slot when that threads event loop regains control.
To solve your problem, make sure you have an event loop in every thread which may be receiving signals, unless you manually specify Qt::DirectConnection (which, I assume, will mean the slot is called from the same thread as the signals emitter - basically the equivelent of a normal function call).
I'm facing a practical problem with Qt. I'm using a class that communicates with QLocalSocket to another process (pipes/unix sockets) and I need to do that communication before other events occur, that is before app.exec() starts (or more precisely,as soon as app starts). The class that I'm using needs an eventloop so it does not work if I call the class methods before an event loop is started. There is any way to start something when the event loop is ready? I thought of making a hidden event-only window and do my duties in the hidden window constructor, and stablish this window as toplevel.
Basically, I need this local-socket communication task to start as soon as the event loop becomes available.
Any ideas?
Thank you.
You could start a separate eventloop, using QEventLoop, before calling QApplication::exec(). You should emit a "done" signal from your class and connect that to the QEventLoop quit() slot, or use an existing signal provided in the Qt class you're using.
Here's a simple example fetching a webpage using QNetworkAccessManager:
app = QtCore.QCoreApplication([])
manager = QtNetwork.QNetworkAccessManager()
req = QtNetwork.QNetworkRequest(QtCore.QUrl("http://www.google.com"))
resp = manager.get(req)
eventloop = QtCore.QEventLoop()
eventloop.connect(resp, QtCore.SIGNAL('finished()'), QtCore.SLOT('quit()'))
eventloop.exec_() # this will block until resp emits finished()
print resp.readAll()
app.exec_()
While this might suit your needs, I couldn't quite understand why you can't simply do whatever business you have prior to calling show() on your window, once that's done, call show().
If you just need to start the communications before everything else, you can simply use a single-shot timer with 0ms delay:
QTimer::singleShot(0, commsInstancePtr, SLOT(startCommunication()));
If you need your operations to actually finish before doing everything else, Daniel's solution might be more suitable.