QWebView setContent в отдельном потоке

У меня есть приложение, которое требует использования QWebView::setContent() для загрузки HTML содержимое в QWebView. Все это происходит на встроенном устройстве с процессором ARMv5 (думаю, 400 МГц). В большинстве случаев я могу загрузить страницу за разумное время (до 5 секунд), однако иногда у меня есть контент, который загружается долго (~ 30 секунд для 300 КБ контента).

Проблема в том, что вызов setContent блокирует основной поток. Мне нужно иметь возможность обрабатывать события во время загрузки и, возможно, даже отменить загрузку, если пользователь решит больше не ждать.

Я думал о том, чтобы запустить вызов setContent в другом потоке, чтобы он не блокировал обработку события, и я мог отменить его, если это необходимо. Однако я получаю ужасные "виджеты должны быть созданы в потоке GUI ", и я не вижу способа легко решить эту проблему.

Можно ли запустить QWebView::setContent в отдельном потоке? Если да, то как? Если нет, можно ли обрабатывать события графического интерфейса во время работы setContent? Можно ли «отменить» вызов setContent?

ИЗМЕНИТЬ

Чтобы уточнить, что меня действительно интересует, так это то, как остановить вызов setContent и/или обработать сообщения графического интерфейса, чтобы интерфейс оставался отзывчивым при больших объемах данных, передаваемых с использованием setContent.

ИЗМЕНИТЬ 2

Чтобы еще больше уточнить, я имею дело с длинным статическим контентом, то есть без JavaScript, просто с большим количеством статического HTML, который пользователь хочет прокручивать, даже когда он загружает больше контента. Основная идея состоит в том, чтобы позволить ей/ему переходить на страницу вниз, даже если страница загружена не полностью.


person Michal Kottman    schedule 03.05.2012    source источник
comment
Существует похожий поток   -  person dschulz    schedule 06.05.2012
comment
Не совсем. Я прочитал много вопросов здесь, на SO, но ни один из них не решает мою проблему с возможностью отмены загрузки setContent, из-за чего графический интерфейс mu перестает отвечать на запросы. Хотя QWebView выполняет загрузку в фоновом режиме, это касается только второстепенных ресурсов, таких как изображения, таблицы стилей, а не основного содержимого. Больше всего меня беспокоит основное текстовое наполнение.   -  person Michal Kottman    schedule 06.05.2012


Ответы (3)


Некоторое время назад столкнулся с похожей проблемой. Насколько я знаю, синхронно работает только основное содержимое страницы.

Дело в том, что ядро ​​GUI "рисует" страницу и на это уходит много времени. Таким образом, основной поток замораживается до тех пор, пока основное содержимое не будет загружено полностью.

В моем случае решение было простым: сделать основное содержимое второстепенным и работать с локальными файлами!!!

Итак, какое мое предложение:

1) Подготовьте локальный файл (/tmp/loader.html), содержащий что-то вроде этого:

<html>
<body onload='setTimeout(function() { window.location="contents.html"; }, 1000);'>
Loading...
</body>
</html>

2) Каждый раз, когда вам нужно загрузить новый контент, сохраняйте его во вторичный файл (/tmp/contents.html) и принудительно обновляйте загрузчик (возможно, также обновите). Легкий:

QFile f("/tmp/contents.html");
if (f.open(QFile::WriteOnly)) {
    qint64 pos = 0;
    while (pos < contents.length()) {
        pos += f.write(contents.mid(pos, 1024)); // chunk of 1024
        qApp->processEvents();
    }
    f.close();
    webview->setUrl(QUrl::fromLocalFile("/tmp/loader.html"));
}

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

3) В любое время, когда вам нужно отменить загрузку, вы можете загрузить другое содержимое, удалить файл содержимого или использовать другие возможные подходы.

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

person felixgaal    schedule 09.05.2012

Поскольку QWebView::setContent() является блокирующим вызовом, я решил использовать обходной путь. Основная идея заключается в том, что обработка XML происходит намного быстрее, чем отрисовка страницы. Поэтому я делаю следующее:

  1. Разберите документ как документ XML DOM (разумное предположение в моем случае) и найдите элемент body.
  2. Сохраняйте только заранее определенное количество дочерних элементов body (примерно 20 элементов). Сохраните остальные элементы в другом документе XML DOM.
  3. Покажите исходный документ (сериализованный XML), используя QWebView::setContent(), что относительно быстро. Запустите таймер с тайм-аутом 0 на SLOT(loadNextChunk()).
  4. loadNextChunk() перемещает еще 20 или около того элементов из документа резервной копии в конец тела, используя body->appendInside(html), где body — это QWebElement.
  5. Остановитесь, когда больше не будет доступных элементов.

Это работает, потому что между вызовами loadNextChunk() графический интерфейс имеет возможность реагировать на события.

person Michal Kottman    schedule 12.05.2012
comment
Да, забыл добавить, что контент статичен, и у меня отключен JavaScript. - person Michal Kottman; 12.05.2012
comment
В таком случае да, это очень интересный подход. Но, строго говоря, вы не полностью отвечаете на вопрос, так как это неприменимо в общей ситуации. Я рекомендую вам отредактировать свой вопрос и добавить тот факт, что содержимое статично, чтобы каждый мог четко знать, в какой ситуации применим ваш ответ. - person felixgaal; 13.05.2012

QWebView, как следует из названия, представляет собой виджет. QWebPage, с другой стороны, представляет собой старый добрый QObject со всеми многопоточность, которую вы могли бы захотеть.

Теперь свяжите вместе:

void QWebView::setPage ( QWebPage * page )
person György Andrasek    schedule 10.05.2012
comment
QWebPage использует как минимум QPixmap, которые нельзя использовать вне основного потока (см. этой страницы ). - person alexisdm; 10.05.2012
comment
К сожалению, это не работает. Я пытался использовать QWebFrame в потоке, а не QWebView. Я получил такое же утверждение. Виджеты должны быть созданы в потоке графического интерфейса. Глядя на исходный код QWebView, все, что он делает, это отправляет параметры прямо в основной фрейм: void QWebView::setHtml(const QString &html, const QUrl &baseUrl) { page()->mainFrame()->setHtml(html, baseUrl); } - person Michal Kottman; 10.05.2012