用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);
// 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_Hhtmlview.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