Cómo mostrar texto en la pantalla sin una ventana usando Python

El problema:

Necesito escribir texto directamente en la pantalla sin una ventana. El texto debe aparecer sobre todas las demás ventanas y aplicaciones de pantalla completa y no debe ser presionado ni interactuable de ninguna manera.

Ejemplo: Captura de pantalla de texto en la pantalla. El texto no necesita tener un fondo transparente como se ve en el ejemplo. Puedo usar Python 2 o 3 en Windows 7.

Mi bash de una solución:

Intenté hacer una etiqueta independiente usando Tkinter:

Edit: Mejorado con la ayuda de 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() 

Que funciona:

  • El texto se muestra sin una ventana.
  • El texto permanece por encima de todas las demás ventanas.
  • El fondo puede ser transparente.

Lo que no hace

  • El texto no se muestra arriba de las aplicaciones de pantalla completa.
  • Los bloques de texto hacen clic en los eventos que ocurren sobre él.
  • La transparencia de fondo no es alfa, por lo que hay bordes duros

Resulta que hay dos problemas completamente diferentes aquí. Para mostrar texto en las ventanas, deberá crear una ventana superior sin decorar y la clave cromática del fondo. Sin embargo, esto no funcionará cuando hay una aplicación de pantalla completa en ejecución (como un juego). La única forma confiable de mostrar texto en una aplicación de pantalla completa es usar un gancho Direct3D.

No he escrito un ejemplo de gancho de Direct3D, pero daré dos soluciones diferentes al primer problema.

Solución 1: Tkinter + pywin32

En este ejemplo, hago la mayoría del trabajo con Tkinter y uso win32api para evitar que el texto bloquee los clics del mouse. Si win32api no está disponible para usted, entonces simplemente puede eliminar esa parte del código.

 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() 

Solución 2: pywin32

Este ejemplo hace todo a través de pywin32. Esto lo hace más complicado y menos portátil, pero considerablemente más potente. He incluido enlaces a las partes relevantes de la API de Windows en todo el código.

 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() 

Tenía una necesidad similar y descubrí que la biblioteca de pygame hizo un trabajo REALMENTE bueno para lo que estaba buscando. Pude generar un texto muy grande que se actualizaría muy rápido y sin parpadeo. Vea este tema a continuación (el primer código es ‘solución’):

¿Una forma sencilla de mostrar texto en pantalla en Python?

Lo aceleré y es rápido. También hizo la fuente mucho más grande y eso no afectó la velocidad en absoluto. Todo eso se ejecuta en un pequeño tablero Orange Pi Lite (<$ 20). Puede iniciarlo desde la línea de comandos sin GUI (o desde un escritorio con ventanas) y, en cualquier caso, es de pantalla completa y no aparece como una aplicación con "ventanas".

Supongo que, como soy nuevo en Python y Pygame, es que puedes cargar un archivo de imagen como fondo y luego poner texto encima de eso.

Ah, e intenté lo mismo usando el ejemplo de Tkinter y fue más lento, parpadeó, y la fuente simplemente no se veía “bien”. Pygame fue claramente el ganador. Está diseñado para que las cosas aparezcan en la pantalla sin “desgarrarse”, ya que los juegos no deben parpadear cuando las imágenes se actualizan. Me sorprendió que no dependiera de OpenGL porque era rápido. No creo que OpenGL sea compatible con Orange Pi (no puedo encontrar una respuesta clara al respecto). Así que para cosas en 2D, wow, pygame es impresionante.