Слот QT не вызывается в основном потоке - PullRequest
0 голосов
/ 08 октября 2018

Из контекста потока слота в моем приложении QT GUI (после нажатия кнопки) я пытаюсь запустить рабочий поток, чтобы обновить другую другую часть GUI с результатами интенсивных вычислений ЦП - эти результаты будут обновленытаблица или виджет карты, похожий на google - так должно происходить в главном потоке приложения QT, где можно безопасно обновлять эти виджеты.

У меня проблема в том, что слот updateGUIWidget никогда не вызывается, если я не изменяю тип соединения на Qt::DirectConnection - в этом случае он вызывается в рабочем потоке (где небезопасно обновлять графический интерфейс).Я проверил результаты каждого из вызовов подключения, и они в порядке, кажется, что где-то есть проблема с циклом событий.Я не уверен, нужно ли мне выделять поток и рабочие объекты в качестве членов основного окна, или это нормально делать из переменных стека в слоте.

void
mainwindow::on_importSimulatedFlight_clicked()
{
    // experimental worker thread model adapted from YouTube tutorial
    // by Giuseppe di Angelo https://www.youtube.com/watch?v=BgqT6SIeRn4
    // auto thread = new QThread;
    // note worker created in gui thread here - we will move its thread
    // affinity to 'thread' below before starting it.
    auto thread = new QThread;
    auto worker = new Worker;
    connect(thread, &QThread::started, worker, &Worker::doWork);
    // connect(worker, &Worker::progressUpdate, this, &mainwindow::updateGUIWidget, Qt::DirectConnection);
    connect(worker, &Worker::progressUpdate, this, &mainwindow::updateGUIWidget, Qt::QueuedConnection);
    connect(worker, &Worker::workDone, thread, &QThread::quit);
    connect(thread, &QThread::finished, worker, &Worker::deleteLater);
    // move worker to separate thread
    worker->moveToThread(thread);
    thread->start();
}

В главном окне объявлены слотыв mainwindow.h следующим образом:

class mainwindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit mainwindow(QWidget *parent = Q_NULLPTR);
    ~mainwindow();
    ...
public slots:
    void on_importSimulatedFlight_clicked();
    void updateGUIWidget(const param& rParam);
    ...
}

и реализован в mainwindow.cpp следующим образом:

void
mainwindow::updateGUIWidget(const param& rParam)
{
... update widget components with rParam partial result here
}

, и мой рабочий выглядит следующим образом:

class Worker : public QObject
{
    Q_OBJECT
public slots:
    void doWork() {
        const QString result;
        for (int i=0; i<5; i++) {
            const MMRTimedRecord foo;
//            QThread::sleep(1);
            emit progressUpdate(foo);
        }
        emit workDone(result);
    }
signals:
    void progressUpdate(const MMRTimedRecord&);
    void workDone(const QString &result);
};

Ответы [ 3 ]

0 голосов
/ 09 октября 2018

Вы можете проверить этот ответ для возможного решения.Вы можете сделать

MainThreadEvent::post([&]()
  {
    // gui update stuff
  }
);

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

0 голосов
/ 12 октября 2018

Это действительно просто - и вам не нужно управлять какими-либо потоками вручную:

void Ui::slot() {
  QtConcurrent::run([this]{
    auto result = compute();
    QMetaObject::invokeMethod(this, [this, r = std::move(result)]{
      m_widget.setSomething(r);
    });
  });
}

Тип вычисляемых данных должен быть подвижным.

0 голосов
/ 09 октября 2018

Причина, по которой он не работает, заключается в том, что в вашем коде есть серьезный недостаток: вы пытаетесь создать ссылку на локальную переменную для обработки в слоте в другом потоке.Это рецепт для катастрофы.

Когда вы используете Qt::QueuedConnection, вы ДОЛЖНЫ излучать по значению, например так:

void progressUpdate(MMRTimedRecord val);

Это означает, что ваш MMRTimedRecordдолжно быть копируемым, и, соответственно, ваш слот также должен принимать значение.И почему существует несоответствие между сигналом progressUpdate(const MMRTimedRecord&) и слотом updateGUIWidget(const param& rParam);?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...