如何使用 Python 在没有窗口的情况下在屏幕上显示文本

Posted

技术标签:

【中文标题】如何使用 Python 在没有窗口的情况下在屏幕上显示文本【英文标题】:How to display text on the screen without a window using Python 【发布时间】:2014-03-17 09:38:06 【问题描述】:

问题:

我需要在没有窗口的情况下直接将文本写入屏幕。文本需要出现在所有其他窗口和全屏应用程序之上,并且不能以任何方式点击或交互。

示例: 如示例中所示,文本不需要具有透明背景。我可以在 Windows 7 上使用 Python 2 或 3。

我的解决方案尝试:

我尝试使用 Tkinter 制作独立标签:

编辑:在 Christian Rapp 的帮助下改进

import Tkinter
label = Tkinter.Label(text='Text on the screen', font=('Times','30'), fg='black', bg='white')
label.master.overrideredirect(True)
label.master.geometry("+250+250")
label.master.lift()
label.master.wm_attributes("-topmost", True)
label.master.wm_attributes("-disabled", True)
label.master.wm_attributes("-transparentcolor", "white")
label.pack()
label.mainloop()

什么有效:

文本显示时没有窗口 文本仍高于所有其他窗口 背景可以是透明的

什么不能:

文本不显示在全屏应用程序上方 文本块上发生的点击事件 背景透明度不是 Alpha,所以有硬边

【问题讨论】:

要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是题外话,因为它们往往会吸引固执己见的答案和垃圾邮件。相反,请描述问题以及迄今为止为解决该问题所做的工作。 @jon 我注意到您引用了this page 的第 5 点,其中包含一个包含更多信息的链接。我已经编辑了我的问题,并认为它现在符合主题问题的 *** 标准。 您是否尝试过 label.master.wm_attributes("-alpha", 1.0) 作为透明背景? @Christian 这让整个标签变得透明,看起来可能是impossible to make only the background transparent。至于让它全屏显示,我目前正在研究hardware overlay 选项。 哦,对不起,我以为它可以用于透明背景。但是你可以滥用画布小部件 --> ***.com/questions/17039481/… 【参考方案1】:

事实证明这里有两个完全不同的问题。要在窗口上显示文本,您需要创建一个未装饰的最顶层窗口并将色度键设置为背景。但是,当运行全屏应用程序(例如游戏)时,这将不起作用。在全屏应用程序上显示文本的唯一可靠方法是使用 Direct3D 挂钩。

我还没有编写 Direct3D 挂钩示例,但我将针对第一个问题给出两种不同的解决方案。

方案一:Tkinter + pywin32

在此示例中,我使用 Tkinter 完成大部分工作,并使用 win32api 来防止文本阻止鼠标单击。如果您无法使用 win32api,那么您可以删除这部分代码。

import Tkinter, win32api, win32con, pywintypes

label = Tkinter.Label(text='Text on the screen', font=('Times New Roman','80'), fg='black', bg='white')
label.master.overrideredirect(True)
label.master.geometry("+250+250")
label.master.lift()
label.master.wm_attributes("-topmost", True)
label.master.wm_attributes("-disabled", True)
label.master.wm_attributes("-transparentcolor", "white")

hWindow = pywintypes.HANDLE(int(label.master.frame(), 16))
# http://msdn.microsoft.com/en-us/library/windows/desktop/ff700543(v=vs.85).aspx
# The WS_EX_TRANSPARENT flag makes events (like mouse clicks) fall through the window.
exStyle = win32con.WS_EX_COMPOSITED | win32con.WS_EX_LAYERED | win32con.WS_EX_NOACTIVATE | win32con.WS_EX_TOPMOST | win32con.WS_EX_TRANSPARENT
win32api.SetWindowLong(hWindow, win32con.GWL_EXSTYLE, exStyle)

label.pack()
label.mainloop()

方案二:pywin32

本示例通过 pywin32 完成所有操作。这使得它更复杂,更不便携,但功能更强大。我在整个代码中都包含了指向 Windows API 相关部分的链接。

import win32api, win32con, win32gui, win32ui

def main():
    hInstance = win32api.GetModuleHandle()
    className = 'MyWindowClassName'

    # http://msdn.microsoft.com/en-us/library/windows/desktop/ms633576(v=vs.85).aspx
    # win32gui does not support WNDCLASSEX.
    wndClass                = win32gui.WNDCLASS()
    # http://msdn.microsoft.com/en-us/library/windows/desktop/ff729176(v=vs.85).aspx
    wndClass.style          = win32con.CS_HREDRAW | win32con.CS_VREDRAW
    wndClass.lpfnWndProc    = wndProc
    wndClass.hInstance      = hInstance
    wndClass.hCursor        = win32gui.LoadCursor(None, win32con.IDC_ARROW)
    wndClass.hbrBackground  = win32gui.GetStockObject(win32con.WHITE_BRUSH)
    wndClass.lpszClassName  = className
    # win32gui does not support RegisterClassEx
    wndClassAtom = win32gui.RegisterClass(wndClass)

    # http://msdn.microsoft.com/en-us/library/windows/desktop/ff700543(v=vs.85).aspx
    # Consider using: WS_EX_COMPOSITED, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TOPMOST, WS_EX_TRANSPARENT
    # The WS_EX_TRANSPARENT flag makes events (like mouse clicks) fall through the window.
    exStyle = win32con.WS_EX_COMPOSITED | win32con.WS_EX_LAYERED | win32con.WS_EX_NOACTIVATE | win32con.WS_EX_TOPMOST | win32con.WS_EX_TRANSPARENT

    # http://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx
    # Consider using: WS_DISABLED, WS_POPUP, WS_VISIBLE
    style = win32con.WS_DISABLED | win32con.WS_POPUP | win32con.WS_VISIBLE

    # http://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx
    hWindow = win32gui.CreateWindowEx(
        exStyle,
        wndClassAtom,
        None, # WindowName
        style,
        0, # x
        0, # y
        win32api.GetSystemMetrics(win32con.SM_CXSCREEN), # width
        win32api.GetSystemMetrics(win32con.SM_CYSCREEN), # height
        None, # hWndParent
        None, # hMenu
        hInstance,
        None # lpParam
    )

    # http://msdn.microsoft.com/en-us/library/windows/desktop/ms633540(v=vs.85).aspx
    win32gui.SetLayeredWindowAttributes(hWindow, 0x00ffffff, 255, win32con.LWA_COLORKEY | win32con.LWA_ALPHA)

    # http://msdn.microsoft.com/en-us/library/windows/desktop/dd145167(v=vs.85).aspx
    #win32gui.UpdateWindow(hWindow)

    # http://msdn.microsoft.com/en-us/library/windows/desktop/ms633545(v=vs.85).aspx
    win32gui.SetWindowPos(hWindow, win32con.HWND_TOPMOST, 0, 0, 0, 0,
        win32con.SWP_NOACTIVATE | win32con.SWP_NOMOVE | win32con.SWP_NOSIZE | win32con.SWP_SHOWWINDOW)

    # http://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
    #win32gui.ShowWindow(hWindow, win32con.SW_SHOW)

    win32gui.PumpMessages()

def wndProc(hWnd, message, wParam, lParam):
    if message == win32con.WM_PAINT:
        hdc, paintStruct = win32gui.BeginPaint(hWnd)

        dpiScale = win32ui.GetDeviceCaps(hdc, win32con.LOGPIXELSX) / 60.0
        fontSize = 80

        # http://msdn.microsoft.com/en-us/library/windows/desktop/dd145037(v=vs.85).aspx
        lf = win32gui.LOGFONT()
        lf.lfFaceName = "Times New Roman"
        lf.lfHeight = int(round(dpiScale * fontSize))
        #lf.lfWeight = 150
        # Use nonantialiased to remove the white edges around the text.
        # lf.lfQuality = win32con.NONANTIALIASED_QUALITY
        hf = win32gui.CreateFontIndirect(lf)
        win32gui.SelectObject(hdc, hf)

        rect = win32gui.GetClientRect(hWnd)
        # http://msdn.microsoft.com/en-us/library/windows/desktop/dd162498(v=vs.85).aspx
        win32gui.DrawText(
            hdc,
            'Text on the screen',
            -1,
            rect,
            win32con.DT_CENTER | win32con.DT_NOCLIP | win32con.DT_SINGLELINE | win32con.DT_VCENTER
        )
        win32gui.EndPaint(hWnd, paintStruct)
        return 0

    elif message == win32con.WM_DESTROY:
        print 'Closing the window.'
        win32gui.PostQuitMessage(0)
        return 0

    else:
        return win32gui.DefWindowProc(hWnd, message, wParam, lParam)


if __name__ == '__main__':
    main()

【讨论】:

很棒的例子。您知道是否可以将LWA_COLORKEY 与已经存在的窗口一起使用?我设法通过LWA_ALPHA 获得完全透明,但到目前为止LWA_COLORKEY | LWA_ALPHA 对部分透明无效。 @dln385 - 感谢这些解决方案!是否有可能以某种方式在不断变化的地方显示文本?例如在特定窗口的某个区域? @dln385 嘿,这可能有点晚了,但是你怎么能退出这个独立的标签呢?如何在不终止脚本的情况下使其在代码中消失? 写完的文字怎么从屏幕上去掉?【参考方案2】:

我也有类似的需求,发现 pygame 库在我所寻求的方面做得非常好。我能够生成非常大的文本,更新速度非常快且没有闪烁。请参阅下面的主题(第一个代码“解决方案”):

Simple way to display text on screen in Python?

我加快了速度,速度很快。还使字体更大,根本不会影响速度。所有这些都在一个小的 Orange Pi Lite 板上运行(

由于我是 Python 和 Pygame 的新手,我的假设是您可以加载一个图像文件作为背景,然后在其上放置文本。

哦,我使用 Tkinter 示例进行了相同的尝试,但速度较慢、闪烁,而且字体看起来不“正确”。 Pygame 显然是赢家。它旨在将内容传送到屏幕上而不会“撕裂”,因为游戏不需要在图像更新时闪烁。我很惊讶它不依赖于 OpenGL,因为它速度很快。我认为 Orange Pi 不支持 OpenGL(对此无法找到明确的答案)。所以对于 2D 的东西,哇,pygame 令人印象深刻。!

【讨论】:

以上是关于如何使用 Python 在没有窗口的情况下在屏幕上显示文本的主要内容,如果未能解决你的问题,请参考以下文章

在没有屏幕管理器类的情况下在 Python 中更改屏幕

如何在没有控制台窗口的情况下在 Windows 上启动最新的 Jupyter QtConsole

如何在没有控件的情况下在 android 中制作相机应用程序,以便仅在屏幕上显示来自镜头的视图? [关闭]

在没有全屏浏览器的情况下在图像上显示全高(使用封面)

python入门——turtle库的使用

如何在不刷新页面的情况下在屏幕上显示新记录?