如何使用QWebChannel从python接收数据到js?

Eva*_*ain 1 javascript python pyqt pyqt5 qtwebchannel

我正在尝试让我的 PyQt 应用程序与 JS 通信,但无法从 python 获取值。我在 python 端有两个插槽来获取和打印数据。在示例中,一个 int 从 JS 传递给 python,python 将其加 5 并将其传回,然后 JS 调用另一个槽来打印新值。

var backend = null;
var x = 15;
new QWebChannel(qt.webChannelTransport, function (channel) {
    backend = channel.objects.backend;
    backend.getRef(x, function(pyval){
        backend.printRef(pyval)
    });
});
Run Code Online (Sandbox Code Playgroud)
@pyqtSlot(int)
def getRef(self, x):
    print('inside getRef', x)
    return x + 5

@pyqtSlot(int)
def printRef(self, ref):
    print('inside printRef', ref)
Run Code Online (Sandbox Code Playgroud)

输出:

inside getRef 15
Could not convert argument QJsonValue(null) to target type int .
Run Code Online (Sandbox Code Playgroud)

预期的:

inside getRef 15
inside printRef 20
Run Code Online (Sandbox Code Playgroud)

我无法弄清楚为什么返回的值为空。我如何将该 pyval 存储到 js 端的变量中以供以后使用?

eyl*_*esc 8

在 C++ 中,方法可以返回一个必须声明为的值,Q_INVOKABLE而 PyQt 中的等效项是result@pyqtSlot装饰器中使用:

??? index.html
??? main.py
Run Code Online (Sandbox Code Playgroud)

主文件

from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets, QtWebChannel


class Backend(QtCore.QObject):
    @QtCore.pyqtSlot(int, result=int)
    def getRef(self, x):
        print("inside getRef", x)
        return x + 5

    @QtCore.pyqtSlot(int)
    def printRef(self, ref):
        print("inside printRef", ref)


if __name__ == "__main__":
    import os
    import sys

    app = QtWidgets.QApplication(sys.argv)

    backend = Backend()

    view = QtWebEngineWidgets.QWebEngineView()

    channel = QtWebChannel.QWebChannel()
    view.page().setWebChannel(channel)
    channel.registerObject("backend", backend)

    current_dir = os.path.dirname(os.path.realpath(__file__))
    filename = os.path.join(current_dir, "index.html")
    url = QtCore.QUrl.fromLocalFile(filename)
    view.load(url)

    view.resize(640, 480)
    view.show()
    sys.exit(app.exec_())
Run Code Online (Sandbox Code Playgroud)

索引.html

??? index.html
??? main.py
Run Code Online (Sandbox Code Playgroud)

更新:

一般来说,QtWebChannel只能从Qt端传输可以转换成QJsonObject的信息,从javascript端传输那些可以转换成JSON的数据。

所以有一些特殊情况:

  • 整数
  • 漂浮
  • 字符串
  • list : 如果支持发送和接收列表,但包含数字和字符串等元素,以及字典和其他支持以前基本类型的列表。
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets, QtWebChannel


class Backend(QtCore.QObject):
    @QtCore.pyqtSlot(int, result=int)
    def getRef(self, x):
        print("inside getRef", x)
        return x + 5

    @QtCore.pyqtSlot(int)
    def printRef(self, ref):
        print("inside printRef", ref)


if __name__ == "__main__":
    import os
    import sys

    app = QtWidgets.QApplication(sys.argv)

    backend = Backend()

    view = QtWebEngineWidgets.QWebEngineView()

    channel = QtWebChannel.QWebChannel()
    view.page().setWebChannel(channel)
    channel.registerObject("backend", backend)

    current_dir = os.path.dirname(os.path.realpath(__file__))
    filename = os.path.join(current_dir, "index.html")
    url = QtCore.QUrl.fromLocalFile(filename)
    view.load(url)

    view.resize(640, 480)
    view.show()
    sys.exit(app.exec_())
Run Code Online (Sandbox Code Playgroud)
<!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">
            var backend = null;
            var x = 5;
            window.onload = function()
            {
                new QWebChannel(qt.webChannelTransport, function(channel) {
                    backend = channel.objects.backend;
                    backend.getRef(x, function(pyval) {
                        backend.printRef(pyval);
                    });
                });
            }
        </script>
    </head>
</html>
Run Code Online (Sandbox Code Playgroud)

输出:

[0.0, 1.5, 'Hello', ['Stack', 5.0], {'a': {'b': 'c'}}]
Run Code Online (Sandbox Code Playgroud)
  • 字典
class Backend(QtCore.QObject):
    @QtCore.pyqtSlot(result=list)
    def return_list(self):
        return [0.0, 1.5, 'Hello', ['Stack', 5.0], {'a': {'b': 'c'}}]

    @QtCore.pyqtSlot(list)
    def print_list(self, l):
        print(l)
Run Code Online (Sandbox Code Playgroud)
backend = channel.objects.backend;
backend.return_list(function(pyval) {
    backend.print_list(pyval);
});
Run Code Online (Sandbox Code Playgroud)

输出:

{'a': <PyQt5.QtCore.QJsonValue object at 0x7f3841d50150>, 'b': <PyQt5.QtCore.QJsonValue object at 0x7f3841d501d0>, 'd': <PyQt5.QtCore.QJsonValue object at 0x7f3841d50250>}
Run Code Online (Sandbox Code Playgroud)

如您所见,QJsonValue 被返回,因此获取信息可能很乏味,因此在这种情况下,解决方法是将它们打包在一个列表中:

class Backend(QtCore.QObject):
    @QtCore.pyqtSlot(result=list)
    def return_list(self):
        d = {"a": 1.5, "b": {"c": 2}, "d": [1, "3", "4"]}
        return [d]

    @QtCore.pyqtSlot(list)
    def print_list(self, ref):
        d, *_ = ref
        print(d)
Run Code Online (Sandbox Code Playgroud)
[0.0, 1.5, 'Hello', ['Stack', 5.0], {'a': {'b': 'c'}}]
Run Code Online (Sandbox Code Playgroud)

输出:

{'a': 1.5, 'b': {'c': 2.0}, 'd': [1.0, '3', '4']}
Run Code Online (Sandbox Code Playgroud)

更新2:

一种通用的传递信息的方式是使用JSON,即将python或js对象分别使用json.dumps()和转换为字符串JSON.stringify(),发送;当在 python 或 js 中接收时,必须分别使用json.loads()和转换字符串JSON.parse()

class Backend(QtCore.QObject):
    @QtCore.pyqtSlot(result="QJsonObject")
    def return_dict(self):
        return {"a": 1.5, "b": {"c": 2}, "d": [1, "3", "4"]}

    @QtCore.pyqtSlot("QJsonObject")
    def print_dict(self, ref):
        print(ref)
Run Code Online (Sandbox Code Playgroud)
backend = channel.objects.backend;
backend.return_dict(function(pyval) {
    backend.print_dict(pyval);
});
Run Code Online (Sandbox Code Playgroud)
inside getRef {"a":"1000","b":["Hello","From","JS"]}
inside printRef {'a': '1000', 'b': ['Hello', 'From', 'JS'], 'c': ['Hello', 'from', 'Python'], 'f': False}
Run Code Online (Sandbox Code Playgroud)