pywebview 无边框窗口移动解决方案
问题
pywebview 设置 frameless=True 后,窗口无边框无法移动。
解决方案
1. Python 后端(main.py)
python
import webview
class Api:
"""API 类,供前端调用"""
def __init__(self):
self._window = None # ⚠️ 必须使用私有属性(下划线开头),避免 COM 错误
def set_window(self, window):
self._window = window
def close_window(self):
"""关闭窗口"""
if self._window:
self._window.destroy()
def minimize_window(self):
"""最小化窗口"""
if self._window:
self._window.minimize()
# 创建 API 实例
api = Api()
window = webview.create_window(
title='窗口标题',
url='./index.html',
frameless=True,
js_api=api # 传入 API 对象
)
api.set_window(window)
webview.start()
2. HTML 前端
2.1 标题栏 HTML
html
<div class="titlebar">
<div class="titlebar-drag-region" id="titlebar-drag">
<span class="titlebar-title">窗口标题</span>
</div>
<div class="titlebar-buttons">
<div class="titlebar-btn minimize" onclick="minimizeWindow()">最小化</div>
<div class="titlebar-btn close" onclick="closeWindow()">关闭</div>
</div>
</div>
2.2 拖拽功能 JavaScript
javascript
let initialX = 0;
let initialY = 0;
function onMouseMove(ev) {
let x = ev.screenX - initialX;
let y = ev.screenY - initialY;
if (window.pywebview && typeof window.pywebview._jsApiCallback === 'function') {
window.pywebview._jsApiCallback('pywebviewMoveWindow', [x, y], 'move');
}
}
function onMouseUp() {
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseup', onMouseUp);
}
function onMouseDown(ev) {
initialX = ev.clientX;
initialY = ev.clientY;
window.addEventListener('mouseup', onMouseUp);
window.addEventListener('mousemove', onMouseMove);
}
// 关闭窗口
function closeWindow() {
if (window.pywebview && window.pywebview.api) {
window.pywebview.api.close_window();
}
}
// 最小化窗口
function minimizeWindow() {
if (window.pywebview && window.pywebview.api) {
window.pywebview.api.minimize_window();
}
}
// 初始化拖拽功能(延迟确保 pywebview API 就绪)
window.onload = function() {
setTimeout(() => {
const dragRegion = document.getElementById('titlebar-drag');
if (dragRegion) {
dragRegion.addEventListener('mousedown', onMouseDown);
}
}, 100);
};
2.3 标题栏样式(可选)
css
.titlebar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 40px;
background: rgba(0, 0, 0, 0.3);
display: flex;
justify-content: space-between;
align-items: center;
user-select: none;
z-index: 1000;
}
.titlebar-drag-region {
flex: 1;
height: 100%;
cursor: move;
}
.titlebar-buttons {
display: flex;
gap: 10px;
padding-right: 10px;
}
.titlebar-btn {
padding: 5px 15px;
cursor: pointer;
}
核心原理
- 拖拽原理 :监听鼠标按下位置(相对坐标),计算鼠标移动后的屏幕位置(绝对坐标),调用 pywebview 内部 API
pywebviewMoveWindow移动窗口 - API 通信 :通过
js_api参数将 Python 类暴露给前端 JavaScript - 延迟初始化 :使用
setTimeout确保 pywebview API 准备就绪后再绑定事件
注意事项
⚠️ 关键 :API 类中的 window 属性必须使用私有属性(_window),否则会触发 Windows COM 组件错误:
arduino
System.InvalidCastException: 无法将类型为"System.__ComObject"的 COM 对象强制转换为接口类型"Accessibility.IAccessible"
✅ 正确 :self._window = None
❌ 错误 :self.window = None