(译)通过WebChannel/WebSockets与QML中的HTML交互

栏目: C++ · 发布时间: 6年前

内容简介:来源:GitHub:作者:狐狸家的鱼

来源: 通过WebChannel/WebSockets与QML中的HTML交互

GitHub: 八至

作者:狐狸家的鱼

本文链接:QML与HTML交互

在查询QML与HTML之间通信交互时资料很少,这篇文章讲解的比较清楚

一、前言

Qt允许使用所谓的混合GUI创建应用程序——在这种GUI中,可以将本机部件与基于html的内容混合在一起。通过WebChannel和WebSockets公开QObject,这种混合甚至支持这些本地部分和html端之间的交互。

(译)通过WebChannel/WebSockets与QML中的HTML交互

二、如何显示HTML内容

  1. 使用 webEngineView
  2. 使用 webView
  3. 使用独立的Web浏览器(不会集成到应用程序中);

这三种方法以不同的方式进行,但都支持QML和HTML之间的通信。

确切的说,WebEngineView以一种方式完成,而WebView(就像网络浏览器一样)以另一种方式完成。WebEngineView和WebView是两码事。

(译)通过WebChannel/WebSockets与QML中的HTML交互

(1) webEngineView

WebEngineView是由Qt自己基于Chromium(Qt WebEngine)的web浏览器引擎提供的web视图。它是一个功能齐全的web浏览器,与Qt捆绑并集成在一起,这很好,但同时这意味着您需要将它与您的应用程序一起拖动,这是一个相当大的东西。

(2) webView

WebView是一个web视图,但不同之处在于它使用平台的本地web浏览器(如果可用的话),因此它不需要将完整的web浏览器堆栈作为应用程序的一部分(WebEngineView就是这种情况),因此您的应用程序更轻量级。另一点是,有些平台根本不允许任何非系统的web浏览器,因此WebView是唯一可用的选项。

(3) webEngineView 和 webView的 区别

根据本文,WebEngineView和WebView的关键区别在于Qt如何与这些视图中的html内容通信。由于Chromium IPC功能,WebEngineView提供了最简单的方式-直接通过WebChannel,。而WebView(以及外部web浏览器)要求您首先为WebChannel建立一些传输。

三、与QML中的HTML交互

好的,我们可以显示HTML,但是如何从QML与之交互呢?一切都通过WebChannel。在HTML端,它是通过特殊的JavaScript库- Qt WebChannel JavaScript API 完成的。

(1) WebEngineView - 直接使用WebChannel

WebEngineView可以直接使用WebChannel,以这个 存储库 为基础进行讲解。

main.qml

// 一个具有属性、信号和方法的对象——就像任何普通的Qt对象一样
QtObject {
    id: someObject

    // ID,在这个ID下,这个对象在WebEngineView端是已知的
    WebChannel.id: "backend"

    property string someProperty: "Break on through to the other side"

    signal someSignal(string message);

    function changeText(newText) {
        txt.text = newText;
        return "New text length: " + newText.length;
    }
}

Text {
    id: txt
    text: "Some text"
    onTextChanged: {
        // 此信号将在WebEngineView端触发一个函数(如果连接的话)
        someObject.someSignal(text)
    }
}

WebEngineView {
    url: "qrc:/index.html"
    webChannel: channel
}

WebChannel {
    id: channel
    registeredObjects: [someObject]
}复制代码

这里我们创建WebChannel并将其ID分配给WebEngineView,并在通道上注册QtObject的ID。当然,您可以从c++端“注入”一个c++ /Qt对象,而不是在QML端定义的QtObject。

index.html

<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>

<script type="text/javascript">
    // 这是QML端的QtObject
    var backend;

    window.onload = function()
    {
        new QWebChannel(qt.webChannelTransport, function(channel) {
            // 在channel.object下,所有发布的对象在通道中都是可用的
            // 在附加的WebChannel.id属性中设置的标识符。
            backend = channel.objects.backend;

            //连接信号
            backend.someSignal.connect(function(someText) {
                alert("Got signal: " + someText);
                document.getElementById("lbl").innerHTML = someText;
            });
        });
    }

    // 演示异步交互
    var result = "ololo";
    function changeLabel()
    {
        var textInputValue = document.getElementById("input").value.trim();
        if (textInputValue.length === 0)
        {
            alert("You haven't entered anything!");
            return;
        }

        // 调用方法并接收返回值
        backend.changeText(textInputValue, function(callback) {
            result = callback;
            // 由于它是异步的,因此稍后将出现此警报并显示实际结果
            alert(result);
            // 将变量重置为默认值
            result = "ololo";
        });
        // 此警告将首先出现,并显示默认的“ololo”
        alert(result);
    }

    // 您还可以从QML端读取/写入QtObject的属性
    function getPropertyValue()
    {
        var originalValue = backend.someProperty;

        alert(backend.someProperty);
        backend.someProperty = "some another value";
        alert(backend.someProperty);

        backend.someProperty = originalValue;
    }
</script>复制代码

在这里,您需要在windows.onload事件上创建一个QWebChannel并获取后端对象。之后,您可以调用它的方法,连接到它的信号并访问它的属性。

下面是一个简单的例子,演示了QML(蓝色矩形外的所有内容)和HTML(蓝色矩形内的部分)之间的通信:

(译)通过WebChannel/WebSockets与QML中的HTML交互

这是它的模式:

(译)通过WebChannel/WebSockets与QML中的HTML交互

注意,交互是异步完成的——查看changeLabel()函数并注意警报的顺序。

(2) WebView - WebSockets上的WebChannel

WebView(和外部Web浏览器)无法直接使用WebChannel。您需要首先创建一个WebSockets传输,然后在其上使用WebChannel。

这仅使用QML是无法实现的,因此您还必须编写一些C ++代码。这有点令人沮丧,但更令人沮丧的是文档没有明确提到它。

所以,当我发现这一点时,我决定重写一个C ++示例。当我差不多完成时,我也得到了Stack Overflow的答案,几乎展示了如何在QML中做的所有事情,我最终得到了两个解决方案,如下。

(a) 主要是c++完成

这个函数的大部分工作都是用c++完成的,QML没用什么。

main.cpp

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    // 不要忘记这个
    QtWebView::initialize();

    QWebSocketServer server(
                QStringLiteral("WebSockets example"),
                QWebSocketServer::NonSecureMode
                );
    if (!server.listen(QHostAddress::LocalHost, 55222)) { return 1; }

    // 在QWebChannelAbstractTransport对象中包装WebSocket客户端
    WebSocketClientWrapper clientWrapper(&server);

    // 设置通道
    QWebChannel channel;
    QObject::connect(&clientWrapper, &WebSocketClientWrapper::clientConnected,
                     &channel, &QWebChannel::connectTo);

    // 设置核心并将其发布到QWebChannel
    Backend *backend = new Backend();
    channel.registerObject(QStringLiteral("backend"), backend);

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("someObject", backend);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty()) { return -1; }

    return app.exec();
}复制代码

这里最重要的是WebSocketClientWrapper(WebSocketTransport在下面使用)。这是必须自己实现的,而文档的帮助不大。

使用WebSocketClientWrapper,您最终可以连接QWebChannel并注册您的对象(在我的例子中是Backend,尽管我保留了相同的ID - someObject),因此它将在HTML端可用。

注意,这次我需要注册一个已经创建的c++对象(不是类型),所以我使用setContextProperty。

index.html

<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>

<script type="text/javascript">
    // 这是QML端的QtObject
    var backend;

    window.onload = function()
    {
        var socket = new WebSocket("ws://127.0.0.1:55222");

        socket.onopen = function()
        {
            new QWebChannel(socket, function(channel) {
                backend = channel.objects.backend;

                // 连接信号
                backend.someSignal.connect(function(someText) {
                    alert("Got signal: " + someText);
                    document.getElementById("lbl").innerHTML = someText;
                });
            });
        };
    }
</script>复制代码

与WebEngineView示例中的index.html不同,这里首先需要建立WebSocket连接,作为QWebChannel的传输。其余的都是一样的。

main.qml

Text {
    id: txt
    text: "Some text"
    onTextChanged: {
        someObject.someSignal(text)
    }
    Component.onCompleted: {
         someObject.textNeedsToBeChanged.connect(changeText)
    }
    function changeText(newText) {
        txt.text = newText;
    }
}

WebView {
    id: webView
    url: "qrc:/index.html"
}复制代码

QML代码也有一点不同。首先,someObject这是一个上下文属性,因此不需要导入和声明它。其次,c++对象和QML组件之间的交互需要再添加一个信号(textNeedsToBeChanged)。

因此,交互模式也变得有点奇怪:

(译)通过WebChannel/WebSockets与QML中的HTML交互

幸运的是,有一个更好的解决方案。下面就是。

(b) 主要是QML

我更喜欢这个例子,因为它主要在QML中完成,C ++上只有一点点。我是在Stack Overflow上得到的这个答案。

首先,我们需要实现WebChannel的传输。

websockettransport.h

class WebSocketTransport : public QWebChannelAbstractTransport
{
    Q_OBJECT

public:
    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);
};复制代码

然后将其注册到QML

main.cpp

#include "websockettransport.h"

int main(int argc, char *argv[])
{
    // ...
    
    qmlRegisterType("io.decovar.WebSocketTransport", 1, 0, "WebSocketTransport");

    // ...
}复制代码

剩下的都在QML

main.qml

import io.decovar.WebSocketTransport 1.0

// ...

// 一个具有属性、信号和方法的对象——就像任何普通的Qt对象一样
QtObject {
    id: someObject

    // ID,在这个ID下,这个对象在WebEngineView端是已知的
    WebChannel.id: "backend"

    property string someProperty: "Break on through to the other side"

    signal someSignal(string message);

    function changeText(newText) {
        txt.text = newText;
        return "New text length: " + newText.length;
    }
}

WebSocketTransport {
    id: transport
}

WebSocketServer {
    id: server
    listen: true
    port: 55222
    onClientConnected: {
        if(webSocket.status === WebSocket.Open) {
            channel.connectTo(transport)
            webSocket.onTextMessageReceived.connect(transport.textMessageReceive)
            transport.onMessageChanged.connect(webSocket.sendTextMessage)
        }
    }
}

Text {
    id: txt
    text: "Some text"
    onTextChanged: {
        //此信号将在WebView端触发一个函数(如果连接)
        someObject.someSignal(text)
    }
}

WebView {
    url: "qrc:/index.html"
}

WebChannel {
    id: channel
    registeredObjects: [someObject]
}复制代码

index.html 与前面的例子相同,创建一个WebSocket并将其用作QWebChannel的传输。

顺便说一下,正如我在前面提到的,WebView和独立/外部浏览器是一样的,所以您可以在web浏览器中打开index.html,它将以相同的方式工作-只是不要忘记从代码中删除qrc:/并复制qwebchannel.js到相同的文件夹。

在这个 存储库 中可以找到这三个示例的完整源代码。

四、后话-关于文档

尽管WebChannel和WebSockets都有超过5个例子,但很难理解它是如何工作的?为什么没有一个让它与QML一起工作的例子?

现在,关于 qwebchannel.js 。看一下文档页面的第一段:

要与QWebChannel或WebChannel通信,客户机必须使用并设置QWebChannel .js提供的JavaScript API。对于运行在Qt WebEngine中的客户机,可以通过qrc:///qtwebchannel/qwebchannel.js加载文件。对于外部客户端,需要将文件复制到web服务器。

因此,对于集成的web视图,我们可以使用一个特殊的资源qrc:///qtwebchannel/qwebchannel。但是我们在哪里可以为外部客户端找到这个文件呢?是的,这个文件在这个或其他任何页面上都找不到。幸运的是,你可以从以下例子中找到答案:

QWebChannelAbstractTransport的文档页面也不是一个详细的页面,因为它没有一行代码,更不用说示例了。它对于WebChannel的必要性是这样不经意间被简单提及的:

请注意,只要将QWebChannel连接到QWebChannelAbstractTransport,它就可以完全运行。

基本上,如果不是我找到的存储库以及在Stack Overflow上获得的帮助 - 我根本无法进行一切工作。


以上所述就是小编给大家介绍的《(译)通过WebChannel/WebSockets与QML中的HTML交互》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Big Java Late Objects

Big Java Late Objects

Horstmann, Cay S. / 2012-2 / 896.00元

The introductory programming course is difficult. Many students fail to succeed or have trouble in the course because they don't understand the material and do not practice programming sufficiently. ......一起来看看 《Big Java Late Objects》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码