1、背景
Web网页的界面交互相比较Qt客户端而言有着比较大的优势:更加的多样化和更高的使用便捷性使得我们即使在客户端中也可以考虑将web网页嵌入到客户端的界面当中。如此便能将web的优势和客户端进行结合。更加丰富客户端的界面及功能,以下将介绍几种常见的Qt客户端中嵌入web网页的实现方案和实现步骤。
2、实现方案设计
1、基于Qt自带控件实现
1.1简介
在Qt中,提供了一个用于访问网页的控件(不同的Qt版本对应不同的控件模块),集成了浏览器的webkit内核和google的引擎,不严谨的说,这个控件其实就是一个简易版的浏览器。
Qt5.0~5.4:webkitwidgets模块 - QWebView
Qt5.4之后:webenginewidgets模块 - QWebEngineView
此处我们客户端的qt选型为qt5.9.6,故采用QWebEngineView。
1.2环境配置
Qt5.9.6 + VS2015
注:查看本地的Qt是否有webenginewidgets模块,最简单的方法可右键任意的Qt工程->Qt Project Settings->Qt Modules查看WebEngine Widgets模块是否为灰色,若为灰色则说明未配置此模块,不可选用,需要重装Qt并选中此模块即可。
1.3开发流程
在配置好Qt模块后,便可像普通的button或label一样通过拖ui控件或者直接new的方式来使用。
代码示例:
效果图展示:
1.4总结
至此一个简单的Web页面就嵌入成功了,但是基于这种方式实现的浏览器存在弊端:不能对视频进行解码播放,只能查看静态页面。因为Qt组件底部集成的浏览器内核(Chrome浏览器的内核,内核版本由Qt版本决定)是不带解码库的,所以不能进行视频播放。但是可以通过加编译配置重新编译这个控件的源码实现,编译对硬件要求较高,且时间较长。故对于有视频播放功能的需求,不建议作为常规方案采用。
2、嵌入Chrome.exe进程实现
2.1简介
这种实现方式和本地的Chrome浏览器功能上没有差别,在本地浏览器能实现的功能,通过这种方式嵌入到Qt窗口中仍是正常的。实现的本质是从客户端启动浏览器进程并传相应的url及其他参数,再通过Windows的API获取到窗口句柄,进而将窗口句柄进行QWigetde 的转换,实现嵌入。
2.2环境配置
Qt5.9.6 + VS2015
依赖本地的Chrome,所以必须要先下载并安装谷歌浏览器。
2.3开发流程
2.3.1检测浏览器是否存在
检测是否已安装谷歌浏览器,如未安装则提示安装。若已安装则利用Qt的QProcess来启动Chrome.exe并传递浏览器进程需要的参数即可。
启动Chrome.exe需要先获取exe的所在路径,虽然谷歌浏览器的路径是默认安装的,但是在不同的机器上面,路径也是大不相同的。故不能通过遍历某个文件夹或者固定路径来获取,此处可以使用读取注册表的方法获取chrome.exe的所在路径。此处读取的注册表为当前用户(HKEY_CURRENT_USER)但是实际情况下chrome的注册表不一定都在当前用户下,如果当前用户下没有,可继续向本地机器(HKEY_LOCAL_MACHINE)的注册表查询,可自行加判断分支。示例代码如下:
2.3.2窗口句柄的读取及嵌入
浏览器启动后先要通过spy++工具获取到窗口的类名和标题。spy++的使用可通过打开VS->点击菜单栏的工具->点击spy+±>点击搜索->查找窗口->拖动靶心到想要获取的窗口上即可。
再通过Windows的API:FindWindow(L"Chrome_WidgetWin_1", NULL);来获取浏览器的窗口句柄,这里入参不能为标题名,应通过类名来获取,因为标题名可由不同的地址而变化,标题名一旦变更窗口句柄则获取不到。
句柄获取成功后将句柄转化为QWindow *pWin = QWindow::fromWinId(wid);
最后再创建一个窗口容器:QWidget *pWid = QWidget::createWindowContainer(pWin, this);这个返回的pWid便可通过布局填充到Qt的对应界面中了。
完整代码如下:
在startDetached启动浏览器后主线程要休眠三秒钟,这个操作不可省略,如果不进行休眠操作则可能会出现嵌入失败的情况。
这里的chrome.exe的进程参数:–chrome-frame为启动的必须参数;-kiosk参数为不显示标题栏启动。其他启动参数可参照以下文档:
2.4总结
这种方式虽然能达到功能完整,但是也存在一定不可控的缺陷:这里是以带参数的方式启动的chrome浏览器,启动时配置为全屏且无标题栏。那么打开客户端后再打开浏览器,打开的浏览器仍会是全屏且无标题栏。若用户先启动了浏览器,再启动客户端。那么客户端的进程参数则不生效,嵌入的web页显示标题栏和地址。这种显然是不合理的。
3、基于Chrome的CEF3实现
3.1简介
CEF全称Chromium Embedded Framework,是一个基于Google Chromium 的开源项目。Google Chromium项目主要是为Google Chrome应用开发的,而CEF的目标则是为第三方应用提供可嵌入浏览器支持。CEF作用是在客户端嵌入网页界面。
CEF本身也是不支持视频播放的,如果需要支持需要对chromium的源码进行重新编译,这里可以参照CEF的官方文档进行编译操作:
https://wiki.hikvision.com.cn/pages/viewpage.action?pageId=168565259 由于编译需要下载CEF及chromium源码,需要稳定的外网环境。我们这里是不具备的。所以这里是在网上找到了一个已经编译好的较符合预期的版本来直接使用。
3.2环境配置
Qt5.9.6 + VS2015
3.3开发流程
3.3.1、cef工程配置编译
下载cef包之后可以看到里面是有一个官网demo的,这里我们可以参照这个示例文档来构建自己的项目
第一步创建一个空项目,项目下添加以下五个源文件及资源文件,并添加main.cpp,这里以后缀cpp打开异常,统一改为_cpp,后续可自行调整。
第二步添加包含目录的路径及静态库的包含路径。这里用到以下三个静态库文件,静态库文件在CEF的包里都有包含,由于库文件为debug编译,所以工程也应相配的配置为debug模式
第三步由于创建项目后的运行时库默认为MDD但是由于静态库文件的运行时库为MTD所以需要同步将项目的运行时库改为MTD
第四步需要在main.cpp的同级目录下添加CefFrame.manifest文件,并在项目属性的清单输入中添加文件路径
至此cef的工程文件便能编译过了,接下来就是按照自己的需求做对应的修正即可。
3.3.2、实现流程描述
我这里仍然采用的是启动cef进程的方式,通过启动时传递进程参数(即要访问的url),再通过进程间传参实现对此目的网页的访问。最后再将cef的进程嵌入到客户端中,此处的嵌入方案和之前略有不同,这里如果仍然采用spy++工具和Windows的API获取窗口句柄的方式则不可行,始终获取为空。所以此处采用进程间通信(共享内存)的方式来传递窗口句柄(cef的窗口句柄自己可以获取)。
这里是在客户端启动cef之后进行一个适当的延迟,以保证cef进程已经将窗口句柄的数据写入到共享内存中(当然这里可替换为更好的while循环,直到读到句柄值退出循环体,可作为优化项考虑),最后将句柄转化为QWindow *pWin = QWindow::fromWinId(wid);
最后再创建一个窗口容器:QWidget *pWid = QWidget::createWindowContainer(pWin, this);这个返回的pWid便可通过布局填充到Qt的对应界面中了。部分代码示例如下:
句柄写入到共享内存:
从共享内存读取句柄:
3.4总结
这种方案由于在公司内网的限制,不能直接下载chromium及cef的源码进行编译,只能通过网上找已经编译好支持mp4格式的库文件,此处的cef库文件对应的chromium的版本相对较低,解码部分存在限制,不能达到实际的用户使用要求。
4、基于微软的WebView2实现
4.1简介
Microsoft Edge WebView2 控件允许你在本机应用程序中嵌入 Web 技术(HTML、CSS 和 JavaScript)。WebView2 控件使用Microsoft Edge作为呈现引擎在本机应用程序中显示 Web 内容。使用 WebView2,您可以将 Web 代码嵌入到本机应用程序的不同部分,或在单个 WebView 实例中构建所有本机应用程序。
4.2环境配置
Qt5.9.6 + VS2015
运行环境必须安装WebView2的运行包:MicrosoftEdgeWebView2RuntimeInstallerX64.exe
可到微软官方下载:https://developer.microsoft.com/zh-cn/microsoft-edge/webview2/
4.3开发流程
我们的目的是在qt中嵌入web页面,但目前没有方法可以直接将webview2嵌入到qt的客户端中,所以仍然采用读取进程的窗口句柄方式将整个进程进行嵌入。那么这里将以webview2的win32示例程序为例进行开发讲解。
4.3.1、WebView2工程配置
首先到官网下载对应的webview2的win32示例文档【Win32_GettingStarted】,用vs2015打开后右键单击WebView2GettingStarted 项目->选择Configuration Properties > General
查看对应的目标平台版本【WindowsSDK】和平台工具集是否正确。
这里的平台版本要选则10.0以上的版本否则会编译时会进行如下报错,如果不是10.0以上版本可自行到Windows官网下载新的SDK即可
4.3.2、安装 Windows 实现库 (WIL)
配置好SDK后需要配置对应的Windows实现库(WIL),这里在解决方案资源管理器中,右键单击WebView2GettingStarted项目节点,然后选择管理 NuGet 包,在NuGet窗口中,单击“浏览“选项卡,在左上角的搜索栏中,输入Microsoft.Windows.ImplementationLibrary,再选中安装此包即可。
4.3.3、安装 WebView2 SDK
这一步需要配置webview2的SDK,这里在解决方案资源管理器中,右键单击WebView2GettingStarted项目节点,然后选择管理 NuGet 包,在NuGet窗口中,单击“浏览“选项卡,在左上角的搜索栏中,输入Microsoft.Web.WebView2,再选中安装此包即可。
4.3.4、实现流程描述
这里采用的是在客户端进程中启动该exe,启动成功后再通过spy++找取窗口句柄来实现的嵌入到客户端的逻辑,故这里仍然通过main函数由客户端向webview2的exe传递最终访问的url,webview的进程获取到url后访问目的网页。这里需要注意一个小细节,由于原代码是以两重lamda表达式的方式实现,所以变量不能直接传递,需要在捕获列表加上此变量,如下:
至此整个流程便可以实现了,但目前在webview的界面中是有标题栏和按钮的,这是我们不需要的,所以最后一步,我们需要隐藏此标题栏和按钮,只需要一个无边框的界面即可。相关代码如下:
4.4总结
至此一个简单的基于WebView2控件的程序便成功实现了,可以发现WebView虽然相较于其他的实现方式较为简单且功能及用户体验上更加优秀,但同时这种实现方式也有一个根本的缺点:这种基于Windows的SDK的实现,是无法实现国产化的。所以在无需考虑国产化的情况下,这种方案仍可作为最优方案采用。
3、总结
通过以上四种Qt客户端中嵌入web网页的方案介绍,大致可了解到,在只需要对网页进行显示,无关视频播放的情况下,第一种方案可作为首选;在需要进行视频播放的情况下,且有充分的外网及硬件资源可选则第三种方案,按照自己的需求对CEF及chromium源码进行重编;如需要进行视频播放且没有上述资源时。可选WebView方案进行实现。至于嵌入chrome.exe的实现方案,综合来看不能符合用户的实际使用场景,存在较多不可控因素,因此这里作为反面案例进行概述。