Пишу приложение для просмотра расписания автобусов в городе. Расписание беру с сайта: downloader_.execute() загружает страницу и запускает на нём мой js код, возвращённые данные обрабатываются и выполняется emit finishedBusProcessing():
void DataManager::processBus(const QVariant& jsResult) {
newBus = new Bus;
newBus->setNumber(jsResult.toMap()["number"].toString());
newBus->setDirections(processDirections(jsResult.toMap()["directions"]));
schedule_->pushBusBack(*newBus);
emit finishedBusProcessing();
}
Но мне необходимо подождать обработку предыдущей страницы перед запуском обработки следующей. Нашёл, что нужно использовать QEventLoop:
QEventLoop loop;
connect(this, SIGNAL(finishedBusProcessing()), &loop, SLOT(quit()));
for (size_t i = 0; i < jsResult.toList().size(); i++) {
downloader_.setUrl(QUrl(SCHEDULE_SOURCE + jsResult.toList().at(0).toString()));
downloader_.execute();
loop.exec();
}
Однако загрузка страницы просто зависает на 10%. До обработки результата дело не доходит. Без QEventLoop одну страницу загружает нормально.
Созданный вами QEventLoop loop не имеет никакого отношения к системной очереди сообщений. Вместо этого вызывайте QCoreApplication::processEvents():
connect(this, SIGNAL(finishedBusProcessing()), &loop, SLOT(quit()));
for (size_t i = 0; i < jsResult.toList().size(); i++) {
downloader_.setUrl(QUrl(SCHEDULE_SOURCE + jsResult.toList().at(0).toString()));
downloader_.execute();
QCoreApplication::processEvents();
}
Но лучше вообще заменить цикл на цепочечный отложенный самовызов вспомогательной функции, выполняющей одну итерацию цикла за проход:
class DataManager : public <либо QObject, либо его потомок>
{
Q_OBJECT
Q_INVOKABLE void processingHelper(QList<QVariant> list, int itemId);
// ...
};
void DataManager::processingHelper(QList<QVariant> list, int itemId)
{
if(itemId < list.size())
{
downloader_.setUrl(QUrl(SCHEDULE_SOURCE + list.at(itemId ).at(0).toString()));
downloader_.execute();
QMetaObject::invokeMethod(
this,
"processingHelper",
Qt::QueuedConnection,
Q_ARG(QList<QVariant>, list),
Q_ARG(int, itemId + 1)
);
}
}
// От вашего кода в основном методе остаётся только это:
connect(this, SIGNAL(finishedBusProcessing()), &loop, SLOT(quit()));
processingHelper(jsResult.toList(), 0);
P. S.: Сначала хотел передавать в DataManager::processingHelper() итераторы на обрабатываемый и последний элементы списка, но потом увидел, что ещё при первом асинхронном вызове главный метод уже успеет завершить свою работу, и список будет уничтожен. Пришлось заменить передачу итераторов на передачу самого списка (точнее, владения им) и номера обрабатываемого элемента (список может быть в любой момент перемещён в памяти, что сделает итератор некорректным).
Теперь о том, что здесь происходит.
Сначала мы помечаем вспомогательный метод как Q_INVOKABLE, чтобы его можно было вызывать отложенно через очередь сообщений.
Затем мы добавляем Q_OBJECT, чтобы заставить Qt извлечь и явно сохранить информацию об этом методе (его имя и тип его аргументов).
Когда мы вызываем вспомогательный метод для обработки первого URL, он в конце своей работы с помощью QMetaObject::invokeMethod(..., Qt::QueuedConnection, ...) помещает в очередь сообщений просьбу вызвать самого себя с передачей следующего URL. Так как downloader_.execute() без очереди сообщений виснет, значит он тоже кладёт в неё какие-то сообщения, и наша просьба окажется аккурат после них.
Современные инструменты для криптотрейдинга: как технологии помогают принимать решения
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости