用Qt开发简单的浏览器(二)


一、功能改进

经过对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 onload="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);


五、最终效果

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







posted on 2012-05-24 22:47  毛小娃  阅读(443)  评论(0编辑  收藏  举报

导航