无法使用win32gui.DrawText更新Window中的文本

时间:2016-11-15 16:07:30

标签: python pywin32

我试图在Windows中使用Python设置非滚动文本输出窗口。

我找到了一些使用win32gui.DrawText在窗口中放置文本的代码示例,但是在初始文本到位后,它们都没有添加或更改文本。

我没有在文档中看到任何内容表明在调用DrawText后窗口文本已修复,但是在第一次绘制窗口后我无法进行任何更改。

我错过了哪些可以让我更新Windows文本?

这是Christophe Keller" Hello World"我修改为1秒后更改窗口文本的示例。 (我添加的代码不起作用。)

import win32api
import win32con
import win32gui
import time

#Code example modified from:
#Christophe Keller
#Hello World in Python using Win32

# New code: Define global
g_str_Text = 'Hello send by Python via Win32!'

def main():
    #get instance handle
    hInstance = win32api.GetModuleHandle()

    # the class name
    className = 'SimpleWin32'

    # create and initialize window class
    wndClass                = win32gui.WNDCLASS()
    wndClass.style          = win32con.CS_HREDRAW | win32con.CS_VREDRAW
    wndClass.lpfnWndProc    = wndProc
    wndClass.hInstance      = hInstance
    wndClass.hIcon          = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
    wndClass.hCursor        = win32gui.LoadCursor(0, win32con.IDC_ARROW)
    wndClass.hbrBackground  = win32gui.GetStockObject(win32con.WHITE_BRUSH)
    wndClass.lpszClassName  = className

    # register window class
    wndClassAtom = None
    try:
        wndClassAtom = win32gui.RegisterClass(wndClass)
    except Exception as e:
        print (e)
        raise e

    hWindow = win32gui.CreateWindow(
        wndClassAtom,                   #it seems message dispatching only works with the atom, not the class name
        'Python Win32 Window',
        win32con.WS_OVERLAPPEDWINDOW,
        win32con.CW_USEDEFAULT,
        win32con.CW_USEDEFAULT,
        win32con.CW_USEDEFAULT,
        win32con.CW_USEDEFAULT,
        0,
        0,
        hInstance,
        None)

    # Show & update the window
    win32gui.ShowWindow(hWindow, win32con.SW_SHOWNORMAL)
    win32gui.UpdateWindow(hWindow)

    # Dispatch messages
    win32gui.PumpMessages()

# New code: Attempt to change the text 1 second later
    time.sleep(1.0)
    g_str_Text = 'Something new'
    win32gui.ShowWindow(hWindow, win32con.SW_SHOWNORMAL)
    win32gui.UpdateWindow(hWindow)
    win32gui.PumpMessages()

def wndProc(hWnd, message, wParam, lParam):

    if message == win32con.WM_PAINT:
        hDC, paintStruct = win32gui.BeginPaint(hWnd)

        rect = win32gui.GetClientRect(hWnd)
        win32gui.DrawText(
            hDC,
            g_str_Text,
            -1,
            rect,
            win32con.DT_SINGLELINE | win32con.DT_CENTER | win32con.DT_VCENTER)

        win32gui.EndPaint(hWnd, paintStruct)
        return 0

    elif message == win32con.WM_DESTROY:
        print ('Being destroyed')
        win32gui.PostQuitMessage(0)
        return 0

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

if __name__ == '__main__':
    main()

1 个答案:

答案 0 :(得分:0)

您的代码存在3个问题:

  1. [Activestate] win32gui.PumpMessages 阻止直到窗口收到WM_QUIT,这意味着它后面的代码(应该重绘)只会在之后执行 / strong>你关闭了窗口,并且还会生成异常,因为那时hWindow句柄将无效。请注意,此问题还会阻止代码运行到下一个代码。
  2. win32gui.ShowWindowwin32gui.UpdateWindow都不会触发窗口重绘,因为从相关窗口看,没有任何变化(没有窗口区域无效)。强制失效的典型用户操作是窗口大小调整(包括最小化/最大化)。请注意,移动窗口也会重绘它。
  3. g_str_Text = 'Something new' - 这不符合您的想法,它是 Python gotcha 的(变体):它将创建一个新的g_str_Text,它将是本地main,而不是修改wndProc用于绘制窗口的全局
  4. 要解决这些问题,重新绘制窗口的代码应该从新的线程执行。为了做到这一点:

    • 它被放置在一个新功能(customDraw)。
    • 线程在 win32gui.PumpMessages之前开始,睡眠一秒钟,然后更改windowText值(注意global)。
    • 强制通过[ActiveState] win32gui.RedrawWindow重叠窗口(这是[MSDN] RedrawWindow function上的包装器):
      • 在窗口中发出无效的客户区(win32con.RDW_INVALIDATE)。
      • 此外,它还表示应擦除客户区(win32con.RDW_ERASE),以避免将新文本绘制在旧文本上。
      • 向窗口发送WM_PAINT条消息。

    以下是具有上述更改的脚本 - 请注意我做了一些其他小的更改(例如将g_str_Text重命名为windowText),只有一个命名约定 - camelCase (哪个 BTW Python 推荐一个):

    import win32api
    import win32con
    import win32gui
    import time
    import threading
    
    #Code example modified from:
    #Christophe Keller
    #Hello World in Python using Win32
    
    # New code: Define global
    windowText = 'Hello send by Python via Win32!'
    
    def main():
        #get instance handle
        hInstance = win32api.GetModuleHandle()
    
        # the class name
        className = 'SimpleWin32'
    
        # create and initialize window class
        wndClass                = win32gui.WNDCLASS()
        wndClass.style          = win32con.CS_HREDRAW | win32con.CS_VREDRAW
        wndClass.lpfnWndProc    = wndProc
        wndClass.hInstance      = hInstance
        wndClass.hIcon          = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
        wndClass.hCursor        = win32gui.LoadCursor(0, win32con.IDC_ARROW)
        wndClass.hbrBackground  = win32gui.GetStockObject(win32con.WHITE_BRUSH)
        wndClass.lpszClassName  = className
    
        # register window class
        wndClassAtom = None
        try:
            wndClassAtom = win32gui.RegisterClass(wndClass)
        except Exception as e:
            print (e)
            raise e
    
        hWindow = win32gui.CreateWindow(
            wndClassAtom,                   #it seems message dispatching only works with the atom, not the class name
            'Python Win32 Window',
            win32con.WS_OVERLAPPEDWINDOW,
            win32con.CW_USEDEFAULT,
            win32con.CW_USEDEFAULT,
            win32con.CW_USEDEFAULT,
            win32con.CW_USEDEFAULT,
            0,
            0,
            hInstance,
            None)
    
        # Show & update the window
        win32gui.ShowWindow(hWindow, win32con.SW_SHOWNORMAL)
        win32gui.UpdateWindow(hWindow)
    
        # New code: Create and start the thread
        thr = threading.Thread(target=customDraw, args=(hWindow,))
        thr.setDaemon(False)
        thr.start()
    
        # Dispatch messages
        win32gui.PumpMessages()
    
    
    # New code: Attempt to change the text 1 second later
    def customDraw(hWindow):
        global windowText
        time.sleep(1.0)
        windowText = 'Something new'
        win32gui.RedrawWindow(hWindow, None, None, win32con.RDW_INVALIDATE | win32con.RDW_ERASE)
    
    
    def wndProc(hWnd, message, wParam, lParam):
    
        if message == win32con.WM_PAINT:
            hDC, paintStruct = win32gui.BeginPaint(hWnd)
    
            rect = win32gui.GetClientRect(hWnd)
            win32gui.DrawText(
                hDC,
                windowText,
                -1,
                rect,
                win32con.DT_SINGLELINE | win32con.DT_CENTER | win32con.DT_VCENTER)
    
            win32gui.EndPaint(hWnd, paintStruct)
            return 0
    
        elif message == win32con.WM_DESTROY:
            print('Being destroyed')
            win32gui.PostQuitMessage(0)
            return 0
    
        else:
            return win32gui.DefWindowProc(hWnd, message, wParam, lParam)
    
    if __name__ == '__main__':
        main()