前言
之前用了Eel做的桌面应用觉得已经够屌了,不过由于Eel是调用Chrome,时常出现各种小问题,比如窗口大小设置后有时候不管用,鼠标右键菜单无法禁用(一眼就能看出来是个web).而且尝试了用pyinstaller打包后文件好大,昨天晚上闲逛又发现了个比Eel更好的解决方案pywebview
,更轻量,可自定义的设置更多.
由于pywebview
是直接调用系统自身的浏览器(Win10调用Edge,Win7调用IE),因此很适合打包发布.
官网:https://pywebview.flowrl.com/
最简单应用上手
先装上轮子
pip install pywebview
实现一个内嵌百度首页的winform
程序,固定窗口大小,禁止选择文字
"""
main.py
"""
import webview
window = webview.create_window(
title='百度一下,全是广告',
url='http://www.baidu.com',
width=850,
height=600,
resizable=False, # 固定窗口大小
text_select=False, # 禁止选择文字内容
confirm_close=True # 关闭时提示
)
webview.start()
无论是启动速度,还是显示效果都要比Eel好很多.
退出提示的窗口默认显示的是英文,可以本地化一下,定义个字典传给webview.start()
当启动参数就行了.
chinese = {
'global.quitConfirmation': u'确定关闭?',
}
webview.start(localization=chinese)
高阶应用
在HTML前端界面中调用Python中的函数并返回结果
基本流程:
- 定义一个类,这里叫
Api
- 在
Api
类中定义要被前端调用的各种函数 - 定义一个
window
对象的时候加入参数js_api=api
,api是实例化的Api
类对象,将上面定义的Api
类传给window
以供js调用 - 前端js调用的语法为
pywebview.api.函数名(参数).then(回调js函数)
直接上代码吧:
Python
Http是由Flask提供的,这里不介绍了
"""
main.py
"""
import webview
chinese = {
'global.quitConfirmation': u'确定关闭?',
}
class Api:
def func(self, txt):
response = {
'message': '从Python调用函数返回内容:' + txt
}
return response
if __name__ == '__main__':
api = Api()
window = webview.create_window(
title='天涯海角',
url='http://127.0.0.1:5000/demo',
width=760,
height=390,
# resizable=False, # 固定窗口大小
text_select=False, # 禁止选择文字内容
# confirm_close=True, # 关闭时提示
js_api=api # 将上面实例化后的Api对象传给前端js调用
)
webview.start(localization=chinese)
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" type="text/css" href="https://www.layuicdn.com/layui/css/layui.css" />
<style>
.setting-label {
width: 150px;
}
</style>
</head>
<body>
<div class="layui-container">
<div class="layui-form">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend>商品信息采集</legend>
</fieldset>
<div class="layui-form-item">
<label class="layui-form-label">请粘贴网址</label>
<div class="layui-input-block">
<input type="text" id="url" lay-verify="title" autocomplete="off"
placeholder="支持淘宝/速卖通搜索结果与单独详情页面地址" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button type="submit" class="layui-btn" lay-submit="" lay-filter="demo1"
onclick="startJob()">开始采集</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
<button type="button" class="layui-btn layui-btn-danger layui-btn-radius layui-layout-right"
onclick="openSetting()" style="display:none"><i class="layui-icon layui-icon-set"></i></button>
</div>
</div>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend>任务控制台</legend>
</fieldset>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">输出信息:</label>
<div class="layui-input-block">
<textarea id="console" placeholder="开始采集后会在这里显示任务详细状态信息" class="layui-textarea" rows="6"></textarea>
</div>
</div>
</div>
</div>
<!-- 设置弹出层 -->
<div id="setting" class="layui-fluid" style="display:none">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
<legend>批量抓取任务参数</legend>
</fieldset>
<div class="layui-form-item">
<label class="layui-form-label setting-label">并发任务数量:</label>
<div class="layui-input-inline">
<input type="number" name="task_num" lay-verify="required" placeholder="请输入" autocomplete="off"
class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label setting-label">商品详情采集图片数量:</label>
<div class="layui-input-inline">
<input type="number" name="pic_num" lay-verify="required" placeholder="请输入" autocomplete="off"
class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button type="submit" class="layui-btn" lay-submit="">保存</button>
<button type="reset" class="layui-btn layui-btn-primary">取消</button>
</div>
</div>
</div>
<script src="https://cdn.bootcss.com/jquery/1.9.1/jquery.min.js"></script>
<script src="https://www.layuicdn.com/layui/layui.all.js"></script>
<script>
var layer = layui.layer;
var form = layui.form;
function output(res) {
$("#console").val(res.message)
}
function startJob() {
var url = $("#url").val();
pywebview.api.startJob(url).then(output);
}
function openSetting() {
// 打开弹出遮罩层
layer.open({
type: 1,
title: '参数设置',
area: ['600px', '300px'],
shadeClose: true,
content: $('#setting')
});
}
</script>
</body>
</html>
打包成单个EXE文件
打包需要将关键的三个DLL
文件一并打进去,所以首先需要复制这几个文件到Python代码根目录下:
.\venv\Lib\site-packages\webview\lib\WebBrowserInterop.x64.dll
.\venv\Lib\site-packages\webview\lib\WebBrowserInterop.x86.dll
.\venv\Lib\site-packages\webview\lib\Microsoft.Toolkit.Forms.UI.Controls.WebView.dll
如果是跑了Flask应用提供HTTP服务,那么还需要额外将templates
和static
文件夹也打包进去,完整的命令如下:
pyinstaller -w -F --add-data "templates;templates" --add-data "static;static" --add-data "WebBrowserInterop.x86.dll;./" --add-data "WebBrowserInterop.x64.dll;./" --add-data
"Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;./" --onefile -y main.py
实现目标
在服务器端设计好界面,用webview
封装成一个exe文件,运行后加载服务器端的html界面,点击界面上的按钮通过js和python函数进行互动.