QInputDialog&线程(工作者,显示对话框,等待输入,继续)

时间:2017-12-25 18:44:33

标签: c++ qt qt5 qthread qinputdialog

我对QInputDialog有疑问。我在Google上度过了最后3天,并认为是时候问这里,因为我找不到任何答案;

我的应用程序有一个主线程(在Qt中也称为GUI线程)。 这个GUI线程创建了一个worker。哪个是在另一个线程中运行的。 此工作人员扫描文件夹它还发送有关进度的GUI线程信息。工作正常。

现在问题就在于此。工作线程可能遇到必须要求用户输入的情况。一个QString。在继续扫描剩余文件夹之前,它必须等待答案。但是,工作线程无法显示QInputDialog。只有GUI线程。

我无法使用插槽和信号,因为它们无法在Qt中返回值。我尝试使用插槽和引用的QString,但它有时会崩溃。我认为不是线程安全的。

我尝试过QMetaObject :: invokeMethod,但无法使其工作。还有,即使是线程安全吗?

此处有人为此提供解决方案吗?

下面是我的代码,如果有帮助,它已被“压缩”,所以我不会浪费你宝贵的时间熟悉我的变量名称和实际的东西。

INITIAL CODE(工作线程中的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线程的孩子。好吧,父母()至少没有工作。

1 个答案:

答案 0 :(得分:1)

您提出的最接近的解决方案是使用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

完整示例可在以下link

中找到