QWebChannel类的作用是向远端HTML客户端暴露 QObject。
QWebChannel填补了C++应用程序和HTML/JavaScript 应用程序之间的空白。通过将QObject派生对象发布到QWebChannel并在HTML中引入qwebchannel.js脚本可从此处获取。在HTML端,可以透明地访问QObject的属性、公共槽和方法。不需要手动消息传递和数据序列化,C++方面的属性更新和信号发射将自动传输到可能远程运行的HTML客户机。在客户端,将为任何发布的C++ QObject创建JavaScript对象。它反映了C++对象的API,因此可以直观地使用。
但是,Web端与C++之间怎么进行通信能? 有两个方法:
QWebEngine
提供一个 web 引擎,用于在 Qt 应用中嵌入任意的网页内容。Qt WebEngine 是基于 Chromium 项目实现的,提供了一个 js 的宿主环境,内部实现了js调用C++的环境;Websocket
C++端建立websocket server,Web端连接,qwebchannel.js会获取到C++端所有的属性、槽函数等。// 要导出的类,此类供js调用
m_myTestClass = new MyTestClass(this);
// 创建QWebChannel,把创建的类注册到QWebChannel中,js才能调用此类的方法
m_webChannel = new QWebChannel(this);
m_webChannel->registerObject("mytestclass", m_myTestClass);
// js与C++通信方式一、使用QWebEngineView加载网页,web端js与C++之间的通信
QString strHtml = QApplication::applicationDirPath() + "/../../testWeb/test.html";
// QWebEngineView 基于 Chromium 的 web 引擎
m_webEngineView = new QWebEngineView(this);
m_webEngineView->load(QUrl::fromLocalFile(strHtml));
m_webEngineView->page()->setWebChannel(m_webChannel);
ui->verticalLayout->addWidget(m_webEngineView);
导出类 MyTestClass
,供js调用
#ifndef MYTESTCLASS_H
#define MYTESTCLASS_H
#include
#include
class MyTestClass : public QObject
{
Q_OBJECT
// 导出的属性
Q_PROPERTY(QString navStatus MEMBER m_navStatus NOTIFY navStatusChanged)
public:
explicit MyTestClass(QObject *parent = nullptr);
signals:
// 导出的事件
void navStatusChanged(const QString& navStatus);
public slots:
// 导出的槽函数
void function1(const QString& str);
private:
void setNavStatus(const QString& status);
QString m_navStatus;
};
#endif // MYTESTCLASS_H
#include "mytestclass.h"
#include
#include
#include
MyTestClass::MyTestClass(QObject *parent) : QObject(parent),
m_navStatus("hello")
{
}
void MyTestClass::function1(const QString& str)
{
setNavStatus(str);
qDebug() << __FUNCTION__ << str;
}
void MyTestClass::setNavStatus(const QString &status)
{
m_navStatus = status;
emit navStatusChanged(m_navStatus);
}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<button id="callcpp" onclick="callcpp()">callcppbutton>
<button onclick="getValue()">getValuebutton>
<script src="qwebchannel.js">script>
<script type="text/javascript">
var webObj;
// 创建Webchannel,与C++端建立连接
new QWebChannel(qt.webChannelTransport, function (channel) {
// 获取类的对象
webObj = channel.objects.mytestclass;
// 类的事件
webObj.navStatusChanged.connect(function(arg){
alert("navStatusChanged: " + arg);
});
});
function callcpp(){
// 类的方法
webObj.function1('this is a test');
}
function getValue(){
// 类的属性值
var status = webObj.navStatus;
alert(status);
}
script>
body>
html>
其中,qwebchannel.js
取自 Qt安装目录的 Qt5.12.0\Examples\Qt-5.12.0\webchannel\shared
目录。
(1)Web端的getValue
获取C++对象的属性navStatus
,属性初始值为 “hello”
(2)Web端的 callcpp
调用 C++端的函数 function1
,C++端function1函数中打出
(3)Web端响应 navStatusChanged
事件
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 要导出的类,此类供js调用
m_myTestClass = new MyTestClass(this);
// 创建QWebChannel,把创建的类注册到QWebChannel中,js才能调用此类的方法
m_webChannel = new QWebChannel(this);
m_webChannel->registerObject("mytestclass", m_myTestClass);
// js与C++通信方式二、创建QWebsocketServer,web端与之建立连接
startServer();
}
MainWindow::~MainWindow()
{
delete ui;
}
// 建立WebSocket服务
void MainWindow::startServer()
{
m_websocketServer = new QWebSocketServer("testWebchannel", QWebSocketServer::NonSecureMode, this);
if(!m_websocketServer->listen(QHostAddress::Any, 12345))
{
qDebug() << "websocket server listen failed, error: " << m_websocketServer->errorString();
return;
}
connect(m_websocketServer, &QWebSocketServer::newConnection, this, &MainWindow::onNewConnection);
qDebug() << "startServer";
}
void MainWindow::onNewConnection()
{
QWebSocket* client = m_websocketServer->nextPendingConnection();
qDebug() << (QString("A new connection from %1.%2").arg(client->peerAddress().toString()).arg(client->localPort()));
auto pTransport = new WebSocketTransport(client);
// 可以不需要,这里只是为了调试打印js端的qwebchannel.js是怎么和C++端通信的,通信协议是什么样的
connect(pTransport, &WebSocketTransport::messageReceived, this, &MainWindow::onTransportMessageReceived);
m_webChannel->connectTo(pTransport);
}
void MainWindow::onTransportMessageReceived(const QJsonObject &message, QWebChannelAbstractTransport *transport)
{
qDebug() << "onTransportMessageReceived: " << message;
}
导出类 MyTestClass
,供js调用。
代码中 WebSocketTransport
类代码,取自 Qt安装目录的 Qt5.12.0\Examples\Qt-5.12.0\webchannel\shared
目录下的 websockettransport.h
websockettransport.cpp
。
此处省略。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<button id="callcpp" onclick="callcpp()">callcppbutton>
<button onclick="getValue()">getValuebutton>
<script src="qwebchannel.js">script>
<script type="text/javascript">
var webObj;
// 连接c++端的 Websocket
var socket = new WebSocket('ws://127.0.0.1:12345');
// 连接成功后
socket.onopen = function(){
alert("onopen");
// 创建Webchannel
new QWebChannel(socket, function (channel) {
// 获取类的对象
webObj = channel.objects.mytestclass;
// 类的事件
webObj.navStatusChanged.connect(function(arg){
alert("navStatusChanged: " + arg);
});
});
}
function callcpp(){
// 类的方法
webObj.function1('this is a test');
}
function getValue(){
// 类的属性值
var status = webObj.navStatus;
alert(status);
}
script>
body>
html>
与2.2中的Web端代码相比,此时,需要先创建WebSocket连接,把socket传入QWebChannel中。
(1)运行C++, C++代码没有加载html, 此时为空界面;
(2)使用Chrome浏览器,或者Edge浏览器打开Web测试网页,一打开就与C++端的websocket连接上了
(3)接下来与2.3的演示一样。
我这里端口用的12345,可能在有些电脑上此端口被占用了,会报错:
所有代码详见:QWebChannel