Qt WebChannel JavaScript API

本页解释了如何在HTML客户端中使用JavaScript QWebChannel API。

设置JavaScript API

要与QWebChannelWebChannel通信,客户端必须使用并设置由qwebchannel.js提供的JavaScript API。对于在Qt WebEngine内部运行的客户端,可以通过qrc:///qtwebchannel/qwebchannel.js加载该文件。对于外部客户端,您需要将该文件复制到您的Web服务器上。然后实例化一个QWebChannel对象,并传递给它一个传输对象和一个回调函数,该回调函数将在通道初始化完成且发布的对象可用时被调用。可选的第三个参数包含一个转换器包装函数数组或单个函数。

传输对象实现了一个最小的消息传递接口。它应该是一个具有send()函数的对象,该函数接受一个字符串化的JSON消息并将其传输到服务器端的QWebChannelAbstractTransport对象。此外,当接收到来自服务器的消息时,应调用其onmessage属性。或者,您可以使用WebSocket来实现该接口。

请注意,JavaScript QWebChannel 对象应在传输对象完全运行后构造。对于 WebSocket 来说,这意味着你应该在套接字的 onopen 处理程序中创建 QWebChannel。查看 Qt WebChannel 独立示例 以了解如何完成此操作。

注意

在同一页面中,每个传输只能创建一个QWebChannel对象。

转换器包装函数可以是一个带有内置转换器名称的字符串,也可以是一个用户提供的函数,该函数将处理的对象作为参数,并返回结果类型,如果函数不适用则返回undefined。如果返回undefined,则处理下一个转换器。如果没有转换器返回undefined以外的值,则正常进行处理。目前唯一的内置转换器函数是“Date”。它接受一个带有ISO 8601日期的字符串,如果语法正确且日期有效,则返回一个新的Date对象。

与QObjects交互

一旦传递给QWebChannel对象的回调被调用,通道就完成了初始化,所有发布的对象都可以通过channel.objects属性被HTML客户端访问。因此,假设一个对象以标识符“foo”发布,那么我们可以如下例所示与之交互。请注意,HTML客户端和QML/C++服务器之间的所有通信都是异步的。属性在HTML端被缓存。此外,请记住,只有可以转换为JSON的QML/C++数据类型才能被正确(反)序列化,从而被HTML客户端访问。

new QWebChannel(yourTransport, function(channel) {

    // Connect to a signal:
    channel.objects.foo.mySignal.connect(function() {
        // This callback will be invoked whenever the signal is emitted on the C++/QML side.
        console.log(arguments);
    });

    // To make the object known globally, assign it to the window object, i.e.:
    window.foo = channel.objects.foo;

    // Invoke a method:
    foo.myMethod(arg1, arg2, function(returnValue) {
        // This callback will be invoked when myMethod has a return value. Keep in mind that
        // the communication is asynchronous, hence the need for this callback.
        console.log(returnValue);
    });

    // Read a property value, which is cached on the client side:
    console.log(foo.myProperty);

    // Writing a property will instantly update the client side cache.
    // The remote end will be notified about the change asynchronously
    foo.myProperty = "Hello World!";

    // To get notified about remote property changes,
    // simply connect to the corresponding notify signal:
    foo.myPropertyChanged.connect(function() {
        console.log(foo.myProperty);
    });

    // One can also access enums that are marked with Q_ENUM:
    console.log(foo.MyEnum.MyEnumerator);
});

重载方法和信号

当你发布一个具有重载方法的QObject时,QWebChannel会将方法调用解析为最佳匹配。请注意,由于JavaScript的类型系统,只有一个单一的'number'类型,它最好映射到C++的'double'类型。当重载仅在类似数字参数的类型上有所不同时,QWebChannel将始终选择与JavaScript的'number'类型最佳匹配的重载。当你连接到一个重载的信号时,QWebChannel客户端默认只会连接到该名称的第一个信号重载。此外,方法和信号的重载可以通过其完整的QMetaMethod签名显式请求。假设我们在C++端有以下QObject子类:

class Foo : public QObject
{
    Q_OBJECT
slots:
    void foo(int i);
    void foo(double d);
    void foo(const QString &str);
    void foo(const QString &str, int i);

signals:
    void bar(int i);
    void bar(const QString &str);
    void bar(const QString &str, int i);
};

然后你可以在JavaScript端像这样与这个类进行交互:

// methods
foo.foo(42); // will call the method named foo which best matches the JavaScript number parameter, i.e. foo(double d)
foo.foo("asdf"); // will call foo(const QString &str)
foo.foo("asdf", 42); // will call foo(const QString &str, int i)
foo["foo(int)"](42); // explicitly call foo(int i), *not* foo(double d)
foo["foo(QString)"]("asdf"); // explicitly call foo(const QString &str)
foo["foo(QString,int)"]("asdf", 42); // explicitly call foo(const QString &str, int i)

// signals
foo.bar.connect(...); // connect to first signal named bar, i.e. bar(int i)
foo["bar(int)"].connect(...); // connect explicitly to bar(int i)
foo["bar(QString)"].connect(...); // connect explicitly to bar(const QString &str)
foo["bar(QString,int)"].connect(...); // connect explicitly to bar(const QString &str, int i)