Не секрет, что проекты, разработанные, к примеру, на Delphi c использованием компонента TWebBrowser, являются очень распространенным решением, применяемым во множестве случаев. А как быть, если вы все-таки предпочтете аналогичный проект в среде Linux? Как раз для такого случая стоит использовать программную платформу Qt (ru.wikipedia.org/wiki/Qt). У нее есть много достоинств, в том числе богатство средств и солидная история. К примеру, на базе Qt создана популярная графическая система KDE.  Не менее важно, что теперь для написания программ на Qt можно применять современную кросс-платформенную IDE Qt Creator,  что с того времени, когда разработчику были доступны только Qt Designer и KDevelop, порог вхождения в мир программирования на Qt стал значительно ниже. В общем, сейчас и в Linux можно программировать перетаскиванием «кнопочек» на «формочки», не рискуя запутаться между разными программными классами и утилитами.

Что такое WebKit и как его использовать
WebKit – современный распространенный движок (ru.wikipedia.org/wiki/WebKit) для отображения веб-контента. Если покопаться в генеалогическом древе, то выяснится, что этот движок находится в родственных отношениях (прямых и не очень) с браузерами Konqueror, Safari и Chrome.
Когда обсуждается возможность использования WebKit на платформе Qt, имеется в виду QWebKit, но имя соответствующего класса в Qt значится как QWebView. Именно этот класс можно так же, как и в Delphi или в C++Builder, визуально перетащить на форму и реализовать за 5 минут собственный веб-браузер.

Чтобы загрузить веб-страницу, нужно написать:

ui->webView->load(QUrl("http://www.osp.ru”));

Следовательно, для QWebView требуется предоставить адрес в формате класса QUrl. Именно этот класс (а не какой-либо строковый тип) используется для работы с интернет-адресами.

Полное описание класса QWebView имеется на сайте doc.qt.nokia.com, а кроме того, в Qt Creator доступна контекстная справка. Базовые методы этого класса понятны и без справок. К ним относятся:

   ui->webView->back();   //Прошлая страница
   ui->webView->forward(); //Следующая страница
   ui->webView->reload(); //Обновление страницы
   ui->webView->stop();    //Останов загрузки страницы

Собственно, это уже не просто описание методов, а фрагменты кода, и если вы еще не создавали проектов в QtCreator, то сделать это просто:

File -> New File or Project -> Qt C++Project->Qt Gui Application

  По умолчанию в таком проекте уже будут существовать объекты menuBar, mainToolBar и statusBar.


 В новый проект добавлен объект класса QWebView

Визуальный доступ к методам  back, forward, reload и stop традиционно принято предоставлять из панели быстрого вызова (mainToolBar). Конечно, это можно реализовать в Qt-приложении, но следует соблюдать осторожность -- не все в Qt Creator интуитивно понятно. Если пункт Add Tool Bar на дереве объектов легко найти через всплывающее меню, то добавить  сами кнопки будет сложнее.
К примеру, требуется добавить возможность обновления страницы. Для этого сначала необходимо ввести нужное действие (Action) в Action Editor, а потом уже методом Drag-and-Drop связать его с mainToolBar и … о чудо! – в ToolBar появляется новая кнопка.
А теперь по порядку.
Класс QAction описывает какое-либо действие вне жесткой связи с источником и типом события, а также с его процедурой-обработчиком ((http://doc.crossplatform.ru/qt/4.7.x/qaction.html#details).

Такой посредник дает  программисту более гибкий  механизм создания интерактивного графического интерфейса, где, благодаря древнему принципу повторяемости кода, одновременно сохраняются и эффективность, и эстетика. Разработчик описывает заголовок (который потом может стать пунктом меню) и другие атрибуты действия. К примеру, мы можем один раз описать действие обновления страницы. Для этого нужно добавить это действие в Action Editor, привязать к нему кнопку быстрого запуска (Shortcut), задать его имя (Object name) и значок, а потом включить в mainToolBar и в menuBar. Причем дело даже не успевает дойти до фактического описания программного события.


. Редактирование информации о действиях  в Action Editor

Осталось создать необходимый код, соответствующий нажатию кнопки, т.е. описать внутреннюю кухню этого нового действия.
  Следует вернуться в Action Editor и для созданного действия actionReload через всплывающее меню в пункте Go to slot...  найти  подходящий сигнал. После чего нужно привязать к нему необходимый код (Qt Creator любезно  подскажет, куда именно его вписать)  и  вставить одну строку программы:

void MainWindow::on_actionReload_triggered()
{
   ui->webView->reload(); //Обновление страницы
}

 


Выбор нужного сигнала

Аналогично можно поступить с методами back(), forward() и stop().

Адресная строка
А вот чтобы создать в том же ToolBar адресную строку с помощью компонента QLineEdit, визуальное программирование уже не поможет. Нужно открыть файл mainwindows.h и добавить в описание класса MainWindow эту самую адресную строку следующим образом:

private:

QLineEdit *UrlPath.

Чтобы компилятор понял, что такое QLineEdit, вверху файла необходимо добавить:

#include
 

Теперь в конструкторе в файле mainwindow.h требуется создать адресную строку и привязать ее к mainToolBar:

MainWindow::MainWindow(QWidget *parent) :  
                                 QMainWindow(parent),    ui(new Ui::MainWindow)
{

    UrlPath = new QLineEdit(this);
    ui->toolBar->addWidget(UrlPath);

}

Пора подумать, как привязать к созданной адресной строке нужный код. Для начала в файле mainwindow.h следует найти секцию private slots  и декларировать там обработчик события нажатия клавиши в адресной строке:

private slots:
...
void slotGoEnterPressed();

   
а в файле mainwindow.cpp нужно описать соответствующую реализацию:

 

void MainWindow::slotGoEnterPressed(){
    //Этот вариант самый простой, но не самый надежный
      ui->webView->load(QUrl(UrlPath->text()));
}

 

Если же осталось ощущение, что slotGoEnterPressed() и UrlPath на самом деле ничего не связывает, то оно верное, потому что нужно записать магическую строку в конструкторе главного окна в файле mainwindow.h. В этой строке будет описано, что к чему привязывается и на каких условиях:

connect(UrlPath,SIGNAL(returnPressed()),SLOT(slotGoEnterPressed()));

Раскроем ее для непосвященных.
Функция connect предназначена для связи сигнала с обработчиком приемника, указанным в макросе SLOT. Адресная строка UrlPath  посылает стандартный сигнал нажатия клавиши . А то, что обрабатывается именно этот сигнал, указано в макросе SIGNAL.

Ура! В самом первом приближении адресная строка готова.

Отображение степени загрузки страницы
Для отображения степени загрузки нужно выделить в визуальном редакторе объект WebView и через пункт Go to slot… выбрать в открывшемся списке сигналов сигнал loadProgress(int). Оказавшись в редакторе кода, следует заполнить автоматически сгенерированную заготовку обработчика:


void MainWindow::on_webView_loadProgress(int progress)
{
  ui->statusBar->showMessage("Загрузка "+ui->webView->url().toString()+"...",500);
}

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


private:
QProgressBar *PageProgress;
А в конструкторе нужно описать
MainWindow::MainWindow(QWidget *parent) :   QMainWindow(parent),   ui(new Ui::MainWindow) {

    PageProgress = new QProgressBar(this);
    PageProgress->setVisible(false);
    ui->statusBar->addPermanentWidget(PageProgress);

}
 

Таким образом PageProgress будет не только создан, но и размещен внутри statusBar. Предполагается, что PageProgress  будет виден только в периоды загрузки страницы, в чем помогут сигналы loadStarted() и loadFinished(bool).
Теперь обработчик on_webView_loadProgress можно дополнить строкой:
 

PageProgress->setValue(progress);
 

В итоге получился вполне достойный результат. С одной стороны, все очень удобно: информация о реальной загружаемой странице отображается в левой части statusBar, а PageProgress сам собой точно расположился в правой части. А каплей дегтя оказались проблемы с русским языком -- вместо слова «Загрузка» получилась "кракозябра". Решается это просто:

ui->statusBar->showMessage(trUtf8("Загрузка ")+ui->webView->url().toString()+"...",500);

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

 

#include

void MainWindow::buildHistory(){
    ui->menuHistory->clear(); //Очищаем этот пункт главного меню
    QAction *mAct;
    //Перебор набора интернет-адресов
    foreach (QWebHistoryItem  HistoryItem,ui->webView->history()->items()){
        QString current_url = HistoryItem.url().toString();
        QAction *curHistMnu = ui->menuHistory->addAction(HistoryItem.icon(),current_url);
        connect(curHistMnu,SIGNAL(triggered()),SLOT(slotLoadHistPage()));
    }
}

 

Таким образом, в динамическом режиме перестраивается список интернет-адресов, и к каждому пункту вновь привязывается обработчик следующего содержания:


//Декларация обработчика пункта меню истории браузера в файле mainwindow.h
...
private slots:
void slotLoadHistPage();

/* Файл  mainwindow.h
Реализация приемника сигнала пункта меню истории браузера выглядит очень загадочно */
void MainWindow::slotLoadHistPage(){
//Извлекаем из источника сигнала информацию  для загрузки страницы
QAction* a = qobject_cast< QAction* >( sender() );

//Загружаем страницу из истории
ui->webView->load(QUrl(a->text()));
}

Улучшение метода загрузки страницы
Попробуем запустить программу с реализованным вариантом адресной строки, набрав там, к примеру, www.mail.ru . В результате ничего не получится, потому что наш браузер оказался очень привередливым. Он знать ничего не хочет про тип ресурса, который ему хотят передать, если этот тип явно не указывают. Значит, имеет смысл написать некий универсальный метод загрузки, решающий эту проблему, например такой:


// Пробуем написать более универсальный метод для загрузки веб-страниц
void MainWindow::smartLoad(QString value){
   QString validLink = value;
//Заменяем палочки на правильные
   if (validLink.toLower().startsWith("http:\") ||
       validLink.toLower().startsWith("ftp:\")){
       validLink = validLink.replace(":\\","://");
   }
//Добавляем информацию о протоколе, если она совсем отсутствует
   if (!validLink.toLower().startsWith("http") &&
      !validLink.toLower().startsWith("ftp")) {
        validLink = "http://" + validLink;
   }
//Загружаем страницу
    ui->webView->load(validLink);
//Обновляем на всякий случай меню с историей адресов
    buildHistory();
}

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

Загрузка файла или HTML-строки
А если нужно загрузить HTML-страницу с диска? Тогда следует просто указать соответствующий тип данных:

ui->webView->load(QUrl("file:///" + filename));

И конечно, допустимо загрузить строку гипертекста напрямую, так можно поступить при выводе информации о браузере:

QString MainWindow::getAbout(){
return "
"
"
"
"


" +
    trUtf8("Этот браузер создан на базе класса QWebView")+ "

";
void MainWindow::on_actionAbout_triggered()
{
     ui->webView->setHtml(getAbout());
}

Вылавливание инструкции на открытие новых окон
Теперь проведем следующий тест. Загрузим в наш браузер сайт www.mail.ru и попробуем перейти по одной из новостных ссылок, которые находятся примерно в середине экрана. Не выходит?  А если добавить в конструктор главного окна строку:

ui->webView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);


Опять ничего? Действительно, мы просто попросили браузер не обрабатывать самостоятельно ссылки со страницы при щелчке, а вместо этого выдать сигнал, под который мы и сделаем обработчик. В списке сигналов для WebView найдем сигнал linkClicked(QUrl) и напишем для него обработчик:

void MainWindow::on_webView_linkClicked(QUrl url)
{
      ui->webView->load(url);
}

На традиционный вопрос: «А что было?» -- ответ прост: мы научили наш однооконный браузер загружать страницу, которую обычный браузер загружал бы в новое окно.

Три волшебные строки
В проекте нужно обязательно упомянуть про три строки, которые, скорее всего, при создании браузера придется добавить в функцию main (main.cpp) :

 


 
  /* Разрешение на автоматическую загрузку картинок в странице*/
    QWebSettings::globalSettings()->setAttribute(QWebSettings::AutoLoadImages,true);
    /* Разрешение на запуск java-скриптов*/
    QWebSettings::globalSettings()->setAttribute(QWebSettings::JavascriptEnabled, true);
    /* Разрешение  на использование плагинов*/
    QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled, true);

В руководстве написано, что первые две опции активны по умолчанию, но в Рунете много раз встречаются все три  разрешения вместе,  особенно когда речь идет об активизации Flash.


 Новый браузер – почти за 5 минут

Браузер готов?
В заключение стоит отметить, что на форумах самыми проблемными темами, связанными с QWebView, являются вопросы взаимодействия с Flash и Java-скриптами. К примеру, если у вас наконец заработал в браузере Flash, то, возможно, радоваться еще рано, погоняйте браузер с разными сайтами -- велика вероятность новых сюрпризов…  Однако в тех случаях, когда речь идет не об обычном публичном браузере, а о проекте с заранее известными характеристиками отображаемого контента (например, платежный терминал), все становится проще. Но такой вариант уже будет темой отдельной статьи.

Литература
Макс Шлее Qt4.5. Профессиональное программирование на C++. Издательство: БХВ-Петербург, 2010.
Russian Qt Forum www.prog.org.ru
SQL.RU. Раздел, посвященный C++ www.sql.ru/forum/actualtopics.aspx?bid=21
Описание класса QWebView qt.nokia.com/4.7-snapshot/qwebview.html
Хороший англоязычный форум www.qtforum.org
  Подраздел Trolltech Qt  www.progz.ru/f70/
 

Купить номер с этой статьей в PDF
18389