Tried this code:
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
Above code works only if i de-comment the SetForegroundWindow
line. Is there any way to send keystrokes without bringing the window into focus?
I wish to keep the terminal in focus while my script does tasks on a window in the background. (mainly sending different keystrokes like ctrl+Tab
, pgDn/pgUp
, etc)
Edit - the suggested question does not answer my question because the solution over there brings the target window into focus. I want to keep the target window in the background itself.
Thank you to @Vasily Ryabov, the code now even works with PyWinAuto. :) This is the final code that works:
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)
I think the problem was that i was using type_keys
method instead of send_keystrokes
. Also, going back & forth between win32
& uia
helped me as well to find the one that works correctly. For Acrobat DC Pro, win32
works.
Thank you @Vasily Ryabov for your excellent help & your wonderful Python library PyWinAuto! :)
If you bring the window to the foreground, it handles all keyboard input and it will direct keyboard input to the control that has focus. When the window is not in the foreground, none of its control have active focus and keys sent to the window won't automatically get sent to the control you want to receive the input.
I use Foxit reader, but the same would apply for Acrobat or other applications. I tried this:
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)
That produced a lot of output, one line for each object in the window, but this is a relevant bit:
...
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
...
Both when a VK_NEXT
was sent to the first Scrollbar
as when it was sent to the FoxitDocWnd
, the viewer scrolled down by one page.
So, I rewrote as this:
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')
That does what you need.
Oddly, something else I tried, didn't work:
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)
But that fails when trying to get viewer_id
. So, even though the FoxitDocWnd
shows up when enumerating all child windows, it cannot find it explicitly. If you can find what's wrong with that, that would be a nicer solution.