PYQT 與 js 交互

PYQT做圖形界面還是沒有HTML做的炫,所以將PYQT與靜態(tài)HTML結(jié)合就會有煥然一新的感覺熬北。

HTML文件

<!DOCTYPE html>

<title>QWebChannel交互Demo

<button onclick="sendMes()">發(fā)送消息

<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() {? // 調(diào)用python端的功能類的方法執(zhí)行操作

// var a = printer.print('你收到一條網(wǎng)頁發(fā)送的消息匿情!')

printer.print('你收到一條網(wǎng)頁發(fā)送的消息深夯!');

}

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

};

}

對應(yīng)的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沮协,中文網(wǎng)絡(luò)上大多稱其為槽辆飘。作用是接收網(wǎng)頁發(fā)起的信號

? ? @pyqtSlot(str, result=str)

def print(self, content):

print(content)# 對接收到的內(nèi)容進行處理啦辐,比如調(diào)用打印機進行打印等等。此處略去劈猪,只在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)# 將功能類注冊到頻道中昧甘,注冊名可以任意,但將在網(wǎng)頁中作為標識

? ? browser.page().setWebChannel(channel)# 在瀏覽器中設(shè)置該頻道

? ? url_string ="file:///D:/wb/PycharmProjects/wb/index.html"? # 內(nèi)置的網(wǎng)頁地址战得,此處我采用的是本地的充边。遠程同樣可以使用。

? ? browser.load(QUrl(url_string))

browser.show()

sys.exit(app.exec_())

效果圖是下面:


做這個demo的目的是為了與echarts結(jié)合做除完美的數(shù)據(jù)分析顯示界面常侦。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浇冰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子聋亡,更是在濱河造成了極大的恐慌肘习,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坡倔,死亡現(xiàn)場離奇詭異漂佩,居然都是意外死亡,警方通過查閱死者的電腦和手機罪塔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門投蝉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人征堪,你說我怎么就攤上這事瘩缆。” “怎么了佃蚜?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵庸娱,是天一觀的道長。 經(jīng)常有香客問我谐算,道長熟尉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任洲脂,我火速辦了婚禮臣樱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己雇毫,他們只是感情好玄捕,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棚放,像睡著了一般枚粘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上飘蚯,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天馍迄,我揣著相機與錄音,去河邊找鬼局骤。 笑死攀圈,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的峦甩。 我是一名探鬼主播赘来,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼凯傲!你這毒婦竟也來了犬辰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤冰单,失蹤者是張志新(化名)和其女友劉穎幌缝,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诫欠,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡涵卵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了荒叼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片轿偎。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖甩挫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情椿每,我是刑警寧澤伊者,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站间护,受9級特大地震影響亦渗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜汁尺,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一法精、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦搂蜓、人聲如沸狼荞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽相味。三九已至酥馍,卻和暖如春薯演,著一層夾襖步出監(jiān)牢的瞬間虚茶,已是汗流浹背仰美。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工猾骡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留即硼,地道東北人钞诡。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓羡微,卻偏偏與公主長得像傻唾,于是被迫代替她去往敵國和親投慈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容