QML 中基于 WebSockets 的 Qt WebView 和 WebChannel
Posted
技术标签:
【中文标题】QML 中基于 WebSockets 的 Qt WebView 和 WebChannel【英文标题】:Qt WebView and WebChannel over WebSockets in QML 【发布时间】:2018-07-12 19:26:54 【问题描述】:我想从在WebView 中运行的 html 页面访问QtObject - 调用方法、读/写属性等。
据我了解,我需要在 QML 和 HTML 端之间建立WebSockets 连接,然后将其用作WebChannel 的传输。
不要将 WebView 与 WebEngineView 混淆 - 我知道如何使用 WebEngineView 来做到这一点,但我需要使用 WebView 来做到这一点。
所以,这就是我所拥有的。
QML 端
QtObject
id: someObject
WebChannel.id: "backend"
property string someProperty: “property value"
WebSocketServer
listen: true
port: 55222
onClientConnected:
console.log(webSocket.status);
//webSocket.onTextMessageReceived.connect(function(message)
// console.log(qsTr("Server received message: %1").arg(message));
//);
WebView
url: "index.html"
//webChannel: channel // invalid property name "webChannel"
//experimental.webChannel.registeredObjects: [someObject] // invalid property name "experimental"
WebChannel
id: channel
registeredObjects: [someObject]
HTML 端
window.onload = function()
// here will be QtObject from QML side
var backend;
var socket = new WebSocket("ws://localhost:55222");
socket.onopen = function()
//socket.send("some message");
new QWebChannel(socket, function(channel)
backend = channel.objects.backend;
);
;
function alertProperty()
alert(backend.someProperty);
简单的消息交换工作正常(socket.send()
),所以传输没问题,但是如何将 WebChannel 分配给 WebView?使用 WebEngineView 很简单,那里有一个 webChannel
属性(甚至不需要使用 WebSockets),但在 WebView 中没有任何相似之处。我的意思是,something 必须告诉 WebView WebChannel 包含我的 QtObject,以便 JS 可以看到它?
如果 WebView 不支持 WebChannel(?),那么如何使用外部浏览器呢? This example 表明使用 C++ 是可能的,但我想使用 QML。
我使用 Qt 5.11.1。
【问题讨论】:
QWebView 不支持 QtWebchannel @eyllanesc,不知道,更新了问题 【参考方案1】:WebView
默认不支持WebChannel
。所以解决方案是使用WebSocketServer
和QWebChannelAbstractTransport
,如下所示:
main.cpp
#include <QGuiApplication>
#include <QJsonDocument>
#include <QQmlApplicationEngine>
#include <QWebChannelAbstractTransport>
#include <QtWebView>
class WebSocketTransport : public QWebChannelAbstractTransport
Q_OBJECT
public:
using QWebChannelAbstractTransport::QWebChannelAbstractTransport;
Q_INVOKABLE void sendMessage(const QJsonObject &message) override
QJsonDocument doc(message);
emit messageChanged(QString::fromUtf8(doc.toJson(QJsonDocument::Compact)));
Q_INVOKABLE void textMessageReceive(const QString &messageData)
QJsonParseError error;
QJsonDocument message = QJsonDocument::fromJson(messageData.toUtf8(), &error);
if (error.error)
qWarning() << "Failed to parse text message as JSON object:" << messageData
<< "Error is:" << error.errorString();
return;
else if (!message.isObject())
qWarning() << "Received JSON message that is not an object: " << messageData;
return;
emit messageReceived(message.object(), this);
signals:
void messageChanged(const QString & message);
;
int main(int argc, char *argv[])
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<WebSocketTransport>("com.eyllanesc.org", 1, 0, "WebSocketTransport");
QtWebView::initialize();
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
#include "main.moc"
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtWebSockets 1.1
import QtWebView 1.1
import QtWebChannel 1.0
import com.eyllanesc.org 1.0
Window
visible: true
width: 640
height: 480
title: qsTr("Hello World")
WebView
url: "qrc:/index.html"
anchors.fill: parent
QtObject
id: someObject
property string someProperty: "prop"
WebChannel.id: "core"
function receiveText(text)
console.log("receiveText: ", text)
signal sendText(string text)
WebSocketTransport
id: transport
WebSocketServer
listen: true
port: 12345
onClientConnected:
if(webSocket.status === WebSocket.Open)
channel.connectTo(transport)
webSocket.onTextMessageReceived.connect(transport.textMessageReceive)
transport.onMessageChanged.connect(webSocket.sendTextMessage)
WebChannel
id: channel
registeredObjects: [someObject]
// testing
Timer
interval: 500
running: true
repeat: true
onTriggered: someObject.sendText(Qt.formatTime(new Date(), "hh:mm:ss") + " from QML")
index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script type="text/javascript">
//BEGIN SETUP
function output(message)
var output = document.getElementById("output");
output.innerHTML = output.innerHTML + message + "\n";
window.onload = function()
if (location.search != "")
var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]);
else
var baseUrl = "ws://localhost:12345";
output("Connecting to WebSocket server at " + baseUrl + ".");
var socket = new WebSocket(baseUrl);
socket.onclose = function()
console.error("web channel closed");
;
socket.onerror = function(error)
console.error("web channel error: " + error);
;
socket.onopen = function()
output("WebSocket connected, setting up QWebChannel.");
new QWebChannel(socket, function(channel)
// make core object accessible globally
window.core = channel.objects.core;
input.innerHTML = core.someProperty;
document.getElementById("send").onclick = function()
var input = document.getElementById("input");
var text = input.value;
if (!text)
return;
output("Sent message: " + text );
input.value = "";
core.receiveText(text + " From HTML");
core.sendText.connect(function(message)
output("Received message-" + core.someProperty + " : " + message);
);
core.receiveText("Client connected, ready to send/receive messages!");
output("Connected to WebChannel, ready to send/receive messages!");
);
//END SETUP
</script>
<style type="text/css">
html
height: 100%;
width: 100%;
#input
width: 400px;
margin: 0 10px 0 0;
#send
width: 90px;
margin: 0;
#output
width: 500px;
height: 300px;
</style>
</head>
<body>
<textarea id="output"></textarea><br />
<input id="input" /><input type="submit" id="send" value="Send" onclick="javascript:click();" />
</body>
</html>
完整的例子可以看下面link
【讨论】:
所以不可能用 QML 做所有事情,必须先完成一些 C++ 代码(传输实现),而这正是我所缺少的。非常感谢您的工作,这是一个很好的解释。以上是关于QML 中基于 WebSockets 的 Qt WebView 和 WebChannel的主要内容,如果未能解决你的问题,请参考以下文章
如何在QML(QT Creator)中使用发光脉动效果制作元素动画?