文章目录
3天速成PyWebView:从零到项目实战的完整攻略
概述
PyWebView 是一个轻量级的跨平台库,用于在 Python 中创建桌面 Web 视图应用程序。它使用系统原生 Web 组件(Windows 使用 Edge/IE,macOS 使用 WebKit,Linux 使用 WebKit/GTK),让你能够用 HTML/CSS/JavaScript 构建 GUI。
主要特点
- 跨平台支持(Windows、macOS、Linux)
- 无需外部 Web 服务器
- 支持 Python 与 JavaScript 双向通信
- 轻量级,依赖少
- 支持多窗口应用
安装与配置
基础安装
pip install pywebview
平台特定依赖
Windows: 通常无需额外依赖(使用系统自带的 Edge/IE)
macOS: 无需额外依赖(使用系统 WebKit)
Linux: 需要安装 WebKitGTK
# Ubuntu/Debian
sudo apt install webkit2gtk-4.0
# Fedora
sudo dnf install webkit2gtk4.0
# Arch Linux
sudo pacman -S webkit2gtk
基础使用
最简单的示例
import webview
# 创建并显示一个简单的浏览器窗口
webview.create_window("Hello World", "https://pywebview.flowrl.com/")
webview.start()
加载本地 HTML
import webview
import os
# 获取当前目录
current_dir = os.path.dirname(os.path.abspath(__file__))
html_file = os.path.join(current_dir, 'index.html')
webview.create_window("本地应用", html_file)
webview.start()
进阶功能
Python 与 JavaScript 交互
从 Python 调用 JavaScript
import webview
def evaluate_js(window):
result = window.evaluate_js("""
document.title = "新标题";
return "JavaScript 执行完成";
""")
print(result) # 输出: JavaScript 执行完成
window = webview.create_window("JS 交互示例", "https://example.com")
webview.start(evaluate_js, window)
从 JavaScript 调用 Python
import webview
class API:
def __init__(self):
self.data = "来自 Python 的数据"
def process_data(self, data):
print(f"收到来自 JavaScript 的数据: {data}")
return f"处理后的数据: {data.upper()}"
api = API()
window = webview.create_window("API 示例", "index.html", js_api=api)
webview.start()
在 HTML 中调用 Python 方法:
<!DOCTYPE html>
<html>
<head>
<title>API 测试</title>
</head>
<body>
<button onclick="callPython()">调用 Python 方法</button>
<script>
async function callPython() {
try {
const result = await pywebview.api.process_data("hello from js");
alert(result);
} catch (e) {
console.error(e);
}
}
</script>
</body>
</html>
多窗口应用
import webview
import threading
def open_second_window():
second_window = webview.create_window(
"第二个窗口",
"second.html",
width=400,
height=300
)
webview.start()
# 主窗口
main_window = webview.create_window(
"主窗口",
"main.html",
width=800,
height=600
)
# 在按钮点击或其他事件中打开第二个窗口
threading.Thread(target=open_second_window).start()
webview.start()
自定义窗口样式
import webview
window = webview.create_window(
title="自定义窗口",
url="index.html",
width=1024,
height=768,
resizable=True,
fullscreen=False,
min_size=(400, 300),
frameless=False, # 无边框窗口
easy_drag=True, # 允许拖动
on_top=False # 置顶窗口
)
webview.start()
代码示例
完整的桌面应用示例
import webview
import json
from datetime import datetime
class TodoApp:
def __init__(self):
self.tasks = []
def add_task(self, task_text):
task = {
'id': len(self.tasks) + 1,
'text': task_text,
'created': datetime.now().isoformat(),
'completed': False
}
self.tasks.append(task)
return task
def get_tasks(self):
return self.tasks
def complete_task(self, task_id):
for task in self.tasks:
if task['id'] == task_id:
task['completed'] = True
return True
return False
# 创建应用实例
todo_app = TodoApp()
# 创建窗口
window = webview.create_window(
"待办事项应用",
"todo_app.html",
js_api=todo_app,
width=600,
height=800,
resizable=True
)
if __name__ == '__main__':
webview.start(debug=True) # debug=True 启用开发者工具
对应的 HTML 文件 (todo_app.html):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>待办事项</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 500px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.input-group {
display: flex;
margin-bottom: 20px;
}
#taskInput {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
padding: 10px 15px;
background: #007cba;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-left: 10px;
}
button:hover {
background: #005a87;
}
.task-item {
padding: 10px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: between;
align-items: center;
}
.task-item.completed {
text-decoration: line-through;
color: #888;
}
.task-text {
flex: 1;
}
</style>
</head>
<body>
<div class="container">
<h1>待办事项</h1>
<div class="input-group">
<input type="text" id="taskInput" placeholder="添加新任务...">
<button onclick="addTask()">添加</button>
</div>
<div id="taskList"></div>
</div>
<script>
async function loadTasks() {
try {
const tasks = await pywebview.api.get_tasks();
renderTasks(tasks);
} catch (e) {
console.error('加载任务失败:', e);
}
}
async function addTask() {
const input = document.getElementById('taskInput');
const text = input.value.trim();
if (text) {
try {
await pywebview.api.add_task(text);
input.value = '';
loadTasks();
} catch (e) {
console.error('添加任务失败:', e);
}
}
}
async function completeTask(taskId) {
try {
await pywebview.api.complete_task(taskId);
loadTasks();
} catch (e) {
console.error('完成任务失败:', e);
}
}
function renderTasks(tasks) {
const taskList = document.getElementById('taskList');
taskList.innerHTML = '';
tasks.forEach(task => {
const taskElement = document.createElement('div');
taskElement.className = `task-item ${task.completed ? 'completed' : ''}`;
taskElement.innerHTML = `
<span class="task-text">${task.text}</span>
${!task.completed ?
`<button onclick="completeTask(${task.id})">完成</button>` :
'<span>✓</span>'
}
`;
taskList.appendChild(taskElement);
});
}
// 页面加载时获取任务
document.addEventListener('DOMContentLoaded', loadTasks);
// 支持回车键添加任务
document.getElementById('taskInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
addTask();
}
});
</script>
</body>
</html>
文件操作示例
import webview
import json
class FileManager:
def save_file(self, content):
try:
# 打开文件保存对话框
file_path = window.create_file_dialog(
webview.SAVE_DIALOG,
file_types=('JSON files (*.json)', 'All files (*.*)')
)
if file_path:
with open(file_path[0], 'w', encoding='utf-8') as f:
json.dump(content, f, ensure_ascii=False, indent=2)
return True
return False
except Exception as e:
return str(e)
def load_file(self):
try:
# 打开文件选择对话框
file_path = window.create_file_dialog(
webview.OPEN_DIALOG,
file_types=('JSON files (*.json)', 'All files (*.*)')
)
if file_path:
with open(file_path[0], 'r', encoding='utf-8') as f:
return json.load(f)
return None
except Exception as e:
return str(e)
file_manager = FileManager()
window = webview.create_window(
"文件管理器",
"file_manager.html",
js_api=file_manager
)
webview.start()
注意事项
1. 跨平台兼容性
import webview
import platform
# 根据平台调整设置
if platform.system() == 'Windows':
# Windows 特定配置
pass
elif platform.system() == 'Darwin': # macOS
# macOS 特定配置
pass
elif platform.system() == 'Linux':
# Linux 特定配置
pass
2. 安全性考虑
import webview
class SecureAPI:
def __init__(self):
self.allowed_methods = ['get_data', 'process_data']
def __getattr__(self, name):
if name not in self.allowed_methods:
raise AttributeError(f"方法 {name} 不允许调用")
return super().__getattribute__(name)
def get_data(self):
return "安全的数据"
def process_data(self, data):
# 验证输入数据
if not isinstance(data, str):
raise ValueError("数据必须是字符串")
return f"处理: {data}"
# 使用安全的 API
secure_api = SecureAPI()
window = webview.create_window("安全应用", "index.html", js_api=secure_api)
3. 错误处理
import webview
import logging
# 配置日志
logging.basicConfig(level=logging.INFO)
class RobustAPI:
def safe_method(self, data):
try:
# 业务逻辑
result = self._process_data(data)
return {'success': True, 'data': result}
except Exception as e:
logging.error(f"方法执行失败: {e}")
return {'success': False, 'error': str(e)}
def _process_data(self, data):
# 实际的数据处理
if not data:
raise ValueError("数据不能为空")
return data.upper()
# 创建窗口时的错误处理
try:
window = webview.create_window("健壮应用", "index.html")
webview.start()
except Exception as e:
logging.error(f"应用启动失败: {e}")
4. 性能优化
import webview
import threading
import time
class PerformanceAPI:
def __init__(self):
self.cache = {}
def heavy_operation(self, data):
# 模拟耗时操作
cache_key = str(hash(data))
if cache_key in self.cache:
return self.cache[cache_key]
# 模拟处理时间
time.sleep(2)
result = f"处理结果: {data}"
self.cache[cache_key] = result
return result
def background_task(self, callback_name):
def long_running_task():
# 模拟后台任务
for i in range(5):
time.sleep(1)
# 通过 JavaScript 回调更新进度
window.evaluate_js(f"{callback_name}({i + 1})")
threading.Thread(target=long_running_task, daemon=True).start()
return "后台任务已启动"
api = PerformanceAPI()
window = webview.create_window("性能优化示例", "performance.html", js_api=api)
总结
优点
- 简单易用: API 设计直观,学习曲线平缓
- 跨平台: 支持主流桌面操作系统
- 轻量级: 不依赖复杂的 GUI 框架
- 灵活: 可以使用现代 Web 技术构建界面
- 功能丰富: 支持文件对话框、自定义窗口等
适用场景
- 快速原型开发
- 企业内部工具
- 数据可视化应用
- 需要 Web 技术但希望桌面部署的应用
最佳实践
- 始终进行错误处理
- 验证 JavaScript 传入的数据
- 使用类型注解提高代码可读性
- 考虑性能,避免阻塞主线程
- 测试不同平台的兼容性
扩展资源
- 官方文档: https://pywebview.flowrl.com/
- GitHub 仓库: https://github.com/r0x0r/pywebview
- 示例项目: https://github.com/r0x0r/pywebview/tree/master/examples
PyWebView 是一个强大的工具,特别适合那些希望利用 Web 技术创建桌面应用的开发者。通过合理的架构设计和遵循最佳实践,你可以构建出既美观又功能强大的跨平台桌面应用程序。
5万+

被折叠的 条评论
为什么被折叠?



