У меня есть вопрос о QInputDialog. Я провел последние 3 дня в Google и подумал, что пришло время спросить здесь, так как я не могу найти никакого ответа;
Мое приложение имеет основной поток (также известный как GUI-поток в Qt).
Этот GUI-поток создает рабочий. Который запускается в другой теме.
Этот работник сканирует папки. Он также отправляет GUI-потоку информацию о прогрессе. Работает нормально.
Теперь вот проблема. Рабочий поток может столкнуться с ситуацией, в которой он должен запросить ввод данных у пользователя. QString. Необходимо дождаться ответа, прежде чем продолжить сканирование оставшихся папок. Однако рабочий поток не может показать QInputDialog, как выясняется. Только GUI-нить.
Я также не могу использовать слоты и сигналы, потому что они не могут возвращать значение в Qt. Я попытался использовать слот и ссылочную QString, но иногда происходит сбой. Не потокобезопасный, я полагаю.
Я попробовал QMetaObject :: invokeMethod, но не смог заставить его работать. Кроме того, это даже поток безопасно?
У кого-нибудь здесь есть решение для этого?
Ниже приведен мой код, если это помогает, он был «сжат», поэтому я не трачу ваше драгоценное время на знакомство с именами моих переменных и фактическими вещами.
НАЧАЛЬНЫЙ КОД (QInputDialog в рабочем потоке)
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 (ссылка на QString, иногда работает, иногда вылетает)
Подлый ссылочный трюк, который я попробовал, был таким … сначала в 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;
}
Потом в рабочий ..
Worker::ask(){
QString sneakyReturnValue;
emit worker_asks(*sneakyReturnValue);
return sneakyReturnValue;
}
INVOKEMETHOD CODE (не может заставить его работать из-за parent (), также не уверен, что потокобезопасен)
Метод invokeMethod, который я пробовал, так и не заработал, но все прошло как в MainWindow.cpp
Q_INVOKABLE QString askUser();
..и в MainWindow.cpp
QString MainWindow::askUser(){
QStringList listToChooseFrom;
listToChooseFrom.append("A");
listToChooseFrom.append("B");
return QInputDialog::getItem(this, "Title", "Message", listToChooseFrom, 0, false, &ok);
}
и, наконец, в 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;
}
Но я не смог получить ссылку на работающий GUI-поток … первый аргумент invokeMethod. родитель не работает. Я думаю, что мой работник не является дочерним элементом моего GUI-потока автоматически. Ну, parent () по крайней мере не работает.
Ближайшее решение, которое вы предложили, это использовать QMetaObject::invokeMethod()
, но вы не должны использовать parent()
но передайте ему объект класса, который имеет метод для вызова, в этом примере я передам GUI:
worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QDebug>
#include <QStringList>
#include <QInputDialog>
#include <QThread>
class Worker : public QObject
{
Q_OBJECT
QObject *mGui;
public:
explicit Worker(QObject *gui, QObject *parent = nullptr):QObject(parent){
mGui = gui;
}
virtual ~Worker(){}
public slots:
void start_work(){
ask_user();
}
void ask_user(){
QStringList choices;
choices.append("one");
choices.append("two");
QString retVal;
for(int i=0; i < 10; i++){
QMetaObject::invokeMethod(mGui, "callGuiMethod", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QString, retVal),
Q_ARG(QStringList, choices));
qDebug()<<retVal;
QThread::sleep(5);//emulate processing
}
}
signals:
void finished();
};
#endif // WORKER_H
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include "worker.h"
#include <QThread>
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
QThread workerThread;
public:
explicit Widget(QWidget *parent = nullptr):QWidget(parent){
worker_create();
}
Q_INVOKABLE QString callGuiMethod(QStringList items){
return QInputDialog::getItem(0, "title", "label", items , 0, false);
}
~Widget() {
workerThread.quit();
workerThread.wait();
}
public slots:
void worker_create(){
Worker *worker = new Worker(this);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::started, worker, &Worker::start_work);
connect(worker, &Worker::finished, worker, &QObject::deleteLater);
connect(worker, &Worker::finished, &workerThread, &QThread::quit);
connect(&workerThread, &QThread::finished, &workerThread, &QObject::deleteLater);
workerThread.start();
}
};
#endif // WIDGET_H
Полный пример можно найти по следующему ссылка на сайт
Других решений пока нет …