- 桌面应用
- 前端
【免费下载链接】pywebview
Build GUI for your Python program with JavaScript, HTML, and CSS
本文基于 pywebview 仓库中的 examples/todos 示例,讲解如何在不启动独立 HTTP 服务器(serverless)的前提下,用 HTML/CSS/JavaScript 构建原生桌面应用:通过相对 URL 加载本地页面,并通过 JS API 对象实现 Python 与 JavaScript 的双向通信。读完本文,你将掌握webview.create_window的本地文件加载、js_api参数注入、webview.windows窗口引用与全屏切换等实战技能,并理解其底层的 HTTP 桥接与 Promise 调用机制。
示例概览:一个相对 URL + JS API 的经典组合
examples/todos是一个功能完整的 Todo 应用(待办事项管理),前端代码改编自 MIT 协议下的 TodoMVC 项目(examples/todos/README.md中保留了原始版权声明,Copyright (c) Addy Osmani, Sindre Sorhus, Pascal Hartig, Stephen Sawchuk)。它体现了两点核心设计:
- 相对 URL 加载:
create_window直接传入'assets/index.html'这样的相对路径,而非http://或file://绝对地址,由 pywebview 内置服务器自动托管本地静态资源; - JS API 通信:Python 端定义一个
Api类并通过js_api参数注入,前端在 JavaScript 中以window.pywebview.api.xxx(...)的形式调用 Python 方法。
目录结构如下:
examples/todos/ ├── README.md # 示例说明与 TodoMVC 许可证声明 ├── main.py # Python 入口:Api 类 + 窗口创建 + start └── assets/ ├── index.html # 页面骨架(TodoMVC 界面) ├── script.js # Store / Model / View / Controller 分层逻辑 ├── styles.css # 界面样式 └── logo.jpg # 页面头部 Logo从入口代码看无服务器架构
examples/todos/main.py 全文件不足 30 行,却完整展示了 serverless 应用的骨架:
import webview class Api: def addItem(self, title): print(f'Added item {title}') def removeItem(self, item): print(f'Removed item {item}') def editItem(self, item): print(f'Edited item {item}') def toggleItem(self, item): print(f'Toggled item {item}') def toggleFullscreen(self): webview.windows[0].toggle_fullscreen() if __name__ == '__main__': api = Api() webview.create_window('Todos magnificos', 'assets/index.html', js_api=api, min_size=(600, 450)) webview.start(ssl=True)核心调用逐行拆解
create_window(title, url, js_api, min_size):创建原生窗口。这里url参数传入的是相对路径'assets/index.html',pywebview 会启动内置 Bottle 服务器托管该目录下的静态资源,页面内引用的styles.css、script.js、logo.jpg也因此能被正确解析(见 examples/todos/assets/index.html 中的<link>、<script>与<img>标签)。js_api=api:把Api实例暴露给前端 JavaScript,前端即可调用window.pywebview.api.addItem(...)等方法。min_size=(600, 450):设置窗口最小尺寸。该参数在 webview/init.py 的create_window签名中默认值为(200, 100),类型为(width, height)二元组。webview.start(ssl=True):启动 GUI 主循环并启用 SSL。从 webview/init.py 的__generate_ssl_cert实现可以看到,启用 SSL 需要安装cryptography包(pip install pywebview[ssl]),pywebview 会现场生成自签名证书(keyfile_*.pem/certfile_*.pem),证书的 SAN 包含localhost,有效期 365 天。webview.windows[0]:pywebview 维护全局窗口列表,第一个创建的窗口索引为 0,这里用于在 API 方法中拿到窗口对象并调用toggle_fullscreen()。该方法定义在 webview/window.py,其调用链为Window.toggle_fullscreen -> gui.toggle_fullscreen(uid),最终由各平台后端(WinForms/Qt/GTK/Cocoa 等)实现原生切换。
为什么说它是"无服务器"?
对比examples/flask_app这类依赖外部 Web 框架的示例,todos示例不需要开发者自己启动任何 HTTP 服务——create_window的url参数只接受相对路径或本地路径,pywebview 内部会为本地资源自动起一个轻量 HTTP 服务器。相关实现位于 webview/http.py:start_server会计算所有本地 URL 的公共路径(os.path.commonpath)作为root_path,静态文件由/与/<file:path>路由托管;同时注册POST /js_api/{uid}端点,用于承接 JavaScript 端发来的 API 调用,再通过server.js_callback回调分发到对应的 Python 方法。这意味着应用打包发布时无需依赖外部端口或 Web 服务器进程,天然适合 PyInstaller 等方式冻结分发。
前端如何调用 Python:JS API 的 Promise 化桥接
在前端 examples/todos/assets/script.js 的controller.js部分可以看到 JS API 的实际调用方式:
self.view.bind('newTodo', function (title) { self.addItem(title); window.pywebview.api.addItem(title) }); self.view.bind('itemEditDone', function (item) { self.editItemSave(item.id, item.title); window.pywebview.api.editItem(item) }); self.view.bind('itemRemove', function (item) { self.removeItem(item.id); window.pywebview.api.removeItem(item) }); self.view.bind('itemToggle', function (item) { self.toggleComplete(item.id, item.completed); window.pywebview.api.toggleItem(item) }); self.view.bind('toggleFullscreen', function () { window.pywebview.api.toggleFullscreen() });可见每个 Todo 操作(新增、编辑、删除、勾选、全屏)都会触发一次对 Python 的跨语言调用。这里的window.pywebview.api并非手写的全局对象,而是由 pywebview 运行时自动注入的。
注入与桥接原理
注入逻辑在 webview/js/api.js 中:window.pywebview._createApi(funcList)接收 Python 端暴露的函数清单(函数名 + 参数列表),在window.pywebview.api下为每个函数生成一个new Function(...)包装器:
- 每次调用会生成唯一 ID(
(Math.random() + "").substring(2)); - 调用前注册一个 Promise:
_checkValue(funcName, resolve, reject, __id)负责异步等待 Python 端的返回值; - 随后通过
_jsApiCallback(funcName, params, id)把参数序列化并分发到各平台后端(mshtml 走window.external.call,edgechromium 走chrome.webview.postMessageWithAdditionalObjects,其余平台走window.external.call带 token 的形式); - 因此
pywebview.api上的每个方法都返回一个 Promise,这是前端then(...)语法得以工作的前提。
值得注意的是_createApi还做了参数名净化(sanitize_params):若 Python 方法参数名是 JS 保留字(如case、new、function等),会自动追加_后缀避免语法错误;并且支持通过funcName.split('.')构造嵌套 API 结构——例如 Python 端heavy_stuff.doHeavyStuff会暴露为pywebview.api.heavy_stuff.doHeavyStuff,该能力在 examples/js_api.py 中也有完整演示(class HeavyStuffAPI被内嵌进Api类成为属性)。
若你的 Python 方法需要返回值(本示例中的addItem等只负责打印日志,无需返回值),可以让方法return任意 Python 对象,pywebview 会将其序列化为 JSON 传回前端 Promise。若需要更精细的桥接控制(如显式expose、事件注入、异步取消等),可参考 examples/js_api.py 与仓库内更完整的 js_api 指南文档。
Todo 应用的前端分层:Store / Model / View / Controller
script.js完整实现了经典 MVC 分层,虽然它不直接与 pywebview 相关,但理解它有助于看清 JS API 调用点在业务中的位置:
- Store(storage):基于
window.todoStorage的"伪数据库",提供find/findAll/save/remove/drop五个方法,新增条目时以new Date().getTime()作为自增 ID(script.js 中 store.js 段落); - Model:在 Store 之上封装
create/read/update/remove/removeAll/getCount,负责业务数据语义(model.js 段落); - Template + View:用 Underscore 风格微模板生成
<li>列表项,封装全部 DOM 读写与事件绑定,包括双击编辑、Enter 提交、Esc 取消、全选等交互(view.js 段落); - Controller:承接视图事件并协调 Model,在每个关键动作处插入
window.pywebview.api.*调用,实现"界面操作 -> 本地存储 -> 通知 Python"的完整链路(controller.js 段落)。
页面结构上,index.html 提供了输入框、列表、底部过滤器(All / Active / Completed)与一个自定义的fullscreen按钮,该按钮的事件在 View 的bind('toggleFullscreen', ...)中被拦截,最终触发pywebview.api.toggleFullscreen()完成窗口全屏切换——这是示例对 TodoMVC 的 pywebview 化改造亮点之一。
运行与验证
在已安装 pywebview(及其平台依赖)的环境中,直接运行即可看到效果:
python examples/todos/main.py预期行为:
- 弹出标题为 "Todos magnificos" 的窗口,最小尺寸被限制为 600×450;
- 页面通过相对 URL 正常加载 Logo、样式与脚本;
- 新增、编辑、删除、勾选 Todo 时,Python 控制台分别打印
Added item ...、Edited item ...、Removed item ...、Toggled item ...; - 点击页面底部
fullscreen按钮,窗口切换为全屏/退出全屏。
若在无cryptography的环境下运行报错,可按提示pip install pywebview[ssl]或把webview.start(ssl=True)改为webview.start()。
小结:从示例到你自己的无服务器桌面应用
examples/todos用 30 行 Python 加一份 TodoMVC 前端,展示了 pywebview 无服务器应用的两个支柱能力:
| 能力 | 用法 | 底层实现位置 |
|---|---|---|
| 相对 URL 加载本地页面 | create_window(title, 'assets/index.html', ...) | webview/http.py 的 Bottle 静态托管 +POST /js_api/{uid}端点 |
| Python ⇄ JS 双向调用 | js_api=api+window.pywebview.api.xxx() | webview/js/api.js 的_createApiPromise 包装;webview/window.py 的expose机制 |
| 窗口级原生操作 | webview.windows[0].toggle_fullscreen() | webview/window.py → 各平台gui.toggle_fullscreen |
| 零依赖静态托管 | 内置服务器自动路由本地资源 | webview/http.py 的静态文件路由 |
这套模式适合:需要桌面壳的本地工具、需要 Python 后端能力但不想维护 Web 服务器的场景、以及后续用 PyInstaller 打包分发的小型应用。更复杂的 JS API 能力(返回复杂对象、嵌套 API、异常捕获、线程安全的长任务)可以继续研读 examples/js_api.py,它演示了init、随机数、异常抛出与可取消重任务等进阶用法,与本文示例相互印证,共同构成 pywebview JS API 机制的完整实践图谱。
- 桌面应用
- 前端
【免费下载链接】pywebview
Build GUI for your Python program with JavaScript, HTML, and CSS
相关推荐
pywebview 实战示例全解:从无服务器架构到 Flask HTTP 服务,构建完整的 Python 桌面应用
pywebview 实战示例全解:从无服务器架构到 Flask HTTP 服务,构建完整的 Python 桌面应用 本文基于 pywebview 仓库的 doc
桌面应用前端pywebview与WebSocket集成:构建实时通信桌面应用的终极指南
pywebview与WebSocket集成:构建实时通信桌面应用的终极指南 在现代应用开发中, pywebview 作为一款强大的Python库,让开发者能够使
桌面应用前端【免费下载】 使用pywebview构建Python桌面应用的Web界面
使用pywebview构建Python桌面应用的Web界面 什么是pywebview pywebview是一个轻量级的跨平台工具库,它允许开发者使用JavaSc
桌面应用前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考