程序员Python

PYQT 与 js 交互

2019-03-05  本文已影响5人  时尚灬IT男

PYQT做图形界面还是没有HTML做的炫,所以将PYQT与静态HTML结合就会有焕然一新的感觉。

HTML文件

<!DOCTYPE html>

<title>QWebChannel交互Demo

<button onclick="sendMes()">发送消息

<p id="mes">

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

    window.onload = function() {

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

window.printer= channel.objects.printer;  // 此处channel.objects.printer中的printer就是上文提到的功能类注册的标识名

});

};

function sendMes() {  // 调用python端的功能类的方法执行操作

// var a = printer.print('你收到一条网页发送的消息!')

printer.print('你收到一条网页发送的消息!');

}

function uptext(msg) {

alert(msg);

}

</html>

这里需要加载qwebchannel.js,下面是qwebchannel.js文件

/****************************************************************************

**

** Copyright (C) 2016 The Qt Company Ltd.

** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff

** Contact: https://www.qt.io/licensing/

**

** This file is part of the QtWebChannel module of the Qt Toolkit.

**

** $QT_BEGIN_LICENSE:BSD$

** Commercial License Usage

** Licensees holding valid commercial Qt licenses may use this file in

** accordance with the commercial license agreement provided with the

** Software or, alternatively, in accordance with the terms contained in

** a written agreement between you and The Qt Company. For licensing terms

** and conditions see https://www.qt.io/terms-conditions. For further

** information use the contact form at https://www.qt.io/contact-us.

**

** BSD License Usage

** Alternatively, you may use this file under the terms of the BSD license

** as follows:

**

** "Redistribution and use in source and binary forms, with or without

** modification, are permitted provided that the following conditions are

** met:

**  * Redistributions of source code must retain the above copyright

**    notice, this list of conditions and the following disclaimer.

**  * Redistributions in binary form must reproduce the above copyright

**    notice, this list of conditions and the following disclaimer in

**    the documentation and/or other materials provided with the

**    distribution.

**  * Neither the name of The Qt Company Ltd nor the names of its

**    contributors may be used to endorse or promote products derived

**    from this software without specific prior written permission.

**

**

** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."

**

** $QT_END_LICENSE$

**

****************************************************************************/

"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 iin 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 objectNamein data) {

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

}

// now unwrap properties, which might reference other registered objects

        for (var objectNamein 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 (responseinstanceof 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 === undefined) {

return response;

}

var objectId = response.id;

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 propertyNamein qObject) {

propertyNames.push(propertyName);

}

for (var idxin 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 propertyIdxin 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 propertyIndexin propertyMap) {

var propertyValue = propertyMap[propertyIndex];

object.__propertyCache__[propertyIndex] = propertyValue;

}

for (var signalNamein 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 namein data.enums) {

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

}

}

//required for use with nodejs

if (typeof module ==='object') {

module.exports = {

QWebChannel: QWebChannel

};

}

对应的python文件

#! /usr/bin/env

# -*- coding: utf-8 -*-

# webview.py

from PyQt5.QtWidgetsimport QApplication

from PyQt5.QtCoreimport QObject, pyqtSlot, QUrl

from PyQt5.QtWebChannelimport QWebChannel

from PyQt5.QtWebEngineWidgetsimport QWebEngineView

import sys

class Print(QObject):

# pyqtSlot,中文网络上大多称其为槽。作用是接收网页发起的信号

    @pyqtSlot(str, result=str)

def print(self, content):

print(content)# 对接收到的内容进行处理,比如调用打印机进行打印等等。此处略去,只在bash中显示接收到的消息

        self.myHello()

return 'python'

    @pyqtSlot(result=str)

def myHello(self):

browser.page().runJavaScript('uptext("hello, Python");')

# print('call received')

        return 'hello, Python'

if __name__ =='__main__':

app = QApplication(sys.argv)

browser = QWebEngineView()# 新增一个浏览器引擎

    browser.setWindowTitle('QWebChannel交互Demo')

browser.resize(900, 600)

channel = QWebChannel()# 增加一个通信中需要用到的频道

    printer = Print()# 通信过程中需要使用到的功能类

    channel.registerObject('printer', printer)# 将功能类注册到频道中,注册名可以任意,但将在网页中作为标识

    browser.page().setWebChannel(channel)# 在浏览器中设置该频道

    url_string ="file:///D:/wb/PycharmProjects/wb/index.html"  # 内置的网页地址,此处我采用的是本地的。远程同样可以使用。

    browser.load(QUrl(url_string))

browser.show()

sys.exit(app.exec_())

效果图是下面:

做这个demo的目的是为了与echarts结合做除完美的数据分析显示界面。

上一篇下一篇

猜你喜欢

热点阅读