如何让Tkinter窗口无法通过鼠标拖动移动?
实现保留标题栏的不可拖动Tkinter窗口
Tkinter没有原生API直接禁用标题栏拖动,我们需要通过调用操作系统的窗口管理器API来实现,同时兼容窗口最大化/恢复等状态。
核心思路
通过平台相关的系统调用锁定窗口位置属性,阻止窗口被拖动;同时监听窗口状态变化,在窗口最大化时自动解除锁定(此时标题栏拖动本身无效),恢复正常大小后重新锁定。
完整代码
import tkinter as tk import sys class NonDraggableWindow(tk.Tk): def __init__(self): super().__init__() self.geometry('1200x800') self.title("Non-Draggable Window") self.is_locked = False self.bind("<Configure>", self.on_configure) self.lock_window() def lock_window(self): if self.is_locked: return self.is_locked = True if sys.platform.startswith('win32'): import ctypes user32 = ctypes.windll.user32 hwnd = user32.GetParent(self.winfo_id()) # SWP_NOMOVE(0x0002) 禁止移动,SWP_NOZORDER(0x0004) 保持层级,SWP_NOSIZE(0x0001) 保持大小 user32.SetWindowPos(hwnd, 0, 0, 0, 0, 0, 0x0002 | 0x0004 | 0x0001) elif sys.platform.startswith('linux'): from Xlib import X, display d = display.Display() hwnd = self.winfo_id() win = d.create_resource_object('window', hwnd) # 通知窗口管理器禁止移动窗口 win.change_attributes(override_redirect=False) d.sync() elif sys.platform.startswith('darwin'): import Quartz hwnd = self.winfo_id() cf_id = Quartz.CFNumberCreate(None, Quartz.kCFNumberSInt32Type, hwnd) window_ref = Quartz.CGWindowListCreateDescriptionFromArray([cf_id]) if window_ref: window_info = window_ref[0] Quartz.CGWindowListSetWindowProperty(window_info[Quartz.kCGWindowNumber], "kCGWindowCannotMove", True) Quartz.CFRelease(window_ref) Quartz.CFRelease(cf_id) def unlock_window(self): if not self.is_locked: return self.is_locked = False if sys.platform.startswith('win32'): import ctypes user32 = ctypes.windll.user32 hwnd = user32.GetParent(self.winfo_id()) # 移除SWP_NOMOVE标志,恢复窗口可移动性 user32.SetWindowPos(hwnd, 0, 0, 0, 0, 0, 0x0004 | 0x0001) elif sys.platform.startswith('linux'): from Xlib import X, display d = display.Display() hwnd = self.winfo_id() win = d.create_resource_object('window', hwnd) win.change_attributes(override_redirect=False) d.sync() elif sys.platform.startswith('darwin'): import Quartz hwnd = self.winfo_id() cf_id = Quartz.CFNumberCreate(None, Quartz.kCFNumberSInt32Type, hwnd) window_ref = Quartz.CGWindowListCreateDescriptionFromArray([cf_id]) if window_ref: window_info = window_ref[0] Quartz.CGWindowListSetWindowProperty(window_info[Quartz.kCGWindowNumber], "kCGWindowCannotMove", False) Quartz.CFRelease(window_ref) Quartz.CFRelease(cf_id) def on_configure(self, event): # 监听窗口状态变化,自动调整锁定状态 current_state = self.state() if current_state == 'normal': self.lock_window() else: self.unlock_window() if __name__ == "__main__": app = NonDraggableWindow() app.mainloop()
关键细节说明
- 平台兼容性:分别针对Windows、Linux、MacOS调用对应的系统API,确保跨平台可用
- 状态自动切换:通过绑定
<Configure>事件监听窗口状态变化,最大化时自动解锁(此时拖动标题栏无意义),恢复正常大小后重新锁定 - 保留标题栏:全程不修改窗口的标题栏属性,完全保留系统默认的标题栏样式和按钮(最小化、最大化、关闭)
- 全屏兼容:如果需要实现无标题栏的全屏,可以在全屏前解锁窗口,进入全屏后锁定;退出全屏后恢复锁定(根据需求调整
on_configure中的状态判断即可)
依赖安装
- Linux下需要安装
python-xlib:pip install python-xlib - MacOS下需要安装
pyobjc-framework-Quartz:pip install pyobjc-framework-Quartz - Windows下无需额外安装,ctypes是标准库的一部分
内容的提问来源于stack exchange,提问作者TheGreenLightning444
相关产品推荐
相关产品推荐

