QEventLoop останавливает загрузку страницы

235
16 декабря 2017, 06:55

Пишу приложение для просмотра расписания автобусов в городе. Расписание беру с сайта: 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 одну страницу загружает нормально.

Answer 1

Созданный вами 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() итераторы на обрабатываемый и последний элементы списка, но потом увидел, что ещё при первом асинхронном вызове главный метод уже успеет завершить свою работу, и список будет уничтожен. Пришлось заменить передачу итераторов на передачу самого списка (точнее, владения им) и номера обрабатываемого элемента (список может быть в любой момент перемещён в памяти, что сделает итератор некорректным).

Теперь о том, что здесь происходит.

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

  2. Затем мы добавляем Q_OBJECT, чтобы заставить Qt извлечь и явно сохранить информацию об этом методе (его имя и тип его аргументов).

  3. Когда мы вызываем вспомогательный метод для обработки первого URL, он в конце своей работы с помощью QMetaObject::invokeMethod(..., Qt::QueuedConnection, ...) помещает в очередь сообщений просьбу вызвать самого себя с передачей следующего URL. Так как downloader_.execute() без очереди сообщений виснет, значит он тоже кладёт в неё какие-то сообщения, и наша просьба окажется аккурат после них.

READ ALSO
Проблема с поиском элемента в списке

Проблема с поиском элемента в списке

Существует шаблон списка:

225
использовать функции из linux .so

использовать функции из linux .so

Линкуюso библиотеку динамически

295
Создание потокового видео x264 на C++

Создание потокового видео x264 на C++

На данный момент система трансляций с камеры и экрана на удалённый сервер передаёт jpeg кадры, то есть используется покадровое сжатиеДля экономии...

267
Явные ожидания Selenium

Явные ожидания Selenium

Недавно начал интересоваться автоматизацией в тестировании и в качестве объекта для изучения выбрал рандомный сайт (ссылка будет ниже)

251