网上有一篇与此类似的文章,而且网上大部分的文章基本上都是复制来复制去的,目前我看过的也就是俩DEMO了,而且都是缺斤少两的货(代码不全,复制过来也跑不起来)。。。
为此我专门自己花了几天(实则加起来也就几个小时)自己硬啃DEMO + 个人理解总算是把完整的DEMO给整出来了。
网上的DEMO这里引用一下:http://www.cnblogs.com/wkcagd/p/7732330.html
跟评论里说的一样,缺俩类的实现,尤其是Core类不知道是个什么鬼,而且我在Qt上无意中还发现了,原来这是官方的DEMO照搬了过来,搞的我好一阵折腾啊,半天没理解是个啥意思,毕竟第一次接触C++与JS的交互编程,要是用JS与PHP那就简单了,一个Ajax就搞定,不过这就扯远了。。。
这里我先把评论里的问题解决一下,评论里的问题也是我的问题。
首先qwebchannel.js文件是在Qt的安装目录下\Examples\Qt-5.11.2\webchannel\shared\qwebchannel.js,直接copy&paste就完事了,在index.html里用<script>标签引用一下,这就是前端的工作了,而且工作量并不大,而且很简单所以就不多说了。
其次就是Dialog和Core这俩压根没找到实现的类,我经过了各种可能的猜测,其中Dialog最好理解,就是那个窗口嘛,而我给的DEMO直接上控制台(Console)就可以了,压根不需要对话框这个多余的东西,不过官方可能也是让我们方便调试才用的图形化窗口。但就连Core都不提供就不能忍了,刚开始看这东西我完全是懵逼状态的,这是个啥啊?后来分析了也猜了好久才明白,这是个连接类,这还得结合JS端来分析,单从C++的角度来分析那是作死,反正我已经作过死了,不能让我们的读者朋友们继续作死了,所以这里重点讲一下Core连接类,当然我自己的DEMO并不是这么写的。
接下来就是实现部分了,从0到1的实现通过QWebChannel让C++与JS端进行交互的编程(这名字我自己都有点受不了,不管了,重点在内容)。
首先我先建一个基于控制台的Qt应用程序,构建组件那块一定要选择MinGW
接下来我们把Qt安装目录下的Examples\Qt-5.11.2\webchannel\shared下的文件全部复制到工程目录下,顺便建一个index.html
然后在Qt中将cpp与h文件全部导入进工程中
打开pro文件,里面加入QT += core websockets webchannel
main.cpp文件里包含websocketclientwrapper.h、websockettransport.h、<QtWebChannel/QWebChannel>、<QtWebSockets/QWebSocketServer>
这样,初始化的工作就基本结束了,我们要通过WebSocket与WebChannel让C++与JS达成交互的目的。
在main.cpp的QCoreApplication a(argc, argv);与return a.exec();之间写上以下代码
QWebSocketServer server(QStringLiteral("QWebChannel Standalone Example Server"),
QWebSocketServer::NonSecureMode);
if (!server.listen(QHostAddress::LocalHost, 12345)) {
qFatal("Failed to open web socket server.");
return 1;
}
// wrap WebSocket clients in QWebChannelAbstractTransport objects
WebSocketClientWrapper clientWrapper(&server);
// setup the channel
QWebChannel channel;
QObject::connect(&clientWrapper, &WebSocketClientWrapper::clientConnected,
&channel, &QWebChannel::connectTo);
// setup the dialog and publish it to the QWebChannel
ChatServer* chatserver = new ChatServer(&a);
channel.registerObject(QStringLiteral("chatserver"), chatserver);
别问我WebSocketClientWrapper是干嘛用的,这我也不清楚,我只知道这样的代码能用。。。
现在要实现ChatServer类了。
在实现之前我们要知道这个Core是个连接类,用于连接C++与JS的,在这里ChatServer起着与Core一样的作用,但是如何来实现呢?
这点就是我研究了几个小时来的成果了,提一个关键字“元数据”。Qt里有一个宏叫Q_INVOKABLE可以用于声明元数据,只要是声明了元数据的公共函数就可以被JS端所调用。
现在我上代码了
chatserver.h
#ifndef CHATSERVER_H
#define CHATSERVER_H
#include <QObject>
class ChatServer : public QObject
{
Q_OBJECT
public:
explicit ChatServer(QObject *parent = nullptr);
virtual ~ChatServer();
Q_INVOKABLE void test1();
signals:
void test2();
};
#endif // CHATSERVER_H
chatserver.cpp
#include "chatserver.h"
#include <QDebug>
ChatServer::ChatServer(QObject *parent)
: QObject(parent)
{}
ChatServer::~ChatServer()
{}
void ChatServer::test1() {
qDebug() << "This is test1";
}
index.html
<!DOCTYPE html>
<html>
<head>
<title>ChatClient</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script type="text/javascript" src="qwebchannel.js"></script>
<script>
'use strict';
var wsUri = "ws://localhost:12345";
window.onload = function() {
var socket = new WebSocket(wsUri);
socket.onclose = function() {
console.error("web channel closed");
};
socket.onerror = function(error) {
console.error("web channel error: " + error);
};
socket.onopen = function() {
window.channel = new QWebChannel(socket, function(channel) {
channel.objects.chatserver.test1();
});
}
}
</script>
</head>
<body>
</body>
</html>
有了上面的代码,编译运行C++端,等黑窗口出来后打开index.html访问网页,回到C++端就会发现“This is test1”字符串,这就说明我们成功的从JS端调用C++的函数了。
这里我解释一下,在C++端channel.registerObject(QStringLiteral("chatserver"), chatserver);就等于是在注册元数据信息,让JS端可以访问到元数据,名称可以把字符串的chatserver改成其他的,比如core变成:channel.registerObject(QStringLiteral("core"), chatserver);
在JS端把chatserver改成core,再编译运行C++端,然后打开index.html发现依然可以输出“This is test1”字符串,就表示QStringLiteral("XXXX")是可以指定暴露给JS端的元数据名称的,而后面的chatserver或是core都是连接类了,暴露给JS端的类。
跑完了JS端调用C++端的,再看看C++调用JS端的,这其实也挺简单的,在C++上建立一个信号,emit一个信号,在JS端绑定信号就可以用于接收C++的信号了,从而实现C++调用JS函数的效果。这说起来可能有点晕,直接上代码。
这回只需要JS端的代码了,因为C++端的上面已经有了,也就是signals一个信号出来,在JS端连接一下。
socket.onopen = function() {
window.channel = new QWebChannel(socket, function(channel) {
channel.objects.core.test2.connect(function () {
console.log('aaaa');
});
channel.objects.core.test1();
});
}
我们在test1函数中emit一下test2
void ChatServer::test1() {
qDebug() << "This is test1";
emit test2();
}
编译C++端并运行,然后打开index.html,按F12点击控制台选项,看到了aaaa,说明从C++端调用JS函数也是没问题的
总结
C++端相当于是服务端,JS端相当于是客户端
C++的元数据可供JS端访问,触发信号可调用JS端函数
不知道各位网友有没有看懂呢?如果还有不明白的可以留言,反正我也不一定知道,但知道的还是会尽量解答一下的,我只能尽我所能了。希望这篇文章能帮助到那些遇到QWebChannel的坑,和我一样半天过不去的猿们。。。