slots are being called multiple times
-
I have the following code that gets called whenever a user opens a file, so this code can be called multiple times.
{ QWidget *tab = getTabWidgetByName(name); mIO = new MIO(this); mIO->moveToThread(&mIOThread); connect(&mIOThread, &QThread::finished, mIO, &QObject::deleteLater); connect(this, &MainWindow::startMIO, this, [this] () -> void { mIO->operator()(); }, Qt::UniqueConnection); connect(mIO, &MIO::notifyMIOFinished, this, &MainWindow::mIOFinished, Qt::UniqueConnection); mIOThread.start(); emit startMIO(); }
It works fine until the second time it's called. When run, it appears to be emitting startMIO() twice. The functor mIO->operator()() is certainly called twice somehow and I'm wondering if I'm starting the thread correctly as I call start() on mIOThread. It shouldn't do anything as the constructor is basically empty, and then when emit startMIO() is called, it starts the functor which is where all the work is at.
I saw several other threads about this, and have added Qt::UniqueConnection, but that doesn't seem to help.
Here's a bit about the MIO object.
class MIO : public QObject { Q_OBJECT private: MIO(MainWindow *win); ~MIO(); void operator()(); // ... } MIO::MIO(MainWindow *win) : mainWin(win) { } void MIO::operator()() { // lots of stuff }
Thoughts?
-
It's a problem with your usage of
Qt::UniqueConnection
in a connect statement with a lambda. Each time a different instance of that lambda object is created so the connection to it is made. The wayQt::UniqueConnection
works is it compares the addresses of the objects and function pointers of the signal and slot and doesn't make a connection if all of thouse match something existing.In short
Qt::UniqueConnection
doesn't work with lambdas so replace the lambda with a normal member function.
Another solution is to store the result of the connect statement ( aQMetaObject::Connection
object) and use it to calldisconnect
after the connection is triggered.Also note that your usage of connect is kinda dangerous, You passed
this
as the first and third parameter so the connection lives as long asthis
lives. Inside the lambda you are using thatmIO
object though, that can be destroyed anytime so your app may crash ifmIO
is deleted and the signal is emited after that. It's better to tie the connection to the object you're using in the lambda, so instead of usingthis
as the third parameter you can usemIO
. This way if eitherthis
ormIO
is deleted the connection will be severed automatically.Another consideration is that slots are executed in the thread of the target object, so if you pass
this
as the third parameter the lambda will be called on the thread thatthis
lives in, notmIO
and that's probably not what you want. PassingmIO
as the third argument would solve that too. -
edit What @Chris-Kawa said.
Given the limited context, use of a dedicated QThread seems overly complicated. QtConcurrent::run may be a better solution.