如何使用Python和pywinauto向后台窗口发送按键,而不将该窗口带到前台?

3

尝试了这段代码:

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行时有效。有没有办法在不使窗口获得焦点的情况下发送按键?我希望在我的脚本在后台执行任务时保持终端处于焦点状态。(主要是发送不同的按键,如ctrl+TabpgDn/pgUp等)。
编辑 - 建议的问题并没有回答我的问题,因为那里的解决方案会将目标窗口置于焦点。我想让目标窗口保持在后台。
感谢@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! :)

这个回答解决了你的问题吗? [Python - 在窗口被最小化或隐藏时使用pywinauto控制窗口] (https://dev59.com/yo_ea4cB1Zd3GeqPQZiB) - Vasily Ryabov
@VasilyRyabov:谢谢,但是当我发送Wizard.type_keys("{VK_NEXT}")时,那个窗口会获得焦点,终端会进入后台。我如何在不失去终端焦点的情况下发送按键?这是我的完整代码:https://pastebin.com/dQH9hYJN - English Rain
你是否尝试使用另一个应用程序进行测试,看看这种情况是否总是发生?也许你发送按键时的应用程序会在接收到键盘输入后将自己置于前台? - Grismar
@EnglishRain 你试过使用 send_charssend_keystrokes 方法吗?type_keys 方法会将窗口置于焦点。抱歉,可能指向了不完整的答案。 - Vasily Ryabov
2
@VasilyRyabov 哦,我找到了最终的解决方案。已经将其添加到问题中。非常感谢您的出色帮助和您精彩的PyWinAuto库。 :) 请继续保持出色的工作! - English Rain
显示剩余8条评论
1个回答

3
如果您将窗口置于前景,则它会处理所有键盘输入,并将键盘输入指向具有焦点的控件。当窗口不在前景时,其控件没有活动焦点,并且发送到该窗口的键不会自动发送到您想要接收输入的控件。
我使用福昕阅读器,但对于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)

那产生了大量输出,每个窗口对象都有一行,但这是一个相关的部分:
...
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
...

当向第一个Scrollbar发送VK_NEXT时,以及向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出现,它也无法明确找到它。如果您能找出问题所在,那将是更好的解决方案。


1
非常感谢您付出的努力和时间。我对您的大脑能力感到惊讶哈哈,我永远无法解决这样的问题,因为它太复杂了。虽然我不得不稍微修改您的代码才能与Adobe Acrobat DC Pro配合使用,因为我一直收到多个同名的”Sending key to 1247202, AVL_AVView“只有第14个和第18个起作用。所以我将代码改为:https://pastebin.com/04EA00a8不过我很惊讶“pywinauto”没有轻松完成这个任务,很多人告诉我它是专门为此构建的。谢谢! - English Rain
未来搜索者的Pastebin链接:https://pastebin.com/ZDfZiX50 - English Rain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接