How to display text on the screen without a window using Python How to display text on the screen without a window using Python python python

How to display text on the screen without a window using Python


It turns out there are two entirely different problems here. To show text over windows, you'll need to create an undecorated topmost window and chroma key the background. However, this won't work when there's a full-screen application running (such as a game). The only reliable way to show text over a full-screen application is to use a Direct3D hook.

I haven't written up a Direct3D hook example, but I'll give two different solutions to the first problem.

Solution 1: Tkinter + pywin32

In this example, I do the majority of the work with Tkinter and use win32api to prevent the text from blocking mouse clicks. If win32api isn't available to you, then you can just remove that part of the code.

import Tkinter, win32api, win32con, pywintypeslabel = 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_TRANSPARENTwin32api.SetWindowLong(hWindow, win32con.GWL_EXSTYLE, exStyle)label.pack()label.mainloop()

Solution 2: pywin32

This example does everything through pywin32. This makes it more complicated and less portable, but considerably more powerful. I've included links to the relevant parts of the Windows API throughout the code.

import win32api, win32con, win32gui, win32uidef 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()


I had a similar need and discovered that the pygame library did a REALLY good job for what I was seeking. I was able to generate very large text that would update very fast and without flicker. See this topic below (first code 'solution' is it):

Simple way to display text on screen in Python?

I sped it up and it is fast. Also made the font much larger and that didn't impact speed much at all. All of that running on a little Orange Pi Lite board (< $20). You can launch it from the command line w/o GUI (or from a windowed desktop) and in either case it is full-screen and doesn't appear as a "windowed" app.

My assumption, as I'm new to Python and Pygame, is that you could load an image file as the background and then put text atop of that.

Oh, and I tried the same using the Tkinter example and it was slower, flickered, and the font just didn't look "right". Pygame was clearly the winner. It's designed to blit things to the screen without "tearing" as games need to not flicker when images update. I was surprised that it wasn't dependent on OpenGL because it was fast. I don't think OpenGL is supported on the Orange Pi (can't figure out a clear answer on that). So for 2D stuff, wow, pygame is impressive.!