一、功能改进
经过对QtWebKit的一些学习,对之前的浏览器进行一些改进:
1.增加分页显示多个网页的功能。每个分页都是一个QWebView控件,实现对多个网页的加载。
2.加入欢迎主页。学习如何创建Qt资源文件,从本地读取欢迎主页的HTML。
3.添加了前进Forward和后退Back按钮。
4.此外还明确了SIGNAL和SLOT的命名,SLOT都以handleXXX开头与SIGNAL区分开。
二、Qt资源文件
Qt可以很方便的通过资源文件来管理各种资源,就像在VS中创建资源文件一样。
项目结构如下:

在Qt Creator中创建四个资源文件html.qrc,script.qrc,style.qrc,resource.qrc分别对应html,
script,style和resource文件夹,用来存储网页、JS脚本、CSS样式文件和各种图片音频视频资源。

以html.qrc为例,添加html文件夹中的welcome.html,那么在程序中我们就可以用路径
qrc:/html/welcome.html来引用它。这里的欢迎页面引用了之前Chrome天气预报插件的
代码。
welcome.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB2312"/>
<link rel="stylesheet" type="text/css" href="qrc:/style/style.css"/>
<script type="text/javascript" src="qrc:/script/jquery-1.7.2.min.js"></script>
<script type="text/javascript">
function init() {
$("#loadingdiv").html("正在加载城市天气预报...");
$.getJSON("http://m.weather.com.cn/data/101010100.html",
function(data) {
$("#loadingdiv").html("");
var weatherinfo = data["weatherinfo"];
var datearray = ["", weatherinfo["date_y"], "第二天", "第三天", "第四天", "第五天", "第六天"];
$("#cityname").html(weatherinfo["city"] + "城市天气预报");
for (i = 1; i <= 6; i++) {
var divid = "#div" + i;
$(divid).append(datearray[i]).append("<br>");
var imgurl = "http://m.weather.com.cn/weather_img/" + weatherinfo["img"+(i*2-1)] + ".gif";
$(divid).append('<img src="' + imgurl + '"/>').append("<br>");
$(divid).append(weatherinfo["temp" + i]).append("<br>");
$(divid).append(weatherinfo["weather" + i]);
}
}
);
}
</script>
</head>
<body οnlοad="init()">
<h1>Welcome!!!</h1>
<div id="weatherreportdiv">
<div id="loadingdiv"></div>
<div id="cityname"></div>
<hr></hr>
<div id="div1" class="weatherdiv"></div>
<div id="div2" class="weatherdiv"></div>
<div id="div3" class="weatherdiv"></div>
<div id="div4" class="weatherdiv"></div>
<div id="div5" class="weatherdiv"></div>
<div id="div6" class="weatherdiv"></div>
</div>
</body>
</html>
style.css
h1 {
text-align: center;
}
#weatherreportdiv {
height: 300px;
width: 700px;
text-align: center;
font-size: 20px;
font-weight: bold;
margin: 5px;
}
#cityname {
text-align: center;
font-size: 20px;
font-weight: bold;
margin: 5px;
}
.weatherdiv {
float: left;
width: 15%;
margin: 5px;
}
管理资源就是这么简单,下面来看改进后的代码。
三、源码实现
在AddressBar中新加一个SLOT函数handleAddressChanged,处理当分页切换时URL的变化。
addressbar.h
#ifndef ADDRESSBAR_H
#define ADDRESSBAR_H
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QHBoxLayout>
#include <QUrl>
#include <QString>
class AddressBar : public QWidget
{
Q_OBJECT
public:
explicit AddressBar(QWidget *parent = 0);
signals:
void go(const QUrl&);
void back();
void forward();
void newPage();
public slots:
void handleGoToSite();
void handleAddressChanged(const QUrl&);
private:
QLineEdit *addressEdit;
QPushButton *newButton;
QPushButton *backButton;
QPushButton *forwardButton;
QPushButton *goButton;
QHBoxLayout *layout;
};
#endif // ADDRESSBAR_H
addressbar.cpp
#include "addressbar.h"
AddressBar::AddressBar(QWidget *parent) :
QWidget(parent)
{
// 1.Create widget
addressEdit = new QLineEdit;
newButton = new QPushButton("New");
backButton = new QPushButton("Back");
forwardButton = new QPushButton("Forward");
goButton = new QPushButton("Go");
// 2.Set property
// 3.Connect signal and slot
connect(goButton, SIGNAL(clicked()), this, SLOT(handleGoToSite()));
connect(addressEdit, SIGNAL(returnPressed()), this, SLOT(handleGoToSite()));
connect(backButton, SIGNAL(clicked()), this, SIGNAL(back()));
connect(forwardButton, SIGNAL(clicked()), this, SIGNAL(forward()));
connect(newButton, SIGNAL(clicked()), this, SIGNAL(newPage()));
// 4.Add to layout
layout = new QHBoxLayout;
layout->addWidget(newButton);
layout->addWidget(backButton);
layout->addWidget(forwardButton);
layout->addWidget(addressEdit);
layout->addWidget(goButton);
this->setLayout(layout);
}
void AddressBar::handleGoToSite()
{
QString address = addressEdit->text();
emit go(QUrl(address));
}
void AddressBar::handleAddressChanged(const QUrl &url)
{
addressEdit->setText(url.toString());
}
新建了TabPage继承了QTabWidget,管理所有分页。
tabpage.h
#ifndef TABPAGE_H
#define TABPAGE_H
#include <QTabWidget>
#include <QList>
#include "htmlview.h"
class TabPage : public QTabWidget
{
Q_OBJECT
public:
explicit TabPage(QWidget *parent = 0);
~TabPage();
signals:
void urlChanged(const QUrl&);
public slots:
void handleNewTab();
void handleLoadNewPage(const QUrl&);
void handleBack();
void handleForward();
private slots:
void handleTabChanged(int);
void handleTabClosed(int);
void handleLinkClicked(const QUrl&);
private:
QList<QWebView*> viewList;
};
#endif // TABPAGE_H
tabpage.cpp
#include "tabpage.h"
#include <QtDebug>
TabPage::TabPage(QWidget *parent) :
QTabWidget(parent)
{
// Set property
this->setTabsClosable(true);
// Connect built-in signal to customized one to convert index to url
connect(this, SIGNAL(currentChanged(int)), SLOT(handleTabChanged(int)));
connect(this, SIGNAL(tabCloseRequested(int)), this, SLOT(handleTabClosed(int)));
// Create new tab for home page
this->handleNewTab();
}
TabPage::~TabPage()
{
}
void TabPage::handleNewTab()
{
HtmlView *view = new HtmlView;
view->load(QUrl("qrc:/html/welcome.html"));
//view->page()->setForwardUnsupportedContent(true);
view->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
// Monitor linkClicked signal
connect(view, SIGNAL(linkClicked(const QUrl&)), this, SLOT(handleLinkClicked(const QUrl&)));
// Add and activate this new tab
int index = this->addTab(view, "Welcome");
this->setCurrentIndex(index);
viewList.append(view);
}
void TabPage::handleLoadNewPage(const QUrl &url)
{
qDebug() << "loadNewPage: " << url.toString();
HtmlView *view = (HtmlView*) this->currentWidget();
view->load(url);
}
void TabPage::handleBack()
{
HtmlView *view = (HtmlView*) this->currentWidget();
view->back();
emit urlChanged(view->url());
}
void TabPage::handleForward()
{
HtmlView *view = (HtmlView*) this->currentWidget();
view->forward();
emit urlChanged(view->url());
}
void TabPage::handleTabChanged(int index)
{
if (index > viewList.length() - 1)
return;
// index is the new tab index
QWebView* view = viewList[index];
emit urlChanged(view->url());
}
void TabPage::handleTabClosed(int index)
{
if (index > viewList.length() - 1)
return;
this->removeTab(index);
QWebView* view = viewList[index];
viewList.removeAt(index);
delete view;
}
void TabPage::handleLinkClicked(const QUrl& url)
{
qDebug() << "handleLinkClicked: " << url.toString();
HtmlView *view = (HtmlView*) this->currentWidget();
view->load(url);
emit urlChanged(url);
}
接下来是MainWindow,改动很小。
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
#include <QtWebKit>
#include <QMainWindow>
#include "addressbar.h"
#include "htmlview.h"
#include "tabpage.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
//ui->setupUi(this);
// 0.Global setting
QWebSettings* defaultSettings = QWebSettings::globalSettings();
// We use JavaScript, so set it to be enabled.
defaultSettings->setAttribute(QWebSettings::JavascriptEnabled, true);
// Plug-ins must be set to be enabled to use plug-ins.
defaultSettings->setAttribute(QWebSettings::PluginsEnabled,true);
defaultSettings->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls,true);
defaultSettings->setObjectCacheCapacities(0, 0, 0);
// 1.Create widget
QWidget *centralWidget = new QWidget(this);
AddressBar *bar = new AddressBar;
TabPage *tab = new TabPage;
// 2.Set property
this->setCentralWidget(centralWidget);
this->setWindowTitle("My Browser v1.0");
this->resize(800, 600);
// 3.Connect widget
QObject::connect(bar, SIGNAL(newPage()),tab, SLOT(handleNewTab()));
QObject::connect(bar, SIGNAL(back()),tab, SLOT(handleBack()));
QObject::connect(bar, SIGNAL(forward()),tab, SLOT(handleForward()));
QObject::connect(bar, SIGNAL(go(QUrl)), tab, SLOT(handleLoadNewPage(QUrl)));
QObject::connect(tab, SIGNAL(urlChanged(QUrl)), bar, SLOT(handleAddressChanged(QUrl)));
// 4.Add widget to layout
QGridLayout *layout = new QGridLayout;
layout->addWidget(bar, 0, 0, 1, 10);
layout->addWidget(tab, 1, 0, 1, 10);
centralWidget->setLayout(layout);
}
MainWindow::~MainWindow()
{
//delete ui;
}
四、关键问题
1.需要为QWebView中的QWebPage设置才能使页面内链接可以正常跳转。
view->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
2.需要为AJAX调用设置:
QWebSettings* defaultSettings = QWebSettings::globalSettings();
// We use JavaScript, so set it to be enabled.
defaultSettings->setAttribute(QWebSettings::JavascriptEnabled, true);
// Plug-ins must be set to be enabled to use plug-ins.
defaultSettings->setAttribute(QWebSettings::PluginsEnabled,true);
defaultSettings->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls,true);
defaultSettings->setObjectCacheCapacities(0, 0, 0);
// We use JavaScript, so set it to be enabled.
defaultSettings->setAttribute(QWebSettings::JavascriptEnabled, true);
// Plug-ins must be set to be enabled to use plug-ins.
defaultSettings->setAttribute(QWebSettings::PluginsEnabled,true);
defaultSettings->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls,true);
defaultSettings->setObjectCacheCapacities(0, 0, 0);
五、最终效果
1.程序开始运行,进入欢迎页面。

2.多页面浏览

2012年7月4日 补充
htmlview.h
#ifndef HTMLVIEW_H
#define HTMLVIEW_H
#include <QWebView>
class HtmlView : public QWebView
{
Q_OBJECT
public:
explicit HtmlView(QWidget *parent = 0);
signals:
public slots:
void loadNewPage(const QUrl &url);
};
#endif // HTMLVIEW_H
htmlview.cpp
#include "htmlview.h"
HtmlView::HtmlView(QWidget *parent) :
QWebView(parent)
{
}
void HtmlView::loadNewPage(const QUrl &url)
{
this->load(url);
}
2014年8月13日
感觉真的很神奇,本来主攻是Java,没想到业余时间写的一个Qt小项目竟成了我访问最高的文章,真是无心插柳柳成荫,谢谢大家的捧场!
2015年8月16日
需要源码的同学还真不少啊,现在源码已经共享到GitHub上了,请自行获取吧!地址是:https://github.com/cdai/qtbrowser