I have a question about a QInputDialog. I have spent the last 3 days on Google and thought it is time to ask here, since I cannot find any answer;
My application has a main thread (also known as the GUI-thread in Qt). This GUI-thread creates a worker. Which is ran in a different thread. This worker scans folders. It also sends the GUI-thread information about progress. Working fine.
Now here is the problem. The worker thread can come across a situation in which it must ask the user for input. A QString. It must wait for an answer before continuing scanning the remaining folders. However, a worker thread cannot show a QInputDialog it turns out. Only the GUI-thread.
I cannot use slots and signals either because they cannot return a value in Qt. I tried using a slot and a referenced QString, but it crashes sometimes. Not thread-safe I suppose.
I tried QMetaObject::invokeMethod but couldn't get it to work either. Also, is that even thread safe?
Anyone here that has a solution for this?
Below is my code if that helps, it has been "compacted" so I don't waste your valuable time on getting familiar with my variable names and actual stuff.
INITIAL CODE (QInputDialog in worker thread) MainWindow.cpp
void MainWindow::worker_create(){
Worker* worker = new Worker;
QThread* thread = new QThread;
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT (start_work()));
connect(worker, SIGNAL(worker_status_changed(QByteArray)), ChanComObject, SLOT(worker_update(QByteArray)));
connect(worker, SIGNAL(finished(QString)), this, SLOT(worker_destroy(QString)));
connect(worker, SIGNAL(finished(QString)), worker, SLOT(deleteLater()));
connect(worker, SIGNAL(finished(QString)), thread, SLOT (quit()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
Worker.cpp
Worker::ask(){
QStringList listToChooseFrom;
listToChooseFrom.append("A");
listToChooseFrom.append("B");
QString answer = QInputDialog::getItem(this, "Title", "Message", listToChooseFrom, 0, false, &ok);
return answer;
}
SNEAKY REFERENCE CODE (Referenced QString, works sometimes, other times it crashes) Sneaky reference trick I tried was like this.. first in MainWindow.cpp:
void MainWindow::worker_create(){
// all other stuff from above, but an extra connect:
connect(worker, SIGNAL(worker_asks(Qstring*)), SLOT(gui_thread_dialog(QString*)));
}
void MainWindow::gui_thread_dialog(*sneakyReturnValue){
QString answer = QInputDialog::getItem(this, "Title", "Message", listToChooseFrom, 0, false, &ok);
*sneakyReturnValue = answer;
}
Then in the worker..
Worker::ask(){
QString sneakyReturnValue;
emit worker_asks(*sneakyReturnValue);
return sneakyReturnValue;
}
INVOKEMETHOD CODE (Can't get it working due to parent(), not sure if thread-safe either) The invokeMethod I tried I never got working, but it went like.. in MainWindow.cpp
Q_INVOKABLE QString askUser();
..and in MainWindow.cpp
QString MainWindow::askUser(){
QStringList listToChooseFrom;
listToChooseFrom.append("A");
listToChooseFrom.append("B");
return QInputDialog::getItem(this, "Title", "Message", listToChooseFrom, 0, false, &ok);
}
and finally in Worker.cpp
Worker::ask(){
QString dialogReturn;
QStringList listToChooseFrom;
listToChooseFrom.append("A");
listToChooseFrom.append("B");
bool u = QMetaObject::invokeMethod(parent,
"askUser",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QString, dialogReturn),
Q_ARG(QStringList, listToChooseFrom));
if(!u){ qDebug() << "invokeMethod failed"; }
result = dialogReturn;
}
But I couldn't get a reference to the GUI-thread working.. the first argument of invokeMethod. parent isn't working. I think my worker is not a child of my GUI-thread automatically. Well, parent() isn't working at least.
The closest solution you have proposed is to use
QMetaObject::invokeMethod()
, but you should not useparent()
but pass it an object of the class that has the method to invoke, in this example I will pass the GUI:worker.h
widget.h
The complete example can be found at the following link