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计算最大优先级队列实例
Dec 18 Python
python网络编程之TCP通信实例和socketserver框架使用例子
Apr 25 Python
Python中使用语句导入模块或包的机制研究
Mar 30 Python
python版本的仿windows计划任务工具
Apr 30 Python
python 读取Linux服务器上的文件方法
Dec 27 Python
使用PIL(Python-Imaging)反转图像的颜色方法
Jan 24 Python
python中下标和切片的使用方法解析
Aug 27 Python
python 实现视频 图像帧提取
Dec 10 Python
django 连接数据库出现1045错误的解决方式
May 14 Python
Django rest framework分页接口实现原理解析
Aug 21 Python
Python性能测试工具Locust安装及使用
Dec 01 Python
python神经网络Xception模型
May 06 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使用GeoIP库实例
2014/06/27 PHP
php字符串的替换,分割和连接方法
2016/05/23 PHP
PHP生成图片缩略图类示例
2017/01/12 PHP
PHP ADODB实现分页功能简单示例
2018/05/25 PHP
写了一个layout,拖动条连贯,内容区可为iframe
2007/08/19 Javascript
javascript div 弹出可拖动窗口
2009/02/26 Javascript
javascript 放大镜效果js组件 qsoft.PopBigImage.v0.35 加入了chrome支持
2009/04/07 Javascript
jQueryPad 实用的jQuery测试工具(支持IE,chrome,FF)
2010/05/22 Javascript
如何让easyui gridview 宽度自适应窗口改变及fitColumns应用
2013/01/25 Javascript
动态载入js提高网页打开速度的方法
2014/07/04 Javascript
JavaScript通过filereader接口读取文件
2017/05/10 Javascript
ionic2自定义cordova插件开发以及使用(Android)
2017/06/19 Javascript
nodejs实现截取上传视频中一帧作为预览图片
2017/12/10 NodeJs
浅谈Node 调试工具入门教程
2018/03/20 Javascript
原生JS实现的碰撞检测功能示例
2018/05/18 Javascript
关于js对textarea换行符的处理方法浅析
2018/08/03 Javascript
vue登录页实现使用cookie记住7天密码功能的方法
2021/02/18 Vue.js
[03:57]2016完美“圣”典风云人物:rOtk专访
2016/12/09 DOTA
[02:58]魔廷新尊——痛苦女王至宝语音台词节选
2020/06/14 DOTA
[02:17]快乐加倍!DOTA2食人魔魔法师至宝+迎霜节活动上线
2019/12/22 DOTA
Python2实现的LED大数字显示效果示例
2017/09/04 Python
基于循环神经网络(RNN)的古诗生成器
2018/03/26 Python
python opencv实现切变换 不裁减图片
2018/07/26 Python
python单例模式的多种实现方法
2019/07/26 Python
Python Django 页面上展示固定的页码数实现代码
2019/08/21 Python
python lambda表达式(匿名函数)写法解析
2019/09/16 Python
python中有函数重载吗
2020/05/28 Python
基于selenium及python实现下拉选项定位select
2020/07/22 Python
Kappa英国官方在线商店:服装和运动器材
2020/11/22 全球购物
药剂专业自荐信范文
2014/04/16 职场文书
《雕塑之美》教学反思
2014/04/24 职场文书
高中教师先进事迹材料
2014/08/22 职场文书
2014年语文教研组工作总结
2014/12/06 职场文书
酒店工程部经理岗位职责
2015/04/09 职场文书
2016年“世界环境日”校园广播稿
2015/12/18 职场文书
Hive常用日期格式转换语法
2022/06/25 数据库