К счастью, эта сложность не нужна.
Все, что вам нужно - это 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. Но это требует большей осторожности, чтобы отключение могло быть выполнено упорядоченным образом - вы определенно не хотели бы, чтобы эта загрузка переживала объект пользовательского интерфейса, например.