如何使用 Python 和 pywinauto 将击键发送到后台 window,而不将 window 带到前台?

How do I send keystrokes to a background window using Python & pywinauto without bringing that window into the foreground?

试过这段代码:

import win32gui
import win32con

windowID = win32gui.FindWindow(None, "testing.pdf - Adobe Acrobat Pro DC")
#win32gui.SetForegroundWindow(windowID)
win32gui.SendMessage(windowID, win32con.WM_KEYDOWN, win32con.VK_NEXT, 0) #VK_NEXT is Page Down button

以上代码仅在我取消注释 SetForegroundWindow 行时才有效。有什么方法可以在不使 window 成为焦点的情况下发送击键吗?

当我的脚本在后台 window 上执行任务时,我希望保持终端处于焦点状态。 (主要是发送不同的击键,如 ctrl+TabpgDn/pgUp 等)

编辑 - 建议的问题没有回答我的问题,因为那里的解决方案使目标 window 成为焦点。我想将目标 window 保留在后台。


感谢@Vasily Ryabov,代码现在甚至可以与 PyWinAuto 一起使用。 :) 这是最终有效的代码:

import time
import pywinauto

app = pywinauto.Application(backend="win32").start(r"C:\Program Files (x86)\Adobe\Acrobat DC\Acrobat\Acrobat.exe")

time.sleep(10)
print('sleeping over')
Wizard = app['testing.pdf - Adobe Acrobat Pro DC']

while True:
    Wizard.send_keystrokes("{VK_NEXT}")
    time.sleep(1)

我认为问题在于我使用的是 type_keys 方法而不是 send_keystrokes。此外,在 win32uia 之间来回切换也帮助我找到了正确工作的那个。对于 Acrobat DC Pro,win32 有效。

感谢@Vasily Ryabov 的出色帮助和精彩的 Python 库 PyWinAuto! :)

如果将 window 置于前台,它会处理所有键盘输入并将键盘输入定向到具有焦点的控件。当 window 不在前台时,其控件的 none 具有活动焦点,并且发送到 window 的键不会自动发送到您要接收输入的控件。

我使用 Foxit reader,但同样适用于 Acrobat 或其他应用程序。我试过这个:

from time import sleep
import win32gui
import win32con


def callback(handle, param):
    s = win32gui.GetClassName(handle)
    try:
        print(f'Sending key to {handle}, {s}')
        win32gui.SendMessage(handle, win32con.WM_KEYDOWN, win32con.VK_NEXT, 0)
        win32gui.SendMessage(handle, win32con.WM_KEYUP, win32con.VK_NEXT, 0)
        sleep(2)
    except Exception:
        print('Exception sending to {handle}, {s}')


window_id = win32gui.FindWindow(None, "my_multipage_doc.pdf - Foxit Reader")
win32gui.EnumChildWindows(window_id, callback, 0)

这产生了很多输出,window 中的每个对象一行,但这是一个相关位:

...
Sending key to 594376, ScrollBar
Sending key to 1904808, ScrollBar
Sending key to 397704, ScrollBar
Sending key to 397598, AfxFrameOrView140su
Sending key to 397580, AfxWnd140su
Sending key to 397734, AfxWnd140su
Sending key to 1971214, AfxWnd140su
Sending key to 856494, FoxitDocWnd
Sending key to 986558, Static
...

VK_NEXT 发送到第一个 Scrollbar 和发送到 FoxitDocWnd 时,查看者都向下滚动了一页。

所以,我改写为:

import win32gui
import win32con


def send_page_down(handle, param):
    if win32gui.GetClassName(handle) == param:
        win32gui.SendMessage(handle, win32con.WM_KEYDOWN, win32con.VK_NEXT, 0)
        win32gui.SendMessage(handle, win32con.WM_KEYUP, win32con.VK_NEXT, 0)


window_id = win32gui.FindWindow(None, "my_multipage_doc.pdf - Foxit Reader")
win32gui.EnumChildWindows(window_id, send_page_down, 'FoxitDocWnd')

这就是您所需要的。

奇怪的是,我试过的其他方法没有用:

import win32gui
import win32con

window_id = win32gui.FindWindow(None, "my_multipage_doc.pdf - Foxit Reader")
viewer_id = win32gui.FindWindowEx(window_id, 0, 'FoxitDocWnd', None)
win32gui.SendMessage(viewer_id , win32con.WM_KEYDOWN, win32con.VK_NEXT, 0)
win32gui.SendMessage(viewer_id , win32con.WM_KEYUP, win32con.VK_NEXT, 0)

但是在尝试获取 viewer_id 时失败了。因此,即使 FoxitDocWnd 在枚举所有子项 windows 时出现,也无法明确找到它。如果你能找到问题所在,那将是一个更好的解决方案。