2017-12-25 21 views
0

QInputDialog에 대한 질문이 있습니다. 나는 지난 3 일을 구글에서 보냈다. 나는 대답을 찾을 수 없기 때문에 여기에서 물어볼 시간이라고 생각했다.QInputDialog & Threading (작업자, 대화 상자 표시, 입력 대기, 계속)

내 응용 프로그램에는 주 스레드 (Qt의 GUI 스레드라고도 함)가 있습니다. 이 GUI 스레드는 작업자를 생성합니다. 다른 스레드에서 실행되는 것입니다. 이 작업자는 폴더를 검사합니다. 또한 진행에 대한 GUI 스레드 정보를 보냅니다. 잘 작동합니다.

이제 여기에 문제가 있습니다. 작업자 스레드는 사용자에게 입력을 요청해야하는 상황을 경험할 수 있습니다. QString. 나머지 폴더를 계속 스캔하기 전에 대답을 기다려야합니다. 그러나 작업자 스레드는 그것이 나온 QInputDialog를 표시 할 수 없습니다. GUI 스레드 만.

슬롯 및 신호는 Qt의 값을 반환 할 수 없기 때문에 사용할 수 없습니다. 슬롯과 참조 된 QString을 사용하여 시도했지만 가끔 충돌이 발생합니다. thread-safe는 아닙니다.

QMetaObject :: invokeMethod를 시도했지만 작동하지 못했습니다. 또한 스레드가 안전합니까?

누구나 여기에 대한 해결책이 있습니까?

아래 코드는 도움이된다면, "압축"되어 있으므로 내 변수 이름과 실제 물건에 익숙해지는 데 귀중한 시간을 낭비하지 않습니다.

초기 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; 
} 

음흉 참조 코드 (참조 된 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; 
} 

16,..

Worker::ask(){ 
    QString sneakyReturnValue; 
    emit worker_asks(*sneakyReturnValue); 
    return sneakyReturnValue; 
} 

INVOKEMETHOD 코드 내가 시도 invokeMethod (이 스레드 안전 하나 확실하지,() 부모로 인해 작업을 가져올 수 없습니다) 일 없어,하지만MainWindow.cpp

Q_INVOKABLE QString askUser(); 

.. 그리고에 MainWindow.cpp

에 .. 같이 갔다 결코

마지막 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; 
} 

에서 5,

QString MainWindow::askUser(){ 
    QStringList listToChooseFrom; 
    listToChooseFrom.append("A"); 
    listToChooseFrom.append("B"); 
    return QInputDialog::getItem(this, "Title", "Message", listToChooseFrom, 0, false, &ok); 
} 
하지만 invokeMethod의 첫 번째 인수는 GUI 스레드 작업 ..에 대한 참조를 가져올 수 없습니다. 부모가 작동하지 않습니다. 나는 내 직원이 내 GUI 스레드의 자식이 아니라고 생각한다. 음, parent()는 적어도 작동하지 않습니다.

+0

설명에 혼란이 있기 때문에 코드를 구현하기가 어렵습니다. github 또는 유사한 방법으로 오류를 재현하는 코드를 공유 할 수 있습니다. – eyllanesc

+0

제 질문 요약 : 스레드가 입력을 기다리는 동안 비 GUI 스레드에서 QInputDialog를 표시하는 방법은 무엇입니까? – JaredNinja

+0

제 아이디어는 코드에 어떤 문제가 있는지 확인하는 것입니다. – eyllanesc

답변

1

당신이 제안 가장 가까운 솔루션은 QMetaObject::invokeMethod()을 사용하는 것입니다,하지만 당신은,이 예제에서 나는 GUI 전달합니다 parent()를 사용하지만 그것을 호출 할 수있는 방법이있는 클래스의 개체를 전달해서는 안 :

을 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 

#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 
widget.h3210

전체 예제는 다음에서 찾을 수 있습니다 link

+0

당신의 대답은 invokeMethod-code를 완성하는데 필요한 것을 알려주었습니다; 내 GUI에 대한 포인터. 1 부 : Worker * worker = 새 작업자 (this); 작업자 클래스 생성자의 2ns 부분입니다. 명시 적 작업자 (QObject * gui, QObject * 부모 = nullptr) : QObject (부모) {mGui = gui; } 부적당 한 매개 변수 예제와 같이 디버그 모드에서 여전히 충돌이 발생합니다. 나는 이것을 스레드 문제라고 생각했다. 대화 상자를 15-40 초 동안 기다릴 때마다 앱이 다운됩니다. 그리고 나는 내가 옳다고 생각한다. 내 석방을 구축하고 QT Creator 외부에서 시작하자마자. 정말 고맙습니다!! – JaredNinja