• 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>Logintitle>
            <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)">Message1button>					
    			<button id="loginBtn" class="but" type="button" onclick="onInvokeMethodClicked('message2', '标题', '这是message2', 'B', 2.2)">Message2button>
    			
            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>  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127

    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;     
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    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;
      
      • 1
      • 2
    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");
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    3. 实例化QCefView对象

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

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

      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("use-mock-keychain");
          config.addCommandLineSwitch("allow-file-access-from-files");
          config.addCommandLineSwitch("disable-spell-checking");
          config.addCommandLineSwitch("disable-site-isolation-trials");
          config.addCommandLineSwitch("enable-aggressive-domstorage-flushing");
          config.addCommandLineSwitchWithValue("renderer-process-limit", "1");
          config.addCommandLineSwitchWithValue("disable-features", "BlinkGenPropertyTrees,TranslateUI,site-per-process");
      
      	// initialize QCefContext instance with config
      	QCefContext cefContext(&a, argc, argv, &config);
      
      
      
      	QCefView_Test w;
      	w.show();
      	return a.exec();
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
    6. 设置好ui部件后开始编译
      此时不出意外的话,应该是编译报错的,报错说缺少什么dll等。

      需要将我们编译好的,/QCefView-main/build/output/Debug/bin/ 路径下的所有文件都拷贝到项目的运行路径,也就是.exe所在的路径。
      在这里插入图片描述

      另外,上图html文件夹是我们另外新建的,新建好后,将上面的html代码和css代码,新建相同名字文件后放到html文件夹中。
      在这里插入图片描述

    7. 再次运行
      不出意外的话,应该是可以正常运行的了!而且也显示出了我们指定的html页面!

    3 QT给html发送消息 (C++中调用Javascript)

    在按钮的槽函数中进行操作

    QVariantList 这是一个链表,将我们需要穿个html的数据存到此链表中

    然后定义事件QCefEvent ,且绑定事件apChange,注意,这个apChange事件需要和html代码中定义的事件一致!

    然后使用setArguments()方法将链表绑定到事件中

    最后使用broadcastEvent(event);将事件进行广播

    void QCefView_Test::on_btnSend_clicked()
    {
        if (cefViewWidget) {
    
            // 设置传输的数据
            QVariantList list;
            list.emplace_back("qt");
            list.emplace_back("字符串");
            list.emplace_back(123);
            list.emplace_back(3.14);
    
            // 绑定事件
            QCefEvent event("apChange");
            // 绑定参数
            event.setArguments(list);
    
            //cefViewWidget->triggerEvent(event);
            cefViewWidget->broadcastEvent(event);   
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在html的javascript代码中注册事件

    
    function ap(flag, ...arg) {
    	// flag 是第一个参数,根据上面个代码传过来的值,他是qt,后续的参数都存在arg中
    	// 获取QT传过来的数据,多个可以继续使用索引进行获取,例如arg[1];arg[2];等
    	var mess = arg[0];	
    	// ...省略了															
    								
    	//var t1 = arg.length;		// arg记录有多少个参数
    	//var t2 = ap.length;		// ap函数有多少个参数,固定只会显示一个,就是flag
    }
    
    function onLoad() {
    	if (typeof CallBridge == "undefined") {
    		alert("Not in CefView context");
    		return;
    	}
    
    	// 注册一个叫apChange的事件,该事件绑定名为ap的函数,当有接收到apChange事件,ap函数调用
    	CallBridge.addEventListener("apChange", ap);
    				
    	// 还可以注册多个事件,绑定不同的函数
    	CallBridge.addEventListener("sendFailChange", sendFail);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    注册的apChange事件,把绑定名为ap的函数,当有此事件传过来时,就会调用ap()函数,就可以在ap函数中接收QT传过来的数据,进行相应的操作了,其实也就相当于QT调用Javascript的代码

    还可以注册更多的事件,让QT去调用!

    4 html给QT发送消息 (Javascript中调用C++)

    有两种方式给QT发射信号:
    invokeMethod 和 CefViewQuery

    1). invokeMethod

    invokeMethod(name, ...args)

    当该方法在Javascript中调用后,下面的Qt signal将被触发:

    void invokeMethod(int browserId,int frameId,const QString & method,const QVariantList & arguments);
    
    • 1

    继续在html中添加Javascript代码

    function onInvokeMethodClicked(name, ...arg) {
       // invoke C++ code
       window.CallBridge.invokeMethod(name, ...arg);
    }
    
    • 1
    • 2
    • 3
    • 4

    然后可以在html代码中添加两个按钮,在按钮的点击事件中调用此方法即可!
    根据需求进行添加参数即可!

    <body onload="onLoad()" id="main" class="noselect">
        <div id="login">		
    		<button id="loginBtn" class="but" type="button" onclick="onInvokeMethodClicked('message1', '标题', '这是message1', 'a', 1.1)">Message1button>	
    		<button id="loginBtn" class="but" type="button" onclick="onInvokeMethodClicked('message2', '标题', '这是message2', 'B', 2.2)">Message2button>		
        div>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    然后再来到QT代码中
    新建一个槽函数

    private slots:
        // 用这个槽函数去接收html传过来的消息
        void onInvokeMethod(int browserId, int frameId, const QString& method, const QVariantList& arguments);
    
    • 1
    • 2
    • 3

    然后可以在构造函数中进行绑定

    connect(cefViewWidget, &QCefView::invokeMethod, this, &QCefView_Test::onInvokeMethod);
    
    • 1

    之后实现槽函数onInvokeMethod
    第三个参数method,就是在html中onInvokeMethodClicked的第一个参数,之后参数都放在链表arguments中;第一个和第二个参数暂时我也还没搞懂有啥用。

    使用第三个参数进行判断,进行相应的操作,可以直接再次槽函数中进行操作,也可以另外定义一个函数进行操作。

    当传输过来的第三个参数,在QT这边没有定义,那么可以给html广播事件回去,告诉它没有找到

    void QCefView_Test::onInvokeMethod(int browserId, int frameId, const QString& method, const QVariantList& arguments)
    {
        if (0 == method.compare("message1")) {
            /* 可以在这里做处理 */
            
            QString str1 = arguments.at(0).toString();
            QString str2 = arguments.at(1).toString();
            QString str3 = arguments.at(2).toString();
            float f1 = arguments.at(3).toFloat();
            int in1 = arguments.size();
    
        } else if (0 == method.compare("message2")) {
            /* 也可以定义函数去做处理 */
            // void _message2(const QString& method, const QVariantList& arguments)
            _message2(method, arguments);
        
        } else {
    		// 当传输过来的第三个参数,在QT这边没有定义,那么可以给html广播事件回去,告诉它没有找到
            if (cefViewWidget) {
    
                // 设置传输的数据
                QVariantList list;
                list.emplace_back("qt");
            
                list.emplace_back(QString::fromLocal8Bit("没有找到与之对应的事件:") + method);
    
                // 绑定事件
                QCefEvent event("sendFailChange");
                // 绑定参数
                event.setArguments(list);
    
                // 官网例子使用的是broadcastEvent,在网上看到有人使用triggerEvent,测试都是可以的
                //cefViewWidget->triggerEvent(event);
                cefViewWidget->broadcastEvent(event);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    2). CefViewQuery

    window.CefViewQuery(query)

    当从Javascript中调用该方法时,以下Qt signal会被触发:

    void cefQueryRequest(int browserId,int frameId,const QCefQuery & query)
    
    • 1

    继续在html中添加Javascript代码

    // 给QT发送成功后,QT给html发送成功消息			
    function on_success(response) {
    	// response是主程序返回的数据
    	alert(response);
    }	
    // 给QT发送失败后,QT给html发送失败消息			
    function on_failure(error_code, error_message) {
    	// error_message是主程序返回的数据
    	alert(error_message);
    }			
    function onCallBridgeQueryClicked(name, ...arg) {
    
    	// 设置参数,传给QT
    	var str1 = '这个是传给QT的数据'
    	var str2 = 123
    	var str3 = 3.14
    	var str = name + str1 + '|' + str2 + '|' + str3;	// 因为只能传一个参数,所以可以使用拼接的方式传给QT,QT接收后,再进行分割即可
    	
    	var query = {
    		request: str,	// 参数
    		onSuccess: on_success,		// 主程序返回成功信号,执行此函数
    		onFailure: on_failure,		// 主程序返回失败信号,执行此函数
    	};
    		
    	// 给QT发射信号
    	window.CefViewQuery(query);					
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    然后可以在html代码中添加个按钮,在按钮的点击事件中调用此方法即可!

    <button id="loginBtn" class="but" type="button" onclick="onCallBridgeQueryClicked('html')">发送button>
    
    • 1

    然后再来到QT代码中
    新建一个槽函数

    private slots:
        // 用这个槽函数去接收html传过来的消息
        void onQCefQueryRequest(int browserId, int frameId, const QCefQuery& query);
    
    • 1
    • 2
    • 3

    然后可以在构造函数中进行绑定

    connect(cefViewWidget, &QCefView::cefQueryRequest, this, &QCefView_Test::onQCefQueryRequest);
    
    • 1

    之后实现槽函数onQCefQueryRequest

    根据参数三query的request方法获得字符串,然后进行分割,就拿到传过来的参数了

    根据需求做完操作后,需要调用方法query.setResponseResult,将操作的结果返回给html,true为成功,false为失败,还可以传递字符串信息回去。

    最后调用cefViewWidget->responseQCefQuery(query);即可

    void QCefView_Test::onQCefQueryRequest(int browserId, int frameId, const QCefQuery& query) {
        
        // 获得html传过来的数据
        std::vector<std::string> vec = _split(query.request().toStdString(), "|");	// 分割获取数据
        if (1 == vec.size() || true == vec.at(1).empty()) {
            QMessageBox::information(this, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("消息为空!"));
            
            // 设置结果为false,给html返回结果
            query.setResponseResult(false, QString::fromLocal8Bit("qt接收失败,数据为空!"));
        
        } else {
    
            // 设置结果为true,给html返回结果
            query.setResponseResult(true, QString::fromLocal8Bit("qt接收成功!"));
        }
    
    
        // 给js返回结果(字符串)
        cefViewWidget->responseQCefQuery(query);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    到此为止,QT与html通信流程就结束了!


    四、总结

    也是经过了好几天的潜心去研究,虽说没有什么特别的技术在这里,但是基本的操作还是写下来了,都是参考官网给出的例子去完成的!
    后续还得再花时间去深入研究一下!

    案例源码:
    https://download.csdn.net/download/cpp_learner/86266086

  • 相关阅读:
    IB心理学如何记住大量的内容?
    idea配置Git以及操作
    内排序算法
    图形库篇 | EasyX | 图像处理
    鸿鹄工程项目管理系统em Spring Cloud+Spring Boot+前后端分离构建工程项目管理系统
    倾斜摄影测量实景三维建模ContextCapture Master
    Rust之Cargo的使用
    抖音api接口工具
    常用redis-lua脚本
    (二)激光线扫描-相机标定
  • 原文地址:https://blog.csdn.net/cpp_learner/article/details/126052641