python如何实现前后端交互_前后端传值问题:python与js进行通信

要解决python与js的交互问题,可以基于qwebchannel来实现

文章末尾附有简单的demo

1. 准备阶段

由于我的python使用的是Anaconda,内置了pyqt,因此直接引入即可。

from PyQt5.QtWidgets import QApplication

from PyQt5.QtCore import QObject, pyqtSlot, QUrl

from PyQt5.QtWebChannel import QWebChannel

from PyQt5.QtWebEngineWidgets import QWebEngineView

在前端部分,引入qwebchannel.js(不想在网上找的话,直接文章末尾的代码自己新建一个文件即可)首先,创建一个QApplication对象app,参数sys.argv是一个序列,具体含义在此不多深究

然后我们创建QWebEngineView对象,相当于内置的浏览器,用于在python中打开web窗口

定义QWebChannel对象,这是通信的频道

为浏览器窗口设置通信频道

load方法用于打开窗口,在QUrl中输入文件路径即可打开本地HTML文件

最后要用show方法让页面显示出来

2. 建立通信

python部分

app =QApplication(sys.argv)

myWeb=QWebEngineView()

myChannel=QWebChannel()

myweb.page().setWebChannel(myChannel)

myWeb.load(QUrl("C:/Users/asus2018/Desktop/test/font-end.html"))

myWeb.show()

通信过程主要是通过函数调用,因此我们还需要在Python中创建一个通信类,并将这个通信类的对象注册到通信频道之中:

class myClass(QObject):

@pyqtSlot()

def testPy2JS(self):

myweb.page().runJavaScript('waitingMessage="message from python"')

@pyqtSlot(str)

def testJS2Py(self,msg):

print(msg)

testWeb=myClass()#用于通信的对象

myChannel.registerObject('testObject',testWeb)#注册,testObject是自己起的名字

这样,在js中就将可以使用这个通信对象的函数。

函数前的pyqtSlot()作用是接收网页发出的信号

注意:没有这行代码,底下的这个函数将无法正常执行**

JS部分:

```javascript

new QWebChannel(qt.webChannelTransport, function (channel) {

window.pyjs = channel.objects.testObject;

//testObject是注册时起的通信类名称,注意前后一致

});

```

新建一个QwebChannel对象即可完成通信连接,回调函数中将Python中的通信类绑定为window的全局变量,方便后续操作中调用该通信类中的函数

3.数据交互

现在,我们已经可以用python来打开HTML页面,并且将python中的通信类赋值给了JS。要进行通信,只需要在JS中调用通信类中的方法,即可完成数据的传输

JS=>Python

pyjs.testJS2Py('message from js');

//testJS2Py是通信类中自己定义的函数,将在python控制台中打印接收到的数据

Python=>JS

pyjs.testPy2JS()//testPy2JS也是通信类中定义的函数

testPy2JS函数实现的原理,是在python中运行JS代码

myweb.page().runJavaScript('waitingMessage="message from python"')

其中waitingMessage是我们在JS中定义的变量,用于接收python中传过来的值。

要传递一个python中定义好的变量x,可以再创建一个字符串变量,在变量x外加上引号,即可加入到字符串中,当做JS代码执行

x=2020

y="'" + x + "'"

myweb.page().runJavaScript('waitingMessage='+ y +';')

注意,不能在JS中接收我们定义的通信类中方法的返回值

4. 示例代码

import sys

from PyQt5.QtWidgets import QApplication

from PyQt5.QtCore import QObject, pyqtSlot, QUrl

from PyQt5.QtWebChannel import QWebChannel

from PyQt5.QtWebEngineWidgets import QWebEngineView

info="2"

class myClass(QObject):

@pyqtSlot()

def testPy2JS(self):

myWeb.page().runJavaScript('waitingMessage='+info+';')

@pyqtSlot(str)

def testJS2Py(self,msg):

print(msg)

if __name__ == '__main__':

testWeb=myClass()#用于通信的实例化对象

app = QApplication(sys.argv)

myWeb=QWebEngineView()

myChannel=QWebChannel()

myweb.page().setWebChannel(myChannel)

myChannel.registerObject('testObject',testWeb) #注册

myWeb.load(QUrl("C:/Users/asus2018/Desktop/test/font-end.html"))

myWeb.show()

sys.exit(app.exec_())

fontEnd.html

QWebChannel测试

this is test !

向前端传值

向后端传值

show the value

new QWebChannel(qt.webChannelTransport, function (channel) {

window.pyjs = channel.objects.testObject; //把对象赋值到JS中

});

function show(){

alert(waitingMessage);

}

var waitingMessage=null;//等待接收python传来的数据

var message2py="message from js";//向python发送的数据

function sentBack(){

pyjs.testJS2Py(message2py);

}

function sent(){

try{

var get=1;

get=pyjs.testPy2JS();

alert(get);

}catch(e){

alert(e);

}

}

qwebchannel.js:

"use strict";

var QWebChannelMessageTypes = {

signal: 1,

propertyUpdate: 2,

init: 3,

idle: 4,

debug: 5,

invokeMethod: 6,

connectToSignal: 7,

disconnectFromSignal: 8,

setProperty: 9,

response: 10,

};

var QWebChannel = function(transport, initCallback)

{

if (typeof transport !== "object" || typeof transport.send !== "function") {

console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." +

" Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send));

return;

}

var channel = this;

this.transport = transport;

this.send = function(data)

{

if (typeof(data) !== "string") {

data = JSON.stringify(data);

}

channel.transport.send(data);

}

this.transport.onmessage = function(message)

{

var data = message.data;

if (typeof data === "string") {

data = JSON.parse(data);

}

switch (data.type) {

case QWebChannelMessageTypes.signal:

channel.handleSignal(data);

break;

case QWebChannelMessageTypes.response:

channel.handleResponse(data);

break;

case QWebChannelMessageTypes.propertyUpdate:

channel.handlePropertyUpdate(data);

break;

default:

console.error("invalid message received:", message.data);

break;

}

}

this.execCallbacks = {};

this.execId = 0;

this.exec = function(data, callback)

{

if (!callback) {

// if no callback is given, send directly channel.send(data);

return;

}

if (channel.execId === Number.MAX_VALUE) {

// wrap channel.execId = Number.MIN_VALUE;

}

if (data.hasOwnProperty("id")) {

console.error("Cannot exec message with property id: " + JSON.stringify(data));

return;

}

data.id = channel.execId++;

channel.execCallbacks[data.id] = callback;

channel.send(data);

};

this.objects = {};

this.handleSignal = function(message)

{

var object = channel.objects[message.object];

if (object) {

object.signalEmitted(message.signal, message.args);

} else {

console.warn("Unhandled signal: " + message.object + "::" + message.signal);

}

}

this.handleResponse = function(message)

{

if (!message.hasOwnProperty("id")) {

console.error("Invalid response message received: ", JSON.stringify(message));

return;

}

channel.execCallbacks[message.id](message.data);

delete channel.execCallbacks[message.id];

}

this.handlePropertyUpdate = function(message)

{

for (var i in message.data) {

var data = message.data[i];

var object = channel.objects[data.object];

if (object) {

object.propertyUpdate(data.signals, data.properties);

} else {

console.warn("Unhandled property update: " + data.object + "::" + data.signal);

}

}

channel.exec({type: QWebChannelMessageTypes.idle});

}

this.debug = function(message)

{

channel.send({type: QWebChannelMessageTypes.debug, data: message});

};

channel.exec({type: QWebChannelMessageTypes.init}, function(data) {

for (var objectName in data) {

var object = new QObject(objectName, data[objectName], channel);

}

// now unwrap properties, which might reference other registered objects for (var objectName in channel.objects) {

channel.objects[objectName].unwrapProperties();

}

if (initCallback) {

initCallback(channel);

}

channel.exec({type: QWebChannelMessageTypes.idle});

});

};

function QObject(name, data, webChannel)

{

this.__id__ = name;

webChannel.objects[name] = this;

// List of callbacks that get invoked upon signal emission this.__objectSignals__ = {};

// Cache of all properties, updated when a notify signal is emitted this.__propertyCache__ = {};

var object = this;

// ----------------------------------------------------------------------

this.unwrapQObject = function(response)

{

if (response instanceof Array) {

// support list of objects var ret = new Array(response.length);

for (var i = 0; i < response.length; ++i) {

ret[i] = object.unwrapQObject(response[i]);

}

return ret;

}

if (!response

|| !response["__QObject*__"]

|| Response.id The Fast Response Tools === undefined) {

return response;

}

var objectId = Response.id The Fast Response Tools;

if (webChannel.objects[objectId])

return webChannel.objects[objectId];

if (!response.data) {

console.error("Cannot unwrap unknown QObject " + objectId + " without data.");

return;

}

var qObject = new QObject( objectId, response.data, webChannel );

qObject.destroyed.connect(function() {

if (webChannel.objects[objectId] === qObject) {

delete webChannel.objects[objectId];

// reset the now deleted QObject to an empty {} object // just assigning {} though would not have the desired effect, but the // below also ensures all external references will see the empty map // NOTE: this detour is necessary to workaround QTBUG-40021 var propertyNames = [];

for (var propertyName in qObject) {

propertyNames.push(propertyName);

}

for (var idx in propertyNames) {

delete qObject[propertyNames[idx]];

}

}

});

// here we are already initialized, and thus must directly unwrap the properties qObject.unwrapProperties();

return qObject;

}

this.unwrapProperties = function()

{

for (var propertyIdx in object.__propertyCache__) {

object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]);

}

}

function addSignal(signalData, isPropertyNotifySignal)

{

var signalName = signalData[0];

var signalIndex = signalData[1];

object[signalName] = {

connect: function(callback) {

if (typeof(callback) !== "function") {

console.error("Bad callback given to connect to signal " + signalName);

return;

}

object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];

object.__objectSignals__[signalIndex].push(callback);

if (!isPropertyNotifySignal && signalName !== "destroyed") {

// only required for "pure" signals, handled separately for properties in propertyUpdate // also note that we always get notified about the destroyed signal webChannel.exec({

type: QWebChannelMessageTypes.connectToSignal,

object: object.__id__,

signal: signalIndex

});

}

},

disconnect: function(callback) {

if (typeof(callback) !== "function") {

console.error("Bad callback given to disconnect from signal " + signalName);

return;

}

object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];

var idx = object.__objectSignals__[signalIndex].indexOf(callback);

if (idx === -1) {

console.error("Cannot find connection of signal " + signalName + " to " + callback.name);

return;

}

object.__objectSignals__[signalIndex].splice(idx, 1);

if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) {

// only required for "pure" signals, handled separately for properties in propertyUpdate webChannel.exec({

type: QWebChannelMessageTypes.disconnectFromSignal,

object: object.__id__,

signal: signalIndex

});

}

}

};

}

/*** Invokes all callbacks for the given signalname. Also works for property notify callbacks.*/

function invokeSignalCallbacks(signalName, signalArgs)

{

var connections = object.__objectSignals__[signalName];

if (connections) {

connections.forEach(function(callback) {

callback.apply(callback, signalArgs);

});

}

}

this.propertyUpdate = function(signals, propertyMap)

{

// update property cache for (var propertyIndex in propertyMap) {

var propertyValue = propertyMap[propertyIndex];

object.__propertyCache__[propertyIndex] = propertyValue;

}

for (var signalName in signals) {

// Invoke all callbacks, as signalEmitted() does not. This ensures the // property cache is updated before the callbacks are invoked. invokeSignalCallbacks(signalName, signals[signalName]);

}

}

this.signalEmitted = function(signalName, signalArgs)

{

invokeSignalCallbacks(signalName, signalArgs);

}

function addMethod(methodData)

{

var methodName = methodData[0];

var methodIdx = methodData[1];

object[methodName] = function() {

var args = [];

var callback;

for (var i = 0; i < arguments.length; ++i) {

if (typeof arguments[i] === "function")

callback = arguments[i];

else

args.push(arguments[i]);

}

webChannel.exec({

"type": QWebChannelMessageTypes.invokeMethod,

"object": object.__id__,

"method": methodIdx,

"args": args

}, function(response) {

if (response !== undefined) {

var result = object.unwrapQObject(response);

if (callback) {

(callback)(result);

}

}

});

};

}

function bindGetterSetter(propertyInfo)

{

var propertyIndex = propertyInfo[0];

var propertyName = propertyInfo[1];

var notifySignalData = propertyInfo[2];

// initialize property cache with current value // NOTE: if this is an object, it is not directly unwrapped as it might // reference other QObject that we do not know yet object.__propertyCache__[propertyIndex] = propertyInfo[3];

if (notifySignalData) {

if (notifySignalData[0] === 1) {

// signal name is optimized away, reconstruct the actual name notifySignalData[0] = propertyName + "Changed";

}

addSignal(notifySignalData, true);

}

Object.defineProperty(object, propertyName, {

configurable: true,

get: function () {

var propertyValue = object.__propertyCache__[propertyIndex];

if (propertyValue === undefined) {

// This shouldn't happen console.warn("Undefined value in property cache for property \"" + propertyName + "\" in object " + object.__id__);

}

return propertyValue;

},

set: function(value) {

if (value === undefined) {

console.warn("Property setter for " + propertyName + " called with undefined value!");

return;

}

object.__propertyCache__[propertyIndex] = value;

webChannel.exec({

"type": QWebChannelMessageTypes.setProperty,

"object": object.__id__,

"property": propertyIndex,

"value": value

});

}

});

}

// ----------------------------------------------------------------------

data.methods.forEach(addMethod);

data.properties.forEach(bindGetterSetter);

data.signals.forEach(function(signal) { addSignal(signal, false); });

for (var name in data.enums) {

object[name] = data.enums[name];

}

}

//required for use with nodejsif (typeof module === 'object') {

module.exports = {

QWebChannel: QWebChannel

};

}

```

若对你有帮助的话,点个赞再走吧!

你可能感兴趣的:(python如何实现前后端交互)