Qt WebChannel JavaScript API

设置 JavaScript API

要通信与 QWebChannel or WebChannel ,客户端必须使用和设置 JavaScript API 提供通过 qwebchannel.js 。若客户端运行在 Qt WebEngine ,可以加载文件通过 qrc:///qtwebchannel/qwebchannel.js 。对于外部客户端,需要将文件拷贝到 Web 服务器。 然后实例化 QWebChannel object and pass it a transport object and a callback function, which will be invoked once the initialization of the channel finishes and the published objects become available. An optional third argument contains an array of converter wrapper functions or a single one.

传输对象实现最少消息传递接口。它应是对象且具有 send() 函数,其接受字符串化 JSON 消息,并将消息传输到服务器侧 QWebChannelAbstractTransport 对象。此外,它的 onmessage 特性应被调用,当从自服务器收到消息时。另外,可以使用 WebSocket 去实现接口。

注意:JavaScript QWebChannel 对象应该被构造一旦传输对象可完整操作。对于 WebSocket,意味着应该创建 QWebChannel 在套接字的 onopen 处理程序。查看 Qt WebChannel 独立范例 看如何做到这点。

注意: Only a single QWebChannel object per transport can be created in the same page.

A converter wrapper function is either a string with the name of a built-in converter or a user supplied function that takes the object to process as an argument and returns the resultant type or undefined if the function does not apply. If undefined is returned the next converter is processed. If there are no converters that returns a value other than undefined, processing proceeds as normal. "Date" is the only currently built-in converter function. It takes a string with an ISO 8601 date and returns a new Date object if the syntax is right and the date is valid.

与 QObject 交互

一旦回调传递给 QWebChannel 对象被援引,通道已完成初始化且 HTML 客户端可访问所有已发布对象凭借 channel.objects 特性。因此,假定采用标识符 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。当重载仅在像 number 参数的类型上有所不同时, QWebChannel 将始终选取最佳匹配 JavaScript number 类型的重载。当连接到被重载信号时, QWebChannel 默认情况下,客户端只会连接到该名称的第一信号重载。此外,可以明确请求方法和信号的重载通过其完整 QMetaMethod 签名。假定有以下 QObject 子类在 C++ 侧:

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)