Qt :: QueuedConnection не вызывает событие потока получателя l oop, - PullRequest
0 голосов
/ 07 марта 2020

Я пытаюсь вызвать слот в другом объекте с его собственным потоком и eventl oop, но по какой-то причине вызываемый слот по-прежнему является основным потоком GUI, а не потоком, принадлежащим объекту. Что я делаю неправильно? Я сделал все, что было сказано в документации, любые подсказки будут оценены по достоинству. Ger.

Вызванный ImportSpectrumFile взят из моего основного GUI из Поместите точку останова в slotOpenFile (QString path_file_name) и видел ее

идентификатор потока

Диспетчер. cpp

#include "dispatcher.h"
#include <QMessageBox>


Dispatcher::Dispatcher()
{
    initApplications();
    initSignalsAndSlots();
}
void Dispatcher::initApplications()
{
    file_import = new File_Import();
    file_import->start();
}
void Dispatcher::initSignalsAndSlots()
{
    connect(this, SIGNAL(signalOpenFile(QString)), file_import, SLOT(slotOpenFile(QString)),Qt::QueuedConnection);
}
void Dispatcher::ImportSpectrumFile(QString path_file_name)
{
    // This is the main GUI thread
    Qt::HANDLE id = QThread::currentThreadId();
    signalOpenFile("File name ger");
}

Dispatcher.h

#ifndef DISPATCHER_H
#define DISPATCHER_H


#include <QObject>
#include "convert_spectrum.h"
#include "file_import.h"


class Dispatcher : public QObject
{
   Q_OBJECT

public:
    Dispatcher();

    void ImportSpectrumFile(QString path_file_name);


private:
    void initApplications();
    void initSignalsAndSlots();

    File_Import * file_import;


signals:

     void signalOpenFile(QString path_file_name);

private slots:


};



#endif // DISPATCHER_H

file_import. c


#include "file_import.h"

File_Import::File_Import(QThread *parent) : QThread(parent)
{

}

void File_Import::slotOpenFile(QString path_file_name)
{
    // This is the same thread ID as the main GUI thread
    // This should be the thread in this object?
    // I called exec below, event loop is running for this thread
    // Why is this the main GUI thread id ?
    Qt::HANDLE id = this->currentThreadId();
}

void File_Import::run()
{
   // Enter thread event loop
   exec();
}
*1013* file_import.h
#ifndef FILE_IMPORT_H
#define FILE_IMPORT_H

#include <QObject>
#include <QThread>

class File_Import : public QThread
{
    Q_OBJECT
public:
    File_Import(QThread *parent = nullptr);

    void run() override;

signals:


public slots:
    void slotOpenFile(QString path_file_name);


};

#endif // FILE_IMPORT_H

Ответы [ 2 ]

0 голосов
/ 07 марта 2020

К счастью, эта сложность не нужна.

Все, что вам нужно - это QObject, который читает файл и т.д. c. Затем создайте для него поток и переместите объект в этот поток. В этот момент вызовы слотов автоматически преобразуются в события и передаются в очередь событий потока, в котором находится объект.

Обратите внимание, что нет необходимости выводить из QThread, нет необходимости вручную запускать циклы событий, и нет необходимости вручную указывать типы подключения. Это невероятно просто. Qt делает всю работу за вас:)

Что касается структуры кода: обычно вы хотите, чтобы GUI и logi c были отдельными, а интерфейс между ними только через сигнал / слот соединения. Эти соединения должны быть установлены вне обоих объектов. Например, в main или везде, где создается экземпляр получателя / отправителя. Класс Dispatcher просто сбивает с толку.

В более крупном приложении вместо сигналов от объектов пользовательского интерфейса вы, конечно, будете использовать QAction s.

Итак, GUI должен выглядеть следующим образом:

class UI : public QMainWindow {
  Q_OBJECT
public:
  Q_SIGNAL void loadFile(const QSteing &filename);
  Q_SLOT void fileLoaded(const QString &filename, QSharedPointer<FileData> data);
  …
};

Объект загрузчика выполняет задачу загрузки файлов:

Loader : public QObject {
  Q_OBJECT
  QFile m_file;
  …
public:
  Loader(QObject *parent = nullptr) : QObject(parent) {}
  QSharedPointer<FileData> loadFile(const QString &filename);
  Q_SIGNAL fileLoaded(QSharedPointer<FileData>);
};

Также есть некоторые struct FileData который несет представление данных, которые вы импортировали. Это спецификация приложения c в соответствии с вашими требованиями.

Это как строительные блоки LE GO. Из них легко построить ваше приложение:

int main(int argc, char **argv) {
  QApplication app(argc, argv);
  UI ui;
  Loader loader;
  QThread loaderThread;
  loader.moveToThread(&loaderThread);
  loaderThread.start();

  QObject::connect(&ui, loadFile, &loader, &Loader::loadFile);
  QObject::connect(&loader, &Loader::fileLoaded, &ui, &UI::FileLoaded);
  int rc = app.exec();
  QMetaObject::invokeMethod(&loader, [&]{
    loader.moveToThread(qApp->thread();
  });
  loaderThread.requestIntereuption(); // optional
  loaderThread.wait();
  return rc;
}

Поскольку процесс загрузки может быть долгим, и вы не хотите, чтобы он задерживал выход из приложения, вы можете вставить if (currentThread()->isInterruptionRequested()) return; в длительные циклы в код чтения файла, чтобы он быстро возвращался к локальному событию l oop при запросе прерывания.

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

Также обратите внимание, что если загрузка файла выполняется единственная функция, она может быть просто выполнена в другом потоке без всего этого махинации - вы бы использовали QtConcurrent::run для лямбды, которая загружает файл и затем вызывает (через QMetaObject :: invoke) метод объекта UI. Но это требует большей осторожности, чтобы отключение могло быть выполнено упорядоченным образом - вы определенно не хотели бы, чтобы эта загрузка переживала объект пользовательского интерфейса, например.

0 голосов
/ 07 марта 2020

Проблема заключается в том, как вы используете QThread. Рассмотрим оператор ...

connect(this, SIGNAL(signalOpenFile(QString)),
        file_import, SLOT(slotOpenFile(QString)), Qt::QueuedConnection);

Когда для этого соединения будет выдан сигнал signalOpenFile, Qt предпримет действие, чтобы гарантировать, что slotOpenFile вызывается в потоке, связанном с file_import. Он идентифицирует этот поток, вызывая

file_import->thread();

К сожалению, поскольку File_Import наследует от QThread, сродство потока file_import является QThread, на котором он был создан - не вновь созданный поток, которым он управляет (т. е. поток, в котором выполняется переопределенный элемент run.

Исправление состоит в том, чтобы просто File_Import наследовать от QObject и переместить его в отдельный QThread.

class File_Import: public QObject {
    Q_OBJECT;
public:
    File_Import(QObject *parent = nullptr);
public slots:
    void slotOpenFile(QString path_file_name);
};

И давайте предположим, что вы добавили члена в Dispatcher ...

QThread file_import_thread;

Тогда Dispatcher::initApplications станет ...

void Dispatcher::initApplications ()
{
    file_import = new File_Import;
    file_import->moveToThread(&file_import_thread);
    file_import_thread.start();
}

Обратите внимание, что вы действительно должны использовать новый синтаксис сигнала / слота ...

connect(this, &Dispatcher::signalOpenFile,
        file_import, &File_Import::slotOpenFile, Qt::QueuedConnection);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...