You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何无窗口显示带背景文本并动态更新,且忽略鼠标交互

解决方案:透明背景动态文本绘制(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 18:40:55