Qt vs2022使用QCefView控件与html通信

Posted cpp_learners

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt vs2022使用QCefView控件与html通信相关的知识,希望对你有一定的参考价值。

此前,主管让我调研cef3的使用,但是cef3比较复杂,太难理解了;偶然间,在网上看到有QT第三方库QCefView,这个库封装了cef3,使得可以很简单的在桌面应用加载显示html,并与其进行通信;经过几天的调研,现在将调研结果记录下来!


目录


前言

  1. QCefView是什么?
    QCefView是为Qt框架开发的一个封装集成了Chromium Embedded Framework库的Wdiget UI组件。使用QCefView可以充分发挥CEF丰富强大的Web能力,快速开发混合架构的应用程序。

    使用Qt开发者熟悉的Forms,signal/slot来开发应用
    方便直观的javascript/C++互操作方式

  2. 为何选择QCefView而不用Electron?
    从设计思路和最终形态来讲QCefView和Electron是完全不同的技术。

    QCefView只是一个为Qt框架开发的UI组件,Electron则是一个功能完备的应用开发框架
    QCefView是为Native系统开发者设计的,Electron对前端开发者更友好
    QCefView使用C++作为主要开发语言,Electron全部基于Javascript
    QCefView提供便捷直观的Javascript/C++互操作方式,Electron通过编写插件实现Web/Native互操作

  3. QCefView适合开发何种类型的应用?
    如果你打算使用Web前端技术来开发你的应用UI,同时保持使用Native方式编写核心业务/功能逻辑,QCefView是最佳选择。

    例如:

    音乐/视频播放器
    游戏平台
    工具类应用
    等等……
    以上场景中的应用几乎都是基于内容的平台,他们都需要展示很多列表,表格或者有各种复杂特效的页面。基于此种目的,Web前端技术是目前的最好的选择,把UI当作Web前端App来开发,而核心的功能和逻辑仍然使用Native的方式来编写,然后通过QCefView整合,能极大的提升生产效率,并且一份UI代码适配所有主流桌面平台。

如果你打算开发一款浏览器,QCefView并不是较好的选择,因为QCefView设计的目的是UI组件,并不提供作为浏览器的全部特性,该类需求应该使用原生CEF来实现较好。

上面内容出自QCefView官网
官网链接:https://cefview.github.io/QCefView/


一、编译QCefView

注意:Window环境编译QCefView依赖VS2019或VS2022 和 QT6以上版本,还有CMake!

官网:https://cefview.github.io/QCefView/zh/docs/intros

  1. 下载最新源码

  2. 下载解压后

    CefViewCode-main中的全部文件拷贝到QCefView-main/CefViewCode 目录下:
    QCefView-main 路径下新建文件夹build

  3. 编辑QCefView-main 路径下的QtConfig.cmake文件

    在这里写上自己的QT安装路径

    建议是QT6以上的版本!

  4. 设置QT6的环境变量

  1. 下载安装CMake
    下载链接:https://cmake.org/download/


    下载后安装即可,这里就不再缀叙安装过程!

  2. 打开CMake
    根据下图步骤进行编译!



    插入小道消息-------------------------------------------------------------------------------------------------------------

    如果网络不好,网上说可以自行下载一个cef版本,然后将cef拷贝到路径/QCefView-main/CefViewCore/dep,然后去下图二文件中进行修改一些操作,CMake编译就不用下载了,具体我没有操作成功!
    cef官网:https://cef-builds.spotifycdn.com/index.html


    小道消息结束-------------------------------------------------------------------------------------------------------------

    下面接着开始下载的步骤,下图显示的是已经将cef下载完毕并解压好了的

    在路径/QCefView-main/CefViewCore/dep下,有CMake帮我们下载的CEF包

  3. 编译VS工程

    在build目录下有一个VS工程,使用VS2022打开它

    根据自己的需求设置好后右键ALL_BUILD,选择重新生成

    编译成功如下图:

  4. 查看编译好的库

    路径中多出了个output路径

    lib目录存放的就是我们需要的QCefView.lib
    bin目录存放的是项目运行需要的动态库等文件!

至此,QCefView已经编译完毕!


二、插曲

有些朋友的项目可能需要使用到Curl,并且,curl得带有openssl,否则无法使用https;请按照下面步骤进行编译!不需要可跳过此步骤!

1). 安装OpenSSL

下载和安装
下载其他人做的便捷版安装包
下载链接:http://slproweb.com/products/Win32OpenSSL.html

下载后安装, 一直狂点下一步就行了。
安装时如果你没有修改安装路径,默认是安装在:C:\\Program Files\\OpenSSL-Win64

设置一下环境变量

查看安装版本

OpenSSL安装完毕!

2). 编译Curl

注意:编译带有openssl的curl库,编译Debug失败,编译Release成功!

下载地址:https://curl.se/download.html


不知为何,这里只有Release版本的下载,所以我们待会编译带有openssl的curl库时也只能编译Release的库,编译Debug的库会失败!
当然,如果只是编译curl没有包含openssl的话,debug的库貌似是可以编得过的!

  1. 下载后解压打开
    在下图路径中,有很多个版本,具体看下图

    使用VS2022打开

  2. 选择 DLL Release – DLL OpenSSL 和 x64

    如果编译带有openssl的curl库,这里一定要选择 DLL Release – DLL OpenSSL ,否则会编译失败!

  3. 包含openssl的库文件
    如果只是需要使用curl的http,而不需要使用https,那么,编译也就不需要带有openssl,可以直接跳到下方第4步骤进行编译;当然,在第二步骤需要选择,DLL Debug或者DLL Release项进行编译即可!

    A. 右键libcurl - 属性 - VC++目录 - 包含目录

    添安装的OPenSSL库的头文件路径进来
    C:\\Program Files\\OpenSSL-Win64\\include

    B. VC++目录 - 库目录

    将安装的OPenSSL库的lib添加进来
    C:\\Program Files\\OpenSSL-Win64\\lib

    C. 链接器 - 输入 - 附加依赖项
    一般来说默认会给我们添加上了这些lib,如果没有请手动打上

  4. 编译
    右键curl,选择重新生成

    编译成功如下图:

  5. 查看编译好的库
    进入下图路径,就会有编译好的带openssl的curl库了

至此,Curl编译完毕!

另外,如果编译的是带有openssl的curl库,那么此库应该是Release版本的;如果此库需要和QCefView结合一起使用,得将QCefView库也编译成Release版本的才可以一起使用,否则会编译报错!


三、案例

下面将根据本人写的一个小案例进行讲解代码知识点。此案例是QT使用QCefView显示html,并与之进行通信,互相发送消息!

具体案例代码放在下面总结处,有需要的可以去下载!

案例运行截图:

上方显示的是html页面
下方显示的是QT页面
当然,也可以直接整个窗体都进行显示html页面,具体根据自己的需求来就好!
因为我对HTML不熟悉,所以搞得html显示的有点奇怪,但不影响我们立即如何使用。

ui布局如下:

根据自己的需求来布局就可以了!

1 HTML代码

1). html代码

文件名:QCefViewTest.html

<!doctype html>
<html lang="en">  
    <head>
        <meta charset="UTF-8">
        <title>Login</title>
        <link rel="stylesheet" type="text/css" href="Login.css"/>
    </head>
	
    <body onload="onLoad()" id="main" class="noselect">
        <div id="login">
			<form method="post">
				<input id="account" type="text" required="required" placeholder="请输入" name="u"></input>
				<button id="loginBtn" class="but" type="button" onclick="onCallBridgeQueryClicked('html')">发送</button>
				<textarea id="output" type="text" required="required" placeholder="内容" name="t"></textarea>									
			</form>
			
			<button id="loginBtn" class="but" type="button" onclick="onInvokeMethodClicked('message1', '标题', '这是message1', 'a', 1.1)">Message1</button>					
			<button id="loginBtn" class="but" type="button" onclick="onInvokeMethodClicked('message2', '标题', '这是message2', 'B', 2.2)">Message2</button>
			
        </div>


        <script>
		
			function ap(flag, ...arg) 
				// flag 是第一个参数,后续的参数都存在arg中
				// 获取QT传过来的数据,多个可以继续使用索引进行获取,例如arg[1];arg[2];等
				var mess = arg[0];	
				
				if (mess != '') 
					var txt = document.getElementById('output').value; //获取textarea的值
					var text = flag + ": " + mess;
					if (txt == '') 
						document.getElementById('output').value =  text; //设置textarea的值
					 else 
						document.getElementById('output').value =  txt + "\\n" + text; //设置textarea的值
					
				
																
								
				//var t1 = arg.length;		// arg记录有多少个参数
				//var t2 = ap.length;		// ap函数有多少个参数,固定只会显示一个,就是flag
			
			
			// 使用事件方式给QT发送失败后,QT给html发送失败消息
			function sendFail(flag, ...arg) 
				var mess = arg[0];	
				
				var txt = document.getElementById('output').value; //获取textarea的值
				var text = flag + ": " + mess;
				if (txt == '') 
					document.getElementById('output').value =  text; //设置textarea的值
				 else 
					document.getElementById('output').value =  txt + "\\n" + text; //设置textarea的值
				
			
								
			function onLoad() 
				if (typeof CallBridge == "undefined") 
				  alert("Not in CefView context");
				  return;
				

				// 注册一个叫apChange的事件,该事件绑定名为ap的函数,当有接收到apChange事件,ap函数调用
				CallBridge.addEventListener("apChange", ap);
				
				// 还可以注册多个事件,绑定不同的函数
				CallBridge.addEventListener("sendFailChange", sendFail);
			
		
		
		
		
		
            function onInvokeMethodClicked(name, ...arg) 
			  // invoke C++ code	// 给QT发射信号
			  window.CallBridge.invokeMethod(name, ...arg);
			
			
			
			
			
			// 给QT发送成功后,QT给html发送成功消息			
			function on_success(response) 
				// response是主程序返回的数据
				var txt = document.getElementById('output').value; //获取textarea的值
				var text = response;
				//document.getElementById('output').value =  txt + "\\n" + text; //设置textarea的值
				
			// 给QT发送失败后,QT给html发送失败消息			
			function on_failure(error_code, error_message) 
				// error_message是主程序返回的数据
				var txt = document.getElementById('output').value; //获取textarea的值
				var text = response;
				document.getElementById('output').value =  txt + "\\n" + text; //设置textarea的值
						
			function onCallBridgeQueryClicked(name, ...arg) 
				var message = document.getElementById("account").value;
				//var name = 'html';			
				var str = name + "|" + message;	 // 由于只能传递一个字符串参数,如果想要传递多个,可以使用一个字符进行隔开组合到一起,qt就收后再进行分割获取即可
				
				document.getElementById("account").value = '';	// 把输入框清空
				
				if (message != '') 
					var query = 
						request: str,	// 参数
						onSuccess: on_success,		// 主程序返回成功信号,执行此函数
						onFailure: on_failure,		// 主程序返回失败信号,执行此函数
					;
					
					// 给QT发射信号
					window.CefViewQuery(query);
					
					var txt = document.getElementById('output').value; //获取textarea的值
					var text = name + ": " + message;
					if (txt == '') 
						document.getElementById('output').value =  text; //设置textarea的值
					 else 
						document.getElementById('output').value =  txt + "\\n" + text; //设置textarea的值
					
															
			
						
        </script>

    </body>
</html>  

2). css代码

文件名:Login.css

html   
    width: 100%;   
    height: 100%;   
    overflow: hidden;   
    font-style: sans-serif;   
   
body   
    width: 100%;   
    height: 100%;   
    font-family: 'Open Sans',sans-serif;   
    margin: 0;   
    background-color: #4A374A;   
   
#login   
    position: absolute;   
    top: 50%;   
    left:50%;   
    margin: -150px 0 0 -150px;   
    width: 300px;   
    height: 300px;   
   
#login h1   
    color: #fff;   
    text-shadow:0 0 10px;   
    letter-spacing: 1px;   
    text-align: center;   
   
h1   
    font-size: 2em;   
    margin: 0.67em 0;   
   
input   
    width: 278px;   
    height: 18px;   
    margin-bottom: 10px;   
    outline: none;   
    padding: 10px;   
    font-size: 13px;   
    color: #fff;   
    //text-shadow:1px 1px 1px;   
    border-top: 1px solid #312E3D;   
    border-left: 1px solid #312E3D;   
    border-right: 1px solid #312E3D;   
    border-bottom: 1px solid #56536A;   
    border-radius: 4px;   
    background-color: #2D2D3F;   
   
.but   
    width: 300px;   
    min-height: 20px;   
    display: block;   
    background-color: #4a77d4;   
    border: 1px solid #3762bc;   
    color: #fff;   
    padding: 9px 14px;   
    font-size: 15px;   
    line-height: normal;   
    border-radius: 5px;   
    margin: 0;   
  

textarea   
    width: 300px;   
    height: 200px;   
    margin-bottom: 10px;   
    outline: none; 
	resize: none;			
	pointer-events: none;	
    font-size: 13px;       
    border-top: 1px solid #312E3D;   
    border-left: 1px solid #312E3D;   
    border-right: 1px solid #312E3D;   
    border-bottom: 1px solid #56536A;   
    border-radius: 4px;     
  

2 QT窗体显示一个html

新建一个项目名为QCefView_Test,将路径**/QCefView-main/include/的头文件拷贝到项目代码路径中;还有将编译好的QCefView.lib拷贝到项目代码路径**中。

右键项目 - C/C++ - 常规 - 附加包含目录
添加我们的项目代码路径和include路径进来
例如我的是:
E:\\Code\\vs2022Code\\QCefView_Test\\QCefView_Test
E:\\Code\\vs2022Code\\QCefView_Test\\QCefView_Test\\include

右键项目 - 链接器 - 输入 - 附加依赖项
添加lib进来:QCefView.lib

在构造函数中进行如下操作

添加头文件:
#include “QCefView.h”
#include “QCefContext.h”

  1. 定义QCefView对象

    // 这个应该要在头文件中进行定义
    QCefView* cefViewWidget;
    
  2. 添加一个本地文件夹到URL映射

    QDir dir = QCoreApplication::applicationDirPath();
    QString path = QDir::toNativeSeparators(dir.filePath("html"));  // 获取运行路径,并拼接上html
    
     // 添加一个本地文件夹到URL映射:参数一是文件夹路径,参数二应该是自定义协议和域名
    QCefContext::instance()->addLocalFolderResource(path, "my://cpp_learners");  // 自定义协议:my     自定义域名:cpp_learners
    //QCefContext::instance()->addLocalFolderResource(path, "https://cpp_learners");
    
  3. 实例化QCefView对象

    // 设置QCefView的
    QCefSetting setting;
    //setting.setBackgroundColor(QColor::fromRgb(100, 80, 60));   // 设置HTML背景颜色
    
    // 创建一个QCfView窗体
    cefViewWidget = new QCefView("my://cpp_learners/QCefViewTest.html", &setting, this);
    
  4. 添加到窗体布局中

    // 定义一个网格布局,并将QCefView窗体设置在此
    QGridLayout* layout = new QGridLayout(this);
    layout->addWidget(cefViewWidget, 0, 0, 1, 1);
    
    // 将布局添加到widget中
    ui.widgetHtml->setLayout(layout);
    

    ui.widgetHtml是我们在ui界面拖动的一个widget部件

  5. main.cpp文件添加如下代码
    一定要添加,否则运行报错!

    #include "QCefContext.h"
    #include "QCefConfig.h"
    
    
    int main(int argc, char *argv[])
    
        QApplication a(argc, argv);
    
    
    
        // build QCefConfig
        QCefConfig config;
        config.setUserAgent("QCefViewTest");
        config.setLogLevel(QCefConfig::LOGSEVERITY_DEFAULT);
        config.setBridgeObjectName("CallBridge");
        config.setRemoteDebuggingPort(9000);
        config.setBackgroundColor(Qt::lightGray);
    
        // add command line args
        // config.addCommandLineSwitch("allow-universal-access-from-files");
        config.addCommandLineSwitch("enable-media-stream");
        config.addCommandLineSwitch以上是关于Qt vs2022使用QCefView控件与html通信的主要内容,如果未能解决你的问题,请参考以下文章

    Qt vs2022使用QCefView控件与html通信

    Qt嵌入浏览器(五)——CEF入口与QCefView控件的使用

    编译QCefView+VS2019+QT5.15.2

    编译QCefView+VS2019+QT5.15.2

    QcefView:一个集成了CEF的Qt Widget

    Qt嵌入浏览器(六)——QCefView实现JS通信接口