欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python+WebKit+HTML开发桌面应用程序

程序员文章站 2022-05-07 19:34:40
...

Python+WebKit+HTML开发桌面应用程序

前言

几天前写了一个备份的小工具,用Python写个脚本,也就花个一天的时间搞定,给客户用了一下,对功能很满意,但对界面不满足,想要一个图形界面来管理;
一个备份脚本,有必要整成这样子吗?没办法,谁让是上帝的要求呢,就研究一下;先后找了python的tkinter、pyqt,尝试着画一些界面,虽然功能可以实现,但界面很难看;恰好,在查看pyqt的API文档的时候 ,发现了QWebView组件,据介绍可以实现一个浏览器,并且浏览器中的JS可以与Python代码进行交互。忽然眼前一亮,我不正是我想要的吗!
抽几天时间把工具写完了,目前运行良好;想写篇博客做个记录,也想给需要进行此类开发的朋友做个示例,就把这个工具最核心的内容做了一个DEMO。现在,就把这个小DEMO的开发过程做个说明;

关于作者:
天涯哥,程序员
Java,Python,C,Javascript
税务、金融、教育
aaa@qq.com

功能简介

这个小DEMO用于显示计算机上的一些信息,显示内容不是主要的,主要为了体现如何用python+HTML开发桌面应用程序。

1.使用HTML开发一个界面;
2.在页面上显示主机信息;
3. 实现功能时,体现JS调用Python和Python调用JS;

最终实现的主界面如下:

Python+WebKit+HTML开发桌面应用程序

准备材料

开发工具:Eclipse
开发语言:python2.7
界面工具包:PyQT4

目录结构

Python+WebKit+HTML开发桌面应用程序

说明:
demoUI.py : python主体程序 ,主要实现窗口创建,加载index.html页面,python与JS的交互;
views/index.html : 主要进行数据展示,及与python交互的js程序

demoUI.py开发

引入的包

from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4 import QtWebKit
from PyQt4 import QtNetwork

#处理中文问题
import sys,json
from PyQt4.Qt import QObject
reload(sys)  
sys.setdefaultencoding('utf8')

前四行代码,主要是引入Qt相关的包,这里使用的PyQt4;
后三行代码,主要是为了处理中文问题,可以忽略,但遇到中文乱码的时候,想起来这句话,把它加上就行了

DemoWin类

class DemoWin(QtWebKit.QWebView):

    def __init__(self):
        QtWebKit.QWebView.__init__(self)
        self.resize(800, 600)
        self.setUrl(QtCore.QUrl('views/index.html'))
        self.show()
        mainFrame = self.page().mainFrame()
        winobj = WinObj(mainFrame)
        mainFrame.javaScriptWindowObjectCleared.connect(lambda: mainFrame.addToJavaScriptWindowObject(QtCore.QString('WinObj'), winobj)) 

DemoWin是整个应用的核心类,主要实现窗体创建,关联与JS交互的槽函数等;

class DemoWin(QtWebKit.QWebView):

像标题所说,我们将使用WebKit作为页面展示,所以这里的主页面是以QtWebKit.QWebView作为基类

QtWebKit.QWebView.init(self)
self.resize(800, 600)
self.setUrl(QtCore.QUrl(‘views/index.html’))
self.show()

设置窗口大小为800*600,并且加载index.html进行显示

mainFrame = self.page().mainFrame()

获取网页主框架,按照QT官方文档解释:QWebFrame代表一个网页框架,每一个QWebFrame至少包含一个主框架,使用QWebFrame.mainFrame()获取。
这是进行后续各类操作的基础,类似于JS中只有获取到网页的dom对象,才可以对其中的元素操作一样;

winobj = WinObj(mainFrame)
mainFrame.javaScriptWindowObjectCleared.connect(lambda: mainFrame.addToJavaScriptWindowObject(QtCore.QString(‘WinObj’), winobj)) ##js调用python

这段代码是关键中的关键!
WinObj类:是封装后用于js调用的槽函数类,后续再详细介绍
addToJavaScriptWindowObject类:第一个参数是对象在javascript里的名字, 可以*命名, 第二个参数是对应的QObject实例指针。 这样在javascript里就可以直接访问WinObj对象拉, 是不是看上去超级简单?但是这个函数的调用时机是有讲究的,按照官方文档的推荐,是需要在javaScriptWindowObjectCleared信号的槽里调用,才有了以上的代码;里面用了lambda表达式,纯粹是为了减少一个槽函数的定义,你如果觉得不好看或不喜欢,完全可以定义一个槽函数;

WinObj类

下面来重点介绍WinObj类,类型定义如下:

class WinObj(QtCore.QObject):
    def __init__(self,mainFrame):
        super(WinObj,self).__init__()
        self.mainFrame = mainFrame

    @QtCore.pyqtSlot(result="QString")
    def getInfo(self):
        import socket,platform
        hostname = socket.gethostname()
        ip = socket.gethostbyname(hostname)
        list_info = platform.uname()
        sys_name = list_info[0] + list_info[2]
        cpu_name = list_info[5]
        dic_info = {"hostname":hostname,"ip":ip,"sys_name":sys_name, \
                    "cpu_name":cpu_name}
        #调用js函数,实现回调
        self.mainFrame.evaluateJavaScript('%s(%s)' % ('onGetInfo',json.dumps(dic_info)))
        return json.dumps(dic_info)

该类封装了JS可直接调用的方法,有一些区别于普通类的地方要注意;

class WinObj(QtCore.QObject):

该类必须继承自QObject,而不能是object。

def __init__(self,mainFrame):
        super(WinObj,self).__init__()
        self.mainFrame = mainFrame

构造函数将mainFrame传进来,主要用于调用js函数(后面将有示例介绍)

    @QtCore.pyqtSlot(result="QString")
    def getInfo(self):
        import socket,platform
        hostname = socket.gethostname()
        ip = socket.gethostbyname(hostname)
        list_info = platform.uname()
        sys_name = list_info[0] + list_info[2]
        cpu_name = list_info[5]
        dic_info = {"hostname":hostname,"ip":ip,"sys_name":sys_name, \
                    "cpu_name":cpu_name}
        #调用js函数,实现回调
        self.mainFrame.evaluateJavaScript('%s(%s)' % ('onGetInfo',json.dumps(dic_info)))
        return json.dumps(dic_info)

getInfo()实现了一个供JS调用的方法,有几下几点要注意:
(1)@QtCore.pyqtSlot(result=”QString”) 用于将python方法转换为供js用的函数,括号里写明数据类型及返回类型;如果没有声明result,则不能返回数据;
(2)self.mainFrame.evaluateJavaScript(‘%s(%s)’ % (‘onGetInfo’,json.dumps(dic_info)))
调用页面中用JS声明的onGetInfo函数(这里仅作一个示例,将查询到的数据进行回调返回)

demoUI.py完整代码下:

#coding=utf-8
'''
Created on 2017年11月3日

@author: Administrator
'''
from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4 import QtWebKit
from PyQt4 import QtNetwork

#处理中文问题
import sys,json
from PyQt4.Qt import QObject
reload(sys)  
sys.setdefaultencoding('utf8')

class DemoWin(QtWebKit.QWebView):

    def __init__(self):
        QtWebKit.QWebView.__init__(self)
        self.resize(800, 600)
        self.setUrl(QtCore.QUrl('views/index.html'))
        self.show()
        mainFrame = self.page().mainFrame()
        winobj = WinObj(mainFrame)
        mainFrame.javaScriptWindowObjectCleared.connect(lambda: mainFrame.addToJavaScriptWindowObject(QtCore.QString('WinObj'), winobj)) ##js调用python

class WinObj(QtCore.QObject):
    def __init__(self,mainFrame):
        super(WinObj,self).__init__()
        self.mainFrame = mainFrame

    @QtCore.pyqtSlot(result="QString")
    def getInfo(self):
        import socket,platform
        hostname = socket.gethostname()
        ip = socket.gethostbyname(hostname)
        list_info = platform.uname()
        sys_name = list_info[0] + list_info[2]
        cpu_name = list_info[5]
        dic_info = {"hostname":hostname,"ip":ip,"sys_name":sys_name, \
                    "cpu_name":cpu_name}
        #调用js函数,实现回调
        self.mainFrame.evaluateJavaScript('%s(%s)' % ('onGetInfo',json.dumps(dic_info)))
        return json.dumps(dic_info)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    demoWin = DemoWin()
    sys.exit(app.exec_())

index.html

<!DOCTYPE script PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src='http://static.runoob.com/assets/vue/1.0.11/vue.min.js'></script>
</head>
<body>

<div id='app'>
<input type='button' onclick='getInfo()' value="获取机器信息">
<H3 >主机名:{{config.hostname}} </H3>
<H3 >IP:{{config.ip}} </H3>
<H3 >操作系统:{{config.sys_name}} </H3>
<H3 >CPU:{{config.cpu_name}} </H3>
<H3>以下为python回调信息:</H3>
<label id='info'></label>
</div>
</body>
</html>
<script type="text/javascript">
var vm = new Vue({
    el: '#app',
    data:{
        config:{}
    },
    created:function(){
        this.config = JSON.parse(window.WinObj.getInfo())
    }
});

function getInfo() {
    info = JSON.parse(window.WinObj.getInfo())
    alert(info)
}

//python回调的js函数
function onGetInfo(info) {
    document.getElementById('info').innerText=JSON.stringify(info)
}

</script>

用于界面显示的html页面比较简单,为了方便,里面用到了vuejs,不了解的可以查查相关资料;