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迭代器和生成器介绍
Mar 06 Python
python制作一个桌面便签软件
Aug 09 Python
Python实现一个Git日志统计分析的小工具
Dec 14 Python
python针对excel的操作技巧
Mar 13 Python
Python实现购物车程序
Apr 16 Python
Python一句代码实现找出所有水仙花数的方法
Nov 13 Python
利用python在excel里面直接使用sql函数的方法
Feb 08 Python
python中 * 的用法详解
Jul 10 Python
python中eval与int的区别浅析
Aug 11 Python
python enumerate内置函数用法总结
Jan 07 Python
pyautogui自动化控制鼠标和键盘操作的步骤
Apr 01 Python
Python 处理表格进行成绩排序的操作代码
Jul 26 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
改德生G88 - 加装等响度低音提升电路
2021/03/02 无线电
Thinkphp关闭缓存的方法
2015/06/26 PHP
Yii2框架dropDownList下拉菜单用法实例分析
2016/07/18 PHP
实例讲解php实现多线程
2019/01/27 PHP
基于jquery的loading效果实现代码
2010/11/05 Javascript
JS中如何判断传过来的JSON数据中是否存在某字段
2014/08/18 Javascript
jQuery实现hover合成事件的方法
2015/08/06 Javascript
使用Javascript实现选择下拉菜单互移并排序
2016/02/23 Javascript
JS基于clipBoard.js插件实现剪切、复制、粘贴
2016/05/03 Javascript
jQuery ajax请求struts action实现异步刷新
2017/04/19 jQuery
利用C/C++编写node.js原生模块的方法教程
2017/07/07 Javascript
vue-cli webpack 引入swiper的操作方法
2018/09/15 Javascript
vue-week-picker实现支持按周切换的日历
2019/06/26 Javascript
微信小程序自定义头部导航栏和导航栏背景图片 navigationStyle问题
2019/07/26 Javascript
Vue+Openlayers自定义轨迹动画
2020/09/24 Javascript
讲解Python中for循环下的索引变量的作用域
2015/04/15 Python
Python中实现三目运算的方法
2015/06/21 Python
Python实现模拟登录及表单提交的方法
2015/07/25 Python
Python中的数据对象持久化存储模块pickle的使用示例
2016/03/03 Python
python OpenCV学习笔记之绘制直方图的方法
2018/02/08 Python
对python Tkinter Text的用法详解
2018/10/11 Python
Python时间和字符串转换操作实例分析
2019/03/16 Python
Pytorch根据layers的name冻结训练方式
2020/01/06 Python
python程序输出无内容的解决方式
2020/04/09 Python
使用 Python 读取电子表格中的数据实例详解
2020/04/17 Python
Python 读取位于包中的数据文件
2020/08/07 Python
美国最大点评网站:Yelp
2018/02/14 全球购物
俄罗斯花园种植材料批发和零售网上商店:Беккер
2019/07/22 全球购物
系统管理员的职责包括那些?管理的对象是什么?
2013/01/18 面试题
幼儿园老师辞职信
2014/01/20 职场文书
安全生产月活动总结
2014/05/04 职场文书
党的作风建设心得体会
2014/10/22 职场文书
公务员个人总结
2015/02/12 职场文书
2015年护士节活动策划方案
2015/05/04 职场文书
MySQL数据迁移相关总结
2021/04/29 MySQL
基于angular实现树形二级表格
2021/10/16 Javascript