You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用Ctypes指定Python弹窗的显示位置

如何使用Ctypes指定Python弹窗的显示位置

嘿,我完全懂你的困扰——默认的MessageBoxW确实会自动居中显示,一堆弹窗挤在一起看起来跟一个似的,完全达不到想要的效果。要让它们分散在屏幕随机位置,我们得借助Windows API的其他函数配合,因为MessageBox本身并没有直接设置位置的参数。下面给你一个简单可行的方案,一步步来:

实现思路

默认的MessageBox没法直接指定位置,我们可以绕个弯:先创建一个隐藏的临时窗口作为弹窗的父窗口,把这个临时窗口放到随机位置,再让MessageBox以它为父窗口——这样弹窗就会相对于临时窗口显示(默认是父窗口中心,我们通过调整临时窗口的位置,就能间接控制弹窗的最终位置),而且临时窗口是隐藏的,不会干扰用户。

修改后的完整代码

import ctypes
import threading
import random
from ctypes import wintypes

# 加载Windows用户态API库
user32 = ctypes.WinDLL('user32', use_last_error=True)

# 定义需要用到的Windows API类型
HWND = wintypes.HWND
LPCSTR = wintypes.LPCSTR
UINT = wintypes.UINT

# 声明创建窗口的API函数
user32.CreateWindowExA.restype = HWND
user32.CreateWindowExA.argtypes = [
    wintypes.DWORD,  # 扩展窗口样式
    LPCSTR,          # 窗口类名
    LPCSTR,          # 窗口标题
    wintypes.DWORD,  # 窗口样式
    wintypes.INT,    # 窗口X坐标
    wintypes.INT,    # 窗口Y坐标
    wintypes.INT,    # 窗口宽度
    wintypes.INT,    # 窗口高度
    HWND,            # 父窗口句柄
    wintypes.HMENU,  # 菜单句柄
    wintypes.HINSTANCE,  # 实例句柄
    wintypes.LPVOID       # 附加参数
]

# 声明销毁窗口的API函数
user32.DestroyWindow.argtypes = [HWND]
user32.DestroyWindow.restype = wintypes.BOOL

# 获取屏幕分辨率,避免弹窗超出屏幕
SM_CXSCREEN = 0  # 屏幕宽度参数
SM_CYSCREEN = 1  # 屏幕高度参数
screen_width = user32.GetSystemMetrics(SM_CXSCREEN)
screen_height = user32.GetSystemMetrics(SM_CYSCREEN)

def Mbox(title, text, style, x, y):
    # 创建隐藏的临时父窗口,用来定位弹窗
    hwnd_parent = user32.CreateWindowExA(
        0,                      # 无扩展样式
        b"STATIC",              # 使用系统默认的静态窗口类
        b"TempWindow",          # 窗口标题(无关紧要)
        0x08000000,             # WS_POPUP样式,隐藏窗口
        x, y,                   # 临时窗口的位置(控制弹窗位置)
        0, 0,                   # 窗口大小设为0,不影响显示
        None,                   # 无父窗口
        None,                   # 无菜单
        None,                   # 使用当前进程实例
        None                    # 无附加参数
    )
    if not hwnd_parent:
        raise ctypes.WinError(ctypes.get_last_error())
    
    try:
        # 显示弹窗,以临时窗口为父,弹窗会在父窗口附近居中
        return ctypes.windll.user32.MessageBoxW(hwnd_parent, text, title, style)
    finally:
        # 弹窗关闭后销毁临时窗口,释放资源
        user32.DestroyWindow(hwnd_parent)

if __name__ == "__main__":
    # 预估弹窗的大致尺寸,避免生成的位置让弹窗超出屏幕
    popup_width = 300
    popup_height = 150
    for i in range(50):
        # 生成随机坐标,确保弹窗完全在屏幕内
        x = random.randint(0, screen_width - popup_width)
        y = random.randint(0, screen_height - popup_height)
        # 启动线程创建弹窗,传入随机位置参数
        t = threading.Thread(target=Mbox, args=(f"弹窗{i+1}", "这是随机位置的弹窗!", 1, x, y))
        t.start()
    
    print("Done!")

关键细节说明

  • GetSystemMetrics用来获取屏幕的宽高,这样生成的随机位置不会让弹窗超出屏幕边界;
  • 临时窗口用WS_POPUP样式,确保它是隐藏状态,不会出现在屏幕上;
  • 每个线程独立处理自己的临时窗口和弹窗,避免线程间的资源冲突;
  • 弹窗关闭后会自动销毁临时窗口,不会留下无用的系统资源。

直接运行这段代码,50个弹窗就会分散在屏幕的不同位置啦!

备注:内容来源于stack exchange,提问作者Deep

火山引擎 最新活动