您的位置:首页 > 编程语言 > Qt开发

用QTextBrowser实现的Web浏览器

2016-04-19 23:04 525 查看
qt5.6上的webEngine暂时无法用gcc编译,也没法用在android上,用了很多办法都没法用jni来调用android java的webview,无奈之下用QTextBrowser实现一个最简单的仅支持HTML4子集的Web浏览器,QTextBrowser是没有网络读取功能的,我添加一个网络下载函数,用QtConcurrent异步加载,再用正则表达式解析img标签,然后signal把QTextDocument呈现出来。

这是原型代码,写得比较草率,有兴趣的可以扩展一下QTextBrowser/QTextDocument的源代码得到更多的HTML/CSS渲染效果

#ifndef QHTMLBROWSER_H
#define QHTMLBROWSER_H

//** for more information, please refer to http://www.one-lab.net 
#include <QtConcurrent>
#include <QtNetwork/QtNetwork>
#include <QTextBrowser>
#include <QTextDocument>
#include <QMap>
#include <QRegExp>
#include <QCryptographicHash>
#include <QApplication>

class QHtmlBrowser : public QObject
{
Q_OBJECT

private:
QTextBrowser* mBrowser;
QString mUrl;
QMap<QString, QByteArray> mResources;

signals:
void loadFinished();

public slots:
void onLoadFinished();
void onRequestUrl(const QUrl& url);

public:
~QHtmlBrowser();
explicit QHtmlBrowser(QTextBrowser* browser);
void load(const QString& url);
void addResource(const QString& key, const QByteArray& bytes);
};

#endif // QHTMLBROWSER_H


#include "qhtmlbrowser.h"
//** for more information, please refer to http://www.one-lab.net 
#define hit_ext(p) \
else if (src.endsWith(#p, Qt::CaseInsensitive)) \
ext = #p
#define index_tag   "index"
#define images_tag   "images://"

QString setUrl(QUrl& parentUrl, const QString& path)
{
QString srcUrl;
QString hostPath = parentUrl.host();
if (parentUrl.port() != 80)
hostPath += ":" + QString::number(parentUrl.port());
hostPath += parentUrl.path();
int pos = hostPath.lastIndexOf('/');
if (pos > -1)
hostPath = hostPath.left(pos);
srcUrl = hostPath + "/" + path;
while (srcUrl.contains("//"))
srcUrl.replace("//", "/");
srcUrl = "http://" + srcUrl;
return srcUrl;
}

void downloadHtml(QHtmlBrowser* browser, const QString& urlPath, int timeoutSeconds)
{
QNetworkAccessManager network;
QNetworkRequest request;
QUrl url(urlPath);
request.setUrl(url);
QByteArray requestContent = urlPath.toUtf8();
request.setHeader(QNetworkRequest::ContentLengthHeader, requestContent.length());
bool timeout = false;
if (QNetworkReply* reply = network.get(request))
{
QDateTime stamp = QDateTime::currentDateTime();
while (reply->isRunning())
{
QApplication::processEvents();
if (stamp.secsTo(QDateTime::currentDateTime()) > timeoutSeconds)
{
reply->abort();
timeout = true;
break;
}
}
if (!timeout)
{
QString content = QString::fromUtf8(reply->readAll());
QRegExp imgTagRegex("\\<img[^\\>]*src\\s*=\\s*\"([^\"]*)\"[^\\>]*\\>", Qt::CaseInsensitive);
imgTagRegex.setMinimal(true);
QStringList imgSrcList;
QStringList imgTagList;
int offset = 0;
while( (offset = imgTagRegex.indexIn(content, offset)) != -1){
offset += imgTagRegex.matchedLength();
imgTagList.append(imgTagRegex.cap(0));
imgSrcList.append(imgTagRegex.cap(1));
}
QString srcUrl;
QString resourcePrefix(images_tag);
QString ext;
int index = -1;
foreach(QString src, imgSrcList)
{
index++;
srcUrl.clear();
if (src.startsWith("http://", Qt::CaseInsensitive))
srcUrl = src;
else
{
if (0){}
hit_ext(.png);
hit_ext(.jpeg);
hit_ext(.jpg);
hit_ext(.gif);
else continue;
if (src.startsWith("://") || src.startsWith("file://", Qt::CaseInsensitive)) continue;
srcUrl = setUrl(url, src);
request.setUrl(srcUrl);
requestContent = srcUrl.toUtf8();
request.setHeader(QNetworkRequest::ContentLengthHeader, requestContent.length());
QCryptographicHash hash(QCryptographicHash::Md5);
if (QNetworkReply* itemReply = network.get(request))
{
timeout = false;
stamp = QDateTime::currentDateTime();
while (itemReply->isRunning())
{
QApplication::processEvents();
if (stamp.secsTo(QDateTime::currentDateTime()) > timeoutSeconds)
{
itemReply->abort();
timeout = true;
break;
}
}
if (timeout)
{
delete itemReply;
continue;
}
QString imgFile = resourcePrefix + "/";
hash.addData(srcUrl.toUtf8());
foreach(char c, hash.result())
{
int cInt = (int)c;
if (cInt < 10)
imgFile += "0" + QString::number(cInt, 16).left(1);
else
imgFile += QString::number(cInt, 16).left(2);
}
imgFile += ext;
QString target = imgTagList[index];
target.replace(src, imgFile);
browser->addResource(imgFile, itemReply->readAll());
content.replace(imgTagList[index], target);
delete itemReply;
}
}
}
browser->addResource(index_tag, content.toUtf8());
}
delete reply;
emit browser->loadFinished();
}
}

void QHtmlBrowser::onLoadFinished()
{
QTextDocument* document = new QTextDocument(mBrowser);
if (mResources.contains(index_tag))
{
foreach(QString key, mResources.keys())
{
if (key.startsWith(images_tag))
document->addResource(QTextDocument::ImageResource, key, QVariant(mResources[key]));
}
document->setHtml(QString::fromUtf8(mResources[index_tag]));
mBrowser->setDocument(document);
}
}

QHtmlBrowser::~QHtmlBrowser()
{
}

QHtmlBrowser::QHtmlBrowser(QTextBrowser* browser) : QObject(browser)
{
mBrowser = browser;
mBrowser->setReadOnly(true);
mBrowser->setContextMenuPolicy(Qt::NoContextMenu);
mBrowser->setAcceptRichText(true);
mBrowser->setAutoFormatting(QTextEdit::AutoAll);
connect(this, SIGNAL(loadFinished()), SLOT(onLoadFinished()));
connect(mBrowser, SIGNAL(anchorClicked(QUrl)), SLOT(onRequestUrl(QUrl)));
}

void QHtmlBrowser::onRequestUrl(const QUrl& url)
{
QUrl currentUrl(mUrl);
QString requestingUrl = setUrl(currentUrl, url.toString());
load(requestingUrl);
}

void QHtmlBrowser::load(const QString &url)
{
mUrl = url;
QtConcurrent::run(downloadHtml, this, url, 30);
}

void QHtmlBrowser::addResource(const QString &key, const QByteArray &bytes)
{
if (mResources.contains(key))
mResources[key] = bytes;
else
mResources.insert(key, bytes);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  qt web browser