3

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! :)

English Rain
  • 311
  • 5
  • 13
  • Does this answer your question? [Python - Control window with pywinauto while the window is minimized or hidden](https://stackoverflow.com/questions/32846550/python-control-window-with-pywinauto-while-the-window-is-minimized-or-hidden) – Vasily Ryabov Jul 30 '20 at 08:12
  • @VasilyRyabov: Thank you, but when i send `Wizard.type_keys("{VK_NEXT}")`, that other window comes into focus & the terminal goes into the background. How do i send the keystroke without losing focus from the terminal? Here is my full code: https://pastebin.com/dQH9hYJN – English Rain Jul 31 '20 at 11:42
  • @VasilyRyabov: How do i send the keystroke without bringing that other window into focus/foreground? I want that other window (`testing.pdf - Adobe Acrobat DC`) to stay in the background only. Also, i do not want `testing.pdf - Adobe Acrobat DC` window minimized, i just want it out of focus in background, so that i can further input commands on my terminal. – English Rain Jul 31 '20 at 11:52
  • Have you tested with another application if this always happens? Perhaps the application you're sending the keystroke to puts itself in the foreground whenever it receives keyboard input? – Grismar Jul 31 '20 at 22:32
  • @Grismar - Tried it just now with something as simple as notepad. Everytime the `PgDn` key gets sent, Notepad window comes into focus. You can try it out on your own PC as well, you will see what I mean. Here's the code: https://pastebin.com/qZi81DER – English Rain Aug 01 '20 at 13:17
  • @EnglishRain did you try `send_chars` and `send_keystrokes` methods? Method `type_keys` brings window to the focus. Sorry, maybe pointed to non-full answer. – Vasily Ryabov Aug 03 '20 at 11:15
  • [Remote Execution Guide](https://pywinauto.readthedocs.io/en/latest/remote_execution.html) tells about which methods are useful and something beyond. – Vasily Ryabov Aug 03 '20 at 11:19
  • @VasilyRyabov Thanks, but i get a `AttributeError: Neither GUI element (wrapper) nor wrapper method 'send_keys' were found (typo?)` error. Same error for `send_keystrokes`. I have done `import pywinauto` at the start. Do i need to import something else too? – English Rain Aug 03 '20 at 14:32
  • No, please add the code you're trying to the question. It looks like incorrect search of the element which can be easily fixed. But I can't say what goes wrong without code or at least full traceback. Please edit your question. – Vasily Ryabov Aug 06 '20 at 07:51
  • @VasilyRyabov Ok, i have added the full code & the full error tracebacks to my question. Please have a look whenever you are free, thank you. – English Rain Aug 06 '20 at 09:50
  • 2
    @VasilyRyabov Ooh, i found the final solution. Have added it to the question. Thank you a lot for your excellent help & your wonderful PyWinAuto library. :) Keep up the amazing work! – English Rain Aug 06 '20 at 10:01
  • @EnglishRain you're welcome! BTW, you can use flexible wait `Wizard.wait('ready', timeout=10)` instead of hard-coded `time.sleep(10)`. See chapter [Waiting for Long Operations](https://pywinauto.readthedocs.io/en/latest/wait_long_operations.html). – Vasily Ryabov Aug 07 '20 at 12:14
  • @VasilyRyabov Oh wow, that is excellent! Thank you so much, appreciate your good help! – English Rain Aug 07 '20 at 19:47

1 Answers1

3

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.

Grismar
  • 27,561
  • 4
  • 31
  • 54
  • 1
    Thank you! Highly appreciate your effort & time that went into this. I'm amazed by the powers of your brains haha i would've never been able to figure out such a solution, the problem is too complex for me. I had to modify your code a little bit though for it to work with Adobe Acrobat DC Pro. I kept getting multiple same named `Sending key to 1247202, AVL_AVView` but only the 14th one & 18th one worked. So i changed the code to this: https://pastebin.com/04EA00a8 I'm surprised though that `pywinauto` was not able to do it easily, many ppl told me it is built esp for this. – English Rain Aug 03 '20 at 05:02
  • Permalink to pastebin for future searchers: https://pastebin.com/ZDfZiX50 – English Rain Aug 03 '20 at 05:08