2012-05-03 4 views
2

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

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

Я думал о запуске вызова setContent в другом потоке, чтобы он не блокировал обработку события, и я могу отменить его, если это необходимо. Тем не менее, я получаю страшный "widgets must be created in the GUI thread", и я не вижу возможности легко разрешить это.

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

EDIT

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

EDIT 2

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

+0

Существует [аналогичная тема] (http://stackoverflow.com/questions/3931909/force-qwebview-to-download-web-page-content-in-a-separate-thread) – dschulz

+0

Не совсем. Я прочитал много вопросов здесь о SO, но никто не решает мою проблему, чтобы отменить загрузку 'setContent', в результате чего mu GUI перестает отвечать на запросы. Хотя QWebView выполняет загрузку в фоновом режиме, это только для вторичных ресурсов, таких как изображения, таблицы стилей, а не для основного контента. Основное текстовое содержимое меня больше всего волнует. –

ответ

1

Поскольку 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(), GUI имеет возможность реагировать на события.

+0

... и если ваша веб-страница не имеет JavaScript, изменяющего свой собственный DOM ... –

+1

Да, я забыл добавить, что контент является статическим, и у меня отключен JavaScript. –

+0

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

2

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

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

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

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

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

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

0

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

Теперь связать вместе:

void QWebView::setPage (QWebPage * page) 
+1

'QWebPage' использует как минимум' QPixmap', который нельзя использовать вне основного потока (см. [Эту страницу] (http://qt-project.org/doc/qt-4.8/threads-modules.html#painting -in-threads)). – alexisdm

+0

К сожалению, это не работает. Я попытался использовать QWebFrame в потоке, а не QWebView. Я получил то же утверждение: «Виджеты должны быть созданы в потоке графического интерфейса. Посмотрев на источник QWebView, все, что он делает, отправляет параметры прямо в основной фрейм:' void QWebView :: setHtml (const QString & html, const QUrl & baseUrl) {page() -> mainFrame() -> setHtml (html, baseUrl);} ' –