相关文章推荐
豪爽的豌豆  ·  注意 ansi c 库函数 ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I want to access QtObject from HTML-page running in WebView - invoke methods, read/write properties, etc.

As far as I understood, I need to establish WebSockets connection between QML and HTML sides, and then use it as a transport for WebChannel .

Don't confuse WebView with WebEngineView - I know how to do it with WebEngineView, but I need to do it with WebView.

So, here's what I have.

QML side

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 side

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);

Simple message exchange works fine (socket.send()), so transport is okay, but how do I assign WebChannel to WebView? With WebEngineView it was simple, there is a webChannel property there (and there is even no need in using WebSockets), but there is nothing alike in WebView. I mean, something has to tell WebView that WebChannel contains my QtObject so it would be visible to JS?

And if WebView does not support WebChannel(?), how to do it with external browser then? This example shows that it is possible with C++, but I want to do it with QML.

I use Qt 5.11.1.

WebView does not support WebChannel by default. So the solution is to use WebSocketServer with QWebChannelAbstractTransport as I show below:

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>
        <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]);
                    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>
        <textarea id="output"></textarea><br />
        <input id="input" /><input type="submit" id="send" value="Send" onclick="javascript:click();" />
    </body>
</html>

The complete example can be found in the following link

So it is not possible to do everything with QML, some C++ code (transport implementation) has to be done first, and that's what I was missing. Thank you very much for your work, that's an excellent explanation. – retif Jul 13, 2018 at 9:30

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.