web-dev-qa-db-ja.com

Pythonを使用して、ウィンドウなしで画面にテキストを表示する方法

問題:

ウィンドウなしで画面に直接テキストを書き込む必要があります。テキストは、他のすべてのウィンドウやフルスクリーンアプリケーションの上に表示する必要があり、クリックしたり操作したりできないようにする必要があります。

例: Screenshot of text on the screen. 例のように、テキストの背景を透明にする必要はありません。 Windows7ではPython 2または3のいずれかを使用できます。

解決策の私の試み:

Tkinterを使用してスタンドアロンラベルを作成してみました。

編集:クリスチャンラップの助けを借りて改善

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

何が機能するか:

  • テキストはウィンドウなしで表示されます
  • テキストは他のすべてのウィンドウの上に残ります
  • 背景は透明にすることができます

しないこと:

  • フルスクリーンアプリケーションの上にテキストが表示されない
  • テキストは、その上で発生するクリックイベントをブロックします
  • 背景の透明度はアルファ版ではないため、ハードエッジがあります
19
dln385

ここには2つのまったく異なる問題があることがわかりました。ウィンドウ上にテキストを表示するには、装飾されていない最上部のウィンドウを作成し、背景にクロマキーを設定する必要があります。ただし、フルスクリーンアプリケーション(ゲームなど)が実行されている場合、これは機能しません。フルスクリーンアプリケーションでテキストを表示する唯一の信頼できる方法は、Direct3Dフックを使用することです。

Direct3Dフックの例は作成していませんが、最初の問題に対して2つの異なる解決策を示します。

解決策1: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()

解決策2:pywin32

この例では、pywin32を介してすべてを実行します。これにより、複雑で移植性が低下しますが、かなり強力になります。コード全体に、WindowsAPIの関連部分へのリンクを含めました。

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

私にも同様のニーズがあり、pygameライブラリが私が探していたものに対して本当に良い仕事をしたことを発見しました。非常に高速でちらつきのない非常に大きなテキストを生成することができました。以下のこのトピックを参照してください(最初のコード「ソリューション」はそれです):

Pythonで画面にテキストを表示する簡単な方法は?

私はそれをスピードアップしました、そしてそれは速いです。また、フォントをはるかに大きくしましたが、速度にはまったく影響しませんでした。そのすべてが小さなOrangePi Liteボード(<$ 20)で実行されています。 GUIなしのコマンドラインから(またはウィンドウ化されたデスクトップから)起動できます。どちらの場合も、フルスクリーンであり、「ウィンドウ化された」アプリとして表示されません。

PythonとPygameに慣れていないので、背景として画像ファイルをロードし、その上にテキストを配置できると思います。

ああ、Tkinterの例を使用して同じことを試しましたが、速度が遅く、ちらつき、フォントが「正しく」見えませんでした。 Pygameが明らかに勝者でした。画像が更新されたときにゲームがちらつく必要がないため、「ティアリング」せずに画面にブリットするように設計されています。高速だったのでOpenGLに依存していなかったのには驚きました。私はOpenGLがOrangePiでサポートされているとは思いません(それについての明確な答えを理解することはできません)。つまり、2Dの場合、すごい、pygameは印象的です。

0
PaulD