QInputDialog & Threading (worker, show dialog, wait for input, continue)
QInputDialog & Threading ( worker, show dialog, wait for input, continue)
我有一个关于 QInputDialog 的问题。我在 Google 上度过了最后 3 天,我想是时候在这里问了,因为我找不到任何答案;
我的应用程序有一个主线程(在 Qt 中也称为 GUI-thread)。
这 GUI-thread 创建了一个工人。这是另一个线程中的 运行。
该工作人员扫描文件夹。它还发送有关进度的 GUI-thread 信息。工作正常。
问题来了。工作线程可能会遇到必须要求用户输入的情况。一个 QString。在继续扫描其余文件夹之前,它必须等待回答。但是,事实证明,工作线程无法显示 QInputDialog。只有 GUI-thread.
我也不能使用槽和信号,因为它们不能 return Qt 中的值。我尝试使用插槽和引用的 QString,但它有时会崩溃。不是 thread-safe 我想。
我试过 QMetaObject::invokeMethod 但也无法正常工作。另外,这甚至是线程安全的吗?
这里有人对此有解决方案吗?
下面是我的代码,如果有帮助的话,它已经 "compacted" 所以我不会浪费你宝贵的时间来熟悉我的变量名和实际的东西。
初始代码(工作线程中的 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;
}
偷偷摸摸的参考代码(引用的 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..
Worker::ask(){
QString sneakyReturnValue;
emit worker_asks(*sneakyReturnValue);
return sneakyReturnValue;
}
INVOKEMETHOD 代码(由于 parent(),无法使其工作,不确定是否 thread-safe)
我试过的 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-thread 工作的引用.. invokeMethod 的第一个参数。 parent 不工作。我认为我的工人不会自动成为我的 GUI-thread 的 child。好吧,parent() 至少不起作用。
您提出的最接近的解决方案是使用 QMetaObject::invokeMethod()
,但您不应使用 parent()
,而应将其传递给具有调用方法的 class 的对象,在这个例子我将通过 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
完整的例子可以在下面找到link
我有一个关于 QInputDialog 的问题。我在 Google 上度过了最后 3 天,我想是时候在这里问了,因为我找不到任何答案;
我的应用程序有一个主线程(在 Qt 中也称为 GUI-thread)。 这 GUI-thread 创建了一个工人。这是另一个线程中的 运行。 该工作人员扫描文件夹。它还发送有关进度的 GUI-thread 信息。工作正常。
问题来了。工作线程可能会遇到必须要求用户输入的情况。一个 QString。在继续扫描其余文件夹之前,它必须等待回答。但是,事实证明,工作线程无法显示 QInputDialog。只有 GUI-thread.
我也不能使用槽和信号,因为它们不能 return Qt 中的值。我尝试使用插槽和引用的 QString,但它有时会崩溃。不是 thread-safe 我想。
我试过 QMetaObject::invokeMethod 但也无法正常工作。另外,这甚至是线程安全的吗?
这里有人对此有解决方案吗?
下面是我的代码,如果有帮助的话,它已经 "compacted" 所以我不会浪费你宝贵的时间来熟悉我的变量名和实际的东西。
初始代码(工作线程中的 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;
}
偷偷摸摸的参考代码(引用的 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..
Worker::ask(){
QString sneakyReturnValue;
emit worker_asks(*sneakyReturnValue);
return sneakyReturnValue;
}
INVOKEMETHOD 代码(由于 parent(),无法使其工作,不确定是否 thread-safe) 我试过的 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-thread 工作的引用.. invokeMethod 的第一个参数。 parent 不工作。我认为我的工人不会自动成为我的 GUI-thread 的 child。好吧,parent() 至少不起作用。
您提出的最接近的解决方案是使用 QMetaObject::invokeMethod()
,但您不应使用 parent()
,而应将其传递给具有调用方法的 class 的对象,在这个例子我将通过 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
完整的例子可以在下面找到link