qt5: как создать и отобразить пользовательский qdialog из статической функции в qthread

Допустим, вы создали новый поток, который после запуска вызывает статическую функцию. Внутри этой статической функции вам нужно создать и отобразить собственный qdialog. Как вы можете создать его, чтобы он не имел родителя и находился в нужном потоке?

Конструктор устанавливает для родителя значение 0, но по-прежнему сообщает об ошибке о невозможности создания дочерних элементов для родителя в другом потоке. Поскольку это статическая функция, я не могу использовать «этот» объект, а без «этого» я ​​не могу получить текущий поток или идентификатор потока. Я думал, что смогу вызвать myCustomDialog->moveToThread(), но понятия не имею, как определить правильный поток из статической функции.

Если я использую одну из статических функций QMessageBox, все работает нормально. Например, вызов QMessageBox::information(0, tr("Title"), tr("Message")) не сообщает об ошибках. Как я могу закодировать свой собственный qdialog для работы, аналогичной статической функции qmessagebox?

Есть ли способ получить список всех запущенных потоков из объекта qApp? Любые другие предложения?

static int myFunction();

int myObject::myFunction()
{
    myCustomDialog *mcd = new myCustomDialog();
    // as soon as I call exec() it reports an error and crashes
    mcd->exec();
    // QObject: Cannot create children for a parent that is in a different thread.

    // can't call mcd->moveToThread() without knowing the current thread
    // how can I determine the current thread from this static function?
    // if parent = 0 then why is this error occurring?
    return 0;
}

myCustomDialog(QWidget *parent = 0);

myCustomDialog::myCustomDialog(QWidget *parent) : QDialog(parent)
{
    // create widgets and layout here
}

person luxtor    schedule 30.07.2013    source источник


Ответы (2)


Пример... Вы можете адаптировать его для вызова статических функций, но я не думаю, что это необходимо. Хорошей практикой является работа с потоками следующим образом: вы должны создать свой диалог в потоке GUI и подключить его слот exec() к вашему сигналу с помощью Qt::BlockingQueuedConnection

Рабочий.ч

#include <QObject>

class Worker
    : public QObject
{
    Q_OBJECT

signals:
    void showDialog();

public:
    Worker( QObject *parent = NULL );
    ~Worker();

public slots:
    void doLongWork();

private:
};

Рабочий.cpp

#include <QThread>
#include <QMessageBox>
#include <QDebug>
#include <QCoreApplication>

#include <Windows.h>


Worker::Worker( QObject *parent )
    : QObject( parent )
{
}

Worker::~Worker()
{
}

void Worker::doLongWork() // You may override QThread::run with same effect, but it is bad practice
{
    qDebug() << "Worker thread id = " << QThread::currentThreadId();
    ::MessageBoxA( NULL, "Click to show dialog in 3 seconds", NULL, 0 );
    ::Sleep( 3000 );
    emit showDialog();  // "Showing" dialog from non-GUI thread. And wait for close
    ::MessageBoxA( NULL, "Dialog closed!", NULL, 0 );
    qApp->quit();
}

main.cpp

#include <QApplication>
#include <QDialog>
#include <QThread>
#include <QDebug>

#include "Worker.h"


int main(int argc, char *argv[])
{
    QApplication a( argc, argv );
    a.setQuitOnLastWindowClosed( false );

    QDialog dlg;
    QThread workerThread;
    Worker worker;

    qDebug() << "Main thread id = " << QThread::currentThreadId();

    QObject::connect( &workerThread, SIGNAL( started() ), &worker, SLOT( doLongWork() ) );
    QObject::connect( &worker, SIGNAL( showDialog() ), &dlg, SLOT( exec() ), Qt::BlockingQueuedConnection ); // !!!See connection type!!!

    worker.moveToThread( &workerThread );
    workerThread.start();

    return a.exec();
}

Примечание. Окна сообщений WinAPI используются только для визуализации того, что поток действительно ожидает закрытия диалогового окна.

person Dmitry Sazonov    schedule 31.07.2013
comment
Хороший указатель на Qt::BlockingQueuedConnection. Я забыл об этом типе подключения. - person Phlucious; 31.07.2013

Не надо. Создание объектов QWidget из другого потока — плохая идея. В документации говорится:

В приложениях с графическим интерфейсом основной поток также называется потоком графического интерфейса, поскольку это единственный поток, которому разрешено выполнять операции, связанные с графическим интерфейсом.

Как я обычно обхожу это, я испускаю сигнал от моего объекта в потоке без графического интерфейса, который подключен к объекту, живущему в основном потоке (часто для меня это MainWindow). Затем принимающий объект создает диалог в основном потоке.

Как упоминалось в другом ответе, это соединение может при необходимости заблокировать ваш рабочий поток, установив тип соединения как Qt::BlockingQueuedConnection.

Дополнительные сведения о вызове функций из других потоков см. в этой публикации.

person Phlucious    schedule 31.07.2013
comment
Я рассматривал этот вариант, но мне нужно, чтобы диалоговое окно блокировало приложение. Код, выполняемый в потоке, запрашивает пользовательский ввод только в крайнем случае. Когда он отображает диалоговое окно, ему необходимо заблокировать продолжение работы приложения до тех пор, пока пользователь не решит, какое действие предпринять. Поток содержит цикл while, поэтому, если я отправлю сигнал, код, скорее всего, будет продолжаться, по крайней мере, временно, пока диалоговое окно не будет сгенерировано и отображено в основном потоке. И даже тогда я не уверен, приостановит ли цикл while во вторичном потоке после вызова dialog.exec(). - person luxtor; 31.07.2013
comment
Как уже упоминалось, вы не должны получать доступ к графическому интерфейсу из потока. Отправьте сигнал и установите QWaitCondition дополнительно. Это заставит поток ждать сигнала wake. - person Sebastian Lange; 31.07.2013
comment
Как упоминалось в другом ответе, вы можете заблокировать свой рабочий поток с помощью emit, установив соединение с Qt::BlockingQueuedConnection. Если у вас есть другие потоки, которые вам нужно заблокировать, помимо основных и рабочих потоков, это будет немного сложнее. - person Phlucious; 31.07.2013
comment
Спасибо за советы. Использование типа Qt::BlockingQueuedConnection, а затем передача сигнала из потока, отличного от графического интерфейса пользователя, работало отлично. - person luxtor; 01.08.2013