11

I have a python script which uses selenium to automate web page, drawing focus away from the terminal where user input is required.

Is there anyway in python to switch focus back to the terminal, programmatically?

I will be running my program in the Windows Command Prompt on Windows 7, if it matters, but a cross platform answer would be most useful.


Attempts

Looking at the pywin32 package bindings for the win32 API I have the following:

import win32console
import win32gui
from selenium import webdriver as wd

d = wd.Firefox()
win32gui.SetFocus(win32console.GetConsoleWindow())
win32gui.FlashWindow(win32console.GetConsoleWindow(), False)
input('Should have focus: ')

SetFocus causes the error pywintypes.error: (5, 'SetFocus', 'Access is denied.') due to Microsoft removing the ability to take focus from another application.

FlashWindow appears to do nothing.

Community
  • 1
  • 1
thodic
  • 2,229
  • 1
  • 19
  • 35
  • Isn't javascript prompt an option for user input? – Saifur May 31 '15 at 18:38
  • @Saifur Not really, my script is written in python (so it's tough to get that running in a browser developer console) and the automation involves the closing and opening of multiple browsers all with their own developer consoles. Ideally I am looking for a single python package or script I can execute from python which brings focus back to the terminal. – thodic Jun 01 '15 at 08:00
  • 1
    why not use a VM to host all your selenium-related activities? Maybe use selenium grid or ssh to launch any selenium scripts? – ex-zac-tly Jun 02 '15 at 21:30
  • Executing `win32gui.SetFocus(win32console.GetConsoleWindow())`, I get the following error: "pywintypes.error: (5, 'SetFocus', 'Access is denied.')". It seems you don't.. Any idea why? (I run Python 2.7+) – Apostolos Jun 24 '18 at 07:41

4 Answers4

7

Here is what I came up with that seems to be working.

class WindowManager:
    def __init__(self):
        self._handle = None

    def _window_enum_callback( self, hwnd, wildcard ):
        if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) != None:
            self._handle = hwnd

    #CASE SENSITIVE
    def find_window_wildcard(self, wildcard):
        self._handle = None
        win32gui.EnumWindows(self._window_enum_callback, wildcard)

    def set_foreground(self):
        win32gui.ShowWindow(self._handle, win32con.SW_RESTORE)
        win32gui.SetWindowPos(self._handle,win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE + win32con.SWP_NOSIZE)  
        win32gui.SetWindowPos(self._handle,win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE + win32con.SWP_NOSIZE)  
        win32gui.SetWindowPos(self._handle,win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_SHOWWINDOW + win32con.SWP_NOMOVE + win32con.SWP_NOSIZE)
        shell = win32com.client.Dispatch("WScript.Shell")
        shell.SendKeys('%')
        win32gui.SetForegroundWindow(self._handle)

    def find_and_set(self, search):
        self.find_window_wildcard(search)
        self.set_foreground()

Then to find a window and make it active you can...

w = WindowManager()
w.find_and_set(".*cmd.exe*")

This is in python 2.7, also here are some links I found to explain why you have to go through so much trouble to switch active windows.

win32gui.SetActiveWindow() ERROR : The specified procedure could not be found

Windows 7: how to bring a window to the front no matter what other window has focus?

Community
  • 1
  • 1
SimKev2
  • 188
  • 8
  • 1
    This is inspired! With fantastic justification, it answers my question perfectly. Here have some bounty! – thodic Jun 05 '15 at 08:06
0

This doesn't really answer your question, but the easy solution is to not take the focus away in the first place:

driver = webdriver.PhantomJS()
# ...

The PhantomJS webdriver doesn't have any UI, so does not steal the focus.

Eric
  • 95,302
  • 53
  • 242
  • 374
  • Thanks for the suggestion but the reason I'm using selenium is to automate multiple browsers with UIs. – thodic May 29 '15 at 07:54
  • Ah, so you're using it for cross-browser testing? – Eric May 29 '15 at 07:57
  • Yes and no, I have multiple use cases for my automation, one of which is programmatic tests but I will also be using them for showcasing and visual inspections. – thodic May 29 '15 at 07:59
0

For getting focus, check the comments to this answer.

A cross-platform method could be to use Tkinter for the user GUI, as it has methods to grab and set focus for its windows.

Community
  • 1
  • 1
Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
  • I've attempted to use the win32 API as you suggest but have run into issues, please see my question edit. – thodic Jun 02 '15 at 09:15
  • Could you please explain how I would use Tkinter for my use case, I've looked at the documentation but I can't find a solution. – thodic Jun 02 '15 at 09:17
  • With `Tkinter` you would create your own console-type window to interact with the user, and `Tkinter` has its own methods for grabbing focus. – Ethan Furman Jun 02 '15 at 14:40
0

If you don't care about clearing any figure displayed in the matplotlib frame -- which I believe is normally the case when one wants to get the focus back on the console for user input -- use simply this:

plt.close("all")
Apostolos
  • 3,115
  • 25
  • 28