# pyquickwebgui **Repository Path**: iiixxxiii/pyquickwebgui ## Basic Information - **Project Name**: pyquickwebgui - **Description**: python使用webui制作桌面应用 - **Primary Language**: Python - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2026-06-13 - **Last Updated**: 2026-07-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # py-quick-webgui 最新版本: v0.2.7 [![Python Version](https://img.shields.io/badge/python-%3E%3D3.8-blue.svg)](https://www.python.org/) [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) 使用 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 开发服务器),方便前后端分离开发 ## 📋 目录 - [安装](#-安装) - [快速开始](#-快速开始) - [核心功能](#-核心功能) - [示例项目](#-示例项目) - [API 参考](#-api-参考) - [打包部署](#-打包部署) - [更新日志](#-更新日志) ## 📦 安装 ### 基础安装 ```bash pip install pyquickwebgui ``` ### 完整依赖 根据不同需求安装额外依赖: ```bash # 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 # Tauri 支持 # 请参考 Tauri 官方文档 ``` ## 🚀 快速开始 ### 1. Hello World 示例 创建 `hello.py`: ```python 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`: ```html Hello World

Hello from QuikeUI!

``` 运行: ```bash python hello.py ``` ### 2. Flask 示例 ```python from flask import Flask from pyquickwebgui import QuikeUI app = Flask(__name__) @app.route('/') def home(): return '

Hello Flask!

' qui = QuikeUI( app=app, server_type="flask", width=800, height=600 ) qui.run() ``` ### 3. FastAPI 示例 ```python 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 的双向实时通信。 #### 架构优势 1. **实时双向通信**:Python 和 JavaScript 可以互相调用函数 2. **低延迟**:WebSocket 保持长连接,避免 HTTP 请求开销 3. **自动函数注册**:前端自动获取后端暴露的函数列表 4. **异步支持**:FastAPI 支持异步函数调用 #### 通信协议 **消息格式:** - JavaScript → Python(函数调用): ```json {"call": 1234567890, "name": "function_name", "args": ["arg1", "arg2"]} ``` - Python → JavaScript(返回结果): ```json {"return": 1234567890, "status": "ok", "value": "result_data"} ``` - Python → JavaScript(发送函数列表): ```json {"type": "py_functions", "functions": ["func1", "func2"]} ``` #### Python 调用 JavaScript QuikeUI 支持两种浏览器类型的 JS 调用: **1. WebView 模式** ```python # webview 直接执行 JS qui.call_js_function('jsFunction', 'arg1', 'arg2') ``` **2. CommandBrowser 模式(通过 WebSocket)** ```python # 方式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 可能尚未建立的问题。 ```javascript // ✅ 即使 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 ``` **工作原理:** 1. **连接前**:调用被加入 `_mock_queue` 队列 2. **连接时**:触发 `onopen` 事件,设置 `_websocket_connected = true` 3. **刷新队列**:调用 `_flush_mock_queue()` 依次发送所有缓存的调用 4. **返回结果**:每个调用都会收到对应的 Promise 结果 **优势:** - ✅ 无需手动等待 WebSocket 连接 - ✅ 无需使用 `setTimeout` 或轮询检查 - ✅ 调用顺序保证(FIFO) - ✅ 错误处理完善(发送失败会重新入队) **详细实现说明:** 队列机制的核心是 `_call_object` 创建调用对象,包含唯一的 `call_id`、函数名和参数。当 WebSocket 未连接时,这些调用对象被推入 `_mock_queue` 数组。连接成功后,`_flush_mock_queue()` 方法会遍历队列,将每个调用序列化为 JSON 并通过 WebSocket 发送。 ```javascript // 内部实现示例 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 函数并等待返回值: ```python # 同步调用 JS 函数(阻塞直到收到响应) result = qui.call_js_function_sync("js_random", timeout=5.0) print(f'从 JavaScript 获取到: {result}') ``` **重要注意事项:** 1. **WebSocket 连接时机** 同步调用必须在 **WebSocket 连接建立之后** 才能执行: ```python # ❌ 错误:在 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") ``` 2. **与异步调用的区别** | 特性 | 同步调用 | 异步调用 | |------|---------|---------| | 方法名 | `call_js_function_sync()` | `call_js_function()` | | 阻塞 | ✅ 阻塞当前线程 | ❌ 不阻塞 | | 返回值 | 直接返回结果 | 需要通过回调或 Promise | | 适用场景 | 需要立即获取结果的场景 | 不需要等待结果的场景 | 3. **异常处理** ```python 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}') ``` **技术实现:** 同步调用的实现原理: 1. Python 端生成唯一的 `call_id` 2. 通过 WebSocket 发送调用请求(带 `sync: true` 标记) 3. 将响应存储在 `_js_call_responses` 字典中 4. 轮询检查响应是否到达(每 50ms) 5. 收到响应后返回结果或抛出异常 服务器端(DefaultServerWebpy)会检测 `sync` 标记并将结果存储到 QuikeUI 实例的响应字典中,同时也会通过 WebSocket 发送标准响应(保持向后兼容)。 **自动重连机制:** QuikeUI 内置了强大的 WebSocket 自动重连功能,确保在网络波动或服务器重启时应用能够自动恢复连接。 ```javascript // 默认启用,每2秒重试,最多10次 // 可自定义配置 QuikeUI.enable_reconnect( interval = 3000, // 重连间隔(毫秒) max_attempts = 5 // 最大重连次数(0=无限) ); // 禁用自动重连 QuikeUI.disable_reconnect(); // 手动触发重连 QuikeUI.manual_reconnect(); ``` **重连特性:** - ✅ 连接断开后自动尝试重连 - ✅ 可配置重连间隔和最大次数 - ✅ 重连成功后自动刷新队列 - ✅ 达到最大次数后拒绝待处理 Promise - ✅ 支持手动控制和禁用 - ✅ 详细的日志输出和告警提示 **工作流程:** ``` 连接断开 (onclose) ↓ 检查是否启用自动重连 ├─ 否 → 结束 └─ 是 ↓ 检查是否达到最大次数 ├─ 是 → 拒绝所有待处理 Promise,显示告警 └─ 否 ↓ 增加重连计数 ↓ 延迟指定时间 ↓ 关闭旧连接(如果存在) ↓ 创建新 WebSocket 连接 ↓ 连接成功 → 重置计数,刷新队列 连接失败 → 再次触发 onclose,循环 ``` **使用场景:** 1. **网络不稳定环境** ```javascript QuikeUI.enable_reconnect(5000, 20); // 每5秒重试,最多20次 ``` 2. **服务器维护期间** ```javascript QuikeUI.enable_reconnect(3000, 0); // 无限重连 ``` 3. **用户登出时** ```javascript function logout() { QuikeUI.disable_reconnect(); // 禁用重连 QuikeUI.callPython('logout').then(() => { window.location.href = '/login'; }); } ``` **告警功能:** 当达到最大重连次数时,系统会提供多层告警: 1. **控制台告警**:详细的错误信息和排查建议 2. **浏览器通知**:如果用户授予权限,显示系统通知 3. **自定义事件**:监听 `qui_reconnect_failed` 事件进行自定义处理 4. **Promise 拒绝**:所有待处理的 Promise 会被拒绝,带有详细错误信息 ```javascript // 监听重连失败事件 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) - 避免频繁的手动重连 - 监控重连次数,及时发现网络问题 ```javascript // 查看重连状态 console.log('重连启用:', QuikeUI._reconnect_enabled); console.log('当前次数:', QuikeUI._reconnect_attempts); console.log('连接状态:', QuikeUI._websocket_connected); ``` ### 参数配置 QuikeUI 支持丰富的配置选项: ```python 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` 自动转换为绝对路径(用于图标等资源查找) ```python # 方式 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` 目录下查找文件: ```python # 方式 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. 绝对路径 直接使用完整的文件路径: ```python 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 代码: ```python qui = QuikeUI( inject_js="console.log('Hello from Python!');" ) # 多行代码 qui = QuikeUI( inject_js=""" console.log('初始化完成'); window.customConfig = { debug: true }; """ ) ``` **注意事项:** - ✅ 如果 HTML 中已通过 ` ``` #### 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 函数,返回 Promise - `QuikeUI.expose(func)` - 暴露 JavaScript 函数给 Python(当前版本暂不支持) **示例:** ```javascript // 无参数调用 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); } ``` #### 工作原理 1. **装饰器注册**:`@qui.expose` 将函数添加到 `_exposed_functions` 字典和 `exposed_functions_list` 列表 2. **WebSocket 连接**:前端通过 WebSocket 连接到后端服务器 3. **函数列表同步**:连接建立后,后端发送所有暴露函数的列表给前端 4. **动态导入**:前端根据函数列表动态创建对应的 JavaScript 函数 5. **调用执行**:JavaScript 调用时,通过 WebSocket 发送请求到后端 6. **结果返回**:后端执行函数并将结果以 JSON 格式返回 ### 支持的 Web 框架 #### Flask **依赖安装:** ```bash pip install flask-socketio eventlet ``` **特点:** - 使用 `flask-socketio` 实现 WebSocket - 自动降级到普通 Flask 服务器(如果不支持 WebSocket) - 不支持 `waitress`(因为它不支持 WebSocket) #### FastAPI **依赖安装:** ```bash pip install uvicorn ``` **特点:** - 原生支持 WebSocket - 支持异步函数(`async def`) - 性能更好 #### web.py **依赖安装:** ```bash # 无需额外依赖,使用内置 socket 实现 ``` **特点:** - 使用原生 socket + threading 实现 WebSocket - WebSocket 运行在独立端口(HTTP 端口 + 1) - 完全手动实现 WebSocket 协议 - 适合轻量级应用 **注意:** - web.py 的 WebSocket 端口为 HTTP 端口 + 1 - 例如:HTTP 在 8080,WebSocket 在 8081 - 前端需要连接到正确的 WebSocket 端口 ### 工作流程 1. **应用启动** - QuikeUI 初始化,收集所有 `@qui.expose` 装饰的函数 - 将函数列表传递给服务器配置 2. **服务器启动** - Flask/FastAPI/web.py 服务器启动 - 注册 WebSocket 端点 `/qui` - 构建暴露函数字典 3. **客户端连接** - 浏览器加载页面,建立 WebSocket 连接 - 后端发送 `py_functions` 消息,包含所有暴露的函数名 - 前端动态创建对应的 JavaScript 函数代理 4. **函数调用** - JavaScript 调用 `QuikeUI.callPython('function_name', args)` - 生成唯一的 `call_id` - 通过 WebSocket 发送调用请求 - Python 执行函数并返回结果 - JavaScript 接收结果并触发回调/Promise ### 注意事项 1. **依赖安装** - Flask: 需要 `flask-socketio` 和 `eventlet` 或 `gevent` - FastAPI: 需要 `uvicorn` 2. **函数序列化** - 参数和返回值必须是 JSON 可序列化的 - 复杂对象需要自定义序列化 3. **异步支持** - FastAPI 支持 `async def` 函数 - Flask 仅支持同步函数 4. **错误处理** - 所有异常都会被捕获并通过 WebSocket 返回 - 建议在 Python 函数中添加适当的错误处理 5. **连接管理** - WebSocket 断开时会自动重连(取决于浏览器实现) - 可以在后端监听 `disconnect` 事件 6. **WebSocket 时序** - Python 调用 JS 前必须等待连接建立 - 建议使用 `on_startup` 回调 + `time.sleep()` - 启用 `debug=True` 查看连接状态日志 ### 迁移指南 如果你之前使用 HTTP API 方式: **之前(HTTP):** ```javascript fetch('/api/exposed/say_hello', { method: 'POST', body: JSON.stringify({args: ['World']}) }) ``` **现在(WebSocket):** ```javascript QuikeUI.callPython('say_hello', 'World').then(result => { console.log(result); }); ``` 更简洁、更高效!🎉 #### 注意事项 1. **函数命名**:暴露的函数名会直接用作 API 端点,建议使用清晰的命名 2. **参数传递**:所有参数会通过 JSON 序列化,确保参数是可序列化的类型 3. **返回值**:返回值也会被序列化为 JSON,复杂对象可能需要特殊处理 4. **错误处理**:建议在 Python 函数中添加异常处理,或在 JavaScript 中使用 `.catch()` 5. **安全性**:暴露的函数可以被任何访问应用的 JavaScript 调用,注意权限控制 6. **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 服务不启动浏览器 | 运行示例: ```bash # Windows bin\run_examples.bat # 或手动运行 cd "examples/01 - hello_world" python hello.py ``` ### 使用 Webview 浏览器 要使用原生 WebView 窗口(而非 Chrome/Edge 浏览器),只需设置 `browser_type="webview"`: ```python 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` | **高级配置:** ```python # 全屏模式 qui = QuikeUI(fullscreen=True) # 调试模式(按 F12 打开开发者工具) qui = QuikeUI(debug=True) ``` **Webview vs CommandBrowser 对比:** | 特性 | Webview | CommandBrowser | |------|---------|----------------| | 窗口外观 | 原生应用窗口 | 浏览器窗口 | | 工具栏/地址栏 | ❌ 无 | ✅ 有(可隐藏) | | 资源占用 | 较轻 | 较重 | | 启动速度 | 较快 | 较慢 | | 调试工具 | F12(开发模式) | F12(始终可用) | | 适用场景 | 桌面应用 | Web 应用/Kiosk | 查看完整示例:[examples/12 - webview_webpy](examples/12%20-%20webview_webpy/) ### 纯服务器模式(不启动浏览器) QuikeUI 支持以纯服务器模式运行,仅启动 HTTP 服务而不启动浏览器窗口。适用于: - 作为后端 API 服务 - 配合外部前端开发服务器 - 部署为 Web 服务 - Docker 容器化部署 #### 基本用法 通过 `only_webserver=True` 参数或 `--server` 命令行参数启用: ```python 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() ``` #### 运行方式 ```bash # 正常模式(启动浏览器窗口) python hello.py # 服务器模式(仅启动 HTTP 服务) python hello.py --server ``` #### 配合前端开发服务器 纯服务器模式可以配合 Vite/Webpack 等前端开发服务器使用: ```python # 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](examples/17%20-%20hello_world_server/) ### Jinja2 模板支持 QuikeUI 支持两种模板引擎,通过 `server_kwargs` 配置: #### 使用 Jinja2 模板 ```python 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 模板(默认) ```python 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 模板语法示例 ```html {{ title }}

{{ message }}

{% for item in items %} {{ item }} {% endfor %} {% if condition %}

Condition is true

{% else %}

Condition is false

{% endif %} ``` #### web.py 模板语法示例 ```python $def with (title, message) $title

$message

$for item in items: $item $if condition:

Condition is true

$else:

Condition is false

``` **安装 Jinja2:** ```bash pip install jinja2 ``` **选择建议:** - ✅ **推荐使用 Jinja2**:如果你熟悉 Jinja2 语法、需要模板继承和块功能 - ✅ **推荐使用 web.py 模板**:如果你不想安装额外依赖、喜欢简洁的 Python 风格语法 查看完整示例:[examples/06 - jinja_templates](examples/06%20-%20jinja_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 装饰器](#quiexpose-装饰器) 章节。 --- ## JavaScript API 使用指南 QuikeUI 提供了统一的 JavaScript API 来调用 Python 函数。所有调用都通过 `QuikeUI.callPython()` 方法完成。 ### 基本用法 #### JavaScript → Python ```javascript // 基本调用(无返回值) 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()`: ```python # 异步调用(不等待返回) qui.call_js_function("js_function_name", arg1, arg2) # 同步调用(等待返回,需要 await) result = qui.call_js_function_sync("js_function_name", arg1, arg2) ``` 在 JavaScript 端暴露函数: ```javascript // 暴露 JavaScript 函数给 Python QuikeUI.expose(js_function_name); function js_function_name(arg1, arg2) { // 处理逻辑 return "返回值"; } ``` ### 💡 特性 #### 1. 自动队列缓存 当 WebSocket 未连接时,调用会自动加入队列,连接后依次执行: ```javascript // 页面加载时立即调用 - 会被缓存 QuikeUI.callPython("init_function", "data").then(result => { console.log("初始化完成:", result); }); // WebSocket 连接后,队列中的调用会自动发送 ``` #### 2. Promise 支持 所有调用都返回 Promise,支持 `.then()` 和 `async/await`: ```javascript // 方式 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` 捕获: ```javascript QuikeUI.callPython("risky_function") .then(result => { // 成功处理 }) .catch(error => { // 错误处理 console.error("错误信息:", error.message); console.error("堆栈跟踪:", error.stack); }); ``` ### 📝 完整示例 #### HTML 文件 ```html QuikeUI 示例

QuikeUI 示例

``` #### Python 文件 ```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() ``` ### ⚠️ 注意事项 1. **WebSocket 连接**:调用前确保 WebSocket 已连接,或使用队列缓存机制 2. **函数暴露**:Python 函数需要使用 `@qui.expose` 装饰器暴露 3. **返回值**:只有同步调用才能获取返回值,异步调用需要通过回调处理 4. **错误处理**:始终添加错误处理逻辑,避免未捕获的异常 ### 🔧 调试模式 启用调试模式可以查看详细的日志输出: ```javascript // 在加载 QuikeUI.js 之前设置 window.QuikeUI = { debug: true }; ``` 调试模式下会输出: - WebSocket 连接状态 - 函数调用日志 - 队列缓存信息 - 错误详细信息 ### API 参考 #### QuikeUI.callPython(func_name, ...args) 调用 Python 函数。 **参数:** - `func_name` (string): Python 函数名 - `...args` (any): 传递给 Python 函数的参数 **返回:** - `Promise`: 解析为 Python 函数的返回值 **示例:** ```javascript const result = await QuikeUI.callPython("my_function", arg1, arg2); ``` #### QuikeUI.expose(func, name) 暴露 JavaScript 函数给 Python。 **参数:** - `func` (function): JavaScript 函数 - `name` (string, optional): 函数名(默认为函数本身的名称) **示例:** ```javascript QuikeUI.expose(my_js_function); // 或 QuikeUI.expose(my_js_function, "custom_name"); ``` #### QuikeUI.enable_reconnect(interval, max_attempts) 启用自动重连功能。 **参数:** - `interval` (number): 重连间隔(毫秒),默认 1000 - `max_attempts` (number): 最大重连次数,默认 10(0 表示无限) **示例:** ```javascript QuikeUI.enable_reconnect(1000, 10); ``` #### QuikeUI.disable_reconnect() 禁用自动重连功能。 **示例:** ```javascript QuikeUI.disable_reconnect(); ``` #### QuikeUI.manual_reconnect() 手动触发重连。 **示例:** ```javascript QuikeUI.manual_reconnect(); ``` ### 🔄 迁移指南 如果你之前使用自动生成的函数(如 `QuikeUI.py_function()`),现在需要改为: ```javascript // ❌ 旧方式(已废弃) QuikeUI.py_function(arg1, arg2).then(result => {...}); // ✅ 新方式(推荐) QuikeUI.callPython("py_function", arg1, arg2).then(result => {...}); ``` **优势:** - 统一的调用方式 - 更清晰的代码意图 - 更好的类型提示(配合 TypeScript) - 更容易维护和重构 --- ## 📦 打包部署 ### 开发环境 ```bash # 克隆项目 git clone cd pyQuickWebGui-master # 创建虚拟环境 python -m venv .venv .venv\Scripts\activate # Windows source .venv/bin/activate # macOS/Linux # 安装依赖 pip install -e . ``` ### 构建包 ```bash # 使用 uv 构建 uv build # 或使用传统方式 python -m build ``` ### 发布到 PyPI ```bash # 安装 twine pip install twine # 上传 python -m twine upload dist/* ``` ### 应用打包 使用 PyInstaller 或其他工具打包为可执行文件: ```bash pip install pyinstaller pyinstaller --onefile your_app.py ``` ### Sphinx 文档生成 如果需要生成 API 文档: ```bash cd docs # 自动生成 .rst 文件 sphinx-apidoc -o source/api ../src/pyquickwebgui --force --no-toc # 构建 HTML 文档 make clean make html ``` ## 🔄 更新日志 ### 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` 不执行 **重要提示:** 1. **外部开发服务器集成增强**:`inject_js` 现在能正确区分 JS 代码字符串和文件路径 ```python # JS 代码字符串(长度 ≥ 260 或包含 JS 特征字符) inject_js_code = f'window.__API_BASE__="http://localhost:{qui.port}";' # 文件路径(短字符串且看起来像文件名) qui = QuikeUI(inject_js="menu.js") ``` 2. **跨域请求支持**:前端开发服务器(如 Vite)访问后端 API 不再受 CORS 限制 ```python # 后端自动处理 OPTIONS 预检请求 @qui.route("/api/login", methods=["POST"]) def login(params): return {"message": "ok"} # 自动添加 CORS 头 ``` 3. **JSON 响应编码修复**:中文等非 ASCII 字符不再乱码 ```python # 返回 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")` 一键启用 **重要提示:** 1. **`start()`/`run()` 参数覆盖**:现在可以在启动时覆盖实例属性 ```python qui = QuikeUI(title="My App") # 启动时覆盖 tray 配置 qui.start(tray={ "icon": "favicon.ico", "menu": [ {"text": "自定义菜单", "action": my_callback} ] }) ``` 2. **外部开发服务器集成**:支持将 `index_html` 设置为外部 URL,适合前端开发场景 ```python qui = QuikeUI( index_html="http://localhost:5173/", # Vite 开发服务器地址 icon="favicon.ico", # 图标从 web_path 查找 ) qui.run() ``` 3. **字典格式托盘菜单**:无需手动创建 `pystray.MenuItem`,直接传字典即可 ```python # ✅ 字典格式(新方式,更简洁) 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}) ``` 4. **SQLite 数据库插件**:通过 `db` 参数一键启用数据库功能 ```python 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) - ✅ 自动路径转换(相对路径转绝对路径) ### 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` 展示系统托盘 + 窗口隐藏/关闭功能 **重要提示:** 1. **参数迁移**:`base_path` 已重命名为 `web_path`,旧代码中的 `base_path` 参数需要更新 ```python # ❌ 旧写法(已废弃) qui = QuikeUI(base_path="web") # ✅ 新写法 qui = QuikeUI(web_path="web") ``` 2. **托盘配置迁移**:配置键从 `stray` 改为 `tray` ```python # ❌ 旧写法 qui = QuikeUI(stray={"icon": "favicon.ico"}) # ✅ 新写法 qui = QuikeUI(tray={"icon": "favicon.ico"}) ``` 3. **窗口图标配置**:新增 `icon` 参数,支持自定义窗口图标 ```python qui = QuikeUI( icon="favicon.ico", # 窗口图标(相对于 web_path) tray={"icon": "favicon.ico"} # 托盘图标 ) ``` 4. **窗口显隐控制**:通过 `hide()` 和 `show()` 方法控制窗口可见性 ```python 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 自行通过 ` ``` 5. **避免 alert() 导致的 WebSocket 断开**: ```javascript // ❌ 避免使用 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`) **重要提示:** 1. **Windows 编码问题**:在 Windows 系统中,日志输出应避免使用 emoji 字符,因为控制台默认使用 GBK 编码 ```python # ❌ 避免使用 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 ```python 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') ``` 2. **Daemon 线程注意事项**:当主线程被阻塞调用(如 `web.httpserver.runsimple()`)占用时,daemon 线程的子线程也会被终止 ```python # ❌ 避免在 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() # 处理连接 ``` 3. **打包测试**:使用 `bin/build_examples.bat` 脚本进行打包测试 ```bash # Windows bin\build_examples.bat # 测试打包后的程序 cd "examples/01 - hello_world" .\dist\hello.exe ``` 4. **路径获取 API**:QuikeUI 提供两个方法获取 PyInstaller 打包后的目录路径 ```python 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 示例对比,帮助用户选择合适的框架 **重要提示:** 1. **Eel 兼容性**:`qui.start()` 与 `qui.run()` 完全等价,方便从 Eel 迁移 ```python # 以下两种写法等价: qui.run() # 推荐使用 qui.start() # 与 Eel 兼容 ``` 2. **延迟初始化**:适合需要动态配置的场景 ```python qui = QuikeUI() qui.init( web_path="./web", index_html="index.html", width=800, height=600 ) qui.run() ``` 3. **自定义路由**:统一的 API 注册路由 ```python @qui.route('/custom') def custom_route(): return 'Hello, World!' @qui.route('/api/data', methods=['POST']) def api_data(): return {'status': 'ok'} ``` 4. **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 命名,修复逻辑错误(如 `!!!` 三感叹号陷阱) **重要提示:** 1. **调试模式配置**:必须在 `QuikeUI.js` 加载前设置 ```html ``` 2. **WebSocket 初始化**:无论 debug 模式如何都会执行,确保通信正常 3. **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 ## 🔗 相关链接 - [参考项目: flaskwebgui](https://github.com/ClimenteA/flaskwebgui) - [Eel - Python + JavaScript](https://github.com/ChrisKnott/Eel) - [Tauri - 构建小型二进制文件](https://tauri.app/) --- **Happy Coding! 🎉**