QT网页 webengine / CEF

news/2024/12/27 2:09:52/

QT + WebEngine

官方文档

WebEngine 架构:

在这里插入图片描述

能看到 WebEngine 有一个核心模块是基于 Chromium 构造的,通过使用 Chromium 的Blink渲染引擎和V8 JavaScript引擎来处理和渲染Web内容,并将这些底层技术封装为一系列高级的C++类和接口,以便开发者可以将Web内容嵌入到Qt应用程序中。

  1. WebEngine Widgets 模块:主要是针对于桌面应用,提供了用于将Web内容嵌入到Qt应用程序中的窗口和小部件, 可以用于各种应用程序来加载web内容。
    在这里插入图片描述

主要架构如上图所示,主要提供了一些组件:

  • QWebEnginePage:一个代表Web页面的类,它包含了页面的内容、状态和功能。它可以执行JavaScript代码、处理表单、捕获页面截图等。
  • QWebEngineView:一个用于展示Web页面的小部件,它是QWebEnginePage的可视化容器。它可以加载URL、导航历史、缩放页面等。
  • QWebEngineProfile:一个表示浏览器配置文件的类,它包含了缓存、Cookie和安全策略等设置。它可以用于实现多用户浏览器或隐私浏览功能。
  • QWebEngineSettings:一个表示浏览器设置的类,它包含了字体、颜色、JavaScript和插件等选项。它可以用于调整页面的渲染效果和功能。
  • QWebEngineScript:执行 JavaScript 脚本的类,用于向网页中注入并执行自定义的 JavaScript 脚本来添加新功能或修改现有的功能。
  • QWebEngineHistory:管理浏览历史的类,它提供了对浏览历史记录的访问和操作,其中每个历史记录项都由 QWebEngineHistoryItem 类表示。
  • WebEngineAction:表示对网页的操作,由WebEngineView::action()方法返回,包含相关的一些信息;

一个QWebEngineView 实例有一个 QWebEnginePage. QWebEnginePage有一个QWebEngineHistory和一些用于操作页面的QAction对象。每一个QWebEnginePage都属于一个QWebEngineProfile, QWebEngineProfile有一个页面设置的QWebEngineSettings, 一个在页面上运行脚本的QWebEngineScriptCollection, 一个访问HTTP cookies的QWebEngineCookieStore。

  1. WebEngine 模块:主要是针对用于QML。
    Qt WebEngine 除了没有单独访问的web引擎页面,包含的其他组件与Qt WebEngine Widgets相同,功能也是一致的。

  2. WebEngine Process:这是可执行文件,对应的是一个独立的用于渲染网页和执行JavaScript进程,用于增强安全性与内存隔离。
    网上有文章说“每个QWebEngineView或WebEngineView实例都会启动一个QtWebEngineProcess.exe进程”,在View对象过多的情况下会导致内存占用;
    但是在官方文档里没有找到相关描述,这个问题暂时未能确定;

  3. WebEngine 对 AMD 显卡支持性不好,在某些A系显卡下会显示不出来甚至闪退; 后续优化计划目前还没在网上搜到;另外嵌入的网页无法播放视频,要播放视频的话需要下载QT源码自己设置编译参数集成视频播放库;

  4. 对显卡有依赖,基于openGL,看网上说如果在显卡和显卡驱动不一致或是旧版版本下还有openGL,程序有可能出异常

QT 与 网页通信

  1. 通过 WebEnginePage 与网页通信
    通过 WebEngine 嵌入网页的方式与网页端的JS交互很容易,QWebEnginePage 自带了一个函数为 runJavaScript ,这个函数可以直接通知 JS 执行指定的函数,
    void QWebEnginePage::runJavaScript(const QString &scriptSource, const std::function<void (const QVariant &)> &resultCallback)

例如这个接口,第一个参数就是要调用的接口以及参数拼成的字符串,第二个参数是对应的回调。
这种方式用起来很方便,但是是单向的

  1. QWebChannel 双向通信
    QT提供了一种通道机制,可以让 JS 与 QT 通过对象建立连接, 然后进行通信;
    QWebChannel 提供了在 QT 和 HTML/JS 之间点对点的通信能力,通过向 HTML/JS 端的 QWebChannel 发布 QObject 的 派生对象,进而实现在 HTML/JS 端可以同步读取来自 Qt 端的 公共插槽 和 QObject 的 属性值 和 方法。在整个通信过程中,无需任何手动序列化传入的参数。所有 Qt 端的数据更新,都会自动且异步的触发 signal 并更新到 client 端。这个机制一般被称为对象模型,QObject 是这个模型的核心,而模型实现的基础就是靠的 QT 的 signal 和 slot 也就是信号槽机制。

这个机制主要分以下几步:

  • QT端 创建 HTML/JS 网页
  • JS端 创建 channel 对象,通过 channel 取到通信对象 QObject;
  • QT端 创建 channel 对象与通信用的 QObject 对象;
  • QT端 channel 与 网页对应的 QWebEngineView 绑定;
  • QT端 将 channel 与 QObject 对象绑定;
  • QT端/JS端 修改QObject属性,另一端对应的监听函数响应;

WebEngine 网页示例

源码见 QTWebtest_WebEngine
在示例代码里,我写了一个中间类名为 JS2QTBridge, 里面定义了两个字符串 m_jscontent 与 m_content;
通过 channel 将这个对象对 JS 与 QT 双向绑定,网页/QT界面上显示内容发生改变时给 QT/JS 发送对应信号,实现效果如:
在这里插入图片描述

其中在QT上点击按钮通知网页画图是用 runJavaScript 方法实现;
在网页点击饼图在QT弹出提示是通过调用 bridge 对象的函数实现;
两边输入框修改另一侧的显示区域一同变化是通过对 bridge 对象的成员变量与界面控件信号槽绑定实现;

QT + cef

CEF3使用多个进程运行。处理窗口创建、绘制和网络访问的主要进程称为浏览器进程,通常与宿主应用程序的进程相同,大多数应用程序的逻辑将在浏览器进程中运行。使用Blink引擎渲染HTML和JavaScript在单独的渲染进程中,相关的应用程序逻辑(如JavaScript绑定和DOM访问)将在渲染进程中运行。默认进程模型将为每个唯一源地址(scheme+domain)运行一个新的渲染进程。其他进程将根据需要生成,例如处理Flash等插件的插件进程和处理加速合成的GPU进程。另外浏览器进程中会进行窗口绘制,不是绘制HTML内容,而是承载网页内容的那个窗体,渲染进程则不会用来创建窗体。

<!-- 编译CEF参考 cef.md – >
cef解决方案中有多个工程:

  • ALL_BUILD与ZERO_CHECK:是cmake自动生成的辅助工程。
  • cef_gtest与ceftests:都是测试模块,这里不关心。
  • cefclient:一个包含CEF各种API演示的浏览器程序Demo。
  • cefsimple:一个简单的CEF浏览器程序Demo。
  • libcef_dll_wrapper:对libcef库的C++代码封装。上述cefclient与cefsimple都会用它。

可以移植 cefsimple 来实现一个简单的浏览器程序:

  • simple_app.h、simple_app.cc、simple_handler.h、simple_handler.cc 这四个文件拷到QT工程中;
  • simple_handler.h中的 PlatformTitleChange 定义注释掉;simple_handler.cc 的 OnTitleChange 对这个函数的调用也注释掉;
  • CEF.lib等相关库 以及 libcef_dll_wrapper.lib 拷贝到工程中,链接到QT工程里;
  • CEF的include文件夹拷贝到QT工程位置并引入;
  • CEF的 Resources 中的五项拷贝到QT编译出来的EXE的环境里;这些是网页渲染引擎需要的资源,不打包过去程序运行时会报错:icu_util.cc(224)] Invalid file descriptor to ICU data received.

QT 与 CEF 嵌入的网页通信有两种方式;

1. 使用 QWebChannel

这种方法与上面使用 webengine 时类似, 在QT 与 JS中声明channel, 通过将这个channel对象来作为中间通信的桥梁;
这种方式有一个优点是,可以通过 websocket 来让 Channel 监听指定的IP和端口来实现远程通信,而不仅仅局限于QT与它启动的web子进程;

2. 通过 CefRenderProcessHandler 的方法回调传递

  • virtual void OnWebKitInitialized() {}
    当 webkit 初始化完毕之后。在这个函数中,可以通过 CefRegisterExtension() 函数来注册JavaScript与C++代码之间的“映射”关系,官方管这种方式叫做扩展(Extension)

  • virtual void OnContextCreated(CefRefPtr browser, CefRefPtr frame, CefRefPtr context) {}
    JavaScript 上下文被创建以后,在这个函数中,可以为JavaScript中的 window 对象绑定属性和方法,官方将这种方式叫做 窗口绑定(window binding)

3. CEF网页通信示例

源码见 QTWebtest_CEF
在示例代码里,我写了一个网页,将这个网页用CEF展示在了QT窗口中,然后另外打开了一个QT对话框;
通过 channel 来在网页与对话框之间发送信息,展示效果如下:
在这里插入图片描述

CEF使用的一些问题:
不支持视频,需要自编译源码;原生的CEF是纯C代码,libcef_dll_wrapper才是在CEF的基础上封装的C++ API;
很大,我使用的版本的 libcef.dll 164MB, libcef_dll_wrapper.lib 33MB ,还有一些相关的加一起超200MB;
MD/MT属性支持的不好,CEF工程默认使用的是MT,其中用到的一些依赖库如cef_sandbox.lib都是MT编译的,使用MD编译会报错,要是改成MD需要自己下载相关源码编译;
多进程架构,WebEngine应该是一个view一个进程,如果只有一个view然后路径切换不会增加新的进程,CEF应该是每打开一次网页就会创建一个渲染进程;


http://www.ppmy.cn/news/1113199.html

相关文章

Java集合之LinedList

LinedList类实现了List接口&#xff0c;他提供了&#xff08;双向的&#xff09;链表数据结构 在该链表中的每一个元素除了存储本身的内容之外还存储指向前一个元素的指针和指向后一个元素的指针&#xff0c;下图展示了一个包含三个元素的双向链表&#xff0c;每个链表都有一个…

无涯教程-JavaScript - AGGREGATE函数

描述 返回列表或数据库中的聚合。 AGGREGATE函数可以将不同的聚合函数应用于列表或数据库,并且可以选择忽略隐藏的行和错误值。 AGGREGATE函数具有两种不同的格式- 参考格式数组格式 参考格式 语法 AGGREGATE (function_num, options, ref1, [ref2] …)争论 Argument描述…

Spring Boot整合Lombok全过程及详细用法

介绍 Lombok是一个Java库&#xff0c;它通过注解的方式简化了Java代码的编写。它可以自动为我们生成一些常见的方法和代码&#xff0c;如getter和setter方法、构造函数以及toString()方法等。在Spring Boot项目中使用Lombok可以大大减少冗余的代码&#xff0c;使代码更加简洁和…

【Nacos】Nacos服务注册服务端源码分析(一)

上篇简单看了下Nacos客户端在服务注册时做了什么。 本篇开始分析Nacos在服务注册时&#xff0c;服务端的相关逻辑。 建议先阅读这篇文章&#xff1a;支持 gRPC 长链接&#xff0c;深度解读 Nacos 2.0 架构设计及新模型 回顾一下&#xff0c;上篇我们看了Nacos在服务注册时&a…

解决 SQLyog 连接 MySQL8.0+ 报错:错误号码2058

文章目录 一、问题现象二、原因分析三、解决方案1. 方案1&#xff1a;更新SQLyog版本2. 方案2&#xff1a;修改用户的授权插件3. 方案3&#xff1a;修复my.cnf 或 my.ini配置文件 四、最后总结 本文将总结如何解决 SQLyog 连接 MySQL8.0 时报错&#xff1a;错误号码2058 一、问…

C++ 动态库与静态库的区别?

一、生成方式 静态库生成 g -c add.cc -o add.o g -c del.cc -o del.o ar rcs libapi.a del.o add.o g -static main.cc -o static main -L./ -lapi -l./ ./static main动态库生成 g -c add.cc -o add.o g -c del.cc -o del.o g -shared -fPIC -o libapi.so del.o add.o g m…

9.10数字逻辑

基础内容 module 模块名&#xff08;【端口列表】&#xff09; 端口信号声明 信号数据类型有wire,reg 信号位宽 模块把输入的input转化为output 数据类型默认为wire&#xff0c;wire表电路间的连线 assign赋值目标必须是wire&#xff0c;始终激活&#xff0c;连续赋值语…

Linux入门教程||Linux 文件与目录管理

我们知道Linux的目录结构为树状结构&#xff0c;最顶级的目录为根目录 /。 其他目录通过挂载可以将它们添加到树中&#xff0c;通过解除挂载可以移除它们。 在开始本教程前我们需要先知道什么是绝对路径与相对路径。 绝对路径&#xff1a; 路径的写法&#xff0c;由根目录 /…