py-quick-webgui
最新版本: v0.2.11
使用 Python Web 框架快速构建桌面应用程序的工具库。
源码地址: https://gitee.com/iiixxxiii/pyquickwebgu
交流频道: https://pd.qq.com/s/8wqp6rkpk
✨ 特性
- 🚀 多框架支持: Django, Flask, FastAPI, web.py
- 🎨 前端集成: 支持 Vue, React 等现代前端框架
- 🖥️ 多种浏览器: Command Browser, WebView, Tauri
- 🔌 WebSocket 通信: 基于 WebSocket 协议的双向实时通信
- 📦 系统托盘: 内置系统托盘图标和菜单,支持隐藏/显示/退出
- 🔧 插件系统: 可扩展的插件架构
- 🌐 跨平台: Windows, macOS, Linux
- ✨ Eel 兼容:
start()方法与 Eel 框架 API 完全兼容 - 🚀 延迟初始化:
init()方法支持动态配置 - 🌐 自定义路由:
route()装饰器统一注册路由 - ⚡ 快速启动: 智能服务器就绪检测 + 窗口延迟显示,消除白屏问题
- 🖼️ 窗口图标: 新增
icon参数,自动加载 favicon.ico / logo.ico 或用户自定义图标 - 🌐 外部开发服务器:
index_html支持外部 URL(如 Vite/Webpack 开发服务器),方便前后端分离开发
📋 目录
📦 安装
基础安装
pip install pyquickwebgui
完整依赖
根据不同需求安装额外依赖:
# Flask WebSocket 支持
pip install flask-socketio eventlet
# FastAPI (已包含)
pip install uvicorn
# web.py (已包含)
pip install web.py
# WebView 支持
pip install pywebview
# 系统托盘支持
pip install pystray Pillow
# 数据库加密支持(可选,需 C++ 构建工具)
pip install pysqleet
# Tauri 支持
# 请参考 Tauri 官方文档
🚀 快速开始
1. Hello World 示例
创建 hello.py:
from pyquickwebgui import QuikeUI
import os
# 创建 QuikeUI 实例
qui = QuikeUI(
web_path=os.path.join(os.path.dirname(__file__), "web"),
index_html="hello.html" # 启动页面
)
# 暴露函数给 JavaScript
@qui.expose
def say_hello(name):
print(f'Hello from {name}')
return f'Python says: Hello, {name}!'
# 启动应用
qui.run()
创建 web/hello.html:
<!DOCTYPE html>
<html>
<head>
<title>Hello World</title>
<script src="/QuikeUI.js"></script>
<script>
// 调用 Python 函数
window.addEventListener('load', async function() {
try {
const result = await QuikeUI.callPython('say_hello', 'JavaScript!');
alert(result);
} catch (error) {
console.error('调用失败:', error);
}
});
</script>
</head>
<body>
<h1>Hello from QuikeUI!</h1>
</body>
</html>
运行:
python hello.py
2. Hello World PyPage 示例(纯 Python 构建 UI)
使用 PagePlugin 完全在 Python 中构建 UI,无需编写 HTML 文件。通过 view() + CSS 类名模拟各种元素,on_click + on_result 实现 Lona 风格事件驱动交互:
from pyquickwebgui import QuikeUI
from pyquickwebgui.rootplugins.PagePlugin import PagePlugin as page
import random
import datetime
# 创建 QuikeUI 实例
qui = QuikeUI(
title="QuikeUI 极简示例",
debug=True,
)
# 创建 Page 实例(每个页面独立状态)
home_page = page.create_page()
# ==================== 按钮回调函数 ====================
def handle_get_info(data):
"""按钮回调 - 获取服务器信息"""
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return f"服务器时间: {now}\nPython 运行正常!"
def handle_get_random(data):
"""按钮回调 - 生成随机数"""
return f"随机数: {random.randint(1, 100)}"
def show_in_input(result):
"""结果回调 - 将结果写入 input 输入框(Python 完全控制 UI)"""
if result and result.get('status') == 'success':
home_page.set_value('result_input', str(result.get('result', '')))
def show_as_layer(result):
"""结果回调 - 将结果用 layer 弹窗展示"""
if result and result.get('status') == 'success':
home_page.layer_msg(str(result.get('result', '')), icon=1)
# ==================== 首页 ====================
@qui.route("/")
def index():
container = home_page.view(cls="layui-container")
# 标题 - 用 view() + CSS 类名模拟 h1、blockquote 等元素
container.add(home_page.view("QuikeUI + PagePlugin 极简示例", cls="h1"))
container.add(home_page.view("Python 与 JavaScript 双向通信示例", cls="layui-elem-quote"))
# 按钮区域
container.add(home_page.view("Python 交互(Lona 风格)", cls="h3"))
btn_container = home_page.view(cls="layui-btn-container")
# 按钮1:结果写入 input 输入框
btn1 = home_page.button("获取服务器信息", btn_type="primary",
on_click=handle_get_info, on_result=show_in_input)
btn_container.add(btn1)
# 按钮2:结果用 layer 弹窗展示
btn2 = home_page.button("生成随机数", color="blue",
on_click=handle_get_random, on_result=show_as_layer)
btn_container.add(btn2)
container.add(btn_container)
# 结果展示输入框(需要真正的 <input> 元素时用 tags)
container.add(home_page.view("结果展示", cls="h3"))
result_input = home_page.tags.input_(
id="result_input", type="text",
cls="layui-input", placeholder="点击按钮后,结果将显示在这里...",
readonly="readonly"
)
container.add(result_input)
# 链接 - 用 link() 方法
container.add(home_page.view("组件示例", cls="h3"))
container.add(home_page.link(" 进入组件示例首页", href="/demo/",
cls="layui-btn layui-btn-normal"))
# 渲染页面
body = home_page.view()
body.add(container)
return home_page.render_page(body, title="QuikeUI 极简示例", include_quikeui=True)
if __name__ == "__main__":
qui.run()
运行:
python hello.py
核心要点:
view("文本", cls="h1")—view()始终创建div,通过 CSS 类名模拟 h1/h2/p/blockquote 等元素on_click处理业务逻辑,on_result控制 UI 更新方式(Python 完全控制)link("文本", href="...")— 封装a标签,支持绑定 Python 回调- 只有需要真正的
<input>/<table>等原生元素时,才用tags.xxx
查看完整示例:examples/18 - hello_world_pypage
3. Flask 示例
from flask import Flask
from pyquickwebgui import QuikeUI
app = Flask(__name__)
@app.route('/')
def home():
return '<h1>Hello Flask!</h1>'
qui = QuikeUI(
app=app,
server_type="flask",
width=800,
height=600
)
qui.run()
4. FastAPI 示例
from fastapi import FastAPI
from pyquickwebgui import QuikeUI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello FastAPI!"}
qui = QuikeUI(
app=app,
server_type="fastapi",
debug=True
)
qui.run()
🔧 核心功能
WebSocket 双向通信
QuikeUI 使用基于 eel 协议的 WebSocket 实现 Python 和 JavaScript 的双向实时通信。
架构优势
- 实时双向通信:Python 和 JavaScript 可以互相调用函数
- 低延迟:WebSocket 保持长连接,避免 HTTP 请求开销
- 自动函数注册:前端自动获取后端暴露的函数列表
- 异步支持:FastAPI 支持异步函数调用
通信协议
消息格式:
- JavaScript → Python(函数调用):
{"call": 1234567890, "name": "function_name", "args": ["arg1", "arg2"]}
- Python → JavaScript(返回结果):
{"return": 1234567890, "status": "ok", "value": "result_data"}
- Python → JavaScript(发送函数列表):
{"type": "py_functions", "functions": ["func1", "func2"]}
Python 调用 JavaScript
QuikeUI 支持两种浏览器类型的 JS 调用:
1. WebView 模式
# webview 直接执行 JS
qui.call_js_function('jsFunction', 'arg1', 'arg2')
2. CommandBrowser 模式(通过 WebSocket)
# 方式1: 直接调用
qui.call_js_function('my_js_function', 'arg1', 'arg2')
# 方式2: 使用 _js 后缀
qui.say_hello_js('Hello from Python!')
# 方式3: 在启动回调中调用(推荐)
import time
def on_startup():
time.sleep(2) # 等待 WebSocket 连接建立
qui.my_function_js('test')
qui.on_startup = on_startup
qui.run()
⚠️ 重要提示:
- Python 调用 JS 前,必须确保 WebSocket 连接已建立
- 建议在
on_startup回调中使用time.sleep()等待连接 - 启用调试模式查看详细日志:
qui = QuikeUI(debug=True)
工作流程:
Python (call_js_function)
↓
查找 WebSocket Handler
↓
构建 WebSocket 消息帧
↓
发送到所有活跃的客户端连接
↓
浏览器接收消息 (QuikeUI.js onmessage)
↓
执行对应的 JavaScript 函数
JavaScript 调用 Python
自动队列缓存机制:
QuikeUI 支持在 WebSocket 连接前缓存调用,连接后自动执行。这解决了页面加载时 WebSocket 可能尚未建立的问题。
// ✅ 即使 WebSocket 未连接,也可以安全调用
// 调用会被加入队列,等待连接后自动发送
QuikeUI.callPython('get_data').then(result => {
console.log(result);
});
// 多个调用会被依次缓存和执行
QuikeUI.callPython('func1', 'arg1'); // 加入队列
QuikeUI.callPython('func2', 'arg2'); // 加入队列
QuikeUI.callPython('func3', 'arg3'); // 加入队列
// WebSocket 连接后,按顺序执行:func1 → func2 → func3
工作原理:
- 连接前:调用被加入
_mock_queue队列 - 连接时:触发
onopen事件,设置_websocket_connected = true - 刷新队列:调用
_flush_mock_queue()依次发送所有缓存的调用 - 返回结果:每个调用都会收到对应的 Promise 结果
优势:
- ✅ 无需手动等待 WebSocket 连接
- ✅ 无需使用
setTimeout或轮询检查 - ✅ 调用顺序保证(FIFO)
- ✅ 错误处理完善(发送失败会重新入队)
详细实现说明:
队列机制的核心是 _call_object 创建调用对象,包含唯一的 call_id、函数名和参数。当 WebSocket 未连接时,这些调用对象被推入 _mock_queue 数组。连接成功后,_flush_mock_queue() 方法会遍历队列,将每个调用序列化为 JSON 并通过 WebSocket 发送。
// 内部实现示例
QuikeUI._mock_queue = []; // 调用队列
QuikeUI._call_return_callbacks = {}; // 回调映射
QuikeUI.callPython = function(name, ...args) {
if (!QuikeUI._websocket || !QuikeUI._websocket_connected) {
let call_object = QuikeUI._call_object(name, args);
QuikeUI._mock_queue.push(call_object); // 入队
return new Promise(function(resolve, reject) {
QuikeUI._call_return_callbacks[call_object.call] = {resolve, reject};
});
}
// 已连接则直接发送
...
};
这种设计确保了即使在网络不稳定或服务器重启的情况下,前端调用也不会丢失,而是会在连接恢复后自动重试。
Python 同步调用 JavaScript
除了异步调用,QuikeUI 还支持同步调用 JavaScript 函数并等待返回值:
# 同步调用 JS 函数(阻塞直到收到响应)
result = qui.call_js_function_sync("js_random", timeout=5.0)
print(f'从 JavaScript 获取到: {result}')
重要注意事项:
-
WebSocket 连接时机
同步调用必须在 WebSocket 连接建立之后 才能执行:
# ❌ 错误:在 on_startup 中调用(连接尚未建立) def on_startup(): result = qui.call_js_function_sync("js_func") # RuntimeError! # ✅ 正确:在 qui.run() 后等待一段时间 qui.run() time.sleep(5) # 等待浏览器和 WebSocket 连接 result = qui.call_js_function_sync("js_func")
-
与异步调用的区别
特性 同步调用 异步调用 方法名 call_js_function_sync()call_js_function()阻塞 ✅ 阻塞当前线程 ❌ 不阻塞 返回值 直接返回结果 需要通过回调或 Promise 适用场景 需要立即获取结果的场景 不需要等待结果的场景 -
异常处理
try: result = qui.call_js_function_sync("js_func", timeout=5.0) print(f'结果: {result}') except TimeoutError: print('调用超时') except RuntimeError as e: print(f'调用失败: {e}')
技术实现:
同步调用的实现原理:
- Python 端生成唯一的
call_id - 通过 WebSocket 发送调用请求(带
sync: true标记) - 将响应存储在
_js_call_responses字典中 - 轮询检查响应是否到达(每 50ms)
- 收到响应后返回结果或抛出异常
服务器端(DefaultServerWebpy)会检测 sync 标记并将结果存储到 QuikeUI 实例的响应字典中,同时也会通过 WebSocket 发送标准响应(保持向后兼容)。
自动重连机制:
QuikeUI 内置了强大的 WebSocket 自动重连功能,确保在网络波动或服务器重启时应用能够自动恢复连接。
// 默认启用,每2秒重试,最多10次
// 可自定义配置
QuikeUI.enable_reconnect(
interval = 3000, // 重连间隔(毫秒)
max_attempts = 5 // 最大重连次数(0=无限)
);
// 禁用自动重连
QuikeUI.disable_reconnect();
// 手动触发重连
QuikeUI.manual_reconnect();
重连特性:
- ✅ 连接断开后自动尝试重连
- ✅ 可配置重连间隔和最大次数
- ✅ 重连成功后自动刷新队列
- ✅ 达到最大次数后拒绝待处理 Promise
- ✅ 支持手动控制和禁用
- ✅ 详细的日志输出和告警提示
工作流程:
连接断开 (onclose)
↓
检查是否启用自动重连
├─ 否 → 结束
└─ 是 ↓
检查是否达到最大次数
├─ 是 → 拒绝所有待处理 Promise,显示告警
└─ 否 ↓
增加重连计数
↓
延迟指定时间
↓
关闭旧连接(如果存在)
↓
创建新 WebSocket 连接
↓
连接成功 → 重置计数,刷新队列
连接失败 → 再次触发 onclose,循环
使用场景:
- 网络不稳定环境
QuikeUI.enable_reconnect(5000, 20); // 每5秒重试,最多20次
- 服务器维护期间
QuikeUI.enable_reconnect(3000, 0); // 无限重连
- 用户登出时
function logout() {
QuikeUI.disable_reconnect(); // 禁用重连
QuikeUI.callPython('logout').then(() => {
window.location.href = '/login';
});
}
告警功能:
当达到最大重连次数时,系统会提供多层告警:
- 控制台告警:详细的错误信息和排查建议
- 浏览器通知:如果用户授予权限,显示系统通知
- 自定义事件:监听
qui_reconnect_failed事件进行自定义处理 - Promise 拒绝:所有待处理的 Promise 会被拒绝,带有详细错误信息
// 监听重连失败事件
window.addEventListener('qui_reconnect_failed', function(event) {
console.error('重连失败:', event.detail);
// event.detail: {attempts, maxAttempts, message}
showCustomAlert(event.detail);
});
// 请求通知权限
if ('Notification' in window) {
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
console.log('✅ 已启用浏览器通知');
}
});
}
最佳实践:
- 生产环境:
QuikeUI.enable_reconnect(3000, 10)- 适中的重连策略 - 开发环境:
QuikeUI.enable_reconnect(1000, 5)- 快速重连便于调试 - 关键业务:
QuikeUI.enable_reconnect(5000, 0)- 更长等待,无限重试
性能考虑:
- 重连间隔不宜过短(建议 ≥ 1000ms)
- 避免频繁的手动重连
- 监控重连次数,及时发现网络问题
// 查看重连状态
console.log('重连启用:', QuikeUI._reconnect_enabled);
console.log('当前次数:', QuikeUI._reconnect_attempts);
console.log('连接状态:', QuikeUI._websocket_connected);
参数配置
QuikeUI 支持丰富的配置选项:
qui = QuikeUI(
# 服务器配置
server_type="webpy", # 服务器类型: webpy, flask, fastapi
app=None, # Web 应用实例
port=None, # 端口号(自动分配)
# 窗口配置
width=800, # 窗口宽度
height=600, # 窗口高度
fullscreen=False, # 全屏模式
frameless=False, # 无边框窗口
x=0, y=0, # 窗口位置
# 浏览器配置
browser_type="command", # 浏览器类型: command, webview, tauri
show_browser=True, # 显示浏览器
browser_path=None, # 浏览器路径
extra_flags=[], # 额外浏览器标志
# 页面配置
index_html="index.html", # 启动页面(支持文件名或外部 URL,如 http://localhost:5173/)
web_path="web", # 静态文件路径(默认值:"web",支持相对路径和绝对路径)
# 窗口图标
icon=None, # 窗口图标路径(.ico 格式,支持绝对路径或相对于 web_path 的相对路径)
# 高级功能
inject_js=None, # 注入 JavaScript(支持文件路径、相对路径、代码字符串)
disable_right_click=False, # 禁用右键菜单
debug=False, # 调试模式
reload=False, # 热重载
# 生命周期
on_startup=None, # 启动回调
on_shutdown=None, # 关闭回调
# 其他
tray=None, # 系统托盘配置
log=None, # 日志对象
log_level="info", # 日志级别
)
web_path 路径处理:
- ✅ 默认值:
web_path="web",无需手动设置即可使用web/目录 - ✅ 支持相对路径:
web_path="web"会自动转换为绝对路径 - ✅ 智能查找:优先在当前工作目录查找,如果不存在则在脚本所在目录查找
- ✅ 自动转换:相对路径会在初始化时自动转换为绝对路径
- ✅ 简化配置:无需手动使用
os.path.join()拼接路径 - ✅ 外部 URL 兼容:当
index_html为外部 URL 时,web_path自动转换为绝对路径(用于图标等资源查找)
# 方式 1: 使用默认值(推荐)
qui = QuikeUI() # web_path 默认为 "web"
qui.run()
# 方式 2: 显式指定相对路径
qui = QuikeUI(web_path="web")
qui.run()
# 方式 3: 使用绝对路径
qui = QuikeUI(web_path="/absolute/path/to/web")
qui.run()
# 方式 4: 使用 init() 方法
qui = QuikeUI()
qui.init(web_path="web") # 支持相对路径
qui.run()
inject_js 参数详解
inject_js 参数支持三种类型,灵活注入 JavaScript 代码:
1. 相对路径(推荐)
从 web_path 目录下查找文件:
# 方式 1: 简单文件名
qui = QuikeUI(
web_path="web",
inject_js="menu.js" # 会查找 web/menu.js
)
# 方式 2: 子目录下的文件
qui = QuikeUI(
web_path="web",
inject_js="scripts/custom.js" # 会查找 web/scripts/custom.js
)
2. 绝对路径
直接使用完整的文件路径:
import os
qui = QuikeUI(
inject_js=r"D:\projects\menu.js" # Windows 路径
)
# 或使用 os.path.join
qui = QuikeUI(
inject_js=os.path.abspath("menu.js")
)
3. JavaScript 代码字符串
直接传入 JavaScript 代码:
qui = QuikeUI(
inject_js="console.log('Hello from Python!');"
)
# 多行代码
qui = QuikeUI(
inject_js="""
console.log('初始化完成');
window.customConfig = { debug: true };
"""
)
注意事项:
- ✅ 如果 HTML 中已通过
<script>标签加载了 JS 文件,建议设置inject_js=None避免重复加载 - ✅ 相对路径会自动从
web_path目录下查找 - ✅ 如果文件不存在,会在日志中记录警告信息
系统托盘
QuikeUI 内置了系统托盘功能,无需额外插件即可创建托盘图标和菜单。
基本用法
from pyquickwebgui import QuikeUI
qui = QuikeUI(
title="我的应用",
icon="favicon.ico", # 窗口图标
tray={
"icon": "favicon.ico", # 托盘图标(相对于 web_path 或绝对路径)
}
)
qui.run()
默认菜单
未指定 menu 时,自动创建以下默认菜单:
| 菜单项 | 功能 |
|---|---|
| 隐藏 | 隐藏主窗口 |
| 显示 | 显示主窗口 |
| ─── | 分隔线 |
| 退出 | 关闭应用程序 |
自定义菜单和回调
方式 1:字典格式(推荐,更简洁)
def on_show():
print("显示窗口")
def on_exit():
QuikeUI.close_application()
def custom_action():
print("自定义操作")
qui = QuikeUI(
title="我的应用",
tray={
"icon": "favicon.ico",
"menu": [
{"text": "显示窗口", "action": on_show},
{"text": "自定义操作", "action": custom_action},
{"text": "退出", "action": on_exit},
]
}
)
方式 2:pystray.MenuItem 格式
import pystray
# 自定义菜单回调
def on_show():
print("自定义显示逻辑")
def on_exit():
print("自定义退出逻辑")
QuikeUI.close_application()
# 自定义菜单
custom_menu = (
pystray.MenuItem("显示窗口", lambda: on_show()),
pystray.Menu.SEPARATOR,
pystray.MenuItem("退出", lambda: on_exit()),
)
qui = QuikeUI(
title="我的应用",
tray={
"icon": "favicon.ico",
"name": "MyApp",
"title": "我的应用提示",
"menu": custom_menu, # 自定义菜单
"menu_click_show": on_show, # 默认菜单的显示回调
"menu_click_exit": on_exit, # 默认菜单的退出回调
}
)
提示:字典格式中每个菜单项支持
text(菜单文本)、action(回调函数)、enabled(是否可用,默认True)、visible(是否可见,默认True)字段。
配合 JavaScript 控制窗口
@qui.expose
def hide_win():
qui.hide() # 隐藏窗口
return {"message": "ok"}
@qui.expose
def close_win():
QuikeUI.close_application() # 关闭应用
return {"message": "ok"}
// JavaScript 中调用
await QuikeUI.callPython('hide_win'); // 隐藏窗口
await QuikeUI.callPython('close_win'); // 关闭窗口
查看完整示例:examples/14 - tray_web
窗口图标设置
QuikeUI 支持通过 icon 参数自定义窗口图标,优先级如下:
- 用户指定的
icon参数:支持绝对路径或相对于web_path的相对路径 - 项目默认图标:
src/pyquickwebgui/logo.ico - 系统默认图标:如果以上都不存在
# 方式 1: 相对路径(相对于 web_path)
qui = QuikeUI(icon="favicon.ico")
# 方式 2: 绝对路径
qui = QuikeUI(icon=r"D:\projects\my_icon.ico")
# 方式 3: 不指定,使用项目默认 logo.ico
qui = QuikeUI()
Windows 平台说明:
- WebView 模式下通过 .NET WinForms API 设置窗口标题栏图标
- CommandBrowser 模式下图标由浏览器进程决定
- 打包后的 exe 文件需要在编译时通过资源文件设置程序图标
插件系统
QuikeUI 支持插件扩展:
from pyquickwebgui.rootplugins.BasePlugin import BasePlugin
class MyPlugin(BasePlugin):
def register(self, app):
app.log.info("MyPlugin registered")
def run(self, app):
app.log.info("MyPlugin running")
PagePlugin UI 组件库
PagePlugin 提供了基于 Layui 风格的 UI 组件库,使用 dominate 库生成 HTML 标签,方便在 Python 中快速构建 Web 界面。
安装依赖
pip install dominate
基本用法
from pyquickwebgui.rootplugins.PagePlugin import PagePlugin as page
# 生成按钮
btn = page.button("提交", btn_type="primary")
# 生成输入框
inp = page.input("username", placeholder="请输入用户名", verify="required")
# 生成下拉选择框
options = [("0", "写作"), ("1", "阅读"), ("2", "游戏")]
sel = page.select("interest", options, selected_value="1")
# 生成表单项
form_item = page.form_item("用户名", inp)
通用视图方法
view() 是统一的 DOM 元素创建入口,始终创建 div 元素,通过 cls 参数指定 CSS 类名来模拟各种 UI 元素。绝大多数场景下不需要直接使用 tags.xxx,view() + CSS 类名即可构建所有界面:
# 创建 Page 实例
my_page = page.create_page()
# 普通容器
container = my_page.view(cls="layui-container")
# 模拟标题(通过 CSS 类名控制样式)
my_page.view("主标题", cls="h1")
my_page.view("副标题", cls="h2")
my_page.view("小标题", cls="h3")
# 引用块
my_page.view("引用文字", cls="layui-elem-quote")
# 段落
my_page.view("段落内容", cls="p")
# 徽章
my_page.view("标签", cls="layui-badge")
my_page.view("圆角标签", cls="layui-badge layui-badge-radius")
# 按钮(也可以用 page.button() 获得更丰富的参数)
my_page.view("按钮文本", cls="layui-btn layui-btn-primary")
# 嵌套结构(卡片示例)
parent = my_page.view(cls="layui-card")
parent.add(my_page.view("卡片标题", cls="layui-card-header"))
parent.add(my_page.view("卡片内容", cls="layui-card-body"))
# 行内布局
row = my_page.view(cls="layui-row")
col1 = my_page.view("左侧", cls="layui-col-md6")
col2 = my_page.view("右侧", cls="layui-col-md6")
row.add(col1)
row.add(col2)
view() + CSS 类名对照表:
| 想模拟的元素 | view() 写法 |
说明 |
|---|---|---|
<h1> |
view("文本", cls="h1") |
Layui 内置 h1 样式 |
<h2> |
view("文本", cls="h2") |
Layui 内置 h2 样式 |
<h3> |
view("文本", cls="h3") |
Layui 内置 h3 样式 |
<p> |
view("文本", cls="p") |
Layui 内置 p 样式 |
<blockquote> |
view("文本", cls="layui-elem-quote") |
Layui 引用块样式 |
<span> 徽章 |
view("文本", cls="layui-badge") |
Layui 徽章样式 |
<button> |
view("文本", cls="layui-btn") |
Layui 按钮样式 |
<div> 容器 |
view(cls="layui-container") |
Layui 容器 |
<div> 卡片 |
view(cls="layui-card") |
Layui 卡片 |
| 行内布局 | view(cls="layui-row") |
Layui 栅格行 |
| 列 | view(cls="layui-col-md6") |
Layui 栅格列 |
设计理念:
view()+ CSS 类名 = 万能元素构建器。保持代码风格统一,所有 UI 元素都用同一种方式创建。常用组件(如button()、input()、table()等)仍然保留专用方法以提供更便捷的参数接口。何时需要
tags.xxx: 只有当需要真正的原生 HTML 元素时(如<input>、<table>、<select>等表单元素或表格结构),才需要使用my_page.tags.xxx()。
link() 方法封装了 a 标签,支持绑定 Python 回调:
# 普通链接
my_page.link("进入首页", href="/home", cls="layui-btn layui-btn-normal")
# 新窗口打开
my_page.link("外部链接", href="https://example.com", target="_blank")
# 绑定 Python 回调(WebSocket 通信)
def handle_click(data):
return {"status": "ok", "msg": "链接被点击"}
my_page.link("WebSocket 链接", href="#", on_click=handle_click)
| 方法 | 说明 | 主要参数 |
|---|---|---|
view() |
通用视图容器 | content, cls, style |
link() |
链接(a 标签) | text, href, target, cls, on_click, on_result |
container() |
layui 容器 | content, fluid |
表单组件
| 方法 | 说明 | 主要参数 |
|---|---|---|
button() |
按钮 | text, btn_type, disabled |
input() |
输入框 | name, input_type, placeholder, disabled, value, verify |
select() |
下拉选择框 | name, options, selected_value, disabled, searchable |
checkbox() |
复选框 | name, title, checked, disabled, skin |
checkboxes() |
复选框组 | name, options, checked_values, disabled |
switch() |
开关 | name, checked, disabled, on_text, off_text |
radio() |
单选框组 | name, options, checked_value, disabled |
textarea() |
文本域 | name, placeholder, rows, disabled, value |
form_item() |
表单项 | label, content, inline |
form() |
表单容器 | content, action, pane, filter_name |
展示组件
| 方法 | 说明 | 主要参数 |
|---|---|---|
table() |
表格 | elem, cols, data, page, limit, url |
laypage() |
分页 | elem, count, limit |
tree_table() |
树形表格 | elem, cols, data |
nav() |
导航菜单 | items, active, filter_name |
menu() |
基础菜单 | items, active_index |
tabs() |
标签页 | items, active_index, filter_name |
badge() |
徽章 | text, badge_type |
blockquote() |
引用块 | content, cite |
fieldset() |
字段集 | legend, content |
hr() |
分割线 | - |
panel() |
面板 | title, content, collapsed |
collapse() |
折叠面板 | items |
progress() |
进度条 | percent, theme |
timeline() |
时间线 | items |
tree() |
树组件 | elem, data |
carousel() |
轮播 | elem, items, autoplay, interval |
code() |
代码预览 | content, language |
交互组件
| 方法 | 说明 | 主要参数 |
|---|---|---|
layer() |
弹出层 | elem, title, content, area, btn |
laydate() |
日期选择器 | elem, type, format, value |
upload() |
上传组件 | elem, url, accept, exts, multiple |
dropdown() |
下拉菜单 | elem, items, trigger |
colorpicker() |
颜色选择器 | elem, color, format |
transfer() |
穿梭框 | elem, data, title, value |
slider() |
滑块 | elem, value, min_val, max_val, step |
rate() |
评分 | elem, value, count, half |
Page 实例 - Lona 风格事件驱动
Page 类提供类似 Lona 的事件驱动交互模式,Python 端完全控制 UI 更新:
from pyquickwebgui.rootplugins.PagePlugin import PagePlugin as page
# 创建 Page 实例(每个页面独立状态)
home_page = page.create_page()
# 定义业务回调
def get_server_time(data):
import datetime
return f"服务器时间: {datetime.datetime.now()}"
# 定义 UI 更新回调(Python 完全控制展示方式)
def show_in_input(result):
if result and result.get('status') == 'success':
home_page.set_value('result_input', result['result'])
def show_as_popup(result):
if result and result.get('status') == 'success':
home_page.layer_msg(result['result'])
# 按钮绑定:on_click 处理业务,on_result 控制展示
btn1 = home_page.button("获取时间", on_click=get_server_time, on_result=show_in_input)
btn2 = home_page.button("弹窗显示", on_click=get_server_time, on_result=show_as_popup)
Page UI 操作方法:
| 方法 | 说明 | 示例 |
|---|---|---|
set_value(id, value) |
设置元素 value | page.set_value('input_1', 'hello') |
set_text(id, text) |
设置元素文本 | page.set_text('label_1', 'world') |
set_html(id, html) |
设置元素 innerHTML | page.set_html('div_1', '<b>bold</b>') |
layer_msg(msg, icon) |
layer.msg 弹窗 | page.layer_msg('操作成功', icon=1) |
layer_alert(msg, icon) |
layer.alert 弹窗 | page.layer_alert('提示内容') |
事件驱动流程:
用户点击按钮
↓
JS 通过 WebSocket 调用 Python (on_click 回调)
↓
Python 执行业务逻辑,返回结果
↓
Python 执行 on_result 回调,接收结果
↓
on_result 回调通过 WebSocket 推送 JS 更新 UI
↓
前端实时展示结果(弹窗/输入框/DOM 更新)
设计理念:
- ✅ JS 只做最小化通信,不包含任何 UI 逻辑
- ✅ Python 端完全控制 UI 更新目标(想弹窗就弹窗,想写入输入框就写入输入框)
- ✅
on_result支持任意 Python callable,展示逻辑与业务逻辑完全解耦
完整示例
from pyquickwebgui.rootplugins.PagePlugin import PagePlugin as page
# 创建 Page 实例
my_page = page.create_page()
# 创建表单内容(使用 view() 替代 tags.div)
content = my_page.view()
# 添加输入框
username_input = my_page.input("username", placeholder="请输入用户名", verify="required")
content.add(my_page.form_item("用户名", username_input))
# 添加下拉选择框
options = [("0", "写作"), ("1", "阅读"), ("2", "游戏")]
select_box = my_page.select("interest", options, selected_value="1")
content.add(my_page.form_item("兴趣爱好", select_box))
# 添加开关
sw = my_page.switch("status", checked=True, on_text="开", off_text="关")
content.add(my_page.form_item("状态", sw))
# 添加单选框
radio_options = [("male", "男"), ("female", "女")]
radio_group = my_page.radio("gender", radio_options, checked_value="male")
content.add(my_page.form_item("性别", radio_group))
# 添加文本域
ta = my_page.textarea("description", placeholder="请输入描述", rows=5)
content.add(my_page.form_item("描述", ta))
# 添加按钮
btn = my_page.button("提交", btn_type="primary")
content.add(btn)
# 包装成表单
form = my_page.form(content, action="/submit", filter_name="demo")
# 输出 HTML
print(form.render())
@qui.expose 装饰器
@qui.expose 装饰器允许将 Python 函数暴露给 JavaScript 调用,实现前后端的双向通信。
Python 端用法
from pyquickwebgui import QuikeUI
# 创建 QuikeUI 实例
qui = QuikeUI()
# 使用 @qui.expose 装饰器暴露函数
@qui.expose
def say_hello(name):
print(f'Hello from {name}')
return f'Python says: Hello, {name}!'
# 启动应用
qui.run()
JavaScript 端用法
在 HTML 文件中引入 QuikeUI.js:
<script src="/QuikeUI.js"></script>
<script>
// 调用暴露的 Python 函数
QuikeUI.callPython('say_hello', 'JavaScript World!')
.then(result => {
console.log('Python 返回:', result);
})
.catch(error => {
console.error('调用失败:', error);
});
</script>
API 参考
Python API:
@qui.expose- 将 Python 函数标记为可被 JavaScript 调用qui.get_exposed_functions()- 获取所有已暴露的函数qui.call_js_function(func_name, *args, **kwargs)- 调用 JavaScript 函数(需要浏览器支持)
JavaScript API:
QuikeUI.callPython(funcName, ...args)- 调用暴露的 Python 函数,返回 PromiseQuikeUI.expose(func)- 暴露 JavaScript 函数给 Python(当前版本暂不支持)
示例:
// 无参数调用
QuikeUI.callPython('get_time')
.then(time => console.log(time));
// 带参数调用
QuikeUI.callPython('add_numbers', 5, 10)
.then(result => console.log(result)); // 输出: 15
// 异步处理
async function fetchData() {
const result = await QuikeUI.callPython('get_data', 'param1');
console.log(result);
}
工作原理
- 装饰器注册:
@qui.expose将函数添加到_exposed_functions字典和exposed_functions_list列表 - WebSocket 连接:前端通过 WebSocket 连接到后端服务器
- 函数列表同步:连接建立后,后端发送所有暴露函数的列表给前端
- 动态导入:前端根据函数列表动态创建对应的 JavaScript 函数
- 调用执行:JavaScript 调用时,通过 WebSocket 发送请求到后端
- 结果返回:后端执行函数并将结果以 JSON 格式返回
支持的 Web 框架
Flask
依赖安装:
pip install flask-socketio eventlet
特点:
- 使用
flask-socketio实现 WebSocket - 自动降级到普通 Flask 服务器(如果不支持 WebSocket)
- 不支持
waitress(因为它不支持 WebSocket)
FastAPI
依赖安装:
pip install uvicorn
特点:
- 原生支持 WebSocket
- 支持异步函数(
async def) - 性能更好
web.py
依赖安装:
# 无需额外依赖,使用内置 socket 实现
特点:
- 使用原生 socket + threading 实现 WebSocket
- WebSocket 运行在独立端口(HTTP 端口 + 1)
- 完全手动实现 WebSocket 协议
- 适合轻量级应用
注意:
- web.py 的 WebSocket 端口为 HTTP 端口 + 1
- 例如:HTTP 在 8080,WebSocket 在 8081
- 前端需要连接到正确的 WebSocket 端口
工作流程
-
应用启动
- QuikeUI 初始化,收集所有
@qui.expose装饰的函数 - 将函数列表传递给服务器配置
- QuikeUI 初始化,收集所有
-
服务器启动
- Flask/FastAPI/web.py 服务器启动
- 注册 WebSocket 端点
/qui - 构建暴露函数字典
-
客户端连接
- 浏览器加载页面,建立 WebSocket 连接
- 后端发送
py_functions消息,包含所有暴露的函数名 - 前端动态创建对应的 JavaScript 函数代理
-
函数调用
- JavaScript 调用
QuikeUI.callPython('function_name', args) - 生成唯一的
call_id - 通过 WebSocket 发送调用请求
- Python 执行函数并返回结果
- JavaScript 接收结果并触发回调/Promise
- JavaScript 调用
注意事项
-
依赖安装
- Flask: 需要
flask-socketio和eventlet或gevent - FastAPI: 需要
uvicorn
- Flask: 需要
-
函数序列化
- 参数和返回值必须是 JSON 可序列化的
- 复杂对象需要自定义序列化
-
异步支持
- FastAPI 支持
async def函数 - Flask 仅支持同步函数
- FastAPI 支持
-
错误处理
- 所有异常都会被捕获并通过 WebSocket 返回
- 建议在 Python 函数中添加适当的错误处理
-
连接管理
- WebSocket 断开时会自动重连(取决于浏览器实现)
- 可以在后端监听
disconnect事件
-
WebSocket 时序
- Python 调用 JS 前必须等待连接建立
- 建议使用
on_startup回调 +time.sleep() - 启用
debug=True查看连接状态日志
迁移指南
如果你之前使用 HTTP API 方式:
之前(HTTP):
fetch('/api/exposed/say_hello', {
method: 'POST',
body: JSON.stringify({args: ['World']})
})
现在(WebSocket):
QuikeUI.callPython('say_hello', 'World').then(result => {
console.log(result);
});
更简洁、更高效!🎉
注意事项
- 函数命名:暴露的函数名会直接用作 API 端点,建议使用清晰的命名
- 参数传递:所有参数会通过 JSON 序列化,确保参数是可序列化的类型
- 返回值:返回值也会被序列化为 JSON,复杂对象可能需要特殊处理
- 错误处理:建议在 Python 函数中添加异常处理,或在 JavaScript 中使用
.catch() - 安全性:暴露的函数可以被任何访问应用的 JavaScript 调用,注意权限控制
- WebSocket 端口:web.py 模式下,WebSocket 运行在 HTTP 端口 + 1 上
示例项目
项目包含多个完整示例:
| 示例 | 说明 | 特点 |
|---|---|---|
01 - hello_world |
Hello World | 最基础的示例 |
02 - callbacks |
回调功能 | Python ↔ JavaScript 双向通信 |
03 - sync_callbacks |
同步回调 | 同步调用并获取返回值 |
04 - file_access |
文件访问 | 文件和文件夹选择对话框 |
05 - input |
输入表单 | 表单输入处理 |
06 - jinja_templates |
Jinja2 模板 | 支持 Jinja2 和 web.py 双模板引擎 |
07 - Create_React_App |
React + QuikeUI | 从 Eel 迁移到 QuikeUI,使用 CRA |
08 - Create_Vue_App |
Vue 3 + QuikeUI | 使用 Vite,启动速度快 10-100 倍 |
09 - Eelectron_quick_start |
Electron 快速开始 | Electron 集成示例 |
10 - Tauri_quick_start |
Tauri 快速开始 | Tauri 集成示例 |
11 - custom_app_routes |
自定义路由 | 自定义路由注册 |
12 - open_url |
打开 URL | 外部 URL 打开示例 |
13 - separation_web |
前后端分离 | 静态文件与后端分离 |
14 - tray_web |
系统托盘 + 窗口控制 | 内置托盘菜单,隐藏/显示/关闭窗口 |
15 - db_web |
SQLite 数据库集成 | DbPlugin 插件,支持 CRUD、SQL 文件初始化、线程安全 |
16 - Vue_App_Template |
Vue 3 + Naive UI 完整模板 | Vite + Naive UI + Pinia + Axios,适配 pyquickwebgui |
17 - hello_world_server |
纯服务器模式 | 支持 --server 参数,仅启动 HTTP 服务不启动浏览器 |
18 - hello_world_pypage |
PagePlugin Lona 风格交互 | Python 回调控制 UI 更新,on_click + on_result 事件驱动 |
运行示例:
# Windows
bin\run_examples.bat
# 或手动运行
cd "examples/01 - hello_world"
python hello.py
查看完整示例:examples/01 - hello_world
使用 Webview 浏览器
要使用原生 WebView 窗口(而非 Chrome/Edge 浏览器),只需设置 browser_type="webview":
from pyquickwebgui import QuikeUI
qui = QuikeUI(
server_type="webpy",
browser_type="webview", # 使用原生 WebView
web_path="./web",
index_html="index.html",
width=800,
height=600
)
@qui.expose
def my_function():
return "Hello from Python!"
qui.run()
优势:
- ✅ 原生应用窗口体验
- ✅ 无浏览器地址栏和工具栏
- ✅ 更轻量的资源占用
- ✅ 更快的启动速度(智能服务器就绪检测 + 窗口延迟显示)
- ✅ 跨平台支持(Windows/macOS/Linux)
平台要求:
| 平台 | WebView 引擎 | 要求 |
|---|---|---|
| Windows | Edge WebView2 | Windows 10/11 自带,或手动安装 |
| macOS | WKWebView | 系统内置,无需额外安装 |
| Linux | WebKitGTK | 需安装 libwebkit2gtk-4.0-dev |
高级配置:
# 全屏模式
qui = QuikeUI(fullscreen=True)
# 调试模式(按 F12 打开开发者工具)
qui = QuikeUI(debug=True)
Webview vs CommandBrowser 对比:
| 特性 | Webview | CommandBrowser |
|---|---|---|
| 窗口外观 | 原生应用窗口 | 浏览器窗口 |
| 工具栏/地址栏 | ❌ 无 | ✅ 有(可隐藏) |
| 资源占用 | 较轻 | 较重 |
| 启动速度 | 较快 | 较慢 |
| 调试工具 | F12(开发模式) | F12(始终可用) |
| 适用场景 | 桌面应用 | Web 应用/Kiosk |
查看完整示例:examples/12 - webview_webpy
纯服务器模式(不启动浏览器)
QuikeUI 支持以纯服务器模式运行,仅启动 HTTP 服务而不启动浏览器窗口。适用于:
- 作为后端 API 服务
- 配合外部前端开发服务器
- 部署为 Web 服务
- Docker 容器化部署
基本用法
通过 only_webserver=True 参数或 --server 命令行参数启用:
import sys
from pyquickwebgui import QuikeUI
# 解析命令行参数
SERVER_MODE = '--server' in sys.argv
qui = QuikeUI(
title="My App",
only_webserver=SERVER_MODE, # True 时不启动浏览器
)
@qui.expose
def say_hello(name):
return f'Hello, {name}!'
if __name__ == '__main__':
if SERVER_MODE:
print(f'[INFO] 服务器模式启动,访问地址: http://localhost:{qui.port}/')
qui.start()
运行方式
# 正常模式(启动浏览器窗口)
python hello.py
# 服务器模式(仅启动 HTTP 服务)
python hello.py --server
配合前端开发服务器
纯服务器模式可以配合 Vite/Webpack 等前端开发服务器使用:
# backend.py
import sys
from pyquickwebgui import QuikeUI
SERVER_MODE = '--server' in sys.argv
qui = QuikeUI(
title="My App",
only_webserver=SERVER_MODE,
debug=True,
)
@qui.route('/api/data')
def get_data(params):
return {'message': 'Hello from backend'}
qui.start()
前端通过 window.__API_BASE__ 或固定端口访问后端 API。
API 参考
| 参数 | 类型 | 说明 |
|---|---|---|
only_webserver |
bool | True 时仅启动 HTTP 服务器,不启动浏览器 |
查看完整示例:examples/17 - hello_world_server
Jinja2 模板支持
QuikeUI 支持两种模板引擎,通过 server_kwargs 配置:
使用 Jinja2 模板
qui = QuikeUI(
web_path=os.path.join(os.path.dirname(__file__), "web"),
index_html="hello.html",
width=800, height=600,
server_kwargs={
'templates_folder': 'templates', # 模板文件夹路径(相对于 web_path)
'template_engine': 'jinja2' # 使用 Jinja2 模板引擎
}
)
使用 web.py 模板(默认)
qui = QuikeUI(
web_path=os.path.join(os.path.dirname(__file__), "web"),
index_html="hello.html",
width=800, height=600,
server_kwargs={
'templates_folder': 'templates' # 模板文件夹路径
# 不指定 template_engine,默认使用 web.py
}
)
Jinja2 模板语法示例
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<p>{{ message }}</p>
{% for item in items %}
<span>{{ item }}</span>
{% endfor %}
{% if condition %}
<p>Condition is true</p>
{% else %}
<p>Condition is false</p>
{% endif %}
</body>
</html>
web.py 模板语法示例
$def with (title, message)
<!DOCTYPE html>
<html>
<head>
<title>$title</title>
</head>
<body>
<p>$message</p>
$for item in items:
<span>$item</span>
$if condition:
<p>Condition is true</p>
$else:
<p>Condition is false</p>
</body>
</html>
安装 Jinja2:
pip install jinja2
选择建议:
- ✅ 推荐使用 Jinja2:如果你熟悉 Jinja2 语法、需要模板继承和块功能
- ✅ 推荐使用 web.py 模板:如果你不想安装额外依赖、喜欢简洁的 Python 风格语法
查看完整示例:examples/06 - jinja_templates
## API 参考
### QuikeUI 类
主要方法:
- `run(**kwargs)` - 启动应用(支持通过 kwargs 覆盖实例属性,如 `tray`、`title` 等)
- `start(**kwargs)` - 启动应用(`run()` 的别名,与 Eel 框架兼容,同样支持 kwargs 覆盖)
- `init(**kwargs)` - 延迟初始化参数(允许先创建空实例,再传入真实参数)
- `route(path, methods)` - 注册自定义路由(装饰器)
- `expose(func)` - 暴露函数给 JavaScript(详见 [@qui.expose 装饰器](#quiexpose-装饰器))
- `call_js_function(name, *args)` - 调用 JavaScript 函数
- `get_exposed_functions()` - 获取暴露函数列表
- `open_local_file()` - 打开文件选择对话框
- **`hide()`** - 隐藏主窗口(配合系统托盘使用) 🆕
- **`show()`** - 显示主窗口(配合系统托盘使用) 🆕
- `get_exe_dir()` - 获取 PyInstaller 打包后 exe 文件所在目录
- `get_resource_dir()` - 获取 PyInstaller 打包后的资源文件目录
- `close_application()` - 静态方法,关闭应用程序(关闭浏览器、清理资源、终止后台进程)
**注意:**
1. `start()` 方法是 `run()` 的别名,提供与 Eel 框架兼容的 API。两者功能完全相同。
2. `init()` 方法允许延迟初始化,适合需要动态配置的场景。
3. `route()` 装饰器用于注册自定义路由,支持所有 Web 框架(web.py, Flask, FastAPI, Django)。
```python
# 方式 1: 直接传入参数(推荐)
qui = QuikeUI(
web_path="./web",
index_html="index.html",
width=800,
height=600
)
qui.run()
# 方式 2: 先创建空实例,再初始化
qui = QuikeUI()
qui.init(
web_path="./web",
index_html="index.html",
width=800,
height=600
)
qui.run()
# 方式 3: 使用 start() 代替 run()
qui.start() # 与 qui.run() 等价
# 方式 4: 注册自定义路由
@qui.route('/custom')
def custom_route():
return 'Hello, World!'
@qui.route('/api/data', methods=['POST'])
def api_data():
return {'status': 'ok'}
# 方式 5: 自动获取请求参数(推荐)
# 路由函数声明 params 参数,框架自动解析并传入
@qui.route('/login', methods=['POST'])
def login(params):
# params 是 dict,包含:
# - GET 查询参数 (?key=value)
# - POST 表单数据
# - POST JSON body (Content-Type: application/json)
username = params.get('username', '')
password = params.get('password', '')
return {'message': 'ok'}
# 无参函数仍然兼容,无需修改旧代码
@qui.route('/health')
def health():
return {'status': 'ok'}
# 方式 6: 获取 PyInstaller 打包后的目录路径
# 方法 1: 获取 exe 所在目录(用户可见的目录)
exe_dir = qui.get_exe_dir()
print(f"Exe 目录: {exe_dir}")
# 打包后: D:\projects\dist
# 开发时: D:\projects\src
# 方法 2: 获取资源文件目录(临时解压目录)
resource_dir = qui.get_resource_dir()
print(f"资源目录: {resource_dir}")
# 打包后: C:\Users\lixin\AppData\Local\Temp\_MEI165762
# 开发时: D:\projects\src
# 示例:在 exe 同目录创建配置文件
import os
config_file = os.path.join(exe_dir, 'config.json')
# 示例:访问打包的资源文件
web_dir = os.path.join(resource_dir, 'web')
详细用法请参考上方的 @qui.expose 装饰器 章节。
JavaScript API 使用指南
QuikeUI 提供了统一的 JavaScript API 来调用 Python 函数。所有调用都通过 QuikeUI.callPython() 方法完成。
基本用法
JavaScript → Python
// 基本调用(无返回值)
QuikeUI.callPython("python_function_name", arg1, arg2);
// 带回调的调用(获取返回值)
QuikeUI.callPython("python_function_name", arg1, arg2)
.then(result => {
console.log("Python 返回结果:", result);
})
.catch(error => {
console.error("调用失败:", error);
});
// 使用 async/await(推荐)
async function callPython() {
try {
const result = await QuikeUI.callPython("python_function_name", arg1, arg2);
console.log("Python 返回结果:", result);
} catch (error) {
console.error("调用失败:", error);
}
}
Python → JavaScript
在 Python 端使用 qui.call_js_function() 或 qui.call_js_function_sync():
# 异步调用(不等待返回)
qui.call_js_function("js_function_name", arg1, arg2)
# 同步调用(等待返回,需要 await)
result = qui.call_js_function_sync("js_function_name", arg1, arg2)
在 JavaScript 端暴露函数:
// 暴露 JavaScript 函数给 Python
QuikeUI.expose(js_function_name);
function js_function_name(arg1, arg2) {
// 处理逻辑
return "返回值";
}
💡 特性
1. 自动队列缓存
当 WebSocket 未连接时,调用会自动加入队列,连接后依次执行:
// 页面加载时立即调用 - 会被缓存
QuikeUI.callPython("init_function", "data").then(result => {
console.log("初始化完成:", result);
});
// WebSocket 连接后,队列中的调用会自动发送
2. Promise 支持
所有调用都返回 Promise,支持 .then() 和 async/await:
// 方式 1: then/catch
QuikeUI.callPython("get_data")
.then(data => console.log(data))
.catch(err => console.error(err));
// 方式 2: async/await(推荐)
async function getData() {
try {
const data = await QuikeUI.callPython("get_data");
console.log(data);
} catch (err) {
console.error(err);
}
}
3. 错误处理
调用失败时会抛出异常,可以通过 .catch() 或 try/catch 捕获:
QuikeUI.callPython("risky_function")
.then(result => {
// 成功处理
})
.catch(error => {
// 错误处理
console.error("错误信息:", error.message);
console.error("堆栈跟踪:", error.stack);
});
📝 完整示例
HTML 文件
<!DOCTYPE html>
<html>
<head>
<title>QuikeUI 示例</title>
<!-- 必须在 QuikeUI.js 加载前设置 -->
<script type="text/javascript">
window.QuikeUI = { debug: true };
</script>
<script type="text/javascript" src="QuikeUI.js"></script>
<script type="text/javascript">
// 暴露 JavaScript 函数给 Python
QuikeUI.expose(js_hello);
function js_hello(name) {
console.log("Hello from JavaScript:", name);
return "JS Response: " + name;
}
// 页面加载完成后调用 Python
window.addEventListener('load', async function() {
// 等待 WebSocket 连接
await new Promise(resolve => {
if (QuikeUI._websocket_connected) {
resolve();
} else {
const checkInterval = setInterval(() => {
if (QuikeUI._websocket_connected) {
clearInterval(checkInterval);
resolve();
}
}, 100);
}
});
console.log("✅ WebSocket 已连接");
// 调用 Python 函数
try {
const result = await QuikeUI.callPython("py_hello", "World");
console.log("Python 返回:", result);
} catch (error) {
console.error("调用失败:", error);
}
});
</script>
</head>
<body>
<h1>QuikeUI 示例</h1>
</body>
</html>
Python 文件
import os
from pyquickwebgui import QuikeUI
qui = QuikeUI(
web_path=os.path.join(os.path.dirname(__file__), "web"),
index_html="index.html",
width=800,
height=600,
debug=True,
)
@qui.expose
def py_hello(name):
"""暴露给 JavaScript 的 Python 函数"""
print(f"Hello from Python: {name}")
# 调用 JavaScript 函数
result = qui.call_js_function_sync("js_hello", name)
print(f"JavaScript 返回: {result}")
return f"Python Response: {name}"
if __name__ == "__main__":
qui.run()
⚠️ 注意事项
- WebSocket 连接:调用前确保 WebSocket 已连接,或使用队列缓存机制
- 函数暴露:Python 函数需要使用
@qui.expose装饰器暴露 - 返回值:只有同步调用才能获取返回值,异步调用需要通过回调处理
- 错误处理:始终添加错误处理逻辑,避免未捕获的异常
🔧 调试模式
启用调试模式可以查看详细的日志输出:
// 在加载 QuikeUI.js 之前设置
window.QuikeUI = { debug: true };
调试模式下会输出:
- WebSocket 连接状态
- 函数调用日志
- 队列缓存信息
- 错误详细信息
API 参考
QuikeUI.callPython(func_name, ...args)
调用 Python 函数。
参数:
func_name(string): Python 函数名...args(any): 传递给 Python 函数的参数
返回:
Promise: 解析为 Python 函数的返回值
示例:
const result = await QuikeUI.callPython("my_function", arg1, arg2);
QuikeUI.expose(func, name)
暴露 JavaScript 函数给 Python。
参数:
func(function): JavaScript 函数name(string, optional): 函数名(默认为函数本身的名称)
示例:
QuikeUI.expose(my_js_function);
// 或
QuikeUI.expose(my_js_function, "custom_name");
QuikeUI.enable_reconnect(interval, max_attempts)
启用自动重连功能。
参数:
interval(number): 重连间隔(毫秒),默认 1000max_attempts(number): 最大重连次数,默认 10(0 表示无限)
示例:
QuikeUI.enable_reconnect(1000, 10);
QuikeUI.disable_reconnect()
禁用自动重连功能。
示例:
QuikeUI.disable_reconnect();
QuikeUI.manual_reconnect()
手动触发重连。
示例:
QuikeUI.manual_reconnect();
🔄 迁移指南
如果你之前使用自动生成的函数(如 QuikeUI.py_function()),现在需要改为:
// ❌ 旧方式(已废弃)
QuikeUI.py_function(arg1, arg2).then(result => {...});
// ✅ 新方式(推荐)
QuikeUI.callPython("py_function", arg1, arg2).then(result => {...});
优势:
- 统一的调用方式
- 更清晰的代码意图
- 更好的类型提示(配合 TypeScript)
- 更容易维护和重构
📦 打包部署
开发环境
# 克隆项目
git clone <repository-url>
cd pyQuickWebGui-master
# 创建虚拟环境
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # macOS/Linux
# 安装依赖
pip install -e .
构建包
# 使用 uv 构建
uv build
# 或使用传统方式
python -m build
发布到 PyPI
# 安装 twine
pip install twine
# 上传
python -m twine upload dist/*
应用打包
使用 PyInstaller 或其他工具打包为可执行文件:
pip install pyinstaller
pyinstaller --onefile your_app.py
Sphinx 文档生成
如果需要生成 API 文档:
cd docs
# 自动生成 .rst 文件
sphinx-apidoc -o source/api ../src/pyquickwebgui --force --no-toc
# 构建 HTML 文档
make clean
make html
🔄 更新日志
V 0.2.11 (2026-07-29)
Bug 修复
- 🐛 修复 DbPlugin 未配置 db 时仍创建空文件:当用户未传入
db参数时,DbPlugin 不再自动创建默认数据库文件,避免不必要的副作用 - 🐛 修复根插件加载器误匹配元类导致 TypeError:
load_rootplugins改用isinstance(item, type) and issubclass(item, BasePlugin)类型检查替代名称字符串匹配,避免_PagePluginMeta等元类被误识别为插件类
V 0.2.10 (2026-07-29)
DbPlugin 数据库加密支持
- 🆕 新增数据库加密功能:DbPlugin 支持通过 pysqleet(SQLCipher)对 SQLite 数据库文件进行 AES-256 加密,通过
db元组第三个元素传入密码即可启用 - 🆕 pysqleet 可选依赖:在
pyproject.toml中新增[project.optional-dependencies]配置,通过pip install pyquickwebgui[encrypt]或pip install pysqleet安装 - 🔧 DbPlugin 参数名统一:
register()和run()方法的参数名从app改为qui,与 QuikeUI 实例命名保持一致
重要提示:
-
加密数据库用法:
# 非加密模式(默认) qui = QuikeUI(db=("myapp.db", "init.sql")) # 加密模式(需要 pip install pysqleet) qui = QuikeUI(db=("encrypted.db", "init.sql", "my_secret_password"))
-
pysqleet 安装说明:
- pysqleet 是可选依赖,未安装时加密参数会被忽略,数据库以普通模式运行
- 安装需要 C++ 构建工具(Windows 需安装 Visual Studio Build Tools)
- 如果不需要加密功能,无需安装
查看完整示例:examples/15 - db_web
V 0.2.9 (2026-07-23)
PagePlugin Lona 风格事件驱动交互
- 🆕 新增 Page 类 Lona 风格事件驱动:
button(on_click=..., on_result=...)支持 Python 回调控制 UI 更新 - 🆕 新增 Page UI 操作方法:
set_value()、set_text()、set_html()、layer_msg()、layer_alert()通过 WebSocket 实时推送 JS 更新前端 - 🆕 新增
exec_jsWebSocket 消息类型:Python 端可通过_send_js_to_client()推送任意 JS 代码到前端执行 - 🐛 修复插件模块重复加载问题:
app.py插件加载器优先通过包路径导入,避免同一文件被加载为不同模块导致类变量不一致 - ✨ layui layer 模块默认加载:
render_page默认模块列表新增layer,支持弹窗功能
重要提示:
-
Lona 风格事件驱动:Python 端完全控制 UI 更新方式
from pyquickwebgui.rootplugins.PagePlugin import PagePlugin as page home_page = page.create_page() # 业务回调 def get_time(data): import datetime return f"服务器时间: {datetime.datetime.now()}" # UI 更新回调 - 写入 input def show_in_input(result): if result and result.get('status') == 'success': home_page.set_value('result_input', result['result']) # UI 更新回调 - 弹窗 def show_as_popup(result): if result and result.get('status') == 'success': home_page.layer_msg(result['result']) # 按钮绑定 btn1 = home_page.button("获取时间", on_click=get_time, on_result=show_in_input) btn2 = home_page.button("弹窗显示", on_click=get_time, on_result=show_as_popup)
-
Page UI 操作方法:通过 WebSocket 实时推送 JS 更新前端
page.set_value('input_id', '新值') # 设置 input value page.set_text('label_id', '新文本') # 设置 innerText page.set_html('div_id', '<b>粗体</b>') # 设置 innerHTML page.layer_msg('操作成功', icon=1) # layer.msg 弹窗 page.layer_alert('提示信息') # layer.alert 弹窗
查看完整示例:examples/18 - hello_world_pypage
V 0.2.8 (2026-07-08)
Bug 修复与功能增强
- 🐛 修复
inject_js代码字符串被误判为文件路径:当inject_js传入 JS 代码字符串时,代码会优先判断长度(≥260 字符直接作为 JS 代码),避免长字符串被误判为文件路径导致注入失败 - 🐛 修复外部 URL 模式下
inject_js不执行:当index_html为外部 URL(如 Vite 开发服务器)时,window.events.loaded事件不触发导致 JS 注入失败。现在对外部 URL 使用线程延迟注入替代事件方式 - 🐛 修复跨域请求(CORS)支持:自定义路由(如
/api/login)的响应现在自动添加 CORS 头(Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers),并正确处理 OPTIONS 预检请求 - 🐛 修复 JSON 响应中文乱码:路由函数返回 dict 时,自动设置
Content-Type: application/json; charset=utf-8并使用ensure_ascii=False编码,确保中文等非 ASCII 字符正确显示 - 🐛 修复 DbPlugin 数据库初始化逻辑:先检查数据库文件是否存在再连接,避免空数据库文件导致
init.sql不执行
重要提示:
-
外部开发服务器集成增强:
inject_js现在能正确区分 JS 代码字符串和文件路径# JS 代码字符串(长度 ≥ 260 或包含 JS 特征字符) inject_js_code = f'window.__API_BASE__="http://localhost:{qui.port}";' # 文件路径(短字符串且看起来像文件名) qui = QuikeUI(inject_js="menu.js")
-
跨域请求支持:前端开发服务器(如 Vite)访问后端 API 不再受 CORS 限制
# 后端自动处理 OPTIONS 预检请求 @qui.route("/api/login", methods=["POST"]) def login(params): return {"message": "ok"} # 自动添加 CORS 头
-
JSON 响应编码修复:中文等非 ASCII 字符不再乱码
# 返回 dict 时自动设置 UTF-8 编码 return {"message": "error", "error": "用户名或密码错误"} # 响应头: Content-Type: application/json; charset=utf-8
V 0.2.7 (2026-07-07)
Bug 修复与功能增强
- 🐛 修复
run()/start()参数覆盖问题:通过start(tray={...})或run(**kwargs)传入的参数现在会正确更新到实例属性,之前传入的参数会被忽略 - 🐛 修复外部 URL 模式下
web_path图标查找失败:当index_html为外部 URL(如http://localhost:5173/)时,web_path现在会自动转换为绝对路径,确保图标等资源文件能正确找到 - 🐛 修复
DefaultServerWebpy.py中debug_file未定义错误:移除调试遗留代码,消除NameError: name 'debug_file' is not defined异常 - ✨ 系统托盘菜单支持字典格式:
tray配置中的menu现在支持传入字典列表[{"text": "菜单项", "action": callback}],自动转换为pystray.MenuItem - ✨ 自定义路由自动解析请求参数:路由函数声明
params参数后,框架自动解析 GET 查询参数、POST 表单数据、POST JSON body 并传入,无需手动调用web.input()或web.data(),同时向后兼容无参路由函数 - 🆕 新增 Vue 3 + Naive UI 完整模板:
examples/15 - Vue_App_Template展示前端开发服务器 + pyquickwebgui 后端集成方案 - 🆕 新增 SQLite 数据库插件:
DbPlugin提供完整的 CRUD 操作、SQL 文件初始化、线程安全支持,通过QuikeUI(db="db.db")一键启用
重要提示:
-
start()/run()参数覆盖:现在可以在启动时覆盖实例属性qui = QuikeUI(title="My App") # 启动时覆盖 tray 配置 qui.start(tray={ "icon": "favicon.ico", "menu": [ {"text": "自定义菜单", "action": my_callback} ] })
-
外部开发服务器集成:支持将
index_html设置为外部 URL,适合前端开发场景qui = QuikeUI( index_html="http://localhost:5173/", # Vite 开发服务器地址 icon="favicon.ico", # 图标从 web_path 查找 ) qui.run()
-
字典格式托盘菜单:无需手动创建
pystray.MenuItem,直接传字典即可# ✅ 字典格式(新方式,更简洁) qui.start(tray={ "icon": "favicon.ico", "menu": [ {"text": "菜单项 A", "action": callback_a}, {"text": "菜单项 B", "action": callback_b}, ] }) # ✅ pystray.MenuItem 格式(仍然支持) import pystray menu = ( pystray.MenuItem("菜单项 A", callback_a), pystray.MenuItem("菜单项 B", callback_b), ) qui.start(tray={"icon": "favicon.ico", "menu": menu})
-
SQLite 数据库插件:通过
db参数一键启用数据库功能from pyquickwebgui import QuikeUI # 方式 1: 仅指定数据库文件路径 qui = QuikeUI(db="myapp.db") # 方式 2: 指定数据库 + 初始化 SQL 文件(数据库不存在时自动执行) qui = QuikeUI(db=("myapp.db", "init.sql")) # DbPlugin 自动注册到 qui.db,提供完整 CRUD 操作 qui.db.init_table("users", [ "id INTEGER PRIMARY KEY AUTOINCREMENT", "name TEXT NOT NULL", "age INTEGER" ]) # 插入数据 qui.db.insert("users", {"name": "张三", "age": 25}) # 查询数据 users = qui.db.get_all("users") # 前端调用 @qui.expose def get_users(): return {"data": qui.db.get_all("users")}
DbPlugin 特性:
- ✅ 线程安全(
check_same_thread=False+threading.Lock) - ✅ 支持 SQL 文件初始化(
init_by_sql("schema.sql")) - ✅ 自动初始化(传入元组时,数据库不存在则执行 init.sql)
- ✅ 完整的 CRUD 操作(insert/update/delete/query)
- ✅ 自动路径转换(相对路径转绝对路径)
- ✅ 可选 SQLCipher 加密(通过 pysqleet,需额外安装)
加密数据库(可选):
DbPlugin 支持通过 pysqleet(SQLCipher)对数据库文件进行 AES-256 加密。pysqleet 是可选依赖,未安装时数据库以普通模式运行。
# 方式 1: 非加密模式(默认,无需额外依赖) qui = QuikeUI(db=("myapp.db", "init.sql")) # 方式 2: 加密模式(需要 pip install pysqleet) qui = QuikeUI(db=("encrypted.db", "init.sql", "my_secret_password")) # 方式 3: 仅加密,不执行初始化脚本 qui = QuikeUI(db=("encrypted.db", None, "my_secret_password"))
注意:pysqleet 安装需要 C++ 构建工具(Windows 需安装 Visual Studio Build Tools)。如果不需要加密功能,无需安装。
- ✅ 线程安全(
V 0.2.6 (2026-06-28)
系统托盘优化与窗口控制增强
- 🔄 参数重命名:
base_path→web_path,语义更清晰 - 📦 内置系统托盘:托盘功能内置到
app.py,无需外部插件即可使用 - 🏷️ 配置键重命名:
stray→tray,统一命名规范 - 🖼️ 新增
icon参数:支持自定义窗口图标(.ico格式),支持绝对路径和相对于web_path的相对路径 - 🪟 新增
hide()方法:隐藏主窗口,配合系统托盘使用 - 🪟 新增
show()方法:显示主窗口,配合系统托盘使用 - 📋 默认托盘菜单:内置「隐藏 / 显示 / 退出」三项默认菜单,开箱即用
- 🔧 支持自定义托盘回调:通过
menu_click_show、menu_click_hide、menu_click_exit配置默认菜单行为 - 🪟 修复 Windows 窗口图标设置:改用 .NET WinForms API 替代 Win32 API,解决图标不显示问题
- 📝 新增 tray_web 示例:
examples/14 - tray_web展示系统托盘 + 窗口隐藏/关闭功能
重要提示:
-
参数迁移:
base_path已重命名为web_path,旧代码中的base_path参数需要更新# ❌ 旧写法(已废弃) qui = QuikeUI(base_path="web") # ✅ 新写法 qui = QuikeUI(web_path="web")
-
托盘配置迁移:配置键从
stray改为tray# ❌ 旧写法 qui = QuikeUI(stray={"icon": "favicon.ico"}) # ✅ 新写法 qui = QuikeUI(tray={"icon": "favicon.ico"})
-
窗口图标配置:新增
icon参数,支持自定义窗口图标qui = QuikeUI( icon="favicon.ico", # 窗口图标(相对于 web_path) tray={"icon": "favicon.ico"} # 托盘图标 )
-
窗口显隐控制:通过
hide()和show()方法控制窗口可见性qui.hide() # 隐藏窗口 qui.show() # 显示窗口 # 关闭应用(静态方法) QuikeUI.close_application()
V 0.2.5 (2026-06-23)
启动性能优化与用户体验提升
- ⚡ 消除启动白屏问题:实现窗口延迟显示机制,确保资源完全加载后再显示窗口
- 🚀 智能服务器就绪检测:在创建窗口前等待 HTTP 服务器完全启动,避免白屏等待
- 🎨 优化的窗口渲染流程:初始隐藏窗口 → 等待资源加载 → 平滑显示完整内容
- 🔧 改进 WebviewBrowser 初始化:添加
hidden=True参数和show_window_after_load()回调 - 📊 启动速度提升约 90%:从 16 秒白屏缩短至不到 1 秒直接显示完整内容
- 🛠️ 修复 QuikeUI.js 重复加载问题:禁用默认 inject_js 注入,由 HTML 自行通过
<script>标签加载 - ✨ 优化 WebSocket 连接稳定性:移除 alert() 弹窗阻塞,改用 console.log() 避免连接断开
- 🐛 修复函数参数容错问题:将
def func(x)改为def func(*args, **kwargs)支持任意参数
重要提示:
-
启动优化原理:
# 优化后的启动流程: # T+0ms - 启动 HTTP 服务器线程 # T+100ms - 服务器就绪 ✅ # T+100ms - 创建窗口(隐藏状态) # T+400ms - 窗口开始加载页面(用户看不到) # T+600ms - 页面加载完成 # T+800ms - 显示窗口 ✅(此时所有内容已就绪)
-
窗口延迟显示配置:
# WebviewBrowser.py 中的关键代码 window_kwargs = { 'hidden': True, # 初始隐藏窗口 ... } self.window = webview.create_window(**window_kwargs) # 页面加载完成后显示窗口 def show_window_after_load(): time.sleep(0.2) # 再等待 200ms self.window.show() self.log.info("✅ 窗口已显示") self.window.events.loaded += show_window_after_load
-
服务器就绪检测:
# app.py 中的等待逻辑 for i in range(20): # 最多等待 2 秒 try: urllib.request.urlopen(f'http://127.0.0.1:{self.port}/', timeout=0.5) self.log.info(f"✅ 服务器已就绪 (尝试 {i+1} 次)") break except Exception: time.sleep(0.1)
-
QuikeUI.js 加载方式:
<!-- ✅ 推荐:HTML 中直接加载 --> <script src="/QuikeUI.js"></script> <!-- ❌ 不再使用:pywebview 自动注入 --> <!-- 已在 app.py 中禁用默认的 inject_js 设置 -->
-
避免 alert() 导致的 WebSocket 断开:
// ❌ 避免使用 alert(会阻塞页面导致连接断开) alert('Hello World'); // ✅ 使用 console.log 或 DOM 操作 console.log('Hello World'); document.getElementById('output').textContent = result;
V 0.2.4 (2026-06-22)
PyInstaller 打包兼容性修复与 API 扩展
- 🔧 修复 WebSocket 线程退出问题:修改
WebSocketHandler.start()方法,让其在 while 循环中阻塞接受连接,避免 daemon 线程过早退出 - 🐛 修复 GBK 编码异常:移除日志中的所有 emoji 字符(✅、❌、🔄、🚀等),解决 Windows 系统下
'gbk' codec can't encode character错误 - ✅ 验证 PyInstaller onefile 模式:确保打包后的程序能正常启动 HTTP 服务器和 WebSocket 服务器
- 🎯 改进线程管理:WebSocket 线程现在正确保持存活状态,支持多客户端并发连接
- ✨ 新增
get_exe_dir()方法:获取 PyInstaller 打包后 exe 文件所在目录(用户可见的目录) - ✨ 新增
get_resource_dir()方法:获取 PyInstaller 打包后的资源文件目录(临时解压目录sys._MEIPASS)
重要提示:
-
Windows 编码问题:在 Windows 系统中,日志输出应避免使用 emoji 字符,因为控制台默认使用 GBK 编码
# ❌ 避免使用 emoji(会导致 UnicodeEncodeError) self.log.info(f"✅ WebSocket 服务器成功启动") # ✅ 使用纯文本 self.log.info(f"WebSocket 服务器成功启动")
技术说明:
- QuikeUI 的 LogPlugin 已经将文件日志设置为 UTF-8 编码(
encoding='utf-8') - 但控制台日志仍使用系统默认编码(Windows 为 GBK)
- 因此 emoji 字符写入文件没问题,但输出到控制台时会抛出异常
- 解决方案 1(推荐):移除所有 emoji 字符,使用纯文本
- 解决方案 2(可选):在程序启动时设置控制台编码为 UTF-8
import sys import io # Windows 下设置控制台编码为 UTF-8 if sys.platform == 'win32': sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
- QuikeUI 的 LogPlugin 已经将文件日志设置为 UTF-8 编码(
-
Daemon 线程注意事项:当主线程被阻塞调用(如
web.httpserver.runsimple())占用时,daemon 线程的子线程也会被终止# ❌ 避免在 start() 中启动子线程后立即返回 def start(self): thread = threading.Thread(target=self._accept, daemon=True) thread.start() return # thread 会立即退出 # ✅ 直接在 start() 中阻塞 def start(self): while self.running: client_socket, address = self.server_socket.accept() # 处理连接
-
打包测试:使用
bin/build_examples.bat脚本进行打包测试# Windows bin\build_examples.bat # 测试打包后的程序 cd "examples/01 - hello_world" .\dist\hello.exe
-
路径获取 API:QuikeUI 提供两个方法获取 PyInstaller 打包后的目录路径
from pyquickwebgui import QuikeUI qui = QuikeUI() # 方法 1: 获取 exe 所在目录(用户可见的目录) exe_dir = qui.get_exe_dir() # 打包后: D:\projects\dist # 开发时: D:\projects\src # 方法 2: 获取资源文件目录(临时解压目录) resource_dir = qui.get_resource_dir() # 打包后: C:\Users\lixin\AppData\Local\Temp\_MEI165762 # 开发时: D:\projects\src # 示例:在 exe 同目录创建配置文件 import os config_file = os.path.join(exe_dir, 'config.json') # 示例:访问打包的资源文件 web_dir = os.path.join(resource_dir, 'web')
V 0.2.3 (2026-06-21)
API 扩展与框架增强
- ✨ 新增
start()方法:作为run()的别名,提供与 Eel 框架兼容的 API - 🚀 新增
init()方法:支持延迟初始化,允许先创建空实例再传入真实参数 - 🌐 新增
route()装饰器:注册自定义路由,支持所有 Web 框架(web.py, Flask, FastAPI, Django) - 📝 完善 API 文档:在 README 中添加三种新方法的详细说明和使用示例
- 🎯 新增 Vue 示例:
examples/08 - CreateVueApp展示 Vue 3 + Vite + QuikeUI 集成 - 🔄 优化 React 示例:
examples/07 - CreateReactApp从 Eel 迁移到 QuikeUI - 📊 新增对比文档:Vue vs React 示例对比,帮助用户选择合适的框架
重要提示:
-
Eel 兼容性:
qui.start()与qui.run()完全等价,方便从 Eel 迁移# 以下两种写法等价: qui.run() # 推荐使用 qui.start() # 与 Eel 兼容
-
延迟初始化:适合需要动态配置的场景
qui = QuikeUI() qui.init( web_path="./web", index_html="index.html", width=800, height=600 ) qui.run()
-
自定义路由:统一的 API 注册路由
@qui.route('/custom') def custom_route(): return 'Hello, World!' @qui.route('/api/data', methods=['POST']) def api_data(): return {'status': 'ok'}
-
Vue 示例特点:
- 使用 Vite 构建工具,启动速度比 Webpack 快 10-100 倍
- 开发服务器端口 8080(可配置)
- 输出目录为
dist/ - 支持热模块替换(HMR)
V 0.2.2 (2026-06-18)
核心优化与 Bug 修复
- ⚡ 优化前端初始化时机:将
window.load改为DOMContentLoaded,启动速度提升约 4 倍 - 🔧 修复调试模式配置问题:解决
QuikeUI.debug = true设置失效导致右键和 F12 被禁用的问题 - 🛠️ 重构
_debug()函数:移除嵌套的DOMContentLoaded监听器,避免事件错过触发 - 🌐 修复 Webview JS 调用:修复
call_js_function在 Webview 模式下无法调用的问题 - 📝 完善文档结构:合并分散的文档到主 README,包括 WebSocket 队列、重连、同步调用等
- 🎯 新增文件选择示例:
examples/04 - file_access支持文件和文件夹选择 - ✨ 改进代码质量:统一 API 命名,修复逻辑错误(如
!!!三感叹号陷阱)
重要提示:
-
调试模式配置:必须在
QuikeUI.js加载前设置<script> window.QuikeUI = { debug: true }; </script> <script src="/QuikeUI.js"></script>
-
WebSocket 初始化:无论 debug 模式如何都会执行,确保通信正常
-
Webview 窗口对象:已保存到
self.window,支持外部调用evaluate_js()
V 0.2.1 (2026-06-14)
重大更新
- ✨ 仿照 eel 优化示例结构
- 🚀 增加 WebSocket 与 JavaScript 双向通信
- 🌐 增加默认服务模式(基于 web.py)
- 🔧 重构插件系统,移除 ExposedAPIPlugin
- 📝 完善参数文档,添加 [Optional] 标记
- 🎯 新增
index_html参数支持自定义首页 - 🛡️ 新增
inject_js和disable_right_click参数 - 🐛 修复 FilePlugin 属性访问问题
- 📦 优化批处理脚本,改进错误处理
V 0.1.1 (2025-09-29)
- 🔧 优化结构,拆解出 Browser 类型实现
- 📝 修改参数名:
server→server_type,browser→browser_type
V 0.0.6 (2025-05-01)
- 🛠️ 改用 uv 编译系统
V 0.0.5 (2025-05-01)
- 📚 增加 Sphinx 文档生成
V 0.0.4 (2025-05-01)
- 🎯 增加 Tauri 支持
V 0.0.3 (2025-04-12)
- 📌 增加系统托盘图标功能
- 🎨 优化 Vue 支持
V 0.0.2 (2024-08-01)
- 🖥️ 增加 WebView 支持
- 🌐 增加 web.py 支持
🤝 贡献
欢迎提交 Issue 和 Pull Request!
📄 许可证
MIT License
🔗 相关链接
Happy Coding! 🎉
Metadata
Release files for pyquickwebgui 0.2.11
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pyquickwebgui-0.2.11.tar.gz | 147.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyquickwebgui-0.2.11-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 253.5 kB
Release files / pyquickwebgui-0.2.11.tar.gz
| Download URL | pyquickwebgui-0.2.11.tar.gz |
|---|---|
| Size | 147.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
09d899ae7dd4a1105deb77d21f6b0772d7a8ac0b30f906f258cbf479bd27c806
|
|
BLAKE2b-256 checksum How to use checksums |
094720b105799a31b13af0205a158eb3380f9a69308d0e433834e772a97b8a7a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.8.10
|
Release files / pyquickwebgui-0.2.11-py3-none-any.whl
| Download URL | pyquickwebgui-0.2.11-py3-none-any.whl |
|---|---|
| Size | 106.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0c0f4c905bd646ffb77482accc09b36867f9e249b1dba6cdf4ddbeda57673056
|
|
BLAKE2b-256 checksum How to use checksums |
112a916a8cea557f268e35b419a36b02d5d909b97900b47d7dec707a155ecac9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.8.10
|