如何无窗口显示带背景文本并动态更新,且忽略鼠标交互
解决方案:透明背景动态文本绘制(Windows平台)
需求说明
- 每秒生成包含文本及其坐标的元组列表
- 仅在文本对应的矩形背景上显示内容,其余区域完全透明
- 无可见程序窗口框架,绘制内容忽略所有鼠标操作
- 每秒自动重绘全部内容
原代码问题分析
- 背景透明逻辑错误:窗口背景设为白色画刷,但未在绘制前填充整个窗口为白色,导致非文本区域无法正确透明
- 动态更新机制缺失:仅在窗口需要重绘时生成单条文本,无法实现每秒批量更新的需求
- 资源泄漏风险:创建的字体对象未释放,长期运行会消耗系统资源
改进实现代码
import win32api import win32con import win32gui import win32ui import time import random # 全局变量存储待绘制的文本与坐标列表 text_items = [] def generate_random_coordinates(): x = random.randint(0, win32api.GetSystemMetrics(win32con.SM_CXSCREEN) - 100) y = random.randint(0, win32api.GetSystemMetrics(win32con.SM_CYSCREEN) - 50) return x, y def generate_random_text(): letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' return ''.join(random.choice(letters) for _ in range(random.randint(1, 8))) def update_text_items(): """每秒生成新的文本坐标列表""" global text_items text_items = [] # 示例:生成5条随机文本,可根据需求调整数量 for _ in range(5): text = generate_random_text() x, y = generate_random_coordinates() text_items.append((text, x, y)) def wndProc(hWnd, message, wParam, lParam): if message == win32con.WM_TIMER: # 定时器触发:更新文本列表并触发重绘 update_text_items() win32gui.InvalidateRect(hWnd, None, True) return 0 elif message == win32con.WM_PAINT: hdc, paintStruct = win32gui.BeginPaint(hWnd) # 先填充整个窗口为颜色键(白色),确保非文本区域透明 win32gui.FillRect(hdc, (0, 0, win32api.GetSystemMetrics(win32con.SM_CXSCREEN), win32api.GetSystemMetrics(win32con.SM_CYSCREEN)), win32gui.GetStockObject(win32con.WHITE_BRUSH)) dpiScale = win32ui.GetDeviceCaps(hdc, win32con.LOGPIXELSX) / 96.0 fontSize = 24 # 创建字体 lf = win32gui.LOGFONT() lf.lfFaceName = "Times New Roman" lf.lfHeight = int(round(dpiScale * fontSize)) lf.lfQuality = win32con.ANTIALIASED_QUALITY hf = win32gui.CreateFontIndirect(lf) old_font = win32gui.SelectObject(hdc, hf) # 设置文本颜色 win32gui.SetTextColor(hdc, win32api.RGB(220, 220, 220)) # 设置背景模式为透明(文本背景不覆盖已绘制的矩形) win32gui.SetBkMode(hdc, win32con.TRANSPARENT) # 批量绘制所有文本及背景矩形 gray_brush = win32gui.CreateSolidBrush(win32api.RGB(50, 50, 50)) for text, x, y in text_items: text_size = win32gui.GetTextExtentPoint32(hdc, text) # 文本背景矩形(预留边距) text_rect = ( x - 5, y - 5, x + text_size[0] + 5, y + text_size[1] + 5 ) # 填充背景矩形 win32gui.FillRect(hdc, text_rect, gray_brush) # 绘制文本(居中显示在矩形内) win32gui.DrawText( hdc, text, -1, text_rect, win32con.DT_CENTER | win32con.DT_SINGLELINE | win32con.DT_VCENTER ) # 释放资源 win32gui.DeleteObject(gray_brush) win32gui.SelectObject(hdc, old_font) win32gui.DeleteObject(hf) win32gui.EndPaint(hWnd, paintStruct) return 0 elif message == win32con.WM_DESTROY: win32gui.KillTimer(hWnd, 1) win32gui.PostQuitMessage(0) return 0 else: return win32gui.DefWindowProc(hWnd, message, wParam, lParam) def main(): hInstance = win32api.GetModuleHandle() className = 'TransparentTextWindowClass' # 注册窗口类 wndClass = win32gui.WNDCLASS() wndClass.style = win32con.CS_HREDRAW | win32con.CS_VREDRAW wndClass.lpfnWndProc = wndProc wndClass.hInstance = hInstance # 设置空鼠标,让系统感知窗口无交互 wndClass.hCursor = win32gui.LoadCursor(None, win32con.IDC_NO) wndClass.hbrBackground = win32gui.GetStockObject(win32con.WHITE_BRUSH) wndClass.lpszClassName = className win32gui.RegisterClass(wndClass) # 扩展样式:确保透明、置顶、无激活、鼠标穿透 exStyle = ( win32con.WS_EX_COMPOSITED | win32con.WS_EX_LAYERED | win32con.WS_EX_NOACTIVATE | win32con.WS_EX_TOPMOST | win32con.WS_EX_TRANSPARENT | win32con.WS_EX_TOOLWINDOW ) # 窗口样式:无框架、禁用、可见 style = win32con.WS_DISABLED | win32con.WS_POPUP | win32con.WS_VISIBLE # 创建全屏窗口 hWindow = win32gui.CreateWindowEx( exStyle, className, None, style, 0, 0, win32api.GetSystemMetrics(win32con.SM_CXSCREEN), win32api.GetSystemMetrics(win32con.SM_CYSCREEN), None, None, hInstance, None ) # 设置分层窗口属性:白色为透明色,完全不透明 win32gui.SetLayeredWindowAttributes(hWindow, win32api.RGB(255,255,255), 255, win32con.LWA_COLORKEY) # 设置窗口置顶且不激活 win32gui.SetWindowPos(hWindow, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOACTIVATE | win32con.SWP_NOMOVE | win32con.SWP_NOSIZE) # 设置定时器:每秒触发一次(1000毫秒) win32gui.SetTimer(hWindow, 1, 1000, None) # 初始化文本列表 update_text_items() # 消息循环 win32gui.PumpMessages() if __name__ == '__main__': try: main() except KeyboardInterrupt: pass
关键改进说明
- 定时更新机制:通过
SetTimer设置1秒定时器,触发时更新文本列表并调用InvalidateRect强制窗口重绘 - 透明背景修正:绘制前先将整个窗口填充为白色(颜色键),确保非文本区域完全透明;文本背景矩形使用灰色填充,与透明区域区分
- 资源管理优化:字体、画刷等GDI对象使用后及时释放,避免资源泄漏
- 鼠标交互屏蔽:通过
WS_EX_TRANSPARENT让鼠标事件完全穿透窗口,同时设置IDC_NO光标并禁用窗口,确保无交互干扰 - DPI适配:根据屏幕DPI调整字体大小,避免高DPI下文本显示异常
内容的提问来源于stack exchange,提问作者Skarloc
相关产品推荐
相关产品推荐

