python GUI库图形界面开发之PyQt5中QWebEngineView内嵌网页与Python的数据交互传参详细方法实例


Posted in Python onFebruary 26, 2020

这几天研究了下PyQt5中QWebEngineView内嵌网页与Python的数据交互,今天把实例方法与代码发布出来供大家参数

数据交互需要load进一个网页,这里我选择load进一个本地html网页:JSTest.html。

同时,QWebEngineView与外面的交互还需要Qt官方提供的一个js文件:qwebchannel.js,这个文件可以在网上下载。

JSTest.html和qwebchannel.js两个文件放在同一个目录下,我这边都是放在Python工程目录下。

qwebchannel.js:

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** 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 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 === 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 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 nodejs
if (typeof module === 'object') {
  module.exports = {
    QWebChannel: QWebChannel
  };
}

Python主函数代码

import sys
import Web_UI
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QObject,pyqtProperty
from PyQt5.QtWebChannel import QWebChannel
from MySharedObject import MySharedObject
from PyQt5.QtWidgets import QApplication
import os

BASE_DIR = os.path.dirname(__file__)#获取当前文件夹的绝对路径

if __name__ == '__main__':
  app = QtWidgets.QApplication(sys.argv)
  MainWindow = QtWidgets.QFrame()
  ui = Web_UI.Ui_Form()
  ui.setupUi(MainWindow)
  ui.webView1.load(QtCore.QUrl(r""+BASE_DIR+"/JSTest.html"))

  channel = QWebChannel() ##创建一个QwebChannel对象,用于传递PyQt的参数到JavaScript
  myObj = MySharedObject()
  print(myObj.strval)

  channel.registerObject('bridge', myObj)
  ui.webView1.page().setWebChannel(channel)

  MainWindow.show()
  sys.exit(app.exec_())

继承了QWidget类的MySharedObject(Python文件)

from PyQt5.QtCore import QObject
from PyQt5.QtCore import pyqtProperty
from PyQt5.QtWidgets import QWidget,QMessageBox

class MySharedObject(QWidget):
  def __init__(self,strval = '100'):
    super(MySharedObject,self).__init__()
    self.strval = strval
  def _getStrValue(self):
    return self.strval
  def _setStrValue(self,str):
    self.strval = str
    print('获得页面参数:%s'% str)
    QMessageBox.information(self,"Infomation", '获得的页面参数%s' % str)
  #需要定义的对外发布的方法
  strValue= pyqtProperty(str,_getStrValue,_setStrValue)

页面代码HTML

<!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>A Demo Page</title>
        <script src = "qwebchannel.js"></script>

        <script language = "javascript">
          function completeAndReturnName(){
            var fname = document.getElementById('fname').value;
            var lname = document.getElementById('lname').value;
            var fullname = fname +' '+ lname;

            document.getElementById('fullname').value = fullname;
            document.getElementById('submit-btn').style.display = 'block';
            return fullname;
          }
          document.addEventListener("DOMContentLoaded",function(){
            new QWebChannel(qt.webChannelTransport, function (channel) {
              //alert('111 chanel='+channel)
              window.bridge = channel.objects.bridge;
              alert('bridge='+bridge.strValue +'\n从pyqt传来的参数='+window.bridge.strValue);
              //alert('111 chanel='+ channel.objects.strValue)
            })
          } )
          function onShowMsgBox() {
            if(window.bridge != null){
              var fname = document.getElementById('fname').value;
              window.bridge.strValue = fname; //调用对象
            }
          }
        </script>
      </head>

      <body>
        <form>
          <label id = "name">User Name:</label>
          <input type = "text" name = "fname" id = "fname"></input>
          <br />
          <input type = "button" value="传递参数到pyqt" οnclick="onShowMsgBox()"></input>
          <br />
          <input type = "reset" value="重置"></input>

        </form>
      </body>
    </html>

实现效果

python GUI库图形界面开发之PyQt5中QWebEngineView内嵌网页与Python的数据交互传参详细方法实例

python GUI库图形界面开发之PyQt5中QWebEngineView内嵌网页与Python的数据交互传参详细方法实例

本文详细介绍了PyQt5中使用QWebEngineView控件内嵌网页与Python的数据交互的方法与实例,更多关于这方面的知识请查看下面的相关链接

Python 相关文章推荐
python使用marshal模块序列化实例
Sep 25 Python
Python计算回文数的方法
Mar 11 Python
Python自定义scrapy中间模块避免重复采集的方法
Apr 07 Python
Python的MongoDB模块PyMongo操作方法集锦
Jan 05 Python
python中如何正确使用正则表达式的详细模式(Verbose mode expression)
Nov 08 Python
python中文分词,使用结巴分词对python进行分词(实例讲解)
Nov 14 Python
将TensorFlow的模型网络导出为单个文件的方法
Apr 23 Python
python实现NB-IoT模块远程控制
Jun 20 Python
关于Django ForeignKey 反向查询中filter和_set的效率对比详解
Dec 15 Python
python中matplotlib条件背景颜色的实现
Sep 02 Python
Django --Xadmin 判断登录者身份实例
Jul 03 Python
python和opencv构建运动检测器的实现
Mar 03 Python
python自动点赞功能的实现思路
Feb 26 #Python
python GUI库图形界面开发之PyQt5时间控件QTimer详细使用方法与实例
Feb 26 #Python
python GUI库图形界面开发之PyQt5窗口控件QWidget详细使用方法
Feb 26 #Python
python GUI库图形界面开发之PyQt5窗口类QMainWindow详细使用方法
Feb 26 #Python
python GUI库图形界面开发之PyQt5中QMainWindow, QWidget以及QDialog的区别和选择
Feb 26 #Python
python实现全排列代码(回溯、深度优先搜索)
Feb 26 #Python
python GUI库图形界面开发之PyQt5 Qt Designer工具(Qt设计师)详细使用方法及Designer ui文件转py文件方法
Feb 26 #Python
You might like
PHP 中的类
2006/10/09 PHP
一个PHP+MSSQL分页的例子
2006/10/09 PHP
PHP+javascript模拟Matrix画面
2006/10/09 PHP
PHP curl 并发最佳实践代码分享
2012/09/05 PHP
YII2框架中使用yii.js实现的post请求
2017/04/09 PHP
PHP unlink与rmdir删除目录及目录下所有文件实例代码
2018/02/07 PHP
phpwind放自动注册方法
2006/12/02 Javascript
JavaScript入门教程(5) js Screen屏幕对象
2009/01/31 Javascript
关于setInterval、setTimeout在jQuery中的使用注意事项
2011/09/28 Javascript
Js 冒泡事件阻止实现代码
2013/01/27 Javascript
jQuery中filter()和find()的区别深入了解
2013/09/25 Javascript
jquery使用jquery.zclip插件复制对象的实例教程
2013/12/04 Javascript
jquery获得option的值和对option进行操作
2013/12/13 Javascript
Node.js模拟浏览器文件上传示例
2014/03/26 Javascript
JS案例分享之金额小写转大写
2014/05/15 Javascript
微信中一些常用的js方法汇总
2015/03/12 Javascript
js控制多图左右滚动切换效果代码分享
2015/08/26 Javascript
jQuery+jsp实现省市县三级联动效果(附源码)
2015/12/03 Javascript
写jQuery插件时的注意点
2017/02/20 Javascript
ionic2中使用自动生成器的方法
2018/03/04 Javascript
NodeJS 实现多语言的示例代码
2018/09/11 NodeJs
vue通过cookie获取用户登录信息的思路详解
2018/10/30 Javascript
使用Python编写一个在Linux下实现截图分享的脚本的教程
2015/04/24 Python
PyCharm设置SSH远程调试的方法
2018/07/17 Python
基于python实现把图片转换成素描
2019/11/13 Python
PyCharm第一次安装及使用教程
2020/01/08 Python
法国在线药房:1001Pharmacies
2021/03/07 全球购物
Nayomi官网:沙特阿拉伯王国睡衣和内衣品牌
2020/12/19 全球购物
请说出以下代码输出什么
2013/08/30 面试题
捐款倡议书
2014/04/14 职场文书
环保项目建议书
2014/08/26 职场文书
员工离职感谢信
2015/01/22 职场文书
幼儿园教学工作总结2015
2015/05/12 职场文书
文艺演出主持词
2015/07/01 职场文书
2016年学校招生广告语
2016/01/28 职场文书
家电创业计划书
2019/08/05 职场文书