之前被人错下了需求调研,竟然让我调研怎么用qt实现浏览器,想想也是醉了,但是没办法,不怕官就怕管啊,最后只好乖乖的调研了一番,还写了一个调研文档交付给了领导,哈哈,其实哪个公司会闲着没事自己做个浏览器呢?也没多大用处,可是万一有用呢,对吧!下面附上调研内容,欢迎拍砖!!
一、整体介绍
自定义浏览器的网页浏览功能实现主要使用QT自带的控件Qwebview,根据官方帮助文档其中Qwebview构成图如下图所示:
其中Qwebview用于查看和编辑网页,QWebiew打开的具体网页就是一个QWebPaged对象,一个QWebpage可以存在多个QWebFrame框架,目前自定义浏览器只使用了主框架即Qwebpage::mainFrame()。
QwebView可以再Qt界面设计器中手动拖入,也可以通过程序创建,自定义浏览器定义了一个继承QWebview类新类(CUserWebView),以便于实现其他高级功能。将CUserWebView添加到主界面中后,就可以实现浏览器的常用功能,自定义浏览器主要实现了网址输入、网页浏览、刷新、回退、前进、查看历史记录和查找等常用功能。各个功能实现方式如下:
- 网址输入功能
创建网址输入编辑框(QLineEdit),并将网址输入编辑框添加到主界面的工具栏中,并将在编辑框中点击Enter键的信号与自定义槽函数OnChangeLocation()关联,自定义槽函数中调用QWebview类的load(strUrl)函数,完成网页的跳转显示。
- 后退功能
在主界面工具栏中添加QWebPage::Back的Action函数,使用QwebPage的后退函数作为主界面的后退功能。
- 前进功能:
在主界面工具栏中添加QWebPage:: Forward的Action函数,使用QwebPage的数前进函数作为主界面的前进功能。
- 刷新功能
在主界面工具栏中添加QWebPage:: Reload的Action函数,使用QwebPage的刷新函数作为主界面的刷新功能。
- 停止刷新功能
在主界面工具栏中添加QWebPage:: Stop的Action函数,使用QwebPage的停止函数作为主界面的停止功能。
- 历史记录
历史记录功能的实现主要用到了QWebHsitory类,该类主要是用来存放QWebPage的访问历史记录,并且提供对于导航到相关页面的支持,自定义浏览器的历史记录功能实现流程如下:
说明:
- 在主界面工具栏中添加“历史记录”响应,点击时自动调用对应槽函数。
- 在槽函数中,通过QWebHistory的items函数获得所有历史记录对象(QList<QWebHistoryItem>)。
- 根据获取的所有历史记录,获取对应item的标题,并显示在QListWidget控件中。
- 关联QlistWidget的Click信号,点击列表时自动调用对应槽函数。
- 通过QWebHistory中的ItemAt(int i)获得点击Item, 并通过QWebhistoryItem中的url()函数获得对应的网址。
- 根据获得网址,调用Qwebpage中load函数,加载历史记录网页。
- 查找功能
查找功能主要借助QWebPage中的findText函数实现查找功能,其声明如下:
bool findText(const QString &subString, FindFlags options = 0);
其中subString是传入的查找字符串,options是查找的处理标识,包括HighlightAllOccurrences(全部高亮)、FindCaseSensitively(区分大小写)等。
二、代理设置模块
代理设置模块主要是为了满足处于局域网的用户的上网需求,首先在系统自定义功能区添加设置按钮,用户点击设置按钮,自动弹出代理设置界面,如下图所示:
其中当“为LAN使用代理服务器”处于勾选状态时,地址和端口编辑框处于可用状态,否则为灰色不可用状态。代理设置的实现流程如下图所示:
说明:
- 当程序启动时,通过QNetworkProxy::applicationProxy()将当前应用程序的代理信息记录下来(程序中通过m_OldProxy记录),初始代理信息操作在系统整个生命周期中只执行一次,用于作为不使用代理的初始信息。
- 点击代理按钮,系统将当前代理设置信息数据传入到代理设置界面中,代理信息结构体定义如下:
struct ProxySetData
{
// IP地址
QString strHostName;
// 端口
int iPortNum;
// 是否使用代理
bool bUserProxyFlag;
ProxySetData()
{
bUserProxyFlag = false;
}
};
代理设置界面根据bUserProxyFlag的值,来决定是否显示IP地址和端口号等信息。
- 根据需要自定义设置代理信息。
- 设置完成后,代理设置界面将代理信息结构体传到自定义功能区对象中,系统通过判断bUserProxyFlag的值判断是否使用代理。
- 不使用代理,则将代理设为初始代理(m_oldProxy),调用语句如下:
QNetworkProxy::setApplicationProxy(m_OldProxy);
4.使用代理,则通过QNetworkProxy类的函数完成设置最新代理,调用语句如下:
QNetworkProxy proxy;
proxy.setType(QNetworkProxy::HttpProxy);
proxy.setHostName(m_stproxyData.strHostName);
proxy.setPort(m_stproxyData.iPortNum);
QNetworkProxy::setApplicationProxy(proxy);
首先创建一个代理策略,然后设置代理类型(自定义浏览器默认使用http代理类型),然后设置代理地址、端口号,最后设置应用程序的代理信息后,即可完成代理设置。
三、JS与QT交互模块
在对JS与Qt交互模块进行了调查之后,下面结合合自定义浏览器中的功能,将通过JS调用QT函数、QT中调用JS函数、QT在JS网页中创建自定义控件和QT读取网页元素四个方面进行描述。
3.1 JS调用QT函数
通过自定义浏览器打开本地view.html文件,界面显示如图所示:
点击调用qt对象,弹出qt界面如下:
说明通过JS按钮调用QT对象成功。
上述过程的实现方式主要由以下几步:
【1】定义通信交互类,该类只要是QObj类的子类即可,该类的主要作用是用于提供JS函数调用的槽函数,因为JS中只能调用暴露对象的公有槽函数,因此该类中的槽函数都是公有的。此处定义了一个槽函数如下
【2】将槽函数的对象暴露给JS
在Cuserview类中添加通信交互类对象作为成员变量,然后将通信对象暴露给JS,该过程是通过QwebFrame类接口实现的:
page()->mainFrame()->addToJavaScriptWindowObject("JsWindowObj", m_JsWindowObjPtr);
其中”JsWIndowObj”是m_JsWindowObjPtr对象在Js文本中的名称,至于将通信对象暴露给JS的时机在官方文档中有推荐,即在javaScriptWindowObjectCleared()信号传来时进行暴露。即保留交互对象给JS的过程如下:
1)在CuserView的构造函数中,关联信号与槽函数
connect(page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared ()),this, SLOT(OnAddJavaScriptObject()));
2)在槽函数中将通信对象暴露给JS
void CUserWebView::OnAddJavaScriptObject()
{
page()->mainFrame()->addToJavaScriptWindowObject("JsWindowObj",
m_JsWindowObjPtr);
}
【3】在JS中调用暴露对象的槽函数,其中html文件内容如下:
此时点击“调用qt对象”按钮,调用JS中openSpiaclFile(file)函数,该函数调用暴露给Js的对象,JSWindowObj中的OnOpenUrl公有槽函数,该槽函数响应并弹出对应提示窗口。
3.2 QT调用JS函数
通过自定义浏览器打开本地view.html文件,界面如6.1节所示,点击测试按钮,系统弹出对话框如下所示:
说明通过JS按钮调用QT对象成功。
上述过程的实现方式与在6.1节基本类似,不同之处是需要在JS文件中绑定QT的信号,基本实现由以下几步组成:
【1】定义通信交互类,与6.1节完全相同,并在头文件中添加信号函数void ObjSignal(int value)。
【2】将槽函数的对象暴露给JS,此处与6.1节完全相同不在赘述。
【3】在Js中将信号与JS函数绑定,如下所示:
首先向暴露对象的信号与JS函数关联,其次定义该函数的相关操作。
【4】添加Qt中测试按钮的点击信号槽函数,在槽函数中发送暴露对象的信号。
四、QT在网页中创建自定义控件
通过QT可以再浏览器中添加自定义控件,主要通过重载QwebPage中的创建控件函数(createPlugin)实现,实现步骤如下,首先创建一个继承与QwebPage的类,在该类中重载createPlugin函数如下所示:
通过该函数实现了了一套创建对应插件的规则,目前在自定义浏览器中的规则十分简单主要用于测试使用,即如果calssid是pushbutton则创建pushbutton,并在按钮上显示ddd字样,如果是lineedit则创建lineedit控件。
然后在CuserView中使用该页面,即在构造函数中添加如下语句
最后创建对应的网页文件,创建文件内容如下
此时通过自定义浏览器打开该文件就会发现按钮创建成功,如下图所示:
通过上述方法创建的QtWebkit中插件可以有两种,一种type必须是application/x-qt-plugin或者application/x-qt-styled-widget的,如果需要去掉此限制,则需要重新实现QWebPluginFactory这个纯虚基类,此处没有继续深入调研。
五、QT读取网页元素
QWebview加载网页完成后可以读取网页中的各个元素节点信息,网页加载完成后通过如下语句获得顶级的网页:
QWebElement document = m_QWebviewPtr->page()->mainFrame()->documentElement();
然后根据获得document信息就可以进一步获取子级节点,以及节点内容等元素,下面简单列举QWebElement的相关函数:
QWebElement firstChild() //获得第一个子节点
QWebElement nextSibling() // 获得相邻节点
bool hasAttribute(const QString & name) // 是否有该属性
……
总之,在QWebElement类中包含大量的解析网页的接口,可以获得当前加载网页的内部元素信息。